diff --git a/Linphone/Assets.xcassets/lock-simple-bold.imageset/Contents.json b/Linphone/Assets.xcassets/lock-simple-bold.imageset/Contents.json new file mode 100644 index 000000000..9040ecedb --- /dev/null +++ b/Linphone/Assets.xcassets/lock-simple-bold.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "lock-simple-bold.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/lock-simple-bold.imageset/lock-simple-bold.svg b/Linphone/Assets.xcassets/lock-simple-bold.imageset/lock-simple-bold.svg new file mode 100644 index 000000000..f8e860b57 --- /dev/null +++ b/Linphone/Assets.xcassets/lock-simple-bold.imageset/lock-simple-bold.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/lock-simple.imageset/Contents.json b/Linphone/Assets.xcassets/lock-simple.imageset/Contents.json new file mode 100644 index 000000000..aefae6c90 --- /dev/null +++ b/Linphone/Assets.xcassets/lock-simple.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "lock-simple.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/lock-simple.imageset/lock-simple.svg b/Linphone/Assets.xcassets/lock-simple.imageset/lock-simple.svg new file mode 100644 index 000000000..0fc6a8a6e --- /dev/null +++ b/Linphone/Assets.xcassets/lock-simple.imageset/lock-simple.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Localizable/cs.lproj/Localizable.strings b/Linphone/Localizable/cs.lproj/Localizable.strings index 74bc592e1..d4791882d 100644 --- a/Linphone/Localizable/cs.lproj/Localizable.strings +++ b/Linphone/Localizable/cs.lproj/Localizable.strings @@ -414,6 +414,8 @@ "conversation_composing_label_multiple" = "%@ píšou…"; "conversation_composing_label_single" = "%@ píše…"; "conversation_end_to_end_encrypted_bottom_sheet_link" = "https://linphone.org/en/features/#security"; +"conversation_end_to_end_encrypted_event_title" = "Koncově šifrovaná konverzace"; +"conversation_end_to_end_encrypted_event_subtitle" = "Zprávy v této konverzaci jsou koncově šifrovány. Dešifrovat je může pouze váš protějšek."; "conversation_event_device_added" = "Nové zařízení pro %@"; "conversation_event_participant_added" = "%@ se připojil"; "conversation_event_subject_changed" = "Nový předmět: %@"; diff --git a/Linphone/Localizable/en.lproj/Localizable.strings b/Linphone/Localizable/en.lproj/Localizable.strings index a02ce5436..427023c6c 100644 --- a/Linphone/Localizable/en.lproj/Localizable.strings +++ b/Linphone/Localizable/en.lproj/Localizable.strings @@ -201,6 +201,8 @@ "conversation_dialog_set_subject" = "Set conversation subject"; "conversation_dialog_subject_hint" = "Conversation subject"; "conversation_end_to_end_encrypted_bottom_sheet_link" = "https://linphone.org/en/features/#security"; +"conversation_end_to_end_encrypted_event_title" = "End-to-end encrypted conversation"; +"conversation_end_to_end_encrypted_event_subtitle" = "Messages in this conversation are e2e encrypted. Only your correspondent can decrypt them."; "conversation_ephemeral_messages_duration_disabled" = "Disabled"; "conversation_ephemeral_messages_duration_multiple_days" = "%d days"; "conversation_ephemeral_messages_duration_one_day" = "1 day"; diff --git a/Linphone/Localizable/fr.lproj/Localizable.strings b/Linphone/Localizable/fr.lproj/Localizable.strings index 23be93da6..6aaf80373 100644 --- a/Linphone/Localizable/fr.lproj/Localizable.strings +++ b/Linphone/Localizable/fr.lproj/Localizable.strings @@ -201,6 +201,8 @@ "conversation_dialog_set_subject" = "Nommer la conversation"; "conversation_dialog_subject_hint" = "Nom de la conversation"; "conversation_end_to_end_encrypted_bottom_sheet_link" = "https://linphone.org/en/features/#security"; +"conversation_end_to_end_encrypted_event_title" = "Conversation chiffrée de bout en bout"; +"conversation_end_to_end_encrypted_event_subtitle" = "Les messages de cette conversation sont chiffrés de bout en bout. Seul votre correspondant peut les déchiffrer."; "conversation_ephemeral_messages_duration_disabled" = "Désactiver"; "conversation_ephemeral_messages_duration_multiple_days" = "%d jours"; "conversation_ephemeral_messages_duration_one_day" = "1 jour"; diff --git a/Linphone/Localizable/uk.lproj/Localizable.strings b/Linphone/Localizable/uk.lproj/Localizable.strings index 0e3c0e468..505429ed9 100644 --- a/Linphone/Localizable/uk.lproj/Localizable.strings +++ b/Linphone/Localizable/uk.lproj/Localizable.strings @@ -424,6 +424,8 @@ "conversation_composing_label_multiple" = "%@ скомпоновано…"; "conversation_composing_label_single" = "%@ скомпоновано…"; "conversation_end_to_end_encrypted_bottom_sheet_link" = "https://linphone.org/en/features/#security"; +"conversation_end_to_end_encrypted_event_title" = "Наскрізне шифрування розмови"; +"conversation_end_to_end_encrypted_event_subtitle" = "Повідомлення в цій розмові зашифровані за методом електронного шифрування (e2e). Розшифрувати їх може лише ваш співрозмовник."; "conversation_event_admin_set" = "%@ є адміном"; "conversation_event_admin_unset" = "%@ більше не є адміном"; "conversation_event_ephemeral_messages_lifetime_changed" = "Термін дії тимчасових повідомлень тепер %@"; diff --git a/Linphone/UI/Main/Conversations/Fragments/UIList.swift b/Linphone/UI/Main/Conversations/Fragments/UIList.swift index 7179f78a8..8b168a1f2 100644 --- a/Linphone/UI/Main/Conversations/Fragments/UIList.swift +++ b/Linphone/UI/Main/Conversations/Fragments/UIList.swift @@ -135,6 +135,13 @@ struct UIList: UIViewRepresentable { tableView.backgroundColor = UIColor(.white) tableView.scrollsToTop = true + if SharedMainViewModel.shared.displayedConversation != nil && SharedMainViewModel.shared.displayedConversation!.encryptionEnabled { + let footerView = Self.makeFooterView() + footerView.frame = CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 120) + footerView.transform = CGAffineTransformMakeScale(1, -1) + tableView.tableFooterView = footerView + } + // Create the floating UIButton let button = FloatingButton(frame: CGRect(x: 0, y: 0, width: 60, height: 60)) button.translatesAutoresizingMaskIntoConstraints = false @@ -172,6 +179,47 @@ struct UIList: UIViewRepresentable { return containerView } + static func makeFooterView() -> UIView { + let host = UIHostingController( + rootView: + VStack { + HStack { + Image("lock-simple-bold") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.blueInfo500) + .frame(width: 25, height: 25) + .padding(10) + + VStack(spacing: 5) { + Text("conversation_end_to_end_encrypted_event_title") + .foregroundStyle(Color.blueInfo500) + .default_text_style_700(styleSize: 14) + .frame(maxWidth: .infinity, alignment: .leading) + .multilineTextAlignment(.leading) + + Text("conversation_end_to_end_encrypted_event_subtitle") + .foregroundStyle(Color.gray400) + .default_text_style(styleSize: 12) + .frame(maxWidth: .infinity, alignment: .leading) + .multilineTextAlignment(.leading) + } + } + .padding(10) + .cornerRadius(10) + .overlay( + RoundedRectangle(cornerRadius: 10) + .inset(by: 0.5) + .stroke(Color.blueInfo500, lineWidth: 0.5) + ) + .padding(10) + } + .frame(height: 120) + ) + host.view.backgroundColor = .clear + return host.view + } + // func updateUIView(_ tableView: UITableView, context: Context) { func updateUIView(_ uiView: UIView, context: Context) { if let button = uiView.viewWithTag(102) as? FloatingButton { @@ -422,10 +470,11 @@ struct UIList: UIViewRepresentable { } func progressView(_ section: Int) -> UIView? { - if section > parent.conversationViewModel.conversationMessagesSection.count + if section < parent.conversationViewModel.conversationMessagesSection.count && parent.conversationViewModel.conversationMessagesSection[section].rows.count < parent.conversationViewModel.displayedConversationHistorySize { let header = UIHostingController(rootView: ProgressView() + .frame(height: 50) .frame(idealWidth: .infinity, maxWidth: .infinity, alignment: .center) ).view header?.backgroundColor = UIColor(.white) @@ -516,6 +565,18 @@ struct UIList: UIViewRepresentable { return configuration } + + func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + if section == tableView.numberOfSections - 1 { + let contentHeight = tableView.contentSize.height + let tableHeight = tableView.frame.height + let progressViewDisplayed = section < parent.conversationViewModel.conversationMessagesSection.count + && parent.conversationViewModel.conversationMessagesSection[section].rows.count < parent.conversationViewModel.displayedConversationHistorySize + let extraSpace = max(progressViewDisplayed ? 50 : 0, tableHeight - contentHeight - 20) + return extraSpace + } + return 0 + } } }