handle translation

This commit is contained in:
mertalev
2026-02-02 12:36:19 -05:00
parent 0bfe018b52
commit 95981e3b76
8 changed files with 189 additions and 22 deletions

View File

@@ -782,6 +782,8 @@
"client_cert_import": "Import",
"client_cert_import_success_msg": "Client certificate is imported",
"client_cert_invalid_msg": "Invalid certificate file or wrong password",
"client_cert_password_message": "Enter the password for this certificate",
"client_cert_password_title": "Certificate Password",
"client_cert_remove_msg": "Client certificate is removed",
"client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate import/removal is available only before login",
"client_cert_title": "SSL client certificate [EXPERIMENTAL]",

View File

@@ -108,6 +108,43 @@ data class ClientCertData (
override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
data class ClientCertPrompt (
val title: String,
val message: String,
val cancel: String,
val confirm: String
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): ClientCertPrompt {
val title = pigeonVar_list[0] as String
val message = pigeonVar_list[1] as String
val cancel = pigeonVar_list[2] as String
val confirm = pigeonVar_list[3] as String
return ClientCertPrompt(title, message, cancel, confirm)
}
}
fun toList(): List<Any?> {
return listOf(
title,
message,
cancel,
confirm,
)
}
override fun equals(other: Any?): Boolean {
if (other !is ClientCertPrompt) {
return false
}
if (this === other) {
return true
}
return NetworkPigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
private open class NetworkPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
@@ -116,6 +153,11 @@ private open class NetworkPigeonCodec : StandardMessageCodec() {
ClientCertData.fromList(it)
}
}
130.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ClientCertPrompt.fromList(it)
}
}
else -> super.readValueOfType(type, buffer)
}
}
@@ -125,6 +167,10 @@ private open class NetworkPigeonCodec : StandardMessageCodec() {
stream.write(129)
writeValue(stream, value.toList())
}
is ClientCertPrompt -> {
stream.write(130)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
@@ -134,7 +180,7 @@ private open class NetworkPigeonCodec : StandardMessageCodec() {
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface NetworkApi {
fun addCertificate(clientData: ClientCertData, callback: (Result<Unit>) -> Unit)
fun selectCertificate(callback: (Result<ClientCertData>) -> Unit)
fun selectCertificate(promptText: ClientCertPrompt, callback: (Result<ClientCertData>) -> Unit)
fun removeCertificate(callback: (Result<Unit>) -> Unit)
companion object {
@@ -168,8 +214,10 @@ interface NetworkApi {
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NetworkApi.selectCertificate$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
api.selectCertificate{ result: Result<ClientCertData> ->
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val promptTextArg = args[0] as ClientCertPrompt
api.selectCertificate(promptTextArg) { result: Result<ClientCertData> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(NetworkPigeonUtils.wrapError(error))

View File

@@ -54,6 +54,7 @@ private class NetworkApiImpl(private val context: Context) : NetworkApi {
private var activity: Activity? = null
private var pendingCallback: ((Result<ClientCertData>) -> Unit)? = null
private var filePicker: ActivityResultLauncher<Array<String>>? = null
private var promptText: ClientCertPrompt? = null
fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
@@ -85,9 +86,10 @@ private class NetworkApiImpl(private val context: Context) : NetworkApi {
}
}
override fun selectCertificate(callback: (Result<ClientCertData>) -> Unit) {
override fun selectCertificate(promptText: ClientCertPrompt, callback: (Result<ClientCertData>) -> Unit) {
val picker = filePicker ?: return callback(Result.failure(IllegalStateException("No activity")))
pendingCallback = callback
this.promptText = promptText
picker.launch(arrayOf("application/x-pkcs12", "application/x-pem-file"))
}
@@ -106,6 +108,7 @@ private class NetworkApiImpl(private val context: Context) : NetworkApi {
val activity = activity ?: throw IllegalStateException("No activity")
promptForPassword(activity) { password ->
promptText = null
if (password == null) {
callback(Result.failure(OperationCanceledException()))
return@promptForPassword
@@ -143,12 +146,13 @@ private class NetworkApiImpl(private val context: Context) : NetworkApi {
val container = FrameLayout(themedContext).apply { addView(textInputLayout) }
val text = promptText!!
MaterialAlertDialogBuilder(themedContext)
.setTitle("Certificate Password")
.setMessage("Enter the password for this certificate")
.setTitle(text.title)
.setMessage(text.message)
.setView(container)
.setPositiveButton("Import") { _, _ -> callback(editText.text.toString()) }
.setNegativeButton("Cancel") { dialog, _ ->
.setPositiveButton(text.confirm) { _, _ -> callback(editText.text.toString()) }
.setNegativeButton(text.cancel) { dialog, _ ->
dialog.cancel()
callback(null)
}

View File

@@ -139,11 +139,50 @@ struct ClientCertData: Hashable {
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct ClientCertPrompt: Hashable {
var title: String
var message: String
var cancel: String
var confirm: String
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> ClientCertPrompt? {
let title = pigeonVar_list[0] as! String
let message = pigeonVar_list[1] as! String
let cancel = pigeonVar_list[2] as! String
let confirm = pigeonVar_list[3] as! String
return ClientCertPrompt(
title: title,
message: message,
cancel: cancel,
confirm: confirm
)
}
func toList() -> [Any?] {
return [
title,
message,
cancel,
confirm,
]
}
static func == (lhs: ClientCertPrompt, rhs: ClientCertPrompt) -> Bool {
return deepEqualsNetwork(lhs.toList(), rhs.toList()) }
func hash(into hasher: inout Hasher) {
deepHashNetwork(value: toList(), hasher: &hasher)
}
}
private class NetworkPigeonCodecReader: FlutterStandardReader {
override func readValue(ofType type: UInt8) -> Any? {
switch type {
case 129:
return ClientCertData.fromList(self.readValue() as! [Any?])
case 130:
return ClientCertPrompt.fromList(self.readValue() as! [Any?])
default:
return super.readValue(ofType: type)
}
@@ -155,6 +194,9 @@ private class NetworkPigeonCodecWriter: FlutterStandardWriter {
if let value = value as? ClientCertData {
super.writeByte(129)
super.writeValue(value.toList())
} else if let value = value as? ClientCertPrompt {
super.writeByte(130)
super.writeValue(value.toList())
} else {
super.writeValue(value)
}
@@ -179,7 +221,7 @@ class NetworkPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
protocol NetworkApi {
func addCertificate(clientData: ClientCertData, completion: @escaping (Result<Void, Error>) -> Void)
func selectCertificate(completion: @escaping (Result<ClientCertData, Error>) -> Void)
func selectCertificate(promptText: ClientCertPrompt, completion: @escaping (Result<ClientCertData, Error>) -> Void)
func removeCertificate(completion: @escaping (Result<Void, Error>) -> Void)
}
@@ -208,8 +250,10 @@ class NetworkApiSetup {
}
let selectCertificateChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NetworkApi.selectCertificate\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
selectCertificateChannel.setMessageHandler { _, reply in
api.selectCertificate { result in
selectCertificateChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let promptTextArg = args[0] as! ClientCertPrompt
api.selectCertificate(promptText: promptTextArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))

View File

@@ -16,8 +16,8 @@ class NetworkApiImpl: NetworkApi {
self.viewController = viewController
}
func selectCertificate(completion: @escaping (Result<ClientCertData, any Error>) -> Void) {
let importer = CertImporter(completion: { [weak self] result in
func selectCertificate(promptText: ClientCertPrompt, completion: @escaping (Result<ClientCertData, any Error>) -> Void) {
let importer = CertImporter(promptText: promptText, completion: { [weak self] result in
self?.activeImporter = nil
completion(result.map { ClientCertData(data: FlutterStandardTypedData(bytes: $0.0), password: $0.1) })
}, viewController: viewController)
@@ -43,10 +43,12 @@ class NetworkApiImpl: NetworkApi {
}
private class CertImporter: NSObject, UIDocumentPickerDelegate {
private let promptText: ClientCertPrompt
private var completion: ((Result<(Data, String), Error>) -> Void)
private weak var viewController: UIViewController?
init(completion: (@escaping (Result<(Data, String), Error>) -> Void), viewController: UIViewController?) {
init(promptText: ClientCertPrompt, completion: (@escaping (Result<(Data, String), Error>) -> Void), viewController: UIViewController?) {
self.promptText = promptText
self.completion = completion
self.viewController = viewController
}
@@ -95,18 +97,18 @@ private class CertImporter: NSObject, UIDocumentPickerDelegate {
return await withCheckedContinuation { continuation in
let alert = UIAlertController(
title: "Certificate Password",
message: "Enter the password for this certificate",
title: promptText.title,
message: promptText.message,
preferredStyle: .alert
)
alert.addTextField { $0.isSecureTextEntry = true }
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
alert.addAction(UIAlertAction(title: promptText.cancel, style: .cancel) { _ in
continuation.resume(returning: nil)
})
alert.addAction(UIAlertAction(title: "Import", style: .default) { _ in
alert.addAction(UIAlertAction(title: promptText.confirm, style: .default) { _ in
continuation.resume(returning: alert.textFields?.first?.text ?? "")
})

View File

@@ -66,6 +66,52 @@ class ClientCertData {
int get hashCode => Object.hashAll(_toList());
}
class ClientCertPrompt {
ClientCertPrompt({required this.title, required this.message, required this.cancel, required this.confirm});
String title;
String message;
String cancel;
String confirm;
List<Object?> _toList() {
return <Object?>[title, message, cancel, confirm];
}
Object encode() {
return _toList();
}
static ClientCertPrompt decode(Object result) {
result as List<Object?>;
return ClientCertPrompt(
title: result[0]! as String,
message: result[1]! as String,
cancel: result[2]! as String,
confirm: result[3]! as String,
);
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
bool operator ==(Object other) {
if (other is! ClientCertPrompt || other.runtimeType != runtimeType) {
return false;
}
if (identical(this, other)) {
return true;
}
return _deepEquals(encode(), other.encode());
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hashAll(_toList());
}
class _PigeonCodec extends StandardMessageCodec {
const _PigeonCodec();
@override
@@ -76,6 +122,9 @@ class _PigeonCodec extends StandardMessageCodec {
} else if (value is ClientCertData) {
buffer.putUint8(129);
writeValue(buffer, value.encode());
} else if (value is ClientCertPrompt) {
buffer.putUint8(130);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
@@ -86,6 +135,8 @@ class _PigeonCodec extends StandardMessageCodec {
switch (type) {
case 129:
return ClientCertData.decode(readValue(buffer)!);
case 130:
return ClientCertPrompt.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
}
@@ -128,7 +179,7 @@ class NetworkApi {
}
}
Future<ClientCertData> selectCertificate() async {
Future<ClientCertData> selectCertificate(ClientCertPrompt promptText) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.immich_mobile.NetworkApi.selectCertificate$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
@@ -136,7 +187,7 @@ class NetworkApi {
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[promptText]);
final List<Object?>? pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);

View File

@@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/platform/network_api.g.dart';
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
import 'package:immich_mobile/utils/http_ssl_options.dart';
import 'package:logging/logging.dart';
@@ -67,7 +68,13 @@ class _SslClientCertSettingsState extends State<SslClientCertSettings> {
Future<void> importCert() async {
try {
final cert = await networkApi.selectCertificate();
final styling = ClientCertPrompt(
title: "client_cert_password_title".tr(),
message: "client_cert_password_message".tr(),
cancel: "cancel".tr(),
confirm: "confirm".tr(),
);
final cert = await networkApi.selectCertificate(styling);
await SSLClientCertStoreVal(cert.data, cert.password).save();
HttpSSLOptions.apply();
setState(() => isCertExist = true);

View File

@@ -7,6 +7,15 @@ class ClientCertData {
ClientCertData(this.data, this.password);
}
class ClientCertPrompt {
String title;
String message;
String cancel;
String confirm;
ClientCertPrompt(this.title, this.message, this.cancel, this.confirm);
}
@ConfigurePigeon(
PigeonOptions(
dartOut: 'lib/platform/network_api.g.dart',
@@ -25,7 +34,7 @@ abstract class NetworkApi {
void addCertificate(ClientCertData clientData);
@async
ClientCertData selectCertificate();
ClientCertData selectCertificate(ClientCertPrompt promptText);
@async
void removeCertificate();