From 293520cb5df4c20043db1a0793de3c657f9a5ae4 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 15 Mar 2021 14:27:59 +0100 Subject: [PATCH 01/78] Composer Update - Typing and sending a message --- CHANGES.rst | 1 + Riot/Assets/Base.lproj/Main.storyboard | 24 +- .../Images.xcassets/Room/Input/Contents.json | 6 +- .../Contents.json | 23 + .../input_text_background.png | Bin 0 -> 1822 bytes .../input_text_background@2x.png | Bin 0 -> 2975 bytes .../input_text_background@3x.png | Bin 0 -> 2676 bytes .../Input/send_icon.imageset/Contents.json | 23 + .../Input/send_icon.imageset/send_icon.png | Bin 0 -> 960 bytes .../Input/send_icon.imageset/send_icon@2x.png | Bin 0 -> 1745 bytes .../Input/send_icon.imageset/send_icon@3x.png | Bin 0 -> 2561 bytes .../Input/upload_icon.imageset/Contents.json | 3 - .../upload_icon.imageset/upload_icon.png | Bin 332 -> 809 bytes .../upload_icon.imageset/upload_icon@2x.png | Bin 615 -> 1529 bytes .../upload_icon.imageset/upload_icon@3x.png | Bin 918 -> 2180 bytes .../Input/video_call.imageset/Contents.json | 23 + .../Input/video_call.imageset/video_call.png | Bin 0 -> 356 bytes .../video_call.imageset/video_call@2x.png | Bin 0 -> 512 bytes .../video_call.imageset/video_call@3x.png | Bin 0 -> 756 bytes Riot/Assets/en.lproj/Vector.strings | 3 + Riot/Generated/Images.swift | 3 + Riot/Generated/Strings.swift | 12 + .../Room/RoomInfo/RoomInfoCoordinator.swift | 45 +- .../RoomInfoCoordinatorBridgePresenter.swift | 10 + .../RoomInfoList/RoomInfoListViewAction.swift | 10 +- .../RoomInfoListViewController.swift | 14 +- Riot/Modules/Room/RoomViewController.h | 1 + Riot/Modules/Room/RoomViewController.m | 1651 ++++++++--------- Riot/Modules/Room/RoomViewController.xib | 32 +- .../Views/Activities/RoomActivitiesView.m | 3 + .../Views/Activities/RoomActivitiesView.xib | 2 +- .../InputToolbar/KeyboardGrowingTextView.m | 6 + .../Views/InputToolbar/RoomInputToolbarView.h | 19 +- .../Views/InputToolbar/RoomInputToolbarView.m | 159 +- .../InputToolbar/RoomInputToolbarView.xib | 120 +- Riot/Modules/Room/Views/Title/RoomTitleView.h | 11 +- Riot/Modules/Room/Views/Title/RoomTitleView.m | 36 +- .../Room/Views/Title/RoomTitleView.xib | 71 +- Riot/SupportingFiles/Riot-Bridging-Header.h | 2 + 39 files changed, 1103 insertions(+), 1210 deletions(-) create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/input_text_background.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/input_text_background@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/input_text_background@3x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/send_icon.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Input/send_icon.imageset/send_icon.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/send_icon.imageset/send_icon@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/send_icon.imageset/send_icon@3x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/video_call.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Input/video_call.imageset/video_call.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/video_call.imageset/video_call@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/video_call.imageset/video_call@3x.png diff --git a/CHANGES.rst b/CHANGES.rst index df35967f7..6f78f7429 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,7 @@ Changes to be released in next version ✨ Features * Improve the status of send messages (sending, sent, received, failed) (#4014) * Retrying & deleting failed messages (#4013) + * Composer Update - Typing and sending a message (#4085) 🙌 Improvements * diff --git a/Riot/Assets/Base.lproj/Main.storyboard b/Riot/Assets/Base.lproj/Main.storyboard index b4e354b0f..ff80d131e 100644 --- a/Riot/Assets/Base.lproj/Main.storyboard +++ b/Riot/Assets/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -34,14 +34,6 @@ - - - - - - - - @@ -96,7 +88,7 @@ - + @@ -320,7 +312,7 @@ - + @@ -589,11 +581,10 @@ - - + + - @@ -602,5 +593,8 @@ + + + diff --git a/Riot/Assets/Images.xcassets/Room/Input/Contents.json b/Riot/Assets/Images.xcassets/Room/Input/Contents.json index da4a164c9..73c00596a 100644 --- a/Riot/Assets/Images.xcassets/Room/Input/Contents.json +++ b/Riot/Assets/Images.xcassets/Room/Input/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/Contents.json new file mode 100644 index 000000000..30afe661d --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "input_text_background.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "input_text_background@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "input_text_background@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/input_text_background.png b/Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/input_text_background.png new file mode 100644 index 0000000000000000000000000000000000000000..45de94894ecb3e1e61dd69d643f538b35d2085fd GIT binary patch literal 1822 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq*&4&eH|GXHuiJ>Nn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N#&bHNrE^*Ox&H$mU>R zV`O7s2C^6#n1F;3l+6faOEG{<2C^8!7^UItI7SVG`u0o)7O0vuAPoW@Kn&6Yp_vvi z!BxmDV1~0rKuU!#=4}H~oCO|{#S9GG!XV7ZFl&wk0|V2e%#etZ2wxwoh+i#(Mch>H3D2mX`VkM*2oZ zx=P7{9O-#x!EwNQn0$BtH5OxaJiqxD4m(1MMJg_e<4Q%wWN}y|W&d|cfNHeS2c-g}Q~^nHU{PS2v*WVSho@gVF1^~SS_TGYF;5rA z5D(t1VQ0NV0!8ef-?28fTCrH!cUc;%aqUin&M+s9t8U_Nm)I+<_{V(Lwcg3aNu-J8 zs*_d>N1)y+*KQ_Pp~OED`(^E)pRN5q@7(20N~Wb>4m`eBe&6zW`M&qg0{k5w{#ayR-^uO>W`{fm4~VwS3(57po&Y zT9cWMz4^rFD5@N(+O}pDhcEk_dF>%Ge6B~&Xrh&u@kI>sPNnomLn;cZSv0 zAFPsTn~x{2{o3&}{(0*<+cT?D%XBAhuiMO=SC;d8(XG9A8RSmulyN&O+P3OX6oZM4 zjf|(-1Epy_vpMFb%RPKebT>UNlcE6?W6|M9)n=b8|GDR)+iTi2p&>i8G zTq}h>+BkZ>G~#mn8^_B1RJq;zjfL(})(0s}tl8=5=^JD;Un-fOJ<}|GVA9v#uvaf$ z+z9p;OPaYqiC1vlq~bT0_fLx~%CX-h(mN@2`IfI|tF^e)zbnUZtT){`LnOTA6A%B* z=dXpnuiY8j@#t;}t23jPr*@l0l<4c!*Ivhsz1QyMls&fnOUXAA!+6!>lB=9zo(kOG zo*>`jcP7q)r{Qf$iqUD#opbVyl4ALO{&WlNuUT8sd>}Teb+>wY*zIk?HZNwjg!`Y< zjo)(QyUcFwYctlhZtOa^|C5#3BG}A7sOa4Bv>;?1h(9m z!?G-jah6l7cFvq>UnLgpTO+F4_+aw$r*iYH0xdO;e$98Roj0T7nu}NT+^4&vR;tZh znUK0=cfuay^*QW1>5a?}7i2uS?tZK(hVjh4k7rl>S+vLCfvxpqj~d|vYiGY__iRh> UIpfeU2UO~Ny85}Sb4q9e0L_4p!2kdN literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/input_text_background@2x.png b/Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/input_text_background@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9994131230d6c9517e4b8b71b59a8d840d6707af GIT binary patch literal 2975 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 literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Input/send_icon.imageset/send_icon@3x.png b/Riot/Assets/Images.xcassets/Room/Input/send_icon.imageset/send_icon@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..4e3719595a010183f8d343d3165f2456fb2fee02 GIT binary patch literal 2561 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 X{+Ezuej0eF00000NkvXXu0mjfRp+Kt literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/Contents.json index b3ad81cfb..776550e12 100644 --- a/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/Contents.json +++ b/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/Contents.json @@ -19,8 +19,5 @@ "info" : { "author" : "xcode", "version" : 1 - }, - "properties" : { - "template-rendering-intent" : "template" } } diff --git a/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/upload_icon.png b/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/upload_icon.png index 8364d79ce1cc1ae77194e534230882d3583ec8ca..3ff1aefaeb19ece92ffc993cb49f086ae865e6f7 100644 GIT binary patch delta 771 zcmV+e1N{8V0;vWeiBL{Q4GJ0x0000DNk~Le0000a0000a2nGNE0O0_bn2{kge*(Tq zL_t(|0nM0AZxTTm$De145D`K(p%y~JO*naQZys!WSMT*M`Vl;7_2fan0DJ(hP1^K| zXuOr0^hCK~4K$EgEQoeHJL5bc*ft>h!Y=$S4D5vcXP);#ut>SujE95%RnA2U5j4gy zBn5=&88T=K0dPcGluHnV@C zplY+Fp&>UBFa{1l#F`Zjrzy;{EI=}Vjqx=g;i_x=AO=MRTyZD972y^|stt_o5olYtqrAn?^--4oP19S5c=eEzbuwOd>Pn~*<|hM^z? z=EziqPkSr-2bCpoz~3qWjKbWld}9%f>Lk;Fufd!U4)|A@B8x_E)YaONGfs&LwX;522@pHVcD751b~n|CL_)UHLQex1e|h>GHu+!cC)=v= zo=MhLy;v==9C=LZVPcdLH(^E?+{g{qLqW;-Vu^-0@EFxC=97tk)9j_v(PcbKvp9GJ znX3DPXD|PDh2EgZ)okXnKarTu*@QunpfqsZ=g+94tbulSr3pIrzRv58A86M;Q9YPlk{gdwqavJDXFBlb1{;pB4_|Py>9R| zWll{iBL{Q4GJ0x0000DNk~Le0000O0000O2nGNE0N{5$_>mzre*oc0 zL_t(|0lij<4TCTYJeYtHyh%rBHz6CqCUk{P5OsrY02?qu8mo>JwE_%{Iq8BF+2O8NvN^)dG^}O614VB9hV#Ic;Gm|6?V8(_#8;1bg1MTl=f3HUOG@MXi2BW2^e8_ zi)OB1f*oEQ;0ZfCc;Opl)gnp`euo1oZHK%Rb~1hJ7_MM|#lh1T5bv=82g8w>K+w+! orpF(D7p(~7RgJROW8GqYZQ>`zMku^UJG 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 delta 881 zcmV-%1CIQJ5taubiBL{Q4GJ0x0000DNk~Le0000;0000;2nGNE09Ea?N0A{ke*-H? zL_t(|0qvZNb=oi#hCgS5bb=}qBpXy4G#i8sl1_j-LD~({5xPK2H%J!1WPy+gaDBl# zalqnWkR{pCcg_#tameN8UcDj1FXxq1r7x8(Dt)eWRX?V-p1Y{@TI;D&QD4s(8Dh$9 zrN5Pyl}ZPa*ScZX)WOg+rR-V8f2=_u#!PXSDP2_5|636uA&s#a6%CqAP@#=!GfEQ> z$(noOGiF=&b*x@d#F;hwvPt0yO&o1aS4qs*^vo?#4qSKi#1tq;qA89UL+13BN!LI* zQfW>^G0X<}P%Sy3cayP00hA+G!I9&nAq)at>7I^^x!tiz(}4nl1{*>Je=VhU#x7IB zEE#;S^r_Z2{G&La>z5~qlfFN_w!rcLaY(PWx51tU2&%LMbsi{|oIMnQg0c}JmUmgy zY=b5YcoEzoV$Q_eXc}}w!tmAnk=56VO?ibPv)sS0{Q5?E(^r#0n#48}xiAK$^4eux zW+t^mrm_%YiOH7tg=CN>f6>PJ9Dx{gMZ$&_gA@rH>kPUiVPkEO_g%*Uap~G1vt*HV zl#Q;h)5}tct4UsO8*)dG(oheGy> z=A@ge_ftq4BnBzM660Jbgz-{<=FKi+;m&s9}aMi>gtVsabP0l+XkKWNVKU literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Input/video_call.imageset/video_call@2x.png b/Riot/Assets/Images.xcassets/Room/Input/video_call.imageset/video_call@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5e4d6597c4a0c96d9284a98be255571894ee8f24 GIT binary patch literal 512 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?2=RS;(M3{v?36l5$8 za(7}_cTVOdki(Mh=<_9rXP!T@G9U8a6DsK zu%NkwA*5llNJFrb)&`d;#S5AOn!Hcgrm1gM@e4>fV5GFWqmuo@Ddoz2<^R0?F?21` zIHlTbA6{AI+UCYF=Y&?H@F50mmlN0iz7&=@IlaNAcQHqNs_5bWL2JC<9W>Bgp!%fF z#`JxMvhw+9ciX20nE2=0n#KzXAC!nn5-2?KKwM={Ud*3KWlG(~<(@}`mCu$Ru=kv- zR8=RjaBJk&ZDPeu#*NRu8@Jqlw!f3NrEkuJV+rRDY@IChjc-Tgbm5}=GuX8coZUU+ z>)Q*Ql4}}fRjz-Um+af^3=MW z&x@ACnm(*{FjBUjQKni@y=Hfzopr0H21^vj6}9 literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Input/video_call.imageset/video_call@3x.png b/Riot/Assets/Images.xcassets/Room/Input/video_call.imageset/video_call@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..adaaf0a70a3043b04e8b8be0a157b3b677d763a0 GIT binary patch literal 756 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!oCO|{#S9FJ79h;%I?XTvD9BhG zN}VonM|uBh!ht6@moU9h&}uBdko(Q$k%jO~VS`T|yZ)x-oHTmz zaDH-l@_R-n4#gG$ATe*&sl8U6iyzEKU03W=xf?LO*(+ete#Y8^)`ZfVu?mhXq`>ePEqos!fpU{M6mL~+64_qCVoZ;nr;_C1$sbaNhX~%>F z|9dq@-b|S4xib6sk8hRp<4s>^`JH*XV6nru{vWMrW_xrl%|893?e!&g5ss6$^|r0` ze%AB)()rl+pLd;If4+U2-rgsvTTcA8jrTXTear9x zhiPgyYrRk00+z^$e&!`-+eNcF4o6DvR$ZuaTklxwslG3Z(-c!5z9{;heP?^+Z;d?n zUn|XGd!HT4bJQrD-!^r(tk&#(>{Uw&7A?>+Ry!kW9vF3Abf#hX3`=lQ0TL#c^o7Ll VY8WN1wE-ql22WQ%mvv4FO#s|%P*MN@ literal 0 HcmV?d00001 diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 49354fd09..cdadc4fc8 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -389,6 +389,7 @@ Tap the + to start adding people."; "room_accessibility_integrations" = "Integrations"; "room_accessibility_upload" = "Upload"; "room_accessibility_call" = "Call"; +"room_accessibility_video_call" = "Video Call"; "room_accessibility_hangup" = "Hang up"; "room_place_voice_call" = "Voice call"; "room_open_dialpad" = "Dial pad"; @@ -684,6 +685,8 @@ Tap the + to start adding people."; "room_details_title_for_dm" = "Details"; "room_details_people" = "Members"; "room_details_files" = "Uploads"; +"room_details_search" = "Search room"; +"room_details_integrations" = "Integrations"; "room_details_settings" = "Settings"; "room_details_photo" = "Room Photo"; "room_details_photo_for_dm" = "Photo"; diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift index 5793e9185..6690852cf 100644 --- a/Riot/Generated/Images.swift +++ b/Riot/Generated/Images.swift @@ -111,7 +111,10 @@ internal enum Asset { internal static let roomContextMenuMore = ImageAsset(name: "room_context_menu_more") internal static let roomContextMenuReply = ImageAsset(name: "room_context_menu_reply") internal static let roomContextMenuRetry = ImageAsset(name: "room_context_menu_retry") + internal static let inputTextBackground = ImageAsset(name: "input_text_background") + internal static let sendIcon = ImageAsset(name: "send_icon") internal static let uploadIcon = ImageAsset(name: "upload_icon") + internal static let videoCall = ImageAsset(name: "video_call") internal static let voiceCallHangonIcon = ImageAsset(name: "voice_call_hangon_icon") internal static let voiceCallHangupIcon = ImageAsset(name: "voice_call_hangup_icon") internal static let addMemberFloatingAction = ImageAsset(name: "add_member_floating_action") diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 218303eaf..2ef199678 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -2294,6 +2294,10 @@ internal enum VectorL10n { internal static var roomAccessibilityUpload: String { return VectorL10n.tr("Vector", "room_accessibility_upload") } + /// Video Call + internal static var roomAccessibilityVideoCall: String { + return VectorL10n.tr("Vector", "room_accessibility_video_call") + } /// Scroll to bottom internal static var roomAccessiblityScrollToBottom: String { return VectorL10n.tr("Vector", "room_accessiblity_scroll_to_bottom") @@ -2614,6 +2618,10 @@ internal enum VectorL10n { internal static var roomDetailsHistorySectionPromptTitle: String { return VectorL10n.tr("Vector", "room_details_history_section_prompt_title") } + /// Integrations + internal static var roomDetailsIntegrations: String { + return VectorL10n.tr("Vector", "room_details_integrations") + } /// Low priority internal static var roomDetailsLowPriorityTag: String { return VectorL10n.tr("Vector", "room_details_low_priority_tag") @@ -2666,6 +2674,10 @@ internal enum VectorL10n { internal static var roomDetailsSaveChangesPrompt: String { return VectorL10n.tr("Vector", "room_details_save_changes_prompt") } + /// Search room + internal static var roomDetailsSearch: String { + return VectorL10n.tr("Vector", "room_details_search") + } /// Set as Main Address internal static var roomDetailsSetMainAddress: String { return VectorL10n.tr("Vector", "room_details_set_main_address") diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift b/Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift index 3d9b179dc..f1efb7577 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift @@ -86,8 +86,13 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType { // MARK: - Setup - init(parameters: RoomInfoCoordinatorParameters) { - self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController()) + init(parameters: RoomInfoCoordinatorParameters, navigationRouter: NavigationRouterType? = nil) { + if let navigationRouter = navigationRouter { + self.navigationRouter = navigationRouter + } else { + self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController()) + } + self.session = parameters.session self.room = parameters.room self.initialSection = parameters.initialSection @@ -102,8 +107,12 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType { self.add(childCoordinator: rootCoordinator) - self.navigationRouter.setRootModule(rootCoordinator) - + if self.navigationRouter.modules.isEmpty == false { + self.navigationRouter.push(rootCoordinator.toPresentable(), animated: true, popCompletion: nil) + } else { + self.navigationRouter.setRootModule(rootCoordinator) + } + switch initialSection { case .addParticipants: self.showRoomDetails(with: .members, animated: false) @@ -129,13 +138,29 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType { } private func showRoomDetails(with target: RoomInfoListTarget, animated: Bool) { - segmentedViewController.selectedIndex = target.tabIndex - - if case .settings(let roomSettingsField) = target { - roomSettingsViewController?.selectedRoomSettingsField = roomSettingsField + switch target { + case .integrations: + if let modularVC = IntegrationManagerViewController(for: session, inRoom: room.roomId, screen: kIntegrationManagerMainScreen, widgetId: nil) { + navigationRouter.present(modularVC, animated: true) + } + case .search: + MXKRoomDataSourceManager.sharedManager(forMatrixSession: session)?.roomDataSource(forRoom: self.room.roomId, create: false, onComplete: { (roomDataSource) in + guard let dataSource = roomDataSource else { return } + let storyboard = UIStoryboard(name: "Main", bundle: nil) + if let search = storyboard.instantiateViewController(withIdentifier: "RoomSearch") as? RoomSearchViewController { + search.roomDataSource = dataSource + self.navigationRouter.push(search, animated: animated, popCompletion: nil) + } + }) + default: + segmentedViewController.selectedIndex = target.tabIndex + + if case .settings(let roomSettingsField) = target { + roomSettingsViewController?.selectedRoomSettingsField = roomSettingsField + } + + navigationRouter.push(segmentedViewController, animated: animated, popCompletion: nil) } - - navigationRouter.push(segmentedViewController, animated: animated, popCompletion: nil) } } diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoCoordinatorBridgePresenter.swift b/Riot/Modules/Room/RoomInfo/RoomInfoCoordinatorBridgePresenter.swift index 3503eb783..5348b9ab1 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoCoordinatorBridgePresenter.swift @@ -63,6 +63,16 @@ final class RoomInfoCoordinatorBridgePresenter: NSObject { self.coordinator = roomInfoCoordinator } + func push(from navigationController: UINavigationController, animated: Bool) { + let navigationRouter = NavigationRouter(navigationController: navigationController) + + let roomInfoCoordinator = RoomInfoCoordinator(parameters: self.coordinatorParameters, navigationRouter: navigationRouter) + roomInfoCoordinator.delegate = self + roomInfoCoordinator.start() + + self.coordinator = roomInfoCoordinator + } + func dismiss(animated: Bool, completion: (() -> Void)?) { guard let coordinator = self.coordinator else { return diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewAction.swift b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewAction.swift index 6d50c1551..945d2844a 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewAction.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewAction.swift @@ -18,11 +18,13 @@ import Foundation -enum RoomInfoListTarget { +enum RoomInfoListTarget: Equatable { case settings(_ field: RoomSettingsViewControllerField = RoomSettingsViewControllerFieldNone) case members case uploads - + case integrations + case search + var tabIndex: UInt { let tabIndex: UInt @@ -33,6 +35,10 @@ enum RoomInfoListTarget { tabIndex = 1 case .settings: tabIndex = 2 + case .integrations: + tabIndex = 3 + case .search: + tabIndex = 4 } return tabIndex diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift index 0ce7f656d..67a75586a 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift @@ -157,11 +157,19 @@ final class RoomInfoListViewController: UIViewController { let rowUploads = Row(type: .default, icon: Asset.Images.scrollup.image, text: VectorL10n.roomDetailsFiles, accessoryType: .disclosureIndicator) { self.viewModel.process(viewAction: .navigate(target: .uploads)) } + let rowSearch = Row(type: .default, icon: Asset.Images.searchIcon.image, text: VectorL10n.roomDetailsSearch, accessoryType: .disclosureIndicator) { + self.viewModel.process(viewAction: .navigate(target: .search)) + } + let rowIntegrations = Row(type: .default, icon: Asset.Images.integrationsIcon.image, text: VectorL10n.roomDetailsIntegrations, accessoryType: .disclosureIndicator) { + self.viewModel.process(viewAction: .navigate(target: .integrations)) + } let sectionSettings = Section(header: VectorL10n.roomInfoListSectionOther, rows: [rowSettings, + rowIntegrations, rowMembers, - rowUploads], + rowUploads, + rowSearch], footer: nil) let leaveTitle = viewData.basicInfoViewData.isDirect ? @@ -205,7 +213,9 @@ final class RoomInfoListViewController: UIViewController { } private func setupViews() { - self.navigationItem.rightBarButtonItem = MXKBarButtonItem(customView: closeButton) + if navigationController?.viewControllers.count ?? 0 <= 1 { + self.navigationItem.rightBarButtonItem = MXKBarButtonItem(customView: closeButton) + } self.title = "" diff --git a/Riot/Modules/Room/RoomViewController.h b/Riot/Modules/Room/RoomViewController.h index 4e9864bfd..f8bbc484f 100644 --- a/Riot/Modules/Room/RoomViewController.h +++ b/Riot/Modules/Room/RoomViewController.h @@ -46,6 +46,7 @@ extern NSNotificationName const RoomCallTileTappedNotification; @property (weak, nonatomic) IBOutlet UILabel *jumpToLastUnreadLabel; @property (weak, nonatomic) IBOutlet UIButton *resetReadMarkerButton; @property (weak, nonatomic) IBOutlet UIView *jumpToLastUnreadBannerSeparatorView; +@property (weak, nonatomic) IBOutlet UIVisualEffectView *inputBackgroundView; /** Preview data for a room invitation received by email, or a link to a room. diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 611a64390..53ad7d9ba 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -157,8 +157,6 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo NSUInteger missedHighlightCount; UIBarButtonItem *missedDiscussionsButton; UILabel *missedDiscussionsBadgeLabel; - UIView *missedDiscussionsBadgeLabelBgView; - UIView *missedDiscussionsBarButtonCustomView; // Potential encryption details view. EncryptionInfoView *encryptionInfoView; @@ -194,9 +192,6 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo // Tell whether the view controller is appeared or not. BOOL isAppeared; - - // The right bar button items back up. - NSArray *rightBarButtonItems; // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. id kThemeServiceDidChangeThemeNotificationObserver; @@ -231,6 +226,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo @property (nonatomic, strong) RoomCreationModalCoordinatorBridgePresenter *roomCreationModalCoordinatorBridgePresenter; @property (nonatomic, strong) RoomInfoCoordinatorBridgePresenter *roomInfoCoordinatorBridgePresenter; @property (nonatomic, strong) CustomSizedPresentationController *customSizedPresentationController; +@property (nonatomic, getter=isActivitiesViewExpanded) BOOL activitiesViewExpanded; @end @@ -371,7 +367,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo // call cells [self.bubblesTableView registerClass:RoomDirectCallStatusBubbleCell.class forCellReuseIdentifier:RoomDirectCallStatusBubbleCell.defaultReuseIdentifier]; - [self.bubblesTableView registerClass:RoomCreationIntroCell.class forCellReuseIdentifier:RoomCreationIntroCell.defaultReuseIdentifier]; + [self.bubblesTableView registerClass:RoomCreationIntroCell.class forCellReuseIdentifier:RoomCreationIntroCell.defaultReuseIdentifier]; [self vc_removeBackTitle]; @@ -388,13 +384,6 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo // Custom the event details view [self setEventDetailsViewClass:EventDetailsView.class]; - // Update navigation bar items - for (UIBarButtonItem *barButtonItem in self.navigationItem.rightBarButtonItems) - { - barButtonItem.target = self; - barButtonItem.action = @selector(onButtonPressed:); - } - // Prepare missed dicussion badge (if any) self.showMissedDiscussionsBadge = _showMissedDiscussionsBadge; @@ -420,6 +409,15 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo }]; [self userInterfaceThemeDidChange]; + + if ([ThemeService.shared.themeId isEqualToString:@"light"]) + { + self.inputBackgroundView.effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]; + } + else if ([ThemeService.shared.themeId isEqualToString:@"dark"] || [ThemeService.shared.themeId isEqualToString:@"black"]) + { + self.inputBackgroundView.effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]; + } } - (void)userInterfaceThemeDidChange @@ -436,7 +434,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { [ThemeService.shared.theme applyStyleOnNavigationBar:mainNavigationController.navigationBar]; } - + // Keep navigation bar transparent in some cases if (!self.previewHeaderContainer.hidden) { @@ -469,7 +467,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { [self.bubblesTableView reloadData]; } - + [self setNeedsStatusBarAppearanceUpdate]; } @@ -487,7 +485,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - + // Screen tracking [[Analytics sharedInstance] trackScreen:@"ChatRoom"]; @@ -558,7 +556,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [self removeWidgetNotificationsListeners]; [self removeTombstoneEventNotificationsListener]; [self removeMXSessionStateChangeNotificationsListener]; - + // Re-enable the read marker display, and disable its update. self.roomDataSource.showReadMarker = YES; self.updateRoomReadMarker = NO; @@ -589,9 +587,9 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo // Observe missed notifications mxRoomSummaryDidChangeObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXRoomSummaryDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - + MXRoomSummary *roomSummary = notif.object; - + if ([roomSummary.roomId isEqualToString:self.roomDataSource.roomId]) { [self refreshMissedDiscussionsCount:NO]; @@ -622,7 +620,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [[NSNotificationCenter defaultCenter] removeObserver:mxRoomSummaryDidChangeObserver]; mxRoomSummaryDidChangeObserver = nil; } - + if (mxEventDidDecryptNotificationObserver) { [[NSNotificationCenter defaultCenter] removeObserver:mxEventDidDecryptNotificationObserver]; @@ -659,7 +657,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo eventDetailsView = nil; } } - + // Check whether the preview header is visible if (previewHeader) { @@ -675,17 +673,17 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo previewHeader.mainHeaderBackgroundHeightConstraint.constant = mainHeaderBackgroundHeight; // Force the layout of previewHeader to update the position of 'bottomBorderView' which - // is used to define the actual height of the preview container. + // is used to define the actual of the preview container. [previewHeader layoutIfNeeded]; } } - + self.edgesForExtendedLayout = UIRectEdgeAll; - + // Adjust the top constraint of the bubbles table CGRect frame = previewHeader.bottomBorderView.frame; self.previewHeaderContainerHeightConstraint.constant = frame.origin.y + frame.size.height; - + self.bubblesTableViewTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant - self.bubblesTableView.mxk_adjustedContentInset.top; self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant; } @@ -694,7 +692,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo // In non expanded header mode, the navigation bar is opaque // The table view must not display behind it self.edgesForExtendedLayout = UIRectEdgeLeft | UIRectEdgeBottom | UIRectEdgeRight; - + self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.bubblesTableView.mxk_adjustedContentInset.top; // no expanded } @@ -772,25 +770,25 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { BOOL canScroll = YES; - + // Scroll by one page CGFloat tableViewHeight = self.bubblesTableView.frame.size.height; - + CGPoint offset = self.bubblesTableView.contentOffset; switch (direction) { case UIAccessibilityScrollDirectionUp: offset.y -= tableViewHeight; break; - + case UIAccessibilityScrollDirectionDown: offset.y += tableViewHeight; break; - + default: break; } - + if (offset.y < 0 && ![self.roomDataSource.timeline canPaginate:MXTimelineDirectionBackwards]) { // Can't paginate more. Let's stick on the first item @@ -810,9 +808,9 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { // Disable VoiceOver while scrolling self.bubblesTableView.accessibilityElementsHidden = YES; - + [self setBubbleTableViewContentOffset:offset animated:NO]; - + NSEnumerator *cells; if (direction == UIAccessibilityScrollDirectionUp) { @@ -823,13 +821,13 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo cells = self.bubblesTableView.visibleCells.reverseObjectEnumerator; } UIView *cell = [self firstCellWithAccessibilityDataInCells:cells]; - + self.bubblesTableView.accessibilityElementsHidden = NO; - + // Force VoiceOver to focus on a visible item UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, cell); } - + // If we cannot scroll, let VoiceOver indicates the border return canScroll; } @@ -837,7 +835,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (UIView*)firstCellWithAccessibilityDataInCells:(NSEnumerator*)cells { UIView *view; - + for (UITableViewCell *cell in cells) { if (![cell isKindOfClass:[RoomEmptyBubbleCell class]]) @@ -846,7 +844,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo break; } } - + return view; } @@ -894,7 +892,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo if (self.roomDataSource) { [self listenToServerNotices]; - + self.eventsAcknowledgementEnabled = YES; // Set room title view @@ -1000,7 +998,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo Class roomInputToolbarViewClass = RoomInputToolbarView.class; BOOL shouldDismissContextualMenu = NO; - + // Check the user has enough power to post message if (self.roomDataSource.roomState) { @@ -1022,7 +1020,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo shouldDismissContextualMenu = YES; } } - + // Do not show toolbar in case of preview if (self.isRoomPreview) { @@ -1047,7 +1045,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (CGFloat)inputToolbarHeight { CGFloat height = 0; - + if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { height = ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarHeightConstraint.constant; @@ -1056,7 +1054,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { height = ((DisabledRoomInputToolbarView*)self.inputToolbarView).mainToolbarMinHeightConstraint.constant; } - + return height; } @@ -1069,6 +1067,11 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo } [super setRoomActivitiesViewClass:roomActivitiesViewClass]; + + if (!self.isActivitiesViewExpanded) + { + self.roomActivitiesContainerHeightConstraint.constant = 0; + } } - (BOOL)isIRCStyleCommand:(NSString*)string @@ -1176,9 +1179,9 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo // Let the datasource send it and manage the local echo [self.roomDataSource sendTextMessage:msgTxt success:nil failure:^(NSError *error) { - // Just log the error. The message will be displayed in red in the room history - NSLog(@"[MXKRoomViewController] sendTextMessage failed."); - }]; + // Just log the error. The message will be displayed in red in the room history + NSLog(@"[MXKRoomViewController] sendTextMessage failed."); + }]; } [self cancelEventSelection]; @@ -1186,19 +1189,22 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)setRoomTitleViewClass:(Class)roomTitleViewClass { - [super setRoomTitleViewClass:roomTitleViewClass]; + // Sanity check: accept only MXKRoomTitleView classes or sub-classes + NSParameterAssert([roomTitleViewClass isSubclassOfClass:MXKRoomTitleView.class]); + + MXKRoomTitleView *titleView = [roomTitleViewClass roomTitleView]; + [self setValue:titleView forKey:@"titleView"]; + titleView.delegate = self; + titleView.mxRoom = self.roomDataSource.room; + self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:titleView]; + + [self updateViewControllerAppearanceOnRoomDataSourceState]; [self updateTitleViewEncryptionDecoration]; } - (void)destroy { - rightBarButtonItems = nil; - for (UIBarButtonItem *barButtonItem in self.navigationItem.rightBarButtonItems) - { - barButtonItem.enabled = NO; - } - if (currentAlert) { [currentAlert dismissViewControllerAnimated:NO completion:nil]; @@ -1244,7 +1250,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [self removeTombstoneEventNotificationsListener]; [self removeMXSessionStateChangeNotificationsListener]; [self removeServerNoticesListener]; - + if (previewHeader) { // Here [destroy] is called before [viewWillDisappear:] @@ -1259,8 +1265,6 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo roomPreviewData = nil; - missedDiscussionsBarButtonCustomView = nil; - missedDiscussionsBadgeLabelBgView = nil; missedDiscussionsBadgeLabel = nil; [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXEventDidChangeSentStateNotification object:nil]; @@ -1271,58 +1275,23 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo #pragma mark - +-(void)setActivitiesViewExpanded:(BOOL)activitiesViewExpanded +{ + if (_activitiesViewExpanded != activitiesViewExpanded) + { + _activitiesViewExpanded = activitiesViewExpanded; + + self.roomActivitiesContainerHeightConstraint.constant = activitiesViewExpanded ? 53 : 0; + [super roomInputToolbarView:self.inputToolbarView heightDidChanged:[self inputToolbarHeight] completion:nil]; + } +} + - (void)setShowMissedDiscussionsBadge:(BOOL)showMissedDiscussionsBadge { - _showMissedDiscussionsBadge = showMissedDiscussionsBadge; - - if (_showMissedDiscussionsBadge && !missedDiscussionsBarButtonCustomView) - { - // Prepare missed dicussion badge - missedDiscussionsBarButtonCustomView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 21)]; - missedDiscussionsBarButtonCustomView.backgroundColor = [UIColor clearColor]; - missedDiscussionsBarButtonCustomView.clipsToBounds = NO; - - NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:missedDiscussionsBarButtonCustomView - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1.0 - constant:21]; - - missedDiscussionsBadgeLabelBgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 21, 21)]; - [missedDiscussionsBadgeLabelBgView.layer setCornerRadius:10]; - - [missedDiscussionsBarButtonCustomView addSubview:missedDiscussionsBadgeLabelBgView]; - missedDiscussionsBarButtonCustomView.accessibilityIdentifier = @"RoomVCMissedDiscussionsBarButton"; - - missedDiscussionsBadgeLabel = [[UILabel alloc]initWithFrame:CGRectMake(2, 2, 17, 17)]; - missedDiscussionsBadgeLabel.translatesAutoresizingMaskIntoConstraints = NO; - [missedDiscussionsBadgeLabelBgView addSubview:missedDiscussionsBadgeLabel]; - - NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:missedDiscussionsBadgeLabel - attribute:NSLayoutAttributeCenterX - relatedBy:NSLayoutRelationEqual - toItem:missedDiscussionsBadgeLabelBgView - attribute:NSLayoutAttributeCenterX - multiplier:1.0 - constant:0]; - NSLayoutConstraint *centerYConstraint = [NSLayoutConstraint constraintWithItem:missedDiscussionsBadgeLabel - attribute:NSLayoutAttributeCenterY - relatedBy:NSLayoutRelationEqual - toItem:missedDiscussionsBadgeLabelBgView - attribute:NSLayoutAttributeCenterY - multiplier:1.0 - constant:0]; - - [NSLayoutConstraint activateConstraints:@[heightConstraint, centerXConstraint, centerYConstraint]]; - } - else - { - missedDiscussionsBarButtonCustomView = nil; - missedDiscussionsBadgeLabelBgView = nil; - missedDiscussionsBadgeLabel = nil; - } + BOOL toto = [UIDevice currentDevice].userInterfaceIdiom != + UIUserInterfaceIdiomPhone || !showMissedDiscussionsBadge; + missedDiscussionsBadgeLabel.hidden = [UIDevice currentDevice].userInterfaceIdiom != + UIUserInterfaceIdiomPhone || !showMissedDiscussionsBadge; } #pragma mark - Internals @@ -1357,20 +1326,26 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo return self.roomDataSource.room.summary.isEncrypted && self.mainSession.crypto != nil; } +- (BOOL)supportCallOption +{ + return BuildSettings.allowVoIPUsage && self.roomDataSource.mxSession.callManager && self.roomDataSource.room.summary.membersCount.joined >= 2; +} + +- (BOOL)isCallActive +{ + MXCall *callInRoom = [self.roomDataSource.mxSession.callManager callInRoom:self.roomDataSource.roomId]; + + return (callInRoom && callInRoom.state != MXCallStateEnded) + || [[AppDelegate theDelegate].jitsiViewController.widget.roomId isEqualToString:self.roomDataSource.roomId]; +} + - (void)refreshRoomTitle { - if (rightBarButtonItems && !self.navigationItem.rightBarButtonItems) - { - // Restore by default the search bar button. - self.navigationItem.rightBarButtonItems = rightBarButtonItems; - } + NSMutableArray *rightBarButtonItems = nil; // Set the right room title view if (self.isRoomPreview) { - // Do not show the right buttons - self.navigationItem.rightBarButtonItems = nil; - [self showPreviewHeader:YES]; } else if (self.roomDataSource) @@ -1379,107 +1354,75 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo if (self.roomDataSource.isLive) { - // Enable the right buttons (Search and Integrations) - for (UIBarButtonItem *barButtonItem in self.navigationItem.rightBarButtonItems) + rightBarButtonItems = [NSMutableArray new]; + + UIEdgeInsets itemInsets = UIEdgeInsetsMake(0, -5, 0, 5); + if (self.supportCallOption) { - barButtonItem.enabled = YES; - } - - if (self.navigationItem.rightBarButtonItems.count == 2) - { - BOOL matrixAppsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"matrixApps"]; - if (!matrixAppsEnabled) - { - // If the setting is disabled, do not show the icon - self.navigationItem.rightBarButtonItems = @[self.navigationItem.rightBarButtonItem]; - } - else if ([self widgetsCount:NO]) - { - // Show there are widgets by changing the "apps" icon color - // Show it in red only for room widgets, not user's widgets - // TODO: Design must be reviewed - UIImage *icon = self.navigationItem.rightBarButtonItems[1].image; - icon = [MXKTools paintImage:icon withColor:ThemeService.shared.theme.warningColor]; - icon = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; - - self.navigationItem.rightBarButtonItems[1].image = icon; - self.navigationItem.rightBarButtonItems[1].accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_integrations", @"Vector", nil); - } - else - { - // Reset original icon - self.navigationItem.rightBarButtonItems[1].image = [UIImage imageNamed:@"integrations_icon"]; - self.navigationItem.rightBarButtonItems[1].accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_integrations", @"Vector", nil); - } + UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"voice_call_hangon_icon"] style:UIBarButtonItemStylePlain target:self action:@selector(onVoiceCallPressed:)]; + item.accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_call", @"Vector", nil); + item.imageInsets = UIEdgeInsetsMake(0, -5, 0, 5); + [rightBarButtonItems addObject:item]; - self.navigationItem.rightBarButtonItems.firstObject.accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_search", @"Vector", nil); + item = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"video_call"] style:UIBarButtonItemStylePlain target:self action:@selector(onVideoCallPressed:)]; + item.imageInsets = UIEdgeInsetsMake(0, 10, 0, -10); + item.accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_video_call", @"Vector", nil); + [rightBarButtonItems addObject:item]; + itemInsets = UIEdgeInsetsMake(0, 20, 0, -20); } - + + if ([self widgetsCount:NO]) + { + UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"integrations_icon"] style:UIBarButtonItemStylePlain target:self action:@selector(onIntegrationsPressed:)]; + item.imageInsets = itemInsets; + item.accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_integrations", @"Vector", nil); + [rightBarButtonItems addObject:item]; + } + // Do not change title view class here if the expanded header is visible. [self setRoomTitleViewClass:RoomTitleView.class]; ((RoomTitleView*)self.titleView).tapGestureDelegate = self; } - else + + MXKImageView *userPictureView = ((RoomTitleView*)self.titleView).pictureView; + + // Set user picture in input toolbar + if (userPictureView) { - // Remove the search button temporarily - rightBarButtonItems = self.navigationItem.rightBarButtonItems; - self.navigationItem.rightBarButtonItems = nil; - - [self setRoomTitleViewClass:SimpleRoomTitleView.class]; - self.titleView.editable = NO; + [self.roomDataSource.room.summary setRoomAvatarImageIn:userPictureView]; } + + missedDiscussionsBadgeLabel = ((RoomTitleView*)self.titleView).missedDiscussionsBadgeLabel; + + [self refreshMissedDiscussionsCount:YES]; } - else - { - self.navigationItem.rightBarButtonItem.enabled = NO; - } + + self.navigationItem.rightBarButtonItems = rightBarButtonItems; } - (void)refreshRoomInputToolbar { MXKImageView *userPictureView; - + // Check whether the input toolbar is ready before updating it. if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { RoomInputToolbarView *roomInputToolbarView = (RoomInputToolbarView*)self.inputToolbarView; - // Check whether the call option is supported - roomInputToolbarView.supportCallOption = BuildSettings.allowVoIPUsage && self.roomDataSource.mxSession.callManager && self.roomDataSource.room.summary.membersCount.joined >= 2; - - // Get user picture view in input toolbar - userPictureView = roomInputToolbarView.pictureView; - - // Show the hangup button if there is an active call or an active jitsi - // conference call in the current room - MXCall *callInRoom = [self.roomDataSource.mxSession.callManager callInRoom:self.roomDataSource.roomId]; - if ((callInRoom && callInRoom.state != MXCallStateEnded) - || [[AppDelegate theDelegate].jitsiViewController.widget.roomId isEqualToString:self.roomDataSource.roomId]) - { - roomInputToolbarView.activeCall = YES; - } - else - { - roomInputToolbarView.activeCall = NO; - - // Hide the call button if there is an active call in another room - roomInputToolbarView.supportCallOption &= ([[AppDelegate theDelegate] callStatusBarWindow] == nil); - } - // Update encryption decoration if needed [self updateEncryptionDecorationForRoomInputToolbar:roomInputToolbarView]; } else if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:DisabledRoomInputToolbarView.class]) { DisabledRoomInputToolbarView *roomInputToolbarView = (DisabledRoomInputToolbarView*)self.inputToolbarView; - + // Get user picture view in input toolbar userPictureView = roomInputToolbarView.pictureView; - + // For the moment, there is only one reason to use `DisabledRoomInputToolbarView` [roomInputToolbarView setDisabledReason:NSLocalizedStringFromTable(@"room_do_not_have_permission_to_post", @"Vector", nil)]; } - + // Set user picture in input toolbar if (userPictureView) { @@ -1516,7 +1459,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo RoomInputToolbarView *roomInputToolbarView = (RoomInputToolbarView*)self.inputToolbarView; sendMode = roomInputToolbarView.sendMode; } - + return sendMode; } @@ -1578,7 +1521,6 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)updateEncryptionDecorationForRoomInputToolbar:(RoomInputToolbarView*)roomInputToolbarView { roomInputToolbarView.isEncryptionEnabled = self.isEncryptionEnabled; - roomInputToolbarView.encryptedRoomIcon.image = self.roomEncryptionBadgeImage; } - (void)handleLongPressFromCell:(id)cell withTappedEvent:(MXEvent*)event @@ -1609,7 +1551,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo CameraPresenter *cameraPresenter = [CameraPresenter new]; cameraPresenter.delegate = self; [cameraPresenter presentCameraFrom:self with:@[MXKUTI.image, MXKUTI.movie] animated:YES]; - + self.cameraPresenter = cameraPresenter; } @@ -1631,7 +1573,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { sourceView = self.inputToolbarView; } - + [mediaPickerPresenter presentFrom:self sourceView:sourceView sourceRect:sourceView.bounds animated:YES]; self.mediaPickerPresenter = mediaPickerPresenter; @@ -1691,7 +1633,8 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo self.roomInfoCoordinatorBridgePresenter = [[RoomInfoCoordinatorBridgePresenter alloc] initWithParameters:parameters]; self.roomInfoCoordinatorBridgePresenter.delegate = self; - [self.roomInfoCoordinatorBridgePresenter presentFrom:self animated:YES]; +// [self.roomInfoCoordinatorBridgePresenter presentFrom:self animated:true]; + [self.roomInfoCoordinatorBridgePresenter pushFrom:self.navigationController animated:YES]; } #pragma mark - Dialpad @@ -1803,7 +1746,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo } self.previewHeaderContainer.hidden = NO; - + // Finalize preview header display according to the screen orientation [self refreshPreviewHeader:UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])]; } @@ -1826,8 +1769,6 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo // Remove details icon RoomTitleView *roomTitleView = (RoomTitleView*)self.titleView; - [roomTitleView.roomDetailsIconImageView removeFromSuperview]; - roomTitleView.roomDetailsIconImageView = nil; // Remove the shadow image used to hide the bottom border of the navigation bar when the preview header is displayed [mainNavigationController.navigationBar setShadowImage:nil]; @@ -1835,19 +1776,19 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn animations:^{ - - self.bubblesTableViewTopConstraint.constant = 0; - self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.bubblesTableView.mxk_adjustedContentInset.top; - - // Force to render the view - [self forceLayoutRefresh]; - - } + + self.bubblesTableViewTopConstraint.constant = 0; + self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.bubblesTableView.mxk_adjustedContentInset.top; + + // Force to render the view + [self forceLayoutRefresh]; + + } completion:^(BOOL finished){ - }]; + }]; } } - + // Consider the main navigation controller if the current view controller is embedded inside a split view controller. UINavigationController *mainNavigationController = self.navigationController; if (self.splitViewController.isCollapsed && self.splitViewController.viewControllers.count) @@ -1875,8 +1816,6 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo // Remove details icon RoomTitleView *roomTitleView = (RoomTitleView*)self.titleView; - [roomTitleView.roomDetailsIconImageView removeFromSuperview]; - roomTitleView.roomDetailsIconImageView = nil; // Set preview data to provide the room name roomTitleView.roomPreviewData = roomPreviewData; @@ -1885,17 +1824,17 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { previewHeader.mainHeaderContainer.hidden = NO; previewHeader.mainHeaderBackgroundHeightConstraint.constant = previewHeader.mainHeaderContainer.frame.size.height; - + if ([previewHeader isKindOfClass:PreviewRoomTitleView.class]) { // In case of preview, update the header height so that we can // display as much as possible the room topic in this header. // Note: the header height is handled by the previewHeader.mainHeaderBackgroundHeightConstraint. PreviewRoomTitleView *previewRoomTitleView = (PreviewRoomTitleView *)previewHeader; - + // Compute the height required to display all the room topic CGSize sizeThatFitsTextView = [previewRoomTitleView.roomTopic sizeThatFits:CGSizeMake(previewRoomTitleView.roomTopic.frame.size.width, MAXFLOAT)]; - + // Increase the preview header height according to the room topic height // but limit it in order to let room for room messages at the screen bottom. // This free space depends on the device. @@ -1904,13 +1843,13 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo // apply a factor to give more priority to the display of more messages. CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height; CGFloat maxRoomTopicHeight = 50 + (screenHeight - 568) / 3; - + CGFloat additionalHeight = MIN(maxRoomTopicHeight, sizeThatFitsTextView.height) - - previewRoomTitleView.roomTopic.frame.size.height; - + - previewRoomTitleView.roomTopic.frame.size.height; + previewHeader.mainHeaderBackgroundHeightConstraint.constant += additionalHeight; } - + [self setRoomTitleViewClass:RoomAvatarTitleView.class]; // Note the avatar title view does not define tap gesture. @@ -1956,18 +1895,18 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn animations:^{ - - self.bubblesTableViewTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant - self.bubblesTableView.mxk_adjustedContentInset.top; - self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant; - - previewHeader.roomAvatar.alpha = 1; - - // Force to render the view - [self forceLayoutRefresh]; - - } + + self.bubblesTableViewTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant - self.bubblesTableView.mxk_adjustedContentInset.top; + self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant; + + previewHeader.roomAvatar.alpha = 1; + + // Force to render the view + [self forceLayoutRefresh]; + + } completion:^(BOOL finished){ - }]; + }]; } } @@ -2288,7 +2227,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo RoomDataSource *roomDataSource = (RoomDataSource*)self.roomDataSource; [roomDataSource acceptVerificationRequestForEventId:eventId success:^{ - + } failure:^(NSError *error) { [[AppDelegate theDelegate] showErrorAsAlert:error]; }]; @@ -2388,7 +2327,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo MXEvent *callInviteEvent = userInfo[kMXKRoomBubbleCellEventKey]; MXCallInviteEventContent *eventContent = [MXCallInviteEventContent modelFromJSON:callInviteEvent.content]; - [self roomInputToolbarView:self.inputToolbarView placeCallWithVideo2:eventContent.isVideoCall]; + [self placeCallWithVideo2:eventContent.isVideoCall]; } else if ([actionIdentifier isEqualToString:RoomCreationIntroCell.tapOnAvatarView]) { @@ -2436,33 +2375,33 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"retry", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - - [self cancelEventSelection]; - - // Let the datasource resend. It will manage local echo, etc. - [self.roomDataSource resendEventWithEventId:selectedEvent.eventId success:nil failure:nil]; - } - - }]]; + + if (weakSelf) + { + typeof(self) self = weakSelf; + + [self cancelEventSelection]; + + // Let the datasource resend. It will manage local echo, etc. + [self.roomDataSource resendEventWithEventId:selectedEvent.eventId success:nil failure:nil]; + } + + }]]; [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_delete", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - - [self cancelEventSelection]; - - [self.roomDataSource removeEventWithEventId:selectedEvent.eventId]; - } - - }]]; + + if (weakSelf) + { + typeof(self) self = weakSelf; + + [self cancelEventSelection]; + + [self.roomDataSource removeEventWithEventId:selectedEvent.eventId]; + } + + }]]; } // Add actions for text message @@ -2480,7 +2419,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo selectedComponent = nil; } - + // Check status of the selected event if (selectedEvent.sentState == MXEventSentStatePreparing || selectedEvent.sentState == MXEventSentStateEncrypting || @@ -2490,41 +2429,41 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - if (weakSelf) - { - typeof(self) self = weakSelf; - - self->currentAlert = nil; - - // Cancel and remove the outgoing message - [self.roomDataSource.room cancelSendingOperation:selectedEvent.eventId]; - [self.roomDataSource removeEventWithEventId:selectedEvent.eventId]; - - [self cancelEventSelection]; - } - - }]]; + if (weakSelf) + { + typeof(self) self = weakSelf; + + self->currentAlert = nil; + + // Cancel and remove the outgoing message + [self.roomDataSource.room cancelSendingOperation:selectedEvent.eventId]; + [self.roomDataSource removeEventWithEventId:selectedEvent.eventId]; + + [self cancelEventSelection]; + } + + }]]; } - + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_quote", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - - [self cancelEventSelection]; - - // Quote the message a la Markdown into the input toolbar composer - self.inputToolbarView.textMessage = [NSString stringWithFormat:@"%@\n>%@\n\n", self.inputToolbarView.textMessage, selectedComponent.textMessage]; - - // And display the keyboard - [self.inputToolbarView becomeFirstResponder]; - } - - }]]; - + + if (weakSelf) + { + typeof(self) self = weakSelf; + + [self cancelEventSelection]; + + // Quote the message a la Markdown into the input toolbar composer + self.inputToolbarView.textMessage = [NSString stringWithFormat:@"%@\n>%@\n\n", self.inputToolbarView.textMessage, selectedComponent.textMessage]; + + // And display the keyboard + [self.inputToolbarView becomeFirstResponder]; + } + + }]]; + if (BuildSettings.messageDetailsAllowShare) { [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_share", @"Vector", nil) @@ -2594,7 +2533,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo }]]; } } - + // Check status of the selected event if (selectedEvent.sentState == MXEventSentStatePreparing || selectedEvent.sentState == MXEventSentStateEncrypting || @@ -2608,37 +2547,37 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_cancel_send", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - // Get again the loader - MXMediaLoader *loader = [MXMediaManager existingUploaderWithId:uploadId]; - if (loader) - { - [loader cancel]; - } - // Hide the progress animation - roomBubbleTableViewCell.progressView.hidden = YES; - - if (weakSelf) - { - typeof(self) self = weakSelf; - - self->currentAlert = nil; - - // Remove the outgoing message and its related cached file. - [[NSFileManager defaultManager] removeItemAtPath:roomBubbleTableViewCell.bubbleData.attachment.cacheFilePath error:nil]; - [[NSFileManager defaultManager] removeItemAtPath:roomBubbleTableViewCell.bubbleData.attachment.thumbnailCachePath error:nil]; - - // Cancel and remove the outgoing message - [self.roomDataSource.room cancelSendingOperation:selectedEvent.eventId]; - [self.roomDataSource removeEventWithEventId:selectedEvent.eventId]; - - [self cancelEventSelection]; - } - - }]]; + + // Get again the loader + MXMediaLoader *loader = [MXMediaManager existingUploaderWithId:uploadId]; + if (loader) + { + [loader cancel]; + } + // Hide the progress animation + roomBubbleTableViewCell.progressView.hidden = YES; + + if (weakSelf) + { + typeof(self) self = weakSelf; + + self->currentAlert = nil; + + // Remove the outgoing message and its related cached file. + [[NSFileManager defaultManager] removeItemAtPath:roomBubbleTableViewCell.bubbleData.attachment.cacheFilePath error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:roomBubbleTableViewCell.bubbleData.attachment.thumbnailCachePath error:nil]; + + // Cancel and remove the outgoing message + [self.roomDataSource.room cancelSendingOperation:selectedEvent.eventId]; + [self.roomDataSource removeEventWithEventId:selectedEvent.eventId]; + + [self cancelEventSelection]; + } + + }]]; } } - + if (attachment.type != MXKAttachmentTypeSticker) { if (BuildSettings.messageDetailsAllowShare) @@ -2695,24 +2634,24 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_cancel_download", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - - [self cancelEventSelection]; - - // Get again the loader - MXMediaLoader *loader = [MXMediaManager existingDownloaderWithIdentifier:downloadId]; - if (loader) - { - [loader cancel]; - } - // Hide the progress animation - roomBubbleTableViewCell.progressView.hidden = YES; - } - - }]]; + + if (weakSelf) + { + typeof(self) self = weakSelf; + + [self cancelEventSelection]; + + // Get again the loader + MXMediaLoader *loader = [MXMediaManager existingDownloaderWithIdentifier:downloadId]; + if (loader) + { + [loader cancel]; + } + // Hide the progress animation + roomBubbleTableViewCell.progressView.hidden = YES; + } + + }]]; } } @@ -2723,35 +2662,35 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_redact", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - - [self cancelEventSelection]; - - [self startActivityIndicator]; - - [self.roomDataSource.room redactEvent:selectedEvent.eventId reason:nil success:^{ - - __strong __typeof(weakSelf)self = weakSelf; - [self stopActivityIndicator]; - - } failure:^(NSError *error) { - - __strong __typeof(weakSelf)self = weakSelf; - [self stopActivityIndicator]; - - NSLog(@"[RoomVC] Redact event (%@) failed", selectedEvent.eventId); - //Alert user - [[AppDelegate theDelegate] showErrorAsAlert:error]; - - }]; - } - - }]]; + + if (weakSelf) + { + typeof(self) self = weakSelf; + + [self cancelEventSelection]; + + [self startActivityIndicator]; + + [self.roomDataSource.room redactEvent:selectedEvent.eventId reason:nil success:^{ + + __strong __typeof(weakSelf)self = weakSelf; + [self stopActivityIndicator]; + + } failure:^(NSError *error) { + + __strong __typeof(weakSelf)self = weakSelf; + [self stopActivityIndicator]; + + NSLog(@"[RoomVC] Redact event (%@) failed", selectedEvent.eventId); + //Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + } + + }]]; } - + if (BuildSettings.messageDetailsAllowPermalink) { [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_permalink", @"Vector", nil) @@ -2786,12 +2725,12 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_reaction_history", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - [self cancelEventSelection]; - - // Show reaction history - [self showReactionHistoryForEventId:selectedEvent.eventId animated:YES]; - }]]; + + [self cancelEventSelection]; + + // Show reaction history + [self showReactionHistoryForEventId:selectedEvent.eventId animated:YES]; + }]]; } if (BuildSettings.messageDetailsAllowViewSource) @@ -2811,8 +2750,8 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo } }]]; - - + + // Add "View Decrypted Source" for e2ee event we can decrypt if (selectedEvent.isEncrypted && selectedEvent.clearEvent) { @@ -2943,37 +2882,37 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo }]]; } - + if (self.roomDataSource.room.summary.isEncrypted) { [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_view_encryption", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - [self cancelEventSelection]; - - // Display encryption details - [self showEncryptionInformation:selectedEvent]; - } - - }]]; + + if (weakSelf) + { + typeof(self) self = weakSelf; + [self cancelEventSelection]; + + // Display encryption details + [self showEncryptionInformation:selectedEvent]; + } + + }]]; } } [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"cancel", @"Vector", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - [self hideContextualMenuAnimated:YES]; - } - - }]]; + + if (weakSelf) + { + typeof(self) self = weakSelf; + [self hideContextualMenuAnimated:YES]; + } + + }]]; // Do not display empty action sheet if (currentAlert.actions.count > 1) @@ -3080,7 +3019,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { NSString *eventId = arguments[1]; MXEvent *event = [self.roomDataSource eventWithEventId:eventId]; - + if (event) { [self reRequestKeysAndShowExplanationAlert:event]; @@ -3157,7 +3096,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo shouldDoAction = NO; break; } - } + } } break; case UITextItemInteractionPresentActions: @@ -3328,19 +3267,19 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { // Search for the sticker picker widget in the user account Widget *widget = [[WidgetManager sharedManager] userWidgets:self.roomDataSource.mxSession ofTypes:@[kWidgetTypeStickerPicker]].firstObject; - + if (widget) { // Display the widget [widget widgetUrl:^(NSString * _Nonnull widgetUrl) { - + StickerPickerViewController *stickerPickerVC = [[StickerPickerViewController alloc] initWithUrl:widgetUrl forWidget:widget]; - + stickerPickerVC.roomDataSource = self.roomDataSource; - + [self.navigationController pushViewController:stickerPickerVC animated:YES]; } failure:^(NSError * _Nonnull error) { - + NSLog(@"[RoomVC] Cannot display widget %@", widget); [[AppDelegate theDelegate] showErrorAsAlert:error]; }]; @@ -3349,73 +3288,61 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { // The Sticker picker widget is not installed yet. Propose the user to install it __weak typeof(self) weakSelf = self; - + [currentAlert dismissViewControllerAnimated:NO completion:nil]; - + NSString *alertMessage = [NSString stringWithFormat:@"%@\n%@", NSLocalizedStringFromTable(@"widget_sticker_picker_no_stickerpacks_alert", @"Vector", nil), NSLocalizedStringFromTable(@"widget_sticker_picker_no_stickerpacks_alert_add_now", @"Vector", nil) ]; - + currentAlert = [UIAlertController alertControllerWithTitle:nil message:alertMessage preferredStyle:UIAlertControllerStyleAlert]; - + [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"no"] style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) - { + { if (weakSelf) { typeof(self) self = weakSelf; self->currentAlert = nil; } - + }]]; - + [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"yes"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) - { + { if (weakSelf) { typeof(self) self = weakSelf; self->currentAlert = nil; - + // Show the sticker picker settings screen IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc] initForMXSession:self.roomDataSource.mxSession inRoom:self.roomDataSource.roomId screen:[IntegrationManagerViewController screenForWidget:kWidgetTypeStickerPicker] widgetId:nil]; - + [self presentViewController:modularVC animated:NO completion:nil]; } }]]; - + [currentAlert mxk_setAccessibilityIdentifier:@"RoomVCStickerPickerAlert"]; [self presentViewController:currentAlert animated:YES completion:nil]; } } -#pragma mark - MXKRoomInputToolbarViewDelegate +#pragma mark - VoIP -- (void)roomInputToolbarView:(MXKRoomInputToolbarView*)toolbarView isTyping:(BOOL)typing -{ - [super roomInputToolbarView:toolbarView isTyping:typing]; - - // Cancel potential selected event (to leave edition mode) - NSString *selectedEventId = customizedRoomDataSource.selectedEventId; - if (typing && selectedEventId && ![self.roomDataSource canReplyToEventWithId:selectedEventId]) - { - [self cancelEventSelection]; - } -} - -- (void)roomInputToolbarView:(MXKRoomInputToolbarView*)toolbarView placeCallWithVideo:(BOOL)video +- (void)placeCallWithVideo:(BOOL)video { __weak __typeof(self) weakSelf = self; - + NSString *appDisplayName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; - + // Check app permissions first [MXKTools checkAccessForCall:video manualChangeMessageForAudio:[NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"microphone_access_not_granted_for_call"], appDisplayName] @@ -3430,15 +3357,15 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { if (video) { - [self roomInputToolbarView:toolbarView placeCallWithVideo2:video]; + [self placeCallWithVideo2:video]; } else if (self.mainSession.callManager.supportsPSTN) { - [self showVoiceCallActionSheetWith:toolbarView]; + [self showVoiceCallActionSheet]; } else { - [self roomInputToolbarView:toolbarView placeCallWithVideo2:NO]; + [self placeCallWithVideo2:NO]; } } else @@ -3449,149 +3376,145 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo }]; } -- (void)showVoiceCallActionSheetWith:(MXKRoomInputToolbarView *)toolbarView +- (void)showVoiceCallActionSheet { // Ask the user the kind of the call: voice or dialpad? currentAlert = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - + __weak typeof(self) weakSelf = self; [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_place_voice_call", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - - [self roomInputToolbarView:toolbarView placeCallWithVideo2:NO]; - } - - }]]; + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + + [self placeCallWithVideo2:NO]; + } + + }]]; [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_open_dialpad", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - - [self openDialpad]; - } - - }]]; + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + + [self openDialpad]; + } + + }]]; [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] - style:UIAlertActionStyleCancel - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - } - - }]]; + style:UIAlertActionStyleCancel + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + } + + }]]; - if ([toolbarView isKindOfClass:[RoomInputToolbarView class]]) - { - RoomInputToolbarView *toolbar = (RoomInputToolbarView *)toolbarView; - [currentAlert popoverPresentationController].sourceView = toolbar.voiceCallButton; - [currentAlert popoverPresentationController].sourceRect = toolbar.voiceCallButton.bounds; - } + [currentAlert popoverPresentationController].barButtonItem = self.navigationItem.rightBarButtonItems.firstObject; + [currentAlert popoverPresentationController].permittedArrowDirections = UIPopoverArrowDirectionUp; [self presentViewController:currentAlert animated:YES completion:nil]; } -- (void)roomInputToolbarView:(MXKRoomInputToolbarView*)toolbarView placeCallWithVideo2:(BOOL)video +- (void)placeCallWithVideo2:(BOOL)video { - __weak __typeof(self) weakSelf = self; - + __weak __typeof(self) weakSelf = self; + // If there is already a jitsi widget, join it Widget *jitsiWidget = [customizedRoomDataSource jitsiWidget]; if (jitsiWidget) { [[AppDelegate theDelegate] displayJitsiViewControllerWithWidget:jitsiWidget andVideo:video]; } - + // If enabled, create the conf using jitsi widget and open it directly else if (RiotSettings.shared.createConferenceCallsWithJitsi && self.roomDataSource.room.summary.membersCount.joined > 2) { [self startActivityIndicator]; - + [[WidgetManager sharedManager] createJitsiWidgetInRoom:self.roomDataSource.room withVideo:video success:^(Widget *jitsiWidget) { - if (weakSelf) - { - typeof(self) self = weakSelf; - [self stopActivityIndicator]; - - [[AppDelegate theDelegate] displayJitsiViewControllerWithWidget:jitsiWidget andVideo:video]; - } - } + if (weakSelf) + { + typeof(self) self = weakSelf; + [self stopActivityIndicator]; + + [[AppDelegate theDelegate] displayJitsiViewControllerWithWidget:jitsiWidget andVideo:video]; + } + } failure:^(NSError *error) { - if (weakSelf) - { - typeof(self) self = weakSelf; - [self stopActivityIndicator]; - - [self showJitsiErrorAsAlert:error]; - } - }]; + if (weakSelf) + { + typeof(self) self = weakSelf; + [self stopActivityIndicator]; + + [self showJitsiErrorAsAlert:error]; + } + }]; } // Classic conference call is not supported in encrypted rooms else if (self.roomDataSource.room.summary.isEncrypted && self.roomDataSource.room.summary.membersCount.joined > 2) { [currentAlert dismissViewControllerAnimated:NO completion:nil]; - + currentAlert = [UIAlertController alertControllerWithTitle:[NSBundle mxk_localizedStringForKey:@"room_no_conference_call_in_encrypted_rooms"] message:nil preferredStyle:UIAlertControllerStyleAlert]; - + [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - } - - }]]; - + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + } + + }]]; + [currentAlert mxk_setAccessibilityIdentifier:@"RoomVCCallAlert"]; [self presentViewController:currentAlert animated:YES completion:nil]; } - + // In case of conference call, check that the user has enough power level else if (self.roomDataSource.room.summary.membersCount.joined > 2 && ![MXCallManager canPlaceConferenceCallInRoom:self.roomDataSource.room roomState:self.roomDataSource.roomState]) { [currentAlert dismissViewControllerAnimated:NO completion:nil]; - + currentAlert = [UIAlertController alertControllerWithTitle:[NSBundle mxk_localizedStringForKey:@"room_no_power_to_create_conference_call"] message:nil preferredStyle:UIAlertControllerStyleAlert]; - + [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - } - }]]; - + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + } + }]]; + [currentAlert mxk_setAccessibilityIdentifier:@"RoomVCCallAlert"]; [self presentViewController:currentAlert animated:YES completion:nil]; } - + // Classic 1:1 or group call can be done else { @@ -3599,7 +3522,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo } } -- (void)roomInputToolbarViewHangupCall:(MXKRoomInputToolbarView *)toolbarView +- (void)hangupCall { MXCall *callInRoom = [self.roomDataSource.mxSession.callManager callInRoom:self.roomDataSource.roomId]; if (callInRoom) @@ -3610,11 +3533,25 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { [[AppDelegate theDelegate].jitsiViewController hangup]; } - + [self refreshActivitiesViewDisplay]; [self refreshRoomInputToolbar]; } +#pragma mark - MXKRoomInputToolbarViewDelegate + +- (void)roomInputToolbarView:(MXKRoomInputToolbarView*)toolbarView isTyping:(BOOL)typing +{ + [super roomInputToolbarView:toolbarView isTyping:typing]; + + // Cancel potential selected event (to leave edition mode) + NSString *selectedEventId = customizedRoomDataSource.selectedEventId; + if (typing && selectedEventId && ![self.roomDataSource canReplyToEventWithId:selectedEventId]) + { + [self cancelEventSelection]; + } +} + - (void)roomInputToolbarView:(MXKRoomInputToolbarView*)toolbarView heightDidChanged:(CGFloat)height completion:(void (^)(BOOL finished))completion { if (self.roomInputToolbarContainerHeightConstraint.constant != height) @@ -3637,9 +3574,9 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo if (!toolbarView.placeholder) { // Restore the placeholder if any - toolbarView.placeholder = savedInputToolbarPlaceholder.length ? savedInputToolbarPlaceholder : nil; + toolbarView.placeholder = self->savedInputToolbarPlaceholder.length ? self->savedInputToolbarPlaceholder : nil; } - savedInputToolbarPlaceholder = nil; + self->savedInputToolbarPlaceholder = nil; }]; } } @@ -3648,7 +3585,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { MXKDocumentPickerPresenter *documentPickerPresenter = [MXKDocumentPickerPresenter new]; documentPickerPresenter.delegate = self; - + NSArray *allowedUTIs = @[MXKUTI.data]; [documentPickerPresenter presentDocumentPickerWith:allowedUTIs from:self animated:YES completion:nil]; @@ -3686,50 +3623,49 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo #pragma mark - Action +- (IBAction)onVoiceCallPressed:(id)sender +{ + if (self.isCallActive) + { + [self hangupCall]; + } + else + { + [self placeCallWithVideo:NO]; + } +} + +- (IBAction)onVideoCallPressed:(id)sender +{ + [self placeCallWithVideo:YES]; +} + +- (IBAction)onIntegrationsPressed:(id)sender +{ + WidgetPickerViewController *widgetPicker = [[WidgetPickerViewController alloc] initForMXSession:self.roomDataSource.mxSession + inRoom:self.roomDataSource.roomId]; + + [widgetPicker showInViewController:self]; +} + - (IBAction)onButtonPressed:(id)sender { - // Search button - if (sender == self.navigationItem.rightBarButtonItem) - { - [self performSegueWithIdentifier:@"showRoomSearch" sender:self]; - } - // Matrix Apps button - else if (self.navigationItem.rightBarButtonItems.count == 2 && sender == self.navigationItem.rightBarButtonItems[1]) - { - if ([self widgetsCount:NO]) - { - WidgetPickerViewController *widgetPicker = [[WidgetPickerViewController alloc] initForMXSession:self.roomDataSource.mxSession - inRoom:self.roomDataSource.roomId]; - - [widgetPicker showInViewController:self]; - } - else - { - // No widgets -> Directly show the integration manager - IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc] initForMXSession:self.roomDataSource.mxSession - inRoom:self.roomDataSource.roomId - screen:kIntegrationManagerMainScreen - widgetId:nil]; - - [self presentViewController:modularVC animated:NO completion:nil]; - } - } - else if (sender == self.jumpToLastUnreadButton) + if (sender == self.jumpToLastUnreadButton) { // Dismiss potential keyboard. [self dismissKeyboard]; - + // Jump to the last unread event by using a temporary room data source initialized with the last unread event id. MXWeakify(self); [RoomDataSource loadRoomDataSourceWithRoomId:self.roomDataSource.roomId initialEventId:self.roomDataSource.room.accountData.readMarkerEventId andMatrixSession:self.mainSession onComplete:^(id roomDataSource) { MXStrongifyAndReturnIfNil(self); - + [roomDataSource finalizeInitialization]; - + // Center the bubbles table content on the bottom of the read marker event in order to display correctly the read marker view. self.centerBubblesTableViewContentOnTheInitialEventBottom = YES; [self displayRoom:roomDataSource]; - + // Give the data source ownership to the room view controller. self.hasRoomDataSourceOwnership = YES; }]; @@ -3925,12 +3861,12 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo MXWeakify(self); [RoomDataSource loadRoomDataSourceWithRoomId:self.roomDataSource.roomId initialEventId:eventId andMatrixSession:self.mainSession onComplete:^(id roomDataSource) { MXStrongifyAndReturnIfNil(self); - + [roomDataSource finalizeInitialization]; ((RoomDataSource*)roomDataSource).markTimelineInitialEvent = YES; - + [self displayRoom:roomDataSource]; - + self.hasRoomDataSourceOwnership = YES; }]; } @@ -4004,7 +3940,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo MXWeakify(self); [self.roomDataSource.room liveTimeline:^(MXEventTimeline *liveTimeline) { MXStrongifyAndReturnIfNil(self); - + [liveTimeline removeListener:self->typingNotifListener]; self->typingNotifListener = nil; }]; @@ -4022,7 +3958,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo MXWeakify(self); self->typingNotifListener = [self.roomDataSource.room listenToEventsOfTypes:@[kMXEventTypeStringTypingNotification] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { MXStrongifyAndReturnIfNil(self); - + // Handle only live events if (direction == MXTimelineDirectionForwards) { @@ -4034,7 +3970,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { [typingUsers removeObjectAtIndex:index]; } - + // Ignore this notification if both arrays are empty if (self->currentTypingUsers.count || typingUsers.count) { @@ -4043,7 +3979,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo } } }]; - + // Retrieve the current typing users list NSMutableArray *typingUsers = [NSMutableArray arrayWithArray:self.roomDataSource.room.typingUsers]; // Remove typing info for the current user @@ -4059,8 +3995,10 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)refreshTypingNotification { - if ([self.activitiesView isKindOfClass:RoomActivitiesView.class]) + if ([self.titleView isKindOfClass:RoomTitleView.class]) { + RoomTitleView *titleView = (RoomTitleView *)self.titleView; + // Prepare here typing notification NSString* text = nil; NSUInteger count = currentTypingUsers.count; @@ -4104,7 +4042,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_many_users_are_typing", @"Vector", nil), names[0], names[1]]; } - [((RoomActivitiesView*) self.activitiesView) displayTypingNotification:text]; + titleView.typingNotificationString = text; } } @@ -4199,7 +4137,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)listenWidgetNotifications { kMXKWidgetManagerDidUpdateWidgetObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kWidgetManagerDidUpdateWidgetNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - + Widget *widget = notif.object; if (widget.mxSession == self.roomDataSource.mxSession && [widget.roomId isEqualToString:customizedRoomDataSource.roomId]) @@ -4221,10 +4159,10 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo error = [NSError errorWithDomain:error.domain code:error.code userInfo:@{ - NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"room_conference_call_no_power", @"Vector", nil) - }]; + NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"room_conference_call_no_power", @"Vector", nil) + }]; } - + // Alert user [[AppDelegate theDelegate] showErrorAsAlert:error]; } @@ -4238,7 +4176,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { widgetsCount += [[WidgetManager sharedManager] userWidgets:self.roomDataSource.room.mxSession].count; } - + return widgetsCount; } @@ -4249,38 +4187,41 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo if ([self.activitiesView isKindOfClass:RoomActivitiesView.class]) { RoomActivitiesView *roomActivitiesView = (RoomActivitiesView*)self.activitiesView; - + // Reset gesture recognizers while (roomActivitiesView.gestureRecognizers.count) { [roomActivitiesView removeGestureRecognizer:roomActivitiesView.gestureRecognizers[0]]; } - + Widget *jitsiWidget = [customizedRoomDataSource jitsiWidget]; - + if ([self.roomDataSource.mxSession.syncError.errcode isEqualToString:kMXErrCodeStringResourceLimitExceeded]) { + self.activitiesViewExpanded = YES; [roomActivitiesView showResourceLimitExceededError:self.roomDataSource.mxSession.syncError.userInfo onAdminContactTapped:^(NSURL *adminContactURL) { [[UIApplication sharedApplication] vc_open:adminContactURL completionHandler:^(BOOL success) { - if (!success) - { + if (!success) + { NSLog(@"[RoomVC] refreshActivitiesViewDisplay: adminContact(%@) cannot be opened", adminContactURL); - } + } }]; }]; } else if ([AppDelegate theDelegate].isOffline) { + self.activitiesViewExpanded = YES; [roomActivitiesView displayNetworkErrorNotification:NSLocalizedStringFromTable(@"room_offline_notification", @"Vector", nil)]; } else if (customizedRoomDataSource.roomState.isObsolete) { + self.activitiesViewExpanded = YES; MXWeakify(self); [roomActivitiesView displayRoomReplacementWithRoomLinkTappedHandler:^{ MXStrongifyAndReturnIfNil(self); - + MXEvent *stoneTombEvent = [self->customizedRoomDataSource.roomState stateEventsWithType:kMXEventTypeStringRoomTombStone].lastObject; - + NSString *replacementRoomId = self->customizedRoomDataSource.roomState.tombStoneContent.replacementRoomId; if ([self.roomDataSource.mxSession roomWithRoomId:replacementRoomId]) { @@ -4292,20 +4233,20 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo // Else auto join it via the server that sent the event NSLog(@"[RoomVC] Auto join an upgraded room: %@ -> %@. Sender: %@", self->customizedRoomDataSource.roomState.roomId, replacementRoomId, stoneTombEvent.sender); - + NSString *viaSenderServer = [MXTools serverNameInMatrixIdentifier:stoneTombEvent.sender]; - + if (viaSenderServer) { [self startActivityIndicator]; [self.roomDataSource.mxSession joinRoom:replacementRoomId viaServers:@[viaSenderServer] success:^(MXRoom *room) { [self stopActivityIndicator]; - + [[AppDelegate theDelegate] showRoom:replacementRoomId andEventId:nil withMatrixSession:self.roomDataSource.mxSession]; - + } failure:^(NSError *error) { [self stopActivityIndicator]; - + NSLog(@"[RoomVC] Failed to join an upgraded room. Error: %@", error); [[AppDelegate theDelegate] showErrorAsAlert:error]; @@ -4327,6 +4268,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo } else { + self.activitiesViewExpanded = YES; [roomActivitiesView displayOngoingConferenceCall:^(BOOL video) { NSLog(@"[RoomVC] onOngoingConferenceCallPressed"); @@ -4353,54 +4295,55 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo } else { + self.activitiesViewExpanded = YES; [roomActivitiesView displayOngoingConferenceCall:^(BOOL video) { - + NSLog(@"[RoomVC] onOngoingConferenceCallPressed (jitsi)"); - + __weak __typeof(self) weakSelf = self; NSString *appDisplayName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; - + // Check app permissions first [MXKTools checkAccessForCall:video manualChangeMessageForAudio:[NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"microphone_access_not_granted_for_call"], appDisplayName] manualChangeMessageForVideo:[NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"camera_access_not_granted_for_call"], appDisplayName] showPopUpInViewController:self completionHandler:^(BOOL granted) { - - if (weakSelf) - { - if (granted) - { - // Present the Jitsi view controller - [appDelegate displayJitsiViewControllerWithWidget:jitsiWidget andVideo:video]; - } - else - { - NSLog(@"[RoomVC] onOngoingConferenceCallPressed: Warning: The application does not have the perssion to join the call"); - } - } - }]; - + + if (weakSelf) + { + if (granted) + { + // Present the Jitsi view controller + [appDelegate displayJitsiViewControllerWithWidget:jitsiWidget andVideo:video]; + } + else + { + NSLog(@"[RoomVC] onOngoingConferenceCallPressed: Warning: The application does not have the perssion to join the call"); + } + } + }]; + } onClosePressed:^{ - + [self startActivityIndicator]; - + // Close the widget __weak __typeof(self) weakSelf = self; [[WidgetManager sharedManager] closeWidget:jitsiWidget.widgetId inRoom:self.roomDataSource.room success:^{ - + if (weakSelf) { typeof(self) self = weakSelf; [self stopActivityIndicator]; - + // The banner will automatically leave thanks to kWidgetManagerDidUpdateWidgetNotification } - + } failure:^(NSError *error) { if (weakSelf) { typeof(self) self = weakSelf; - + [self showJitsiErrorAsAlert:error]; [self stopActivityIndicator]; } @@ -4418,13 +4361,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo // Retrieve the unread messages count NSUInteger unreadCount = self.roomDataSource.room.summary.localUnreadEventCount; - if (unreadCount == 0) - { - // Refresh the typing notification here - // We will keep visible this notification (if any) beside the "scroll to bottom" icon. - [self refreshTypingNotification]; - } - + self.activitiesViewExpanded = YES; [roomActivitiesView displayScrollToBottomIcon:unreadCount onIconTapGesture:^{ [self goBackToLive]; @@ -4433,17 +4370,19 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo } else if (serverNotices.usageLimit && serverNotices.usageLimit.isServerNoticeUsageLimit) { - [roomActivitiesView showResourceUsageLimitNotice:serverNotices.usageLimit onAdminContactTapped:^(NSURL *adminContactURL) { + self.activitiesViewExpanded = YES; + [roomActivitiesView showResourceUsageLimitNotice:serverNotices.usageLimit onAdminContactTapped:^(NSURL *adminContactURL) { [[UIApplication sharedApplication] vc_open:adminContactURL completionHandler:^(BOOL success) { - if (!success) - { + if (!success) + { NSLog(@"[RoomVC] refreshActivitiesViewDisplay: adminContact(%@) cannot be opened", adminContactURL); - } + } }]; }]; } else { + self.activitiesViewExpanded = NO; [self refreshTypingNotification]; } } @@ -4470,22 +4409,22 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { // Switch back to the room live timeline managed by MXKRoomDataSourceManager MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mainSession]; - + MXWeakify(self); [roomDataSourceManager roomDataSourceForRoom:self.roomDataSource.roomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) { MXStrongifyAndReturnIfNil(self); - + // Scroll to bottom the bubble history on the display refresh. self->shouldScrollToBottomOnTableRefresh = YES; - + [self displayRoom:roomDataSource]; - + // The room view controller do not have here the data source ownership. self.hasRoomDataSourceOwnership = NO; - + [self refreshActivitiesViewDisplay]; [self refreshJumpToLastUnreadBannerDisplay]; - + if (self.saveProgressTextInput) { // Restore the potential message partially typed before jump to last unread messages. @@ -4500,11 +4439,17 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)refreshMissedDiscussionsCount:(BOOL)force { // Ignore this action when no room is displayed - if (!self.roomDataSource || !missedDiscussionsBarButtonCustomView) + if (!self.roomDataSource || !missedDiscussionsBadgeLabel) { return; } + if ([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPhone) + { + missedDiscussionsBadgeLabel.text = nil; + return; + } + NSUInteger highlightCount = 0; NSUInteger missedCount = [[AppDelegate theDelegate].masterTabBarController missedDiscussionsCount]; @@ -4538,8 +4483,6 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo missedDiscussionsCount = missedCount; missedHighlightCount = highlightCount; - NSMutableArray *leftBarButtonItems = [NSMutableArray arrayWithArray: self.navigationItem.leftBarButtonItems]; - if (missedCount) { // Refresh missed discussions count label @@ -4552,65 +4495,12 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo missedDiscussionsBadgeLabel.text = [NSString stringWithFormat:@"%tu", missedCount]; } - [missedDiscussionsBadgeLabel sizeToFit]; - - // Update the label background view frame - CGRect frame = missedDiscussionsBadgeLabelBgView.frame; - frame.size.width = round(missedDiscussionsBadgeLabel.frame.size.width + 18); - - if ([GBDeviceInfo deviceInfo].osVersion.major < 11) - { - // Consider the main navigation controller if the current view controller is embedded inside a split view controller. - UINavigationController *mainNavigationController = self.navigationController; - if (self.splitViewController.isCollapsed && self.splitViewController.viewControllers.count) - { - mainNavigationController = self.splitViewController.viewControllers.firstObject; - } - UINavigationItem *backItem = mainNavigationController.navigationBar.backItem; - UIBarButtonItem *backButton = backItem.backBarButtonItem; - - if (backButton && !backButton.title.length) - { - // Shift the badge on the left to be close the back icon - frame.origin.x = ([GBDeviceInfo deviceInfo].displayInfo.display > GBDeviceDisplay4Inch ? -35 : -25); - } - else - { - frame.origin.x = 0; - } - } - - // Caution: set label background view frame only in case of changes to prevent from looping on 'viewDidLayoutSubviews'. - if (!CGRectEqualToRect(missedDiscussionsBadgeLabelBgView.frame, frame)) - { - missedDiscussionsBadgeLabelBgView.frame = frame; - } - - // Set the right background color - if (highlightCount) - { - missedDiscussionsBadgeLabelBgView.backgroundColor = ThemeService.shared.theme.noticeColor; - } - else - { - missedDiscussionsBadgeLabelBgView.backgroundColor = ThemeService.shared.theme.noticeSecondaryColor; - } - - if (!missedDiscussionsButton || [leftBarButtonItems indexOfObject:missedDiscussionsButton] == NSNotFound) - { - missedDiscussionsButton = [[UIBarButtonItem alloc] initWithCustomView:missedDiscussionsBarButtonCustomView]; - - // Add it in left bar items - [leftBarButtonItems addObject:missedDiscussionsButton]; - } + missedDiscussionsBadgeLabel.textColor = highlightCount ? ThemeService.shared.theme.noticeColor : ThemeService.shared.theme.tintColor; } - else if (missedDiscussionsButton) + else { - [leftBarButtonItems removeObject:missedDiscussionsButton]; - missedDiscussionsButton = nil; + missedDiscussionsBadgeLabel.text = nil; } - - self.navigationItem.leftBarButtonItems = leftBarButtonItems; } } @@ -4622,7 +4512,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo if ([self.activitiesView isKindOfClass:RoomActivitiesView.class]) { sentStatus = self.roomDataSource.room.sentStatus; - + if (sentStatus != RoomSentStatusOk) { NSString *notification = sentStatus == RoomSentStatusSentFailedDueToUnknownDevices ? @@ -4630,6 +4520,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo NSLocalizedStringFromTable(@"room_unsent_messages_notification", @"Vector", nil); RoomActivitiesView *roomActivitiesView = (RoomActivitiesView*) self.activitiesView; + self.activitiesViewExpanded = YES; [roomActivitiesView displayUnsentMessagesNotification:notification withResendLink:^{ [self resendAllUnsentMessages]; @@ -4651,40 +4542,40 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_resend_unsent_messages", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - [self resendAllUnsentMessages]; - self->currentAlert = nil; - } - - }]]; + + if (weakSelf) + { + typeof(self) self = weakSelf; + [self resendAllUnsentMessages]; + self->currentAlert = nil; + } + + }]]; [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_delete_unsent_messages", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - [self cancelAllUnsentMessages]; - self->currentAlert = nil; - } - - }]]; + + if (weakSelf) + { + typeof(self) self = weakSelf; + [self cancelAllUnsentMessages]; + self->currentAlert = nil; + } + + }]]; [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"cancel", @"Vector", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - } - - }]]; + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + } + + }]]; [currentAlert mxk_setAccessibilityIdentifier:@"RoomVCUnsentMessagesMenuAlert"]; [currentAlert popoverPresentationController].sourceView = roomActivitiesView; @@ -4736,39 +4627,39 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"unknown_devices_verify"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - - [self performSegueWithIdentifier:@"showUnknownDevices" sender:self]; - } - - }]]; + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + + [self performSegueWithIdentifier:@"showUnknownDevices" sender:self]; + } + + }]]; [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"unknown_devices_send_anyway"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - - // Acknowledge the existence of all devices - [self startActivityIndicator]; - [self.mainSession.crypto setDevicesKnown:self->unknownDevices complete:^{ - - self->unknownDevices = nil; - [self stopActivityIndicator]; - - // And resend pending messages - [self resendAllUnsentMessages]; - }]; - } - - }]]; + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + + // Acknowledge the existence of all devices + [self startActivityIndicator]; + [self.mainSession.crypto setDevicesKnown:self->unknownDevices complete:^{ + + self->unknownDevices = nil; + [self stopActivityIndicator]; + + // And resend pending messages + [self resendAllUnsentMessages]; + }]; + } + + }]]; [currentAlert mxk_setAccessibilityIdentifier:@"RoomVCUnknownDevicesAlert"]; [self presentViewController:currentAlert animated:YES completion:nil]; @@ -4779,7 +4670,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { MXEvent *event = notif.object; NSString *previousId = notif.userInfo[kMXEventIdentifierKey]; - + if ([customizedRoomDataSource.selectedEventId isEqualToString:previousId]) { NSLog(@"[RoomVC] eventDidChangeIdentifier: Update selectedEventId"); @@ -4834,7 +4725,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)cancelAllUnsentMessages { currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"room_unsent_messages_cancel_title", @"Vector", nil) message:NSLocalizedStringFromTable(@"room_unsent_messages_cancel_message", @"Vector", nil) preferredStyle:UIAlertControllerStyleAlert]; - + MXWeakify(self); [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); @@ -4856,8 +4747,10 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo index ++; } } + + [self refreshActivitiesViewDisplay]; }]]; - + [self presentViewController:currentAlert animated:YES completion:nil]; } @@ -4973,21 +4866,21 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [UIView animateWithDuration:1.5 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn animations:^{ - - readMarkerTableViewCell.readMarkerViewLeadingConstraint.constant = readMarkerTableViewCell.readMarkerViewTrailingConstraint.constant = readMarkerTableViewCell.bubbleOverlayContainer.frame.size.width / 2; - readMarkerTableViewCell.readMarkerView.alpha = 0; - - // Force to render the view - [readMarkerTableViewCell.bubbleOverlayContainer layoutIfNeeded]; - - } + + readMarkerTableViewCell.readMarkerViewLeadingConstraint.constant = readMarkerTableViewCell.readMarkerViewTrailingConstraint.constant = readMarkerTableViewCell.bubbleOverlayContainer.frame.size.width / 2; + readMarkerTableViewCell.readMarkerView.alpha = 0; + + // Force to render the view + [readMarkerTableViewCell.bubbleOverlayContainer layoutIfNeeded]; + + } completion:^(BOOL finished){ - - readMarkerTableViewCell.readMarkerView.hidden = YES; - readMarkerTableViewCell.readMarkerView.alpha = 1; - - readMarkerTableViewCell = nil; - }]; + + readMarkerTableViewCell.readMarkerView.hidden = YES; + readMarkerTableViewCell.readMarkerView.alpha = 1; + + readMarkerTableViewCell = nil; + }]; }); } @@ -5066,110 +4959,110 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - } - - }]]; + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + } + + }]]; [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"invite", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - // Sanity check - if (!weakSelf) - { - return; - } - - typeof(self) self = weakSelf; - self->currentAlert = nil; - - MXSession* session = self.roomDataSource.mxSession; - NSString* roomId = self.roomDataSource.roomId; - MXRoom *room = [session roomWithRoomId:roomId]; - - NSArray *identifiers = contact.matrixIdentifiers; - NSString *participantId; - - if (identifiers.count) - { - participantId = identifiers.firstObject; - - // Invite this user if a room is defined - [room inviteUser:participantId success:^{ - - // Refresh display by removing the contacts picker - [contactsTableViewController withdrawViewControllerAnimated:YES completion:nil]; - - } failure:^(NSError *error) { - - NSLog(@"[RoomVC] Invite %@ failed", participantId); - // Alert user - [[AppDelegate theDelegate] showErrorAsAlert:error]; - - }]; - } - else - { - if (contact.emailAddresses.count) - { - // This is a local contact, consider the first email by default. - // TODO: Prompt the user to select the right email. - MXKEmail *email = contact.emailAddresses.firstObject; - participantId = email.emailAddress; - } - else - { - // This is the text filled by the user. - participantId = contact.displayName; - } - - // Is it an email or a Matrix user ID? - if ([MXTools isEmailAddress:participantId]) - { - [room inviteUserByEmail:participantId success:^{ - - // Refresh display by removing the contacts picker - [contactsTableViewController withdrawViewControllerAnimated:YES completion:nil]; - - } failure:^(NSError *error) { - - NSLog(@"[RoomVC] Invite be email %@ failed", participantId); - // Alert user - if ([error.domain isEqualToString:kMXRestClientErrorDomain] - && error.code == MXRestClientErrorMissingIdentityServer) - { - NSString *message = [NSBundle mxk_localizedStringForKey:@"error_invite_3pid_with_no_identity_server"]; - [[AppDelegate theDelegate] showAlertWithTitle:message message:nil]; - } - else - { - [[AppDelegate theDelegate] showErrorAsAlert:error]; - } - }]; - } - else //if ([MXTools isMatrixUserIdentifier:participantId]) - { - [room inviteUser:participantId success:^{ - - // Refresh display by removing the contacts picker - [contactsTableViewController withdrawViewControllerAnimated:YES completion:nil]; - - } failure:^(NSError *error) { - - NSLog(@"[RoomVC] Invite %@ failed", participantId); - // Alert user - [[AppDelegate theDelegate] showErrorAsAlert:error]; - - }]; - } - } - - }]]; + + // Sanity check + if (!weakSelf) + { + return; + } + + typeof(self) self = weakSelf; + self->currentAlert = nil; + + MXSession* session = self.roomDataSource.mxSession; + NSString* roomId = self.roomDataSource.roomId; + MXRoom *room = [session roomWithRoomId:roomId]; + + NSArray *identifiers = contact.matrixIdentifiers; + NSString *participantId; + + if (identifiers.count) + { + participantId = identifiers.firstObject; + + // Invite this user if a room is defined + [room inviteUser:participantId success:^{ + + // Refresh display by removing the contacts picker + [contactsTableViewController withdrawViewControllerAnimated:YES completion:nil]; + + } failure:^(NSError *error) { + + NSLog(@"[RoomVC] Invite %@ failed", participantId); + // Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + } + else + { + if (contact.emailAddresses.count) + { + // This is a local contact, consider the first email by default. + // TODO: Prompt the user to select the right email. + MXKEmail *email = contact.emailAddresses.firstObject; + participantId = email.emailAddress; + } + else + { + // This is the text filled by the user. + participantId = contact.displayName; + } + + // Is it an email or a Matrix user ID? + if ([MXTools isEmailAddress:participantId]) + { + [room inviteUserByEmail:participantId success:^{ + + // Refresh display by removing the contacts picker + [contactsTableViewController withdrawViewControllerAnimated:YES completion:nil]; + + } failure:^(NSError *error) { + + NSLog(@"[RoomVC] Invite be email %@ failed", participantId); + // Alert user + if ([error.domain isEqualToString:kMXRestClientErrorDomain] + && error.code == MXRestClientErrorMissingIdentityServer) + { + NSString *message = [NSBundle mxk_localizedStringForKey:@"error_invite_3pid_with_no_identity_server"]; + [[AppDelegate theDelegate] showAlertWithTitle:message message:nil]; + } + else + { + [[AppDelegate theDelegate] showErrorAsAlert:error]; + } + }]; + } + else //if ([MXTools isMatrixUserIdentifier:participantId]) + { + [room inviteUser:participantId success:^{ + + // Refresh display by removing the contacts picker + [contactsTableViewController withdrawViewControllerAnimated:YES completion:nil]; + + } failure:^(NSError *error) { + + NSLog(@"[RoomVC] Invite %@ failed", participantId); + // Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + } + } + + }]]; [currentAlert mxk_setAccessibilityIdentifier:@"RoomVCInviteAlert"]; [self presentViewController:currentAlert animated:YES completion:nil]; @@ -5188,22 +5081,22 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [self presentReviewUnverifiedSessionsAlert]; return; } - + // Make the re-request [self.mainSession.crypto reRequestRoomKeyForEvent:event]; - + // Observe kMXEventDidDecryptNotification to remove automatically the dialog // if the user has shared the keys from another device mxEventDidDecryptNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXEventDidDecryptNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { MXStrongifyAndReturnIfNil(self); - + MXEvent *decryptedEvent = notif.object; - + if ([decryptedEvent.eventId isEqualToString:event.eventId]) { [[NSNotificationCenter defaultCenter] removeObserver:self->mxEventDidDecryptNotificationObserver]; self->mxEventDidDecryptNotificationObserver = nil; - + if (self->currentAlert == alert) { [self->currentAlert dismissViewControllerAnimated:YES completion:nil]; @@ -5211,51 +5104,51 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo } } }]; - + // Show the explanation dialog alert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"rerequest_keys_alert_title", @"Vector", nil) - message:NSLocalizedStringFromTable(@"rerequest_keys_alert_message", @"Vector", nil) - preferredStyle:UIAlertControllerStyleAlert]; + message:NSLocalizedStringFromTable(@"rerequest_keys_alert_message", @"Vector", nil) + preferredStyle:UIAlertControllerStyleAlert]; currentAlert = alert; - - + + [alert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) - { - MXStrongifyAndReturnIfNil(self); - - [[NSNotificationCenter defaultCenter] removeObserver:self->mxEventDidDecryptNotificationObserver]; - self->mxEventDidDecryptNotificationObserver = nil; - - self->currentAlert = nil; - }]]; - + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) + { + MXStrongifyAndReturnIfNil(self); + + [[NSNotificationCenter defaultCenter] removeObserver:self->mxEventDidDecryptNotificationObserver]; + self->mxEventDidDecryptNotificationObserver = nil; + + self->currentAlert = nil; + }]]; + [self presentViewController:currentAlert animated:YES completion:nil]; } - (void)presentReviewUnverifiedSessionsAlert { NSLog(@"[MasterTabBarController] presentReviewUnverifiedSessionsAlertWithSession"); - + [currentAlert dismissViewControllerAnimated:NO completion:nil]; - + UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"key_verification_self_verify_unverified_sessions_alert_title", @"Vector", nil) message:NSLocalizedStringFromTable(@"key_verification_self_verify_unverified_sessions_alert_message", @"Vector", nil) preferredStyle:UIAlertControllerStyleAlert]; - + [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"key_verification_self_verify_unverified_sessions_alert_validate_action", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - [self showSettingsSecurityScreen]; - }]]; - + [self showSettingsSecurityScreen]; + }]]; + [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"later", @"Vector", nil) style:UIAlertActionStyleCancel handler:nil]]; - + [self presentViewController:alert animated:YES completion:nil]; - + currentAlert = alert; } @@ -5306,12 +5199,12 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)listenMXSessionStateChangeNotifications { kMXSessionStateDidChangeObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionStateDidChangeNotification object:self.roomDataSource.mxSession queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - + if (self.roomDataSource.mxSession.state == MXSessionStateSyncError || self.roomDataSource.mxSession.state == MXSessionStateRunning) { [self refreshActivitiesViewDisplay]; - + // update inputToolbarView [self updateRoomInputToolbarViewClassIfNeeded]; } @@ -5408,10 +5301,10 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo fromSingleTapGesture:usedSingleTapGesture animated:animated completion:^{ - }]; + }]; preventBubblesTableViewScroll = YES; - [self selectEventWithId:selectedEventId]; + [self selectEventWithId:selectedEventId]; } - (void)hideContextualMenuAnimated:(BOOL)animated @@ -5466,7 +5359,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [self cancelEventSelection]; [self.roomDataSource resendEventWithEventId:event.eventId success:nil failure:nil]; }; - + return resendMenuItem; } @@ -5494,7 +5387,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [self presentViewController:self->currentAlert animated:YES completion:nil]; }]; }; - + return deleteMenuItem; } @@ -5507,7 +5400,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo MXStrongifyAndReturnIfNil(self); [self hideContextualMenuAnimated:YES cancelEventSelection:NO completion:nil]; [self editEventContentWithId:event.eventId]; - + // And display the keyboard [self.inputToolbarView becomeFirstResponder]; }; @@ -5521,7 +5414,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell *)cell; MXKAttachment *attachment = roomBubbleTableViewCell.bubbleData.attachment; - + MXWeakify(self); BOOL isCopyActionEnabled = !attachment || attachment.type != MXKAttachmentTypeSticker; @@ -5609,7 +5502,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo }]; } }; - + return copyMenuItem; } @@ -5624,7 +5517,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [self hideContextualMenuAnimated:YES cancelEventSelection:NO completion:nil]; [self selectEventWithId:event.eventId inputToolBarSendMode:RoomInputToolbarViewSendModeReply showTimestamp:NO]; - + // And display the keyboard [self.inputToolbarView becomeFirstResponder]; }; @@ -5642,7 +5535,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [self hideContextualMenuAnimated:YES completion:nil]; [self showAdditionalActionsMenuForEvent:event inCell:cell animated:YES]; }; - + return moreMenuItem; } @@ -5702,7 +5595,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo if (cellRow >= 0) { - NSIndexPath *cellIndexPath = [NSIndexPath indexPathForRow:cellRow inSection:0]; + NSIndexPath *cellIndexPath = [NSIndexPath indexPathForRow:cellRow inSection:0]; UITableViewCell *cell = [self.bubblesTableView cellForRowAtIndexPath:cellIndexPath]; sourceView = cell; diff --git a/Riot/Modules/Room/RoomViewController.xib b/Riot/Modules/Room/RoomViewController.xib index 15ac46dc7..1d1205661 100644 --- a/Riot/Modules/Room/RoomViewController.xib +++ b/Riot/Modules/Room/RoomViewController.xib @@ -1,9 +1,9 @@ - + - + @@ -13,6 +13,7 @@ + @@ -36,7 +37,7 @@ - + @@ -51,10 +52,18 @@ + + + + + + + + @@ -153,9 +91,8 @@ - - + @@ -164,19 +101,14 @@ - - - - - + - - - - + + + diff --git a/Riot/Modules/Room/Views/Title/RoomTitleView.h b/Riot/Modules/Room/Views/Title/RoomTitleView.h index 882d79fda..a96cb2d26 100644 --- a/Riot/Modules/Room/Views/Title/RoomTitleView.h +++ b/Riot/Modules/Room/Views/Title/RoomTitleView.h @@ -35,9 +35,11 @@ @interface RoomTitleView : MXKRoomTitleView @property (weak, nonatomic) IBOutlet UIView *titleMask; -@property (weak, nonatomic) IBOutlet NSLayoutConstraint *displayNameCenterXConstraint; -@property (weak, nonatomic) IBOutlet UIImageView *roomDetailsIconImageView; @property (weak, nonatomic) IBOutlet UIImageView *badgeImageView; +@property (weak, nonatomic) IBOutlet MXKImageView *pictureView; +@property (weak, nonatomic) IBOutlet UILabel *missedDiscussionsBadgeLabel; +@property (weak, nonatomic) IBOutlet UILabel *typingLabel; +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *displayNameCenterYConstraint; /** The room preview data may be used when mxRoom instance is not available @@ -49,6 +51,11 @@ */ @property (nonatomic) id tapGestureDelegate; +/** + the typing notification string to be displayed (default nil if notification is hidden). + */ +@property (copy, nonatomic) NSString *typingNotificationString; + /** The method used to handle the gesture recognized by a receiver. */ diff --git a/Riot/Modules/Room/Views/Title/RoomTitleView.m b/Riot/Modules/Room/Views/Title/RoomTitleView.m index 679fd719f..1680095fd 100644 --- a/Riot/Modules/Room/Views/Title/RoomTitleView.m +++ b/Riot/Modules/Room/Views/Title/RoomTitleView.m @@ -54,8 +54,8 @@ { [super layoutSubviews]; - self.roomDetailsIconImageView.image = self.roomDetailsIconImageView.image; - + self.pictureView.layer.cornerRadius = self.pictureView.bounds.size.width / 2.; + if (self.superview) { // Force the title view layout by adding 2 new constraints on the UINavigationBarContentView instance. @@ -84,7 +84,7 @@ self.backgroundColor = UIColor.clearColor; self.displayNameTextField.textColor = (self.mxRoom.summary.displayname.length ? ThemeService.shared.theme.textPrimaryColor : ThemeService.shared.theme.textSecondaryColor); - self.roomDetailsIconImageView.tintColor = ThemeService.shared.theme.textPrimaryColor; + self.typingLabel.textColor = ThemeService.shared.theme.textSecondaryColor; } - (void)setRoomPreviewData:(RoomPreviewData *)roomPreviewData @@ -133,4 +133,34 @@ } } +- (void)setTypingNotificationString:(NSString *)typingNotificationString +{ + if (typingNotificationString.length > 0) + { + self.typingLabel.text = typingNotificationString; + [self layoutIfNeeded]; + + [UIView animateWithDuration:.3 animations:^{ + self.typingLabel.alpha = 1; + self.displayNameCenterYConstraint.constant = -8; + [self layoutIfNeeded]; + }]; + } + else + { + [UIView animateWithDuration:.3 animations:^{ + self.typingLabel.alpha = 0; + self.displayNameCenterYConstraint.constant = 0; + [self layoutIfNeeded]; + } completion:^(BOOL finished) { + self.typingLabel.text = nil; + }]; + } +} + +- (NSString *)typingNotificationString +{ + return self.typingLabel.text; +} + @end diff --git a/Riot/Modules/Room/Views/Title/RoomTitleView.xib b/Riot/Modules/Room/Views/Title/RoomTitleView.xib index 47d170601..0fbeea444 100644 --- a/Riot/Modules/Room/Views/Title/RoomTitleView.xib +++ b/Riot/Modules/Room/Views/Title/RoomTitleView.xib @@ -1,9 +1,9 @@ - + - + @@ -13,69 +13,82 @@ + + + + + + + + + + - + - - + + - + - - - - - - - - - + - - + + - - - - - - + + + + - + + + + - + - + - - + + + - diff --git a/Riot/SupportingFiles/Riot-Bridging-Header.h b/Riot/SupportingFiles/Riot-Bridging-Header.h index fbf30cb3a..f8ff71ea5 100644 --- a/Riot/SupportingFiles/Riot-Bridging-Header.h +++ b/Riot/SupportingFiles/Riot-Bridging-Header.h @@ -23,6 +23,8 @@ #import "DirectoryServerPickerViewController.h" #import "MXSession+Riot.h" #import "RoomFilesViewController.h" +#import "RoomSearchViewController.h" +#import "IntegrationManagerViewController.h" #import "RoomSettingsViewController.h" #import "JitsiWidgetData.h" #import "InviteRecentTableViewCell.h" From 07f1ec19feb1b9d68bff34731d9fe5fff87a2ab0 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 15 Mar 2021 14:36:37 +0100 Subject: [PATCH 02/78] Composer Update - Typing and sending a message - Code tweaks --- Riot/Modules/Room/RoomViewController.m | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 53ad7d9ba..9647d21cd 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -673,7 +673,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo previewHeader.mainHeaderBackgroundHeightConstraint.constant = mainHeaderBackgroundHeight; // Force the layout of previewHeader to update the position of 'bottomBorderView' which - // is used to define the actual of the preview container. + // is used to define the actual height of the preview container. [previewHeader layoutIfNeeded]; } } @@ -1288,10 +1288,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)setShowMissedDiscussionsBadge:(BOOL)showMissedDiscussionsBadge { - BOOL toto = [UIDevice currentDevice].userInterfaceIdiom != - UIUserInterfaceIdiomPhone || !showMissedDiscussionsBadge; - missedDiscussionsBadgeLabel.hidden = [UIDevice currentDevice].userInterfaceIdiom != - UIUserInterfaceIdiomPhone || !showMissedDiscussionsBadge; + missedDiscussionsBadgeLabel.hidden = !showMissedDiscussionsBadge; } #pragma mark - Internals @@ -1633,7 +1630,6 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo self.roomInfoCoordinatorBridgePresenter = [[RoomInfoCoordinatorBridgePresenter alloc] initWithParameters:parameters]; self.roomInfoCoordinatorBridgePresenter.delegate = self; -// [self.roomInfoCoordinatorBridgePresenter presentFrom:self animated:true]; [self.roomInfoCoordinatorBridgePresenter pushFrom:self.navigationController animated:YES]; } From 400e437d371bb5792283da6903c9ae9b0e6773f3 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 15 Mar 2021 15:12:15 +0100 Subject: [PATCH 03/78] Composer Update - Typing and sending a message - Updated shield icons --- .../encryption_normal.imageset/Contents.json | 12 ++++++------ .../encryption_normal.png | Bin 384 -> 339 bytes .../encryption_normal@2x.png | Bin 627 -> 537 bytes .../encryption_normal@3x.png | Bin 913 -> 760 bytes .../encryption_trusted.imageset/Contents.json | 12 ++++++------ .../encryption_trusted.png | Bin 476 -> 414 bytes .../encryption_trusted@2x.png | Bin 804 -> 724 bytes .../encryption_trusted@3x.png | Bin 1156 -> 973 bytes .../encryption_warning.imageset/Contents.json | 12 ++++++------ .../encryption_warning.png | Bin 423 -> 363 bytes .../encryption_warning@2x.png | Bin 688 -> 601 bytes .../encryption_warning@3x.png | Bin 980 -> 836 bytes 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Riot/Assets/Images.xcassets/Encryption/encryption_normal.imageset/Contents.json b/Riot/Assets/Images.xcassets/Encryption/encryption_normal.imageset/Contents.json index 91448ccb6..3b025c1f0 100644 --- a/Riot/Assets/Images.xcassets/Encryption/encryption_normal.imageset/Contents.json +++ b/Riot/Assets/Images.xcassets/Encryption/encryption_normal.imageset/Contents.json @@ -1,23 +1,23 @@ { "images" : [ { - "idiom" : "universal", "filename" : "encryption_normal.png", + "idiom" : "universal", "scale" : "1x" }, { - "idiom" : "universal", "filename" : "encryption_normal@2x.png", + "idiom" : "universal", "scale" : "2x" }, { - "idiom" : "universal", "filename" : "encryption_normal@3x.png", + "idiom" : "universal", "scale" : "3x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Riot/Assets/Images.xcassets/Encryption/encryption_normal.imageset/encryption_normal.png b/Riot/Assets/Images.xcassets/Encryption/encryption_normal.imageset/encryption_normal.png index c628f322ab8e7df9d1f339202ed5a039f60e8fcc..6222e3d16183125ea075150fac17c015ad0f7b0a 100644 GIT binary patch delta 297 zcmV+^0oMM21JeQ_iBL{Q4GJ0x0000DNk~Le0000C0000C2nGNE09JKe=aC^ae*ox7 zL_t(|0lktz3W87&hDQ%z?II{>Z;O`Y$|9(3igtjgb={yF)By^&<|T6VY}q2$3AXp) z1zs~EWuQcxe)t)f`Q|;|n*qorgs7u+^d5cB?NAO1k}%Q1xI^nK2 z8MUJZBV3<%fRJ`7^)Vg}`gE-m+@S^y<{$YAmdf)yS*h0K=ht`0VyP^BE#JpVM;k9f plFSu?Tvw>SU{PGKDf^Hy4)#TendXDDH9-IX002ovPDHLkV1fpOk%<5R diff --git a/Riot/Assets/Images.xcassets/Encryption/encryption_normal.imageset/encryption_normal@2x.png b/Riot/Assets/Images.xcassets/Encryption/encryption_normal.imageset/encryption_normal@2x.png index 07555f27b507248350c4bf6b3a9d187cb4e52caa..e1ff179133623f6f007e0a60d900763a868f7ce1 100644 GIT binary patch delta 497 zcmVmzre*vyZ zL_t(|0qs^jPXa*@o%07EoiTxg#+Y~%7RCsTXp9`CAJi6#tx@QOT8R7uu|qLRi4|u9 zc4{Iu1nl|v1MAFY?^rGedWUwr89 zW4JW327>{7HX2Z=JVWuW#OHSK`+X1&g&-D-^62q+K+?L5~K@m*@)fLyewn(4xVnwm615f7EEo20C91 zs#BeUo?es*!iQ;qHG__$Q^IND7~DeBqQ<3;nk~~nAVWhUal)&lQ_!AN^1_%Fj0@91 z)bBE#;_;#6$b@pGJq6UdHFf1g>SP$+kbzF;3-%A=K+%DC3%O;y9$QfB6MdKCDw|_# z>le^x9h|Ag2R>~8i?pttBdMWa#nyJe`oy^TgjhRN^u zQwO&d-Ag}fnwD?{;KY+MX~2FSNe#nbKT|T9Bsb~0PQl+YuRF^-2h@ZU@iArwxu}c2JnCbHWIJ3w78IKUXlv{I@6#XA0H#Hgjl-X zf@Z?L6BYn)C!Ws~S;X_6ybFkdSW+8mf5BJ45GWoFv3s4Lom2#YYtPS4t(_1t)w{Ry zIbA3#B7@zH0%T@(p=2SVe+pI7!Tw%u>SLU|$7!77Iv^hf-ozO0;`ZkHPwxcA;?8CA zh>YbZtyC~n!2Gj}b~yYkM@O diff --git a/Riot/Assets/Images.xcassets/Encryption/encryption_normal.imageset/encryption_normal@3x.png b/Riot/Assets/Images.xcassets/Encryption/encryption_normal.imageset/encryption_normal@3x.png index 85e974954d12eefba707fde9226614a6d3feb9d0..ce4f7314a88157712fbba394d5c79bc433a1dcb3 100644 GIT binary patch delta 722 zcmV;@0xkWK2lxdciBL{Q4GJ0x0000DNk~Le0000a0000a2nGNE0O0_bn2{kge*%q3 zL_t(|0qvMEZ__{&$6xpWAxsDD@xTnzp`a60zkw_R21cj^{yGXS&8j>A+|(=c;$P}FtEW;K66j~@qSDIpk~4G_`mR+6U-D4p2q&1G z`ww)_LVj9FvQtH%Ejw_1_I%AmhU-|IufKf*VN1AY&j~XI#MQJ9sP28;d<9`?Wx2ub z>Cmj$XAFpEsbR;p+H-cJi;5mhm^)1zt2Z{j*9~w!ZNllok%^W>fAz*v;5kKehF+79 z8Cn=F%KCf$b>Ib6y3meKmn|)*l;^>(=u7?jWlx0J@`CXt%+#v5wQ+phw_es8*3DaK zsp+@Z2$lO07*qoM6N<$ Ef}o^NKL7v# delta 876 zcmV-y1C#vt1(63KiBL{Q4GJ0x0000DNk~Le0000m0000m2nGNE09OL}hmj#Pe*-2- zL_t(|0qvQ;Z__{&$6pX@MOQLqB20iFok`fK!U(AZT}nCuJ0(fGfhxk(z|vMCu+qTX zn3V>Vk+Q@~!q`ap1I+u{=hj@#j`PF0fc%l3Y+tYO_v?G-7M=!EwNI z<`Yd~K!}Fm+v4ae`jEv!^a+c1MF-)US;}N6nY`@R4U^yCfDst#Q5XYaf0FHz^Td5c zABd;iYJI0k$YgHO^77qbW&n(=%0^TM%6urv?cMHg7TvG~Pb%6Em~ql9=V#^_8>o64 z0u3M~4x3F*k1uSXOS2@MzG53%DHObBDr}(a?RPQgLm<9Zdq+CJ!$aT&uwH!Z+wTJX z(P&5#Y6eA@>{}aXlid)AVDYxzAt8J>+ zKhQK$h&MTM!^-kuYojSXU?fs*8_F#^wYM}4kn>X7m=r=h2))6fGJ(<}Mq}Kf7v6By z*W2=hH7TTcFwzcFDxQ&DfY-<#7B60LfkE-NMTWrtl-T)?5RDa zc}ApFSu%1e+NPc^k!^GXj0hhtaqN%sYK)1A3C|y~)ndIUFZeJ30000Oon%B)(Ah!&DW7Ju47o)d0JYkq9|O~X53?QePlZNdV=wz>RQG{N^L8?L+Lbc z^CB}RjzGQg@)qTAC=D==0;PNV+Yt8Kjo7YL1l3XE1xN#>jyy>`jdOZ3NzD+MD4C<8 S>~69E0000D#}xurJ&Ye~zW{Jl-j{r;Zj+yYDxAtXi(LF7Pt=+^sp-ylP^ z&>}khcR$Ax!Ps=cYOlmb4U10k7(SUA$gM>Vv>ah$>eDpF55 zI)0Hrw%!0kJUc8brrOe?jLEkul{6gFXs{_-s12a889CrgJz{fiXilFw*f8o?zf0QoW(GF^S zB4auQ*p-`5f0aF9A(^AM61{M&fTP@(E$w9ezV0~*_DT6@!XDvRfD=-~5V7=liEp*W zVEX2(l`RUNBU}bA(L<$t7-Z+^n#|l(ZI>P{i19U`N4f%jM9)0BOM}{am;DPx2r7ii dQ-MMB3rHo+VFe!P(*OVf2>?k&PDHLkV1nC~zJ&k) diff --git a/Riot/Assets/Images.xcassets/Encryption/encryption_trusted.imageset/encryption_trusted@2x.png b/Riot/Assets/Images.xcassets/Encryption/encryption_trusted.imageset/encryption_trusted@2x.png index 02488c979930704f7d79ed2bf9926c351b6b090e..361df06c02c08eae8e223da728cf8d100c795b30 100644 GIT binary patch delta 685 zcmV;e0#f~?2Gj*1iBL{Q4GJ0x0000DNk~Le0000O0000O2nGNE0N{5$_>mzse`!fX zK~#7FrB+L5Q$ZBHDNkELeb0qb!Gs99swq-;A;om5?ZTaqE?l{=8@IZ$-Kfi~bZG;A zSFKQVnMkXk5Pyldu$ZI~m3EQWA|ztrdC%+2IHsAG-+?oe%;%i>+?jbqZQyyH!%Q;M z%#z?elu@q`qF0#<%$jFLO|-ehe~h-$3TdKA&dxHEf(pxSl~!t1T4n-Mhfh%AP?$#d zA8FQm&wRmL4242DWx(RIoZXPE-0cPxer?dw`YJvAS)|SF-_#0`p@WogBEs+(Io6O~ zJ;x@q)_)`A5#B1jV)1pOsE)P9mOlUxNH}4-emqX&JUfX2y~7HYg4v09e=ErTItHju zVANPD6Z$fk9v-1*V;3nke9G9yq8@>@1}lwCy8A6}Y}eBabn(fzPXhO>YXd0`Kpc@2 z1f#+2lM_^oU#3ZpalK56U;C16D-Ia;m1X=E>$mG<|f$ufthEStpPN zE)v@sR2A4bXV0KN;+gOne^tJ;vFaO`plSG;a*4`FVoi4ogdY6(r2b=5uY7(^L5Lkt z8YO)OIV7W8i4o{g0YTf^OObsJr9LhMMhyCnJL;PFpk~0A5~h8>f z_rDjs`0I>!F8|oO`Sqpe{;BJ4b6U*4J8-Ad%xu7;pY7}GqRD7p`)o;fD*P7<%79Em T^Loqx0000z0UuIA}s#9K_bittk$5tJ1;EN*$e&ZVu8U zyR_NtpluvlIuw&Zp@Jr%f)$jWg0*1be4j7hC71h`YfMuLec;Vq?!NDRe&64GAv(^| zbv=%tZc3V_ICk`g${h2D0>epzf6{c`Vyc&a@95>-rboIggR*0&+bX}}X3Cc_jwDBv zqn@bnqry?GU!h2Uh>~X_6dO{-y+w;M3W}msJOS8vLYl_S&taC|-lY7uO{(r{Wu1-z80O(X zZOfSZye^h)R{*T$Pd^rD`oog1y1oFgsqEjHp^a;kwsCbbNBEU50Amx041{Ro`lK)w z-}{I8`~9}fodCo>s|K2Xe?!tdo|QkfEg0|KaEs9^XnF8-uP<{0+VF4#DB@#uc%!IuS?U3&YRTmF;&e@7U5OjJ2s6IKA* zD&PV*Jfu+plg%`F+A=py)U?%H_&HUSC!%GJC=MWrwd{d=0!tJVRd#)m%`ZBYQ`{1m zAut>m>$&ne`4Pr^h9{kD`3lD!2G9-!U;;_;5VzjIF8LyQ+q{aIk6h%m>3arUn8k^J7fMLn^a(Tmzw^Ravx vW$Cblle7FIVpUl#;qJ%;f=JSdj;sCwPKd4rD_Fx!00000NkvXXu0mjfA9!#b diff --git a/Riot/Assets/Images.xcassets/Encryption/encryption_trusted.imageset/encryption_trusted@3x.png b/Riot/Assets/Images.xcassets/Encryption/encryption_trusted.imageset/encryption_trusted@3x.png index 9154725c9392d4f51d3798edd699e14903b6f34a..7abd021d44f8d89d579b9d5c0828d00b38be52c5 100644 GIT binary patch delta 936 zcmV;Z16TZn3C#x~iBL{Q4GJ0x0000DNk~Le0000a0000a2nGNE0O0_bn2{kge*3(;v&5F}cKAtbpEgVX5QTQJ@ZET!{NR+^WMF`cka3Oo_mRk zR5qIpu=TRdvKNrEYzZ0~4v`;HPB6P?=Mrx|4rv7tnG*H({J%L8DQJC#1gN0Ec*KrDUx zNE^vGZKmRsKK?^)RO3HQ-3&EQ+iK-Kc&g77XoDNxWB?VyO9iWH8Lo}`8Sqk7oes!v z^wP}NEzdzzq03?T*w>fu=>9)N|Cvq%2yx_8Wd#DrT>CAGUw@zkwiEfOOacsIBq##c z>F6wrhcFLmVP+1tIFOGVf4%Fuhg6s*G2c9kzf*vm%2N-?Fgxs($;g_kIy?iCVPcsy z4eo@trHh8@J9IFBUHZO9#mbR~u&$vYf7MHflfAC{y64=E zC?jb-6jLvnE30)6q9Los5X5d^nkx&^khBp{E^`a!(*hloQBVU0LF^`1LhNa@Z3Gmy z1A-* zM@3jZMnA8*9^Qx3#vZ#G-vk=|cPlL?dKFU~Fi0000< KMNUMnLSTY8)vtH} delta 1121 zcmV-n1fKiN2ZRYBiBL{Q4GJ0x0000DNk~Le0000m0000m2nGNE09OL}hmj#Pe*_sx zL_t(|0qt4OPZL2Dekdm50U9*{6A6VxA$n*p1~rj_B;pCsgGUfeIHGdkrhtC{0q^|k z(ZmGw#DP#QCdLrxL4#hp6%QrBP)q<1ChPmAyG_~7>~6O$#iU>IGP~1h`+f7{y_q*e zWt0{bR3zxS?&G4LEy=c8QBk4Me@O?E_Op$$t?1c6-7~bIlLRKzZjg@Y?y=4`$rhwi zz#t6*yLK!iJ27DnK!aKQCV7CXHK3>FGkU9+QuEp;YnS}{hoXB43U5VeG4V~j1HQ_$ z)Kk|?-E}RZw1LJJVO!)ivq1R>Fi3-N$S9Sh-!s|ytqmr>ohEGuA*Qy4e?k`<$Y14C zHN)bMut5j}CCQBdW0B2Ls>hY2!#}u}w>N3^SDf-e=t2YaT@E^l0U%_+Dnuq=jEhlg zy>^hK@Xsg{*77E;0>t$2$aG(7R*8AR0wyynD(!wS`a2=!@;nodA0(;RK*zv_zb;Xr zx=sihYHH0Clcma50LZ`of4)kk06GumJ9Cy^T@5MQoudC7WdWr|Nd*L|YiX@*kk;-D zIt4f-N+tm0mv8k8Zxg7gqv=2oc|l18z&(OIJdBAw7ZF^BN;+or*9WqC8U!smNI-U6`GbORar_+c_H@_=UnAhgppLOb_IY$GLrf6ry)@8(G!@GRiv zl~8&Sf}l!->@$-*%6#EjfYocrh!&eXAQPaO5B_uN{W-jbW+4Hx6W8vgp9?`A@gf6R zUzE8~&3tG(-#{vu3vl^%-;urhP}5EJ^L`PWC{OZ$r4u)-pUXx7jgsbUo5!R6? z6#0g#8WE{Y@L?ume{GW`z&Ns{mG=BYFI1fBliP(Y&mkRDdy5FySg^g1zJfs|cK(@t*X!$C(Kpi5n5w^buT10VC10 zV!q>Od0fX!i3e*`^g3SWzkl zB>N>yoesP2xDfzHyw2t~>xRE9(VN&ODkLQ2HMS0?w7lb1!f>k{lo8-2u6H?Z;BtXv zv!{^@@`4|^gpqQ@bn=^Zi-}D!8u7zD@&atd-`#j5B6kKfa!DS@>4wIbPRN_4{VFnc z@J`6y;xroQ_32=@EW*Rvj8Ts8d`D)) z35l`jhnq{q%=%VR(-1 zOD}UKjcHk0Fo|AWaz_Rs3gR)ZS4u?%lX|S%U-m)GH*-44Fs9X*MaZsESMcdlw2N*4 nBfU-I?zo}cgDd(H(y_`aVbn#48n5pu&mBp}G4lU)%_*tZ<)DcR z8e5m*7I7-1!reJyz6_F7DCB{~e+)46rY0Snm`tve14ekhDDKH$O{vI7VJe_52-Q@` zD82R#$Y%c*TwoM`y?n`ZfuZx)(S#5#1ub+taq#W{r*ph+nJi#Yt`ps1ib3D~c1Ct3 z>>B&VL!QHkmm(6Oc~r@4aj_P< zK~#7F%~LH;13?gdSIHr2Km!s|Ndm4OgQ>w3gFyfW6iKf^kW>&7f(L`)M-&tdpeP`@ zljIOIVHZ2h>~>j7KhkSWU-Ir_=gsWwzFlAdh$t^a7POOgnRWV7pJY*ImYH+^^?^&` zSDCpEt&6yO1RD21^TnzPOTfx9f3V67!y6vCVBQ8nkhlU7iz~Rg2Hp}tf7`?bo=|BZ z{qR#37J-eD6s>ZBDEAv^zFN9u!4@}l9Ch~WbZz7?CMKsq3$rubQQ40)?H;$Q(nBc%`Q~@zr>6t6bqBTQ%jH{r|kQs zvCS<<>}ovtnI*;=@xO0}a;WG%B|14LS}R!|)rgcLbKr)7oOxz!3Ozl$yHG?{aMI>T biCORiGZCXIzK8&^00000NkvXXu0mjfoZ6pi diff --git a/Riot/Assets/Images.xcassets/Encryption/encryption_warning.imageset/encryption_warning@2x.png b/Riot/Assets/Images.xcassets/Encryption/encryption_warning.imageset/encryption_warning@2x.png index 15bd69270b338ecdee0bdd7bc1ae973c4cd1e16f..d99aa7fa100f8fa3abfdef726c5b88b9a2176661 100644 GIT binary patch delta 562 zcmV-20?qxf1=$24iBL{Q4GJ0x0000DNk~Le0000O0000O2nGNE0N{5$_>mzre*x}E zL_t(|0i{+UP{S}3{pF^p!LUhsljFFCfIEGKS|rPYqtd6-D`SH@?P@ulN4YIJkOg$mXQr)SMZ#^!CxUntH^6) z-%CedwAn#gNm_;^TE=1xSr!cOf7U_1a}r7`080gcycO4bK|UZ`hG95?0gBsL90bjV zLx7J@fX63*J8U-`K_asAXtW5hTI7bNDc9D}#Lh%ViX*(dLUA`;R7UR`;NIaRkjeHI zHx-zP8gw{S#FOCnmoNi5FlRY6pl(3%Jv-*qFv%#lWTQ7jGsQ5f&wv)xe+OlmBJ|e4 z7uHAu^$-z$Mw}YZGwAiSv&c6XuxE59j^prTdi+BTrs=PN%bxj#$(?iA$SzGpsL_=T z9A0Tn4nZA{tG(UydK!H=fY-nP52XP!p$3)BbO8#{=|y^dsp!8ssObg<0yXxmZ17Vu z3G%@f2lb(Yj-%#e{Qi^)a`VWG;!*aV||*a6-?8R+tE#i#WT`^FWkBNc60|% z7k82Cet5qQ~usp-bW6Ah%9*vKmTK(nFC=;l^rN@rAb6OOG%X!ailyXHxQ*vq$FKBOht!G0Ug3L zNQeSlKn227AZRQh=m3}46u5zQnSXarcem%evn+!^@-L0=wKwza<7UUeLzYr1N5fsp z1}%wQsz7IA3%3FzPM_MK3r8HU1&$7n#g42U zn|sfPfp)9j2fA-7e7{KGU zjXUXa;hO+_2%MMZ(%UEae1vYrpuUC%(XF}%=#lyWeSmuh(9*xmV7*r0vxO9Zn*L=j z&(hKg&}d46xiXnBOH9e(7*=j3tbiNQr~p(pkSeC$pTE(u3Sa%Ra`?780({ugw0@fH zh3IQYPeDDr6UG4KA?N3Zf4(4b+FIbV`B`J}8X(#(=zY$q?KL z*(+M%OLB1fpfB<1_P#AZh_t4iCqbOngLA(!3X7w{cg@h(B=}#nQQMM?vSryeSdww2 zbJe>G(=Rey0M0pw-SoUF_SsHv=Kvv)k{tf%3sy%2Nh&_LMgRK+U;Pys%_Q}_V*m?5 zClg?C+PE+fTh(ZzXs_AZNeNy;uOtw|Vylsoe0o-?|3#%v&cbE+HrwL1#LC(J5pl(P jdEAvkAha>`^-y&KCp1Jv;FKP!00000NkvXXu0mjfAIL7F diff --git a/Riot/Assets/Images.xcassets/Encryption/encryption_warning.imageset/encryption_warning@3x.png b/Riot/Assets/Images.xcassets/Encryption/encryption_warning.imageset/encryption_warning@3x.png index 2629a149aede296257c10ae790be93ec0d9bff85..33b1b439721b6b9f01ba5f68a0e5729ef2a54dd3 100644 GIT binary patch delta 798 zcmV+(1L6GC2gC*;iBL{Q4GJ0x0000DNk~Le0000a0000a2nGNE0O0_bn2{kge*)P_ zL_t(|0o|BCOcX&B#@`t#_lj#gOD~ohON>?=(Mk(!Y;5j~!NO>O##Ts7Y)r&LOF0@6 zwGev!2d=d25EEAqAV}?KGF7$0QkWm zyL;w#^-DyzM~QCT6oA=s+1@kQavW&Q2m^=}_V(5vVg4bNl5>%vt~1xQ_#+7NIs)MGLOEq$>ENz=9`4E?`(mH7DroiUv`pb@eaX4F%j zoCiEsmcFLXyi)^u^iWK1sLC_y^;@!$eaNbgsykJ(l|9gbuj<`hvXwnhbj*}pG!3!` z@_loCKn)>e5wCRZW*Br1e?6p)A$uVEbqHa??81P?H)@DLJ7KMkEkePME4=dLIh7Vx zc%}X0N;G6@=VsUzWD0v8{Q>#XpRs8Ia#iCiR9M)2eeMu_`XK6sfwX7*&IUrqc3iBL{Q4GJ0x0000DNk~Le0000m0000m2nGNE09OL}hmj#Pe*1ZlAeg8`)1eCs?Z8q7l7S7W$v=R?zyd>( z1qrcGB$$HgP9Q)6fy&sJjiy7pl=XOD?()O-`C>abj*vg;$9z=tuWU88OOyl@X%ILv z()_+B&5Sl%9rvUN2($)-^kv)HzI&Ag^@1SrxVd`%twNq_i4^qxuY+XK=jBZwQxwFSjhB&LPDP3=HgfgJ?b{1>L?iEXLyzW&t4o;uS^03P1SH znmpfr7ydpNJXkh6xQ_;>e<%*jo+rAuE+5__06&ib@d#MFsA*fEI1rD32_hAcG*SUc zBNdP|QUOWhzZ78i&+tu?H1JhAfG4Oi3($Sj1cC0;J8BH!9K>(#zjXr8Nh+P=i{~#; zXdvjp=kq~FMsGMrFccObKQsdz%vkO&o_GKu8%JdjQRp@xLPDS(e-D&h}y+#MKfW>SG>$Jek zy{t8#ZMeJ4H_1X6H=m|Q*hOk7&dNXd|`=tt^{COgAj=tQsUlT z*QuB;39=Kf((vAZe+R%lOo*}%c*)yjaGf54=0^vu5<97m~h@9=cu5rhw#uP!$@L@I~IV*l$G2G0IL0(xpwg z+Yex|t@`RI~uW S_0IqR002ov22Mn-LSTZ`P^fkQ From edce991e141811685072cd1b86d591d653e321e5 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 15 Mar 2021 15:57:36 +0100 Subject: [PATCH 04/78] Composer Update - Typing and sending a message - Updated typing animation after UI review --- Riot/Modules/Room/Views/Title/RoomTitleView.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/Room/Views/Title/RoomTitleView.m b/Riot/Modules/Room/Views/Title/RoomTitleView.m index 1680095fd..b9ae630f6 100644 --- a/Riot/Modules/Room/Views/Title/RoomTitleView.m +++ b/Riot/Modules/Room/Views/Title/RoomTitleView.m @@ -140,7 +140,7 @@ self.typingLabel.text = typingNotificationString; [self layoutIfNeeded]; - [UIView animateWithDuration:.3 animations:^{ + [UIView animateWithDuration:.1 animations:^{ self.typingLabel.alpha = 1; self.displayNameCenterYConstraint.constant = -8; [self layoutIfNeeded]; @@ -148,7 +148,7 @@ } else { - [UIView animateWithDuration:.3 animations:^{ + [UIView animateWithDuration:.1 animations:^{ self.typingLabel.alpha = 0; self.displayNameCenterYConstraint.constant = 0; [self layoutIfNeeded]; From 9544d156d8f53e1e03e3e20e03a0eae594ad2b0d Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Thu, 18 Mar 2021 07:06:59 +0100 Subject: [PATCH 05/78] Composer Update - Typing and sending a message - Updated input bar paddings according to design review --- .../Views/InputToolbar/RoomInputToolbarView.m | 6 +- .../InputToolbar/RoomInputToolbarView.xib | 67 +++++++++---------- 2 files changed, 35 insertions(+), 38 deletions(-) diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index 3afe8c8fb..140e841e9 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -86,6 +86,8 @@ growingTextView.textColor = ThemeService.shared.theme.textPrimaryColor; growingTextView.tintColor = ThemeService.shared.theme.tintColor; + growingTextView.internalTextView.showsVerticalScrollIndicator = NO; + growingTextView.internalTextView.keyboardAppearance = ThemeService.shared.theme.keyboardAppearance; if (growingTextView.isFirstResponder) { @@ -187,7 +189,7 @@ { [UIView animateWithDuration:.4 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:8 options:UIViewAnimationOptionCurveEaseIn animations:^{ self.rightInputToolbarButton.alpha = 1; - self.messageComposerContainerTrailingConstraint.constant = self.frame.size.width - self.rightInputToolbarButton.frame.origin.x + 4; + self.messageComposerContainerTrailingConstraint.constant = self.frame.size.width - self.rightInputToolbarButton.frame.origin.x + 12; [self layoutIfNeeded]; } completion:^(BOOL finished) { }]; @@ -196,7 +198,7 @@ { [UIView animateWithDuration:.4 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:8 options:UIViewAnimationOptionCurveEaseIn animations:^{ self.rightInputToolbarButton.alpha = 0; - self.messageComposerContainerTrailingConstraint.constant = 8; + self.messageComposerContainerTrailingConstraint.constant = 12; [self layoutIfNeeded]; } completion:^(BOOL finished) { }]; diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib index d0b4ac337..fab7ebe7d 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib @@ -10,20 +10,31 @@ - + - + + - + - + - + @@ -31,50 +42,35 @@ - - - + + + - + - - - - - - - - - - - + + + + + + + + + + @@ -98,7 +94,6 @@ - From 86a9abc5ca96fe0dc39988f8049feaa86d5e0c56 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Sat, 20 Mar 2021 21:31:17 +0100 Subject: [PATCH 06/78] Composer update - UI enhancements - Composer height =58px - Frame = 42px - Replace current scroll to bottom implementation with a scroll to Bottom FAB on both themes - The text inside the composer frame should be centered - The padding between the "reveal plus" button, composer frame and Left + Right sides should be at equal distance on default mode - Verify that the "Send a message..." String matches with the colour indicated on Figma. - The padding between each component (buttons and composer frame) should be at equal distance on default mode. E.g : It should be at 12px for each gap. - The input text jumps down when users start typing. It should be centered at all times. - Remove the Scroll bar in the text frame. - DARK THEME --- .../scrolldown.imageset/Contents.json | 3 - .../scrolldown.imageset/scrolldown.png | Bin 733 -> 588 bytes .../scrolldown.imageset/scrolldown@2x.png | Bin 1306 -> 1071 bytes .../scrolldown.imageset/scrolldown@3x.png | Bin 1723 -> 1615 bytes .../scrolldown_dark.imageset/Contents.json | 23 +++++++ .../scrolldown_dark.png | Bin 0 -> 776 bytes .../scrolldown_dark@2x.png | Bin 0 -> 1442 bytes .../scrolldown_dark@3x.png | Bin 0 -> 2031 bytes .../Contents.json | 3 + .../upload_icon_dark.imageset/Contents.json | 23 +++++++ .../upload_icon_dark.png | Bin 0 -> 823 bytes .../upload_icon_dark@2x.png | Bin 0 -> 1563 bytes .../upload_icon_dark@3x.png | Bin 0 -> 2232 bytes Riot/Generated/Images.swift | 2 + Riot/Managers/Theme/Theme.swift | 1 + Riot/Managers/Theme/Themes/DarkTheme.swift | 2 + Riot/Managers/Theme/Themes/DefaultTheme.swift | 4 +- Riot/Modules/BadgeLabel/BadgeLabel.swift | 2 +- Riot/Modules/Room/RoomViewController.h | 6 ++ Riot/Modules/Room/RoomViewController.m | 63 +++++++++++++----- Riot/Modules/Room/RoomViewController.xib | 25 +++++++ .../Views/Activities/RoomActivitiesView.h | 10 --- .../Views/Activities/RoomActivitiesView.m | 56 ---------------- .../Views/InputToolbar/RoomInputToolbarView.m | 33 +++++---- .../InputToolbar/RoomInputToolbarView.xib | 26 ++++---- 25 files changed, 167 insertions(+), 115 deletions(-) create mode 100644 Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark.png create mode 100644 Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark@3x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark@3x.png diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/Contents.json index 8d7bcd9ce..4e597c1e4 100644 --- a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/Contents.json +++ b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/Contents.json @@ -19,8 +19,5 @@ "info" : { "author" : "xcode", "version" : 1 - }, - "properties" : { - "template-rendering-intent" : "template" } } diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown.png b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown.png index ac61fd1ead4bb29ef04f8ddba1bbc52f2720aaaa..4e639fb9b10123c918815eedb85e2b73a9c708b2 100644 GIT binary patch delta 548 zcmV+<0^9xF1xKx9hLFt$RWwAf;$9O* z*gfxOLicKF9&sM08tk`woHQvG+^;0&xseG$9F9iD{bOaoNlOFalAtU!-3;s_eD?T9 zbH?68`0zWB;0&Y@4c64o2G$WBYiwZNuh!GcD-Ms(a9?}&s|vH-!9n%}f49{KNMNu5 z-LF>5?BMPF1Lv35f0|(NqrwGDs^4mVLIRx)_%0Ny%Zo@Qm#y+36JGJ9g*j|(CLuvQ z21!!Mm4POD#do$hWShkV?9V}JxnvVesa&;8Ftdr7p~YF7=K-Go4V9-#x^4LwGtm&L$24Id&2nONxQ*fG7-46lDAX_=VcoBN m4U&kTm!VGK$P8wJW030erP zg7O|$w^FIJWo#-@z;rtOXS3Nfe|=Dt0Jl|DwU*1}ZM9mpV}nPd5rLPoz%3u}h`v{) zQt4J2JRA;96ism$nSfu2C@`Qc6oxpgT7Wmg57bvs^An&{9k&059|R-r_xlE%q9s00 z4z3G~{5k-`do^sA4xB-gwX#~RPS`({N<9d9P0{ZZ-ad1u8e8U994N)Df0E5+Z4ple z4A6C27mRIVh$9ID{LT2FVr^@2N4uxoD5wH@CXOrm)enNdizAEio@-nb$CpO%hhp)b z%B$jwMDR?eAx|5LHd408B-oZ{XqC(5Qy9K_=RLz=J(J0NL)jJ4ny4_=lW3@o$72QX z%Pnw18wiEgd_LEhokSU9f40IA%cLr2i^bv}k^==nK7i+Pxd4jt1cBjBA{aS1jJgWL z7Xm%kFVeeVv<*l@*Yzpt?lbfkF`%xmaM@Iz6u7w&5<c(|Wz; z899~gSR{xu6&K3Uf@QHDBmx*kGm%J`LQ>2oQRHJJgRbZM$={2de`_!pm@ssxB=05F zajvnp=l=bhWDmz=3tnf40}AK8g+#v|e)cA0vH`zNJWtKuqOI4^tze2%g0F1IgvlL1 zI_r6Odc%tP3&SMPK=r5RxjWl?K*sAE?i6m-_u54o^5vySIui#NFQP2?Ak=Ly^tJ(< d>;n3F0%Ol6*^gPm4I%&l002ovPDHLkV1h!(IEDZK diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@2x.png b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@2x.png index 490d720c2c941ea2b300257991b774ce85000abc..aabb2b1213f7c4fc8e41418c31392fe28bff4372 100644 GIT binary patch delta 1035 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 literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark@3x.png b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..14ae3aa1b10c73fb47cde7d1a40d0e5380bed409 GIT binary patch literal 2031 zcmVv6NPz($GlIHb|(gkSdTWmHY)w{(=?>35iS7 z3u#kO(zF$jMsgxJm5)Y3aN=V*P;O07q$VQS9p+ts;MiGv?cE*kI`g~4UOS2XJTvdR zJ3I3ZLqfSSozHe>Hv5=FAEdMx6*Ih(c*TTr_IU5O}tpEl0Ew~c#F=2U84;@i6l%C+SCHHm--y_ah{b@(>Ab}=&L!3A zkD~)%921TZ<8dVG$PM`X_WWtr#pM)*o1nzf8zM7+lI-Kcy{JyOKSW2vED;iffd+5ieW zrseyw`Ysu>3Rkt6zpT&xA^0fj4EBYqQDMa>%0um?=e&Z#?A{0a=s6Cf$qHDCu0 zF+|OZ8nWoGH)K&a1aZDO1|xnLvTDfUQoT3$_5c%lqQhk}H*)Oo!gjCu_5dY*pd?~w zO!2PZ_OESskXk=X=!p``Z@h?af6%o4s9|6i>3XXAc#Q$3^~Z)cEvNb*uRUN|e_Ryj zL~UV<0oQNM(<}xHhPbe3s!rvBEe1#_&%h8XG$ph0dHFh^c-8v{46&mI>Uv58b^rGO zu`vLH7Ali**#H3^>gKW`I5Zu<^wzU0y6@s9X-+%iR-+r@*^|duT`}_-d z<<&!I2lP4S=f3hj$KKvv92*)&_wL=$1uetPcQ{#m^eKJzG1qh`v0TLM>4T)#8_+#z3D%8%le1 zO%U;d=oZmsf}i*$bTxtk(&Ea2C!mqN`+9v}i`#@o;`i*?gQuQ;2D(t0cR!b47Bn(= zK0ul zBv^bR8+wgc7~(b|;o^I=)c_(c6Vf0)(RKrfs7*+V_yng)Q%C?&nUE&&6)SWanwU~v zVNFPx_=LJ4!;;YhO0;T13dN@jF`IkE?Gi>%6KL>^NTc|KE9F1pty8BDGKPal7|*F; zY7+QwpTV)!)m8lR^H1L6nJ;em{+* zU0nQbK+NLo873>FQ%O22e6i%5BOH!``m&cZ~s*jMXWr$psO!UF#=&7Y3MI z5GggWzl86!6qm`fFhqi1>nD5-C{}n%)fiC{ksK5CHhri&G0Szts7c77u%!@n^J!fJ zMuu~?8n+mQp(VU6Pa16U&~R!Zbss5Vt__-mGz+rdOrW`;yIe7*e%LSs#a=6D){sC3 zc-IDw$eDZ)2ycIxpqu3<^5xbP(>n4ed$8j3A4Rn=qeq>#7r|}E$kq? zGeRM9&Bpv~{!VzzR)(z;$s#=(vUto9{X5}`nZ6_K4-?hawviVKD&~cb+jE9O4vmW? zc01cjC?tVzj&a61qZB5?jGGaoso2oDk`Cw%Hr<%=+x;R6wA3m~n-LUl=)QQX=k`th zFMXuI2%DzmtT~y9sCLY5gjNGecxCb>C_fxW0-!0GG%GO9P*dlBw+}B_i;V%QTdjL) zJc&*-!gaQGpU;sDN@hT^0TreWfXRrfIWn6;E;wL|>kgl3r|6X=#(-*Q!yBrr>6O)# z(qfG7S06S@CDy3QS9PH&N}OKZ3#o<(n6$kL0F2qR6qHsYCPnaS{sTg-j_j+-ut@*_ N002ovPDHLkV1i&)zX<>U literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/Contents.json index 30afe661d..390e9a858 100644 --- a/Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/Contents.json +++ b/Riot/Assets/Images.xcassets/Room/Input/input_text_background.imageset/Contents.json @@ -19,5 +19,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" } } diff --git a/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/Contents.json new file mode 100644 index 000000000..c5bb14104 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "upload_icon_dark.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "upload_icon_dark@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "upload_icon_dark@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark.png b/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0316cf93ded219526f03359826fe276832b665 GIT binary patch literal 823 zcmV-71IYY|P)OxYD(<3{Q#&-+eAw>(T2uEgDy?5TNftotUD7m?lE09lqxB;O@!hL zS8B@#kgW+MO-vW`5<%`6XP_Xj%6-5sztu1txc}Tab6yArS&nUcTnwg3!zpCoMFbac z7^!@TkS7i~2ABn;G*KZH@cXky{vY)kM%EKIIAhnv54>Oi0>}_i`0m2orXKsQf>yQ? zUPWU|!f+g{fS4_7+N*BDjuf;Ui;XzZ5k$ZM^bk%wtYRT_&+pHJUO~}#e7satUNC@3 z=m+9$5gjac@-OTp=1)-xSw13@qFCbxc}%v5k#Oz-bUv5QgrfvieZ~ec{~kW+)zdiQmbz}m}BK&DZXs8PU zX6>kwiFXsb-@lH51r(75DpKS%7bn@Dcw+#Wl4PD_M@S4t)AQ2`F0jeFzdVa^YW~3Pme7Fos7EOB{0eU z{;y%ZwitPv{8TSNP4ejBgUZEt`s`&ItbmwjoWlVO3Y_N{GN#LiY(dmWA`WR87#J|k zhEAUP3~v<1w33ORq_;@+tHjOnl-DU05%qNTR6 zx{=(pvwUqrIP>lKEAw($4~&dx362TbK-SiELAMs>(j0ggY(_q_v72p59V#z`go%A& zKpd6I9igVJR-0)2i)a*!HZ_X0C~sEdc3X0lVc^8@a8QJT9Q299iAbB6j)H2URIc9X zhfm4>Eb!C!JBg8W9mgEbg@U~p$DpP?k(cMcW}p)(tlBOa>riAC&_v+v*}$q5>Gb8& z@0n_IAnI{h0ym8GWI4kL(oEq$NQG1&;4?JR`wNe~9XAj~MjHSC002ovPDHLkV1iT& BY@GlA literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark@2x.png b/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b58c78907c54b319abb9faa07e09c97e08f77655 GIT binary patch literal 1563 zcmV+$2ITpPP)*EW6Nn4aTvaqiP;-vC6|&G#C|ITdSakk=s?0WI% z6o^Tc-NyFHZbe;;>l{wyO)CyV)Ty%Dmwc_oPAMwTb`jV>sk~ZL3E(GR(fDd9P-Jd? zu-+?VJ3)b=$*G%`T?b2nP_{L8ty2Z0$@5mORv-}9J1yZ_`6yT8jzs>;IRbIkQsN9l ziy{z1`Bs!8&_+fZcFTHFsg!o}`^Bg3-2UkhAA=jHtd?&@IRd$9J&~*599~?Q@0p$c z?(o-_!$-g+@~voBpoyu4VK-yX-FUpqJid4DZ})01WknsLbIAO5S0EB%2s{jkBn;8OarSSxKX$R1U07`1{d2kN=R3C#fz8-1 zP&%D_Z_s=dKlXUI(c&7!I5LV4`H9_>3Jr3{P?rB|6RN`vou!^J?M|leUMYp zfAZ-U`?{Oe3f^gJy9G59yaid+LAVSamhe7pRR_sc@UTPz#Z`|Q9NmcB$Y`h~1Zl$c zU|6r9j&YINTw7JqsHz>H%?|G!61+WDwLIeBkpqu7@Yn5@E7C3S$bsiS_^Tn%np}^9 zM-D1`J+>gj&2~)6W81w#!B>pzzFSFqZuS_{y_j8r3^#Kzn?0=-o96LR7Z$s_ASSYB z&vJITy17|=T{YT%Q@y1BRlDRl)e*3}@l`HLknm{MCiAxJH`gf@=@5QbX1;t%kb!|0 z9)O!D>&bR6CTBIz!CDfOJ;%^&AP?g01ZR_ib_ZJ#_d%f5KwKHMnBOQ6h^!7s z)b)?8Ubk$RjgFj-5m3vx8ec14v(16v!JCLU&i zq?9z4ADFY*vnTz=t1qsiEb;?=Y}v?zF2t7O$WX6IWNLm;Xl>^kE^s6Kr{d|kV(Gm2k5vI@ zx6MqZksd6tBBgx4R6w?(synNk-BOf#s{x8h;{S0?w!dACI84nCH({4U(RQ{DA8xY3 z%ZEx`s^HgvO-!m@k^~rbT_}z<1lN*uFmfjNe9a9&TOY8*u}RNlhFuVfxGJ_&7!${y zVaDTCJmTQsE`Hki6jS~5AJ(KVYNs$Jjz8qhSkI-?Cu|VQ5QHs-Z*)2-N;ZxQl+WbU zLO*HPOC-A0HTUv1WZr3O^_V~~s$N`H$NwP8#DOgl`&YEDIxR{zj{wE#R6G%pHl_do N002ovPDHLkV1j1R+rj_< literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark@3x.png b/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..b04f2a977beb382725f9ac0cf90d50f1b799e9a1 GIT binary patch literal 2232 zcmV;p2uJscP)uK~#7F?VU?( zTSXK=?;R&i{YhX!L5b)`0s@F-x|Bzdr+-qMb07 zln}9PM7TwLxIPF07FdvST;S|PvcG>4G(rnuI!LNUQQlKKA36yd5UMa16~lN^ASC@r zb)JGaDGT%yU^W$I)P*AKvs0(KWj-<}0UiYdkrKwk5*Mg2j;Jn-?=w}@Qo;}yh%lp4 zU%en^B+*cE$Ac*d0N;fj9Y5U{DTo8=4|PK;qpopS5U&mFyl(=0#5Z9aRdI|BB483$ z5hQ77_s*>c!6$qX#znRrR8-ZR$&E z*Y^8|z(YJ0HZ*p2Pyh^qrG%=8Q(hJ%o(OYBDkAZH>eIDEx$ZSmjg;hY_MjJIluZj; zDB0gvk<|;9s;Edy=P3Jpv(lOpW@Xif7t$IQW@R--v(g$8W@R-;GtvY!02dfmR^#xw zi&1nxG(g?LxOBJGVDktewmIjr-uKmW@W_cXNqe8=9egf%prOw9xPynA)Fsc#@|dQd_JN@A9=k@{WG=Z!w(MMIyE&F<)6E{x~@F8_r-JTZn*IZ7|0A| z?_D~gzbwK`j;^uepRO;;Vy363*Btx!=q50fN)_WWVFiHAU?_j2)7LM3?b4--#-_$f z72_gdBWmQ>-gjw)su&ZZb6fdKi-eJyqXCOn5Xmj&KbM4Yv7y_;bQY&7>?==;C1LZk zXzaDC)hSPlC1Exl>PvZAbcAup*>tEcfJz+7ExU41W6Cm1bgc*Vm zqFeA6u$Y2MiBj5>Xb5V$B_R;VHrkj1k%}-tE6fmxS6v-BarT?A+S#}VW8~wHHvMJD ztnm8v8!`h=K6O5{9udTh2($O_@Vjr{Zi+01+YJuC`_AnU7W||Ogv7bueo1hX3>XG~ zW^SW{kO)m!G#W2}q2zKiZ4eTn3EQx707Epjxd2X!VJMliBihTU@?VBA*?WSkdPK_(ddt0F@laY9h2hm zhwr%@=DnE3J&!$dPudyjAOfQ{JzI@9dz?XOXo{>b&5;X^9AQGzw_wp4lz1(|GB|4^ zOmlLWy(WyGR4?X8i+-@1;%Wk==@mqq7IlTUbS!>f5;h}q<6zMefmq6ZC1Ex#>IpkZ z_HQi>&@Y;CwrNp6%F`jUNSKor?Wl)7AWkmbj(CwUPK%Pj)BUc3`|Ct|drIc>#tI9w4b3wuL>E)BFOjEQgb+rqnb-JpW$=N=epK$uAW}WQZ8jvqmQUmxO>Oe zgKiFWC5%&Jdq>Bhtr~qmol`YPvDfY4uAdKc?#%#Q{V%Z8gGjAZ6)rm(>fnj(4~(m^ ztQ1&0fzRcx?f2JJAJqK>CYPP+O?DKdNrZrtwMzJk?}G-IrtBfx%h)(-=;_>aA$kZJ zpsB6x6Bv8nE{<6rEGtLR7xwhu9o*LTMXa>?XjT@}lrY9hbB7nQn5LI~;Cr+g$~|uj zk?L@P;e{-w=?E+5*x1=YvRmJmL+S({ZloHlhNr@qkrQ7fRRXa4XCh2PLB6$XN!L^J zzt#snJN13{Tt1&#wOd=Mu|Xbgs%Hel^{)mW86G>cf0gaBPHNPBI7_jV7uTyw_$rJ! zcKmdo6yiY3N1KW&p3ibKMbgxtfq-xd65hzW)vj&oTBWA-hcgr%@++zq|G5C+Ynz;# z8#KA+u?$l8R$r)c{a_HHiUcaGh~eTKQQX<&M^RCWP=pmJiYrLDS@UZg@kC>gsjnKL z3M+?qxaC~}HQKm`7QRd%(swg5HXf#^BH9Qm!cjLrJG(g~d0I*;M>}EV92xzgGe(&{ z6zxhJ^?EGOk;AF(>cTSkyO4!%GZ;_nCaD~)g;nD7&%f-=<6Muk$&Z8{6wuAPTkwVn zh>%!z+i+FLj5=nT*M^jmvTDeCk{3mj|KUV3xz(ukBL4?9ByDP2`{ecj0000 + + @@ -130,6 +132,19 @@ + + @@ -158,12 +173,16 @@ + + + + @@ -184,8 +203,14 @@ + + + + + + diff --git a/Riot/Modules/Room/Views/Activities/RoomActivitiesView.h b/Riot/Modules/Room/Views/Activities/RoomActivitiesView.h index 54eb3feb7..b66c61a5f 100644 --- a/Riot/Modules/Room/Views/Activities/RoomActivitiesView.h +++ b/Riot/Modules/Room/Views/Activities/RoomActivitiesView.h @@ -73,16 +73,6 @@ */ - (void)displayOngoingConferenceCall:(void (^)(BOOL video))ongoingConferenceCallPressed onClosePressed:(void (^)(void))ongoingConferenceCallClosePressed; -/** - Display a "scroll to bottom" icon. - Replace the current notification if any. - - @param newMessagesCount the count of the unread messages. - @param onIconTapGesture block called when user taps on notification icon. - */ -- (void)displayScrollToBottomIcon:(NSUInteger)newMessagesCount onIconTapGesture:(void (^)(void))onIconTapGesture; - - /** Notify that the a room is obsolete and a replacement room is available. diff --git a/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m b/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m index 46bce5f6f..1f65678ad 100644 --- a/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m +++ b/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m @@ -268,62 +268,6 @@ [self checkHeight:YES]; } -- (void)displayScrollToBottomIcon:(NSUInteger)newMessagesCount onIconTapGesture:(void (^)(void))onIconTapGesture -{ - if (newMessagesCount) - { - [self reset]; - - self.iconImageView.image = [UIImage imageNamed:@"scrolldown"]; - self.iconImageView.tintColor = ThemeService.shared.theme.noticeColor; - - NSString *notification; - if (newMessagesCount > 1) - { - notification = NSLocalizedStringFromTable(@"room_new_messages_notification", @"Vector", nil); - } - else - { - notification = NSLocalizedStringFromTable(@"room_new_message_notification", @"Vector", nil); - } - self.messageLabel.text = [NSString stringWithFormat:notification, newMessagesCount]; - self.messageLabel.textColor = ThemeService.shared.theme.warningColor; - self.messageLabel.hidden = NO; - } - else - { - self.unsentMessagesContentView.hidden = YES; - - // We keep the current message if any - [self resetIcon]; - - self.messageLabel.text = nil; - self.iconImageView.image = [UIImage imageNamed:@"scrolldown"]; - self.iconImageView.tintColor = ThemeService.shared.theme.textPrimaryColor; - } - self.iconImageView.hidden = NO; - - // Make VoiceOver consider it as a button - self.iconImageView.accessibilityLabel = NSLocalizedStringFromTable(@"room_accessiblity_scroll_to_bottom", @"Vector", nil); - self.iconImageView.isAccessibilityElement = YES; - self.iconImageView.accessibilityTraits = UIAccessibilityTraitButton; - - if (onIconTapGesture) - { - objc_setAssociatedObject(self.iconImageView, "onIconTapGesture", [onIconTapGesture copy], OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - // Listen to icon tap - UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onIconTap:)]; - [tapGesture setNumberOfTouchesRequired:1]; - [tapGesture setNumberOfTapsRequired:1]; - [tapGesture setDelegate:self]; - [self.iconImageView addGestureRecognizer:tapGesture]; - self.iconImageView.userInteractionEnabled = YES; - } - - [self checkHeight:YES]; -} - - (void)displayRoomReplacementWithRoomLinkTappedHandler:(void (^)(void))onRoomReplacementLinkTapped { [self reset]; diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index 140e841e9..00cbb0d78 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -85,7 +85,7 @@ growingTextView.font = [UIFont systemFontOfSize:15]; growingTextView.textColor = ThemeService.shared.theme.textPrimaryColor; growingTextView.tintColor = ThemeService.shared.theme.tintColor; - + growingTextView.placeholderColor = ThemeService.shared.theme.textTertiaryColor; growingTextView.internalTextView.showsVerticalScrollIndicator = NO; growingTextView.internalTextView.keyboardAppearance = ThemeService.shared.theme.keyboardAppearance; @@ -100,6 +100,19 @@ UIImage *image = [UIImage imageNamed:@"input_text_background"]; image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(9, 15, 10, 16)]; self.inputTextBackgroundView.image = image; + self.inputTextBackgroundView.tintColor = ThemeService.shared.theme.roomInputTextBorder; + + if ([ThemeService.shared.themeId isEqualToString:@"light"]) + { + [self.attachMediaButton setImage:[UIImage imageNamed:@"upload_icon"] forState:UIControlStateNormal]; + } + else if ([ThemeService.shared.themeId isEqualToString:@"dark"] || [ThemeService.shared.themeId isEqualToString:@"black"]) + { + [self.attachMediaButton setImage:[UIImage imageNamed:@"upload_icon_dark"] forState:UIControlStateNormal]; + } + else if (@available(iOS 12.0, *) && ThemeService.shared.theme.userInterfaceStyle == UIUserInterfaceStyleDark) { + [self.attachMediaButton setImage:[UIImage imageNamed:@"upload_icon_dark"] forState:UIControlStateNormal]; + } } #pragma mark - @@ -187,21 +200,15 @@ if (self.rightInputToolbarButton.isEnabled && !self.rightInputToolbarButton.alpha) { - [UIView animateWithDuration:.4 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:8 options:UIViewAnimationOptionCurveEaseIn animations:^{ - self.rightInputToolbarButton.alpha = 1; - self.messageComposerContainerTrailingConstraint.constant = self.frame.size.width - self.rightInputToolbarButton.frame.origin.x + 12; - [self layoutIfNeeded]; - } completion:^(BOOL finished) { - }]; + self.rightInputToolbarButton.alpha = 1; + self.messageComposerContainerTrailingConstraint.constant = self.frame.size.width - self.rightInputToolbarButton.frame.origin.x + 12; + [self layoutIfNeeded]; } else if (!self.rightInputToolbarButton.isEnabled && self.rightInputToolbarButton.alpha) { - [UIView animateWithDuration:.4 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:8 options:UIViewAnimationOptionCurveEaseIn animations:^{ - self.rightInputToolbarButton.alpha = 0; - self.messageComposerContainerTrailingConstraint.constant = 12; - [self layoutIfNeeded]; - } completion:^(BOOL finished) { - }]; + self.rightInputToolbarButton.alpha = 0; + self.messageComposerContainerTrailingConstraint.constant = 12; + [self layoutIfNeeded]; } } diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib index fab7ebe7d..c8ac8203d 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib @@ -14,13 +14,13 @@ - + - + - + - + @@ -52,7 +52,7 @@ - + - - + - - - + + + + From 4834042b8e176c5491bf2d207f4d3638c6470ba7 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 22 Mar 2021 19:55:41 +0100 Subject: [PATCH 07/78] Explore typing notifications inspired by web - First iteration --- Riot/Modules/DotsView/DotsView.swift | 152 ++++++++++++++++++ .../Modules/Room/DataSources/RoomDataSource.h | 5 + .../Modules/Room/DataSources/RoomDataSource.m | 41 ++++- Riot/Modules/Room/RoomViewController.m | 63 +++----- Riot/Modules/Room/RoomViewController.xib | 4 +- .../BubbleCells/RoomTypingBubbleCell.swift | 105 ++++++++++++ .../BubbleCells/RoomTypingBubbleCell.xib | 54 +++++++ 7 files changed, 375 insertions(+), 49 deletions(-) create mode 100644 Riot/Modules/DotsView/DotsView.swift create mode 100644 Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift create mode 100644 Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.xib diff --git a/Riot/Modules/DotsView/DotsView.swift b/Riot/Modules/DotsView/DotsView.swift new file mode 100644 index 000000000..ca69fb6de --- /dev/null +++ b/Riot/Modules/DotsView/DotsView.swift @@ -0,0 +1,152 @@ +// +// Copyright 2021 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +@IBDesignable +@objcMembers +class DotsView: UIView { + // MARK: - Public properties + + @IBInspectable var highlightedDotColor: UIColor = .darkGray { + didSet { + } + } + + @IBInspectable var dotColor: UIColor = .lightGray { + didSet { + } + } + + @IBInspectable var dotMaxWidth: CGFloat = 10 { + didSet { + self.sizeToFit() + } + } + + @IBInspectable var dotMinWidth: CGFloat = 8 { + didSet { + self.sizeToFit() + } + } + + @IBInspectable var numberOfDots: UInt = 3 { + didSet { + createDotViews() + } + } + + @IBInspectable var interSpaceMargin: CGFloat = 7 { + didSet { + self.sizeToFit() + } + } + + // MARK: - Private members + + private var dotLayers: Array = Array() + private var highlightedDotIndex: UInt = 0 { + didSet { + updateDotViews() + } + } + private let updateInterval: TimeInterval = 0.4 + private var lastUpdateDate: Date = Date() + private var animating: Bool = false { + didSet { + let displayLink = CADisplayLink(target: self, selector: #selector(fireTimer)) + displayLink.add(to: .current, forMode: .default) + } + } + + // MARK: - Lifecycle + + required init?(coder: NSCoder) { + super.init(coder: coder) + createDotViews() + } + + override init(frame: CGRect) { + super.init(frame: frame) + createDotViews() + } + + override func layoutSubviews() { + super.layoutSubviews() + + updateDotViews() + } + + override var intrinsicContentSize: CGSize { + return CGSize(width: dotMaxWidth + (CGFloat(numberOfDots) - 1) * (dotMinWidth + interSpaceMargin), height: dotMaxWidth) + } + + override func didMoveToSuperview() { + animating = superview != nil + } + + // MARK: - Interface Builder + + override func prepareForInterfaceBuilder() { + super.prepareForInterfaceBuilder() + createDotViews() + } + + // MARK: - Private methods + + private func createDotViews() { + while dotLayers.count > numberOfDots { + dotLayers.popLast()?.removeFromSuperlayer() + } + + while dotLayers.count < numberOfDots { + let dotLayer = CALayer() + dotLayer.masksToBounds = true + layer.addSublayer(dotLayer) + dotLayers.append(dotLayer) + } + + if highlightedDotIndex >= dotLayers.count { + highlightedDotIndex = 0 + updateDotViews() + } + } + + private func updateDotViews() { + CATransaction.begin() + CATransaction.setAnimationDuration(1) + var x: CGFloat = 0 + for (index, dotLayer) in dotLayers.enumerated() { + if index == highlightedDotIndex { + dotLayer.frame = CGRect(x: x, y: (bounds.height - dotMaxWidth) / 2, width: dotMaxWidth, height: dotMaxWidth) + dotLayer.backgroundColor = dotColor.cgColor + } else { + dotLayer.frame = CGRect(x: x, y: (bounds.height - dotMinWidth) / 2, width: dotMinWidth, height: dotMinWidth) + dotLayer.backgroundColor = index == ((highlightedDotIndex + 1) % numberOfDots) ? highlightedDotColor.cgColor : dotColor.cgColor + } + dotLayer.cornerRadius = dotLayer.bounds.height / 2 + x = dotLayer.frame.maxX + interSpaceMargin + } + lastUpdateDate = Date() + CATransaction.commit() + } + + func fireTimer() { + if Date().timeIntervalSince(lastUpdateDate) >= updateInterval { + self.highlightedDotIndex = (self.highlightedDotIndex + 1) % self.numberOfDots + } + } +} diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.h b/Riot/Modules/Room/DataSources/RoomDataSource.h index f28f91fac..ced9e687a 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.h +++ b/Riot/Modules/Room/DataSources/RoomDataSource.h @@ -48,6 +48,11 @@ */ @property(nonatomic, readonly) RoomEncryptionTrustLevel encryptionTrustLevel; +/** + List of members who are typing in the room. + */ +@property(nonatomic, nullable) NSArray *currentTypingUsers; + /** Check if there is an active jitsi widget in the room and return it. diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 8975281c8..880f6dc65 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -53,6 +53,8 @@ @property (nonatomic) BOOL showRoomCreationCell; +@property (nonatomic) NSInteger typingCellIndex; + @end @implementation RoomDataSource @@ -185,6 +187,16 @@ [self setNeedsUpdateAdditionalContentHeightForCellData:cellData]; } +- (CGFloat)cellHeightAtIndex:(NSInteger)index withMaximumWidth:(CGFloat)maxWidth +{ + if (index == self.typingCellIndex) + { + return 24; + } + + return [super cellHeightAtIndex:index withMaximumWidth:maxWidth]; +} + - (void)setNeedsUpdateAdditionalContentHeightForCellData:(id)cellData { RoomBubbleCellData *roomBubbleCellData; @@ -261,16 +273,37 @@ [self updateStatusInfo]; } - // we may have changed the number of bubbles in this block, consider that change - return bubbles.count; + if (self.currentTypingUsers.count == 0) + { + // we may have changed the number of bubbles in this block, consider that change + return bubbles.count; + } + + self.typingCellIndex = bubbles.count; + return bubbles.count + 1; } - // leave it as is, if coming as 0 from super - return count; + if (self.currentTypingUsers.count == 0) + { + self.typingCellIndex = -1; + + // leave it as is, if coming as 0 from super + return count; + } + + self.typingCellIndex = count; + return count + 1; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.row == self.typingCellIndex) + { + RoomTypingBubbleCell *cell = [tableView dequeueReusableCellWithIdentifier:RoomTypingBubbleCell.cellIdentifier forIndexPath:indexPath]; + [cell updateTypingUsers:_currentTypingUsers mediaManager:self.mxSession.mediaManager]; + return cell; + } + // Do cell data customization that needs to be done before [MXKRoomBubbleTableViewCell render] RoomBubbleCellData *roomBubbleCellData = [self cellDataAtIndex:indexPath.row]; diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 963fc991f..442a6fd79 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -370,6 +370,8 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [self.bubblesTableView registerClass:RoomCreationIntroCell.class forCellReuseIdentifier:RoomCreationIntroCell.defaultReuseIdentifier]; + [self.bubblesTableView registerNib:[UINib nibWithNibName:@"RoomTypingBubbleCell" bundle:nil] forCellReuseIdentifier:RoomTypingBubbleCell.cellIdentifier]; + [self vc_removeBackTitle]; // Replace the default input toolbar view. @@ -4025,54 +4027,29 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)refreshTypingNotification { - if ([self.titleView isKindOfClass:RoomTitleView.class]) + RoomDataSource *roomDataSource = (RoomDataSource *) self.roomDataSource; + BOOL needsUpdate = currentTypingUsers.count != roomDataSource.currentTypingUsers.count; + + NSMutableArray *typingUsers = [NSMutableArray new]; + for (NSUInteger i = 0 ; i < currentTypingUsers.count ; i++) { + NSString *userId = currentTypingUsers[i]; + MXRoomMember* member = [self.roomDataSource.roomState.members memberWithUserId:userId]; + [typingUsers addObject:member]; + needsUpdate = needsUpdate || member.userId != ((MXRoomMember *) roomDataSource.currentTypingUsers[i]).userId; + } + + if (needsUpdate) { - RoomTitleView *titleView = (RoomTitleView *)self.titleView; - - // Prepare here typing notification - NSString* text = nil; - NSUInteger count = currentTypingUsers.count; - - // get the room member names - NSMutableArray *names = [[NSMutableArray alloc] init]; - - // keeps the only the first two users - for(int i = 0; i < MIN(count, 2); i++) + roomDataSource.currentTypingUsers = typingUsers; + [self.bubblesTableView reloadData]; + if (self.isScrollToBottomHidden) { - NSString* name = currentTypingUsers[i]; - - MXRoomMember* member = [self.roomDataSource.roomState.members memberWithUserId:name]; - - if (member && member.displayname.length) + NSInteger count = [self.roomDataSource tableView:self.bubblesTableView numberOfRowsInSection:0]; + if (count) { - name = member.displayname; - } - - // sanity check - if (name) - { - [names addObject:name]; + [self scrollBubblesTableViewToBottomAnimated:YES]; } } - - if (0 == names.count) - { - // something to do ? - } - else if (1 == names.count) - { - text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_one_user_is_typing", @"Vector", nil), names[0]]; - } - else if (2 == names.count) - { - text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_two_users_are_typing", @"Vector", nil), names[0], names[1]]; - } - else - { - text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_many_users_are_typing", @"Vector", nil), names[0], names[1]]; - } - - titleView.typingNotificationString = text; } } diff --git a/Riot/Modules/Room/RoomViewController.xib b/Riot/Modules/Room/RoomViewController.xib index a43b40a18..afcb068b5 100644 --- a/Riot/Modules/Room/RoomViewController.xib +++ b/Riot/Modules/Room/RoomViewController.xib @@ -140,7 +140,7 @@ diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index 184dd2187..80c070485 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -135,6 +135,27 @@ _sendMode = sendMode; [self updatePlaceholder]; + [self updateToolbarButtonLabel]; +} + +- (void)updateToolbarButtonLabel +{ + UIImage *buttonImage; + + switch (_sendMode) + { + case RoomInputToolbarViewSendModeReply: + buttonImage = [UIImage imageNamed:@"send_icon"]; + break; + case RoomInputToolbarViewSendModeEdit: + buttonImage = [UIImage imageNamed:@"save_icon"]; + break; + default: + buttonImage = [UIImage imageNamed:@"send_icon"]; + break; + } + + [self.rightInputToolbarButton setImage:buttonImage forState:UIControlStateNormal]; } - (void)updatePlaceholder From 0dbae5acc2d805a1929622949645d1c85feb7084 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Wed, 24 Mar 2021 22:17:09 +0100 Subject: [PATCH 10/78] Composer update - UI enhancements - composer max height - unread messages on scroll to bottom button - changed input toolbar background - new missed discussions notifications - Edit a message mode - Reply to a message mode - support for landscape mode --- .../input_close_icon.imageset/Contents.json | 26 ++++++ .../input_close_icon.png | Bin 0 -> 324 bytes .../input_close_icon@2x.png | Bin 0 -> 429 bytes .../input_close_icon@3x.png | Bin 0 -> 528 bytes .../input_edit_icon.imageset/Contents.json | 26 ++++++ .../input_edit_icon.png | Bin 0 -> 304 bytes .../input_edit_icon@2x.png | Bin 0 -> 465 bytes .../input_edit_icon@3x.png | Bin 0 -> 635 bytes .../input_reply_icon.imageset/Contents.json | 26 ++++++ .../input_reply_icon.png | Bin 0 -> 299 bytes .../input_reply_icon@2x.png | Bin 0 -> 463 bytes .../input_reply_icon@3x.png | Bin 0 -> 604 bytes Riot/Assets/en.lproj/Vector.strings | 2 + Riot/Generated/Images.swift | 3 + Riot/Generated/Strings.swift | 8 ++ Riot/Modules/Room/RoomViewController.m | 75 ++++++++++++---- .../Views/InputToolbar/RoomInputToolbarView.h | 17 ++++ .../Views/InputToolbar/RoomInputToolbarView.m | 80 ++++++++++++++++-- .../InputToolbar/RoomInputToolbarView.xib | 55 ++++++++++-- Riot/Modules/Room/Views/Title/RoomTitleView.h | 9 ++ Riot/Modules/Room/Views/Title/RoomTitleView.m | 26 ++++++ .../Room/Views/Title/RoomTitleView.xib | 24 +++++- 22 files changed, 346 insertions(+), 31 deletions(-) create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_close_icon.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_close_icon.imageset/input_close_icon.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_close_icon.imageset/input_close_icon@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_close_icon.imageset/input_close_icon@3x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_edit_icon.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_edit_icon.imageset/input_edit_icon.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_edit_icon.imageset/input_edit_icon@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_edit_icon.imageset/input_edit_icon@3x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_reply_icon.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_reply_icon.imageset/input_reply_icon.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_reply_icon.imageset/input_reply_icon@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Input/input_reply_icon.imageset/input_reply_icon@3x.png diff --git a/Riot/Assets/Images.xcassets/Room/Input/input_close_icon.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Input/input_close_icon.imageset/Contents.json new file mode 100644 index 000000000..9c70622e2 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Input/input_close_icon.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "input_close_icon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "input_close_icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "input_close_icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Input/input_close_icon.imageset/input_close_icon.png b/Riot/Assets/Images.xcassets/Room/Input/input_close_icon.imageset/input_close_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b78911642f1cbb888630b6f3da5ca78ca6cc90fe GIT binary patch literal 324 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&k&H|6fVg?3oVGw3ym^DWND9BhG z*=8)x>^8vnk>WYYLn#(mrC z{_OX$_1hwlBF)|JC*D1#P7Q3FJN$?+PPzKw93(z z4ayHLUrsC)l$o|YQaZosz&WQQJ1Q*2-%nq7WkHrBUuu|W;lzbb0@>oc(_YEUk_x!x z@jd>k&(ub*16SPcx@Y`9QskL>LZardO}U}#mux5WTbC~9s1T!W=zVx*ja93d-`?GV$ZYSMhX zm$84zk}bdY`zekMuu+j};+?CB4=p1vgHExZXA`#}jQP>&7~GCefSBR+_)ZA^rH!7J z3Vs3NBwHl#N|88t!a&Vi^H~Z)bsAe?duc&Tk>ue?WQFdn?z3WpR6&OuK20~gMMeMl ze)xukhw37#6<{S8c%`68f`!)#swB*UkA*i1N+pY6;3z?v1PgcJfXJ5SN2_1Jfce61 zA7FY#Q$g{E{^J35)8+Ag_b~eWc>k{J=M=a*7V2YU6nJ*#l*h=Z@GMvfYXhf9SX($& zvJ{-%1bqhlg#xdV7-RRshtbT<*KQFN?h>leEO)zhOk7$xxO(bJr95aEsUCedV59N} XDJ>Vpn|mMs00000NkvXXu0mjfd55!5 literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Input/input_close_icon.imageset/input_close_icon@3x.png b/Riot/Assets/Images.xcassets/Room/Input/input_close_icon.imageset/input_close_icon@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6f628586d3ca0d868cbed613cbdbca89d7a96c42 GIT binary patch literal 528 zcmV+r0`L8aP)ccmr<$H;^N614)b<#GH5CQ!n!H?o!Xs8CfH5q z4Anrk{>yBd9>qYm0=abHP?QnmGY&#fNR0_a;UO>~C_EI#7lnt!_@H+B(1cIB)R-() z4-SgqqPVxdF%n?vP^x6#4rs=8`J{T#JF_yrQ(=lI{g}Se-?0*r8bFgK#Q6g`Ro0zu Sg~f&d0000H?p}L9rJOUJoASmLHGh^kR8tF!TKB(>#Y9Sd>bqPO;32I{G{| zF+GQ4ikBTn;VRi_X2t*b*ST74baN=-Tyf=G_RTj|X?oeoyr(MFT&`x{Ydd+?LAM}& z+xPg*@~Y3wKD9YW|1gwW&mH`fB_PvXdc(hc#fyBU5{_|jv6P;2xxi@NE^$nm??t200009a7bBm000&x z000&x0ZCFM@Bjb+0drDELIAGL9O(c600d`2O+f$vv5yPkq z0x=YaU;i9{Bh1nz9AR#NCqPahh=VavDFI_lz-hdIcmrLzCAl4(yIOFlH=x5*a6apDs&5nPC zU;B!b)(`$a90IVy4j@QBLx9|{kFK9pZa)!e(`fx#&NVYFDTXUQtJMCPNFys;mU9Ht zEek72olt4zgbhC@2K#rD;bL12uO=jspBun&dY#C>>PRi&BoVL!wk_<5v_X`50YVN! zg^sQ5bETF@NV5mO`+!RF$6$Mqjte( z-?irIPzsSl0JOL%X!=6KR4m1L27&w>R8QPQs39i^E4$Q_q3XGc(AE{kwq|`$P6&0d zSEL*3^!7ARP_{$3ST(pbB>?jk*5>O+BTlB1box00000NkvXX Hu0mjf)2F-X literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Input/input_edit_icon.imageset/input_edit_icon@3x.png b/Riot/Assets/Images.xcassets/Room/Input/input_edit_icon.imageset/input_edit_icon@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..4baa61fd8429df16a98d1e990851c704f788cc90 GIT binary patch literal 635 zcmV->0)+jEP)0K=o$z z4!#{89AX&;&cpwHHp_%F1NAT}ky4*Pbp2v1HgW6r-O|@D2cIv-JUCLI&ept=7S}L8 z6Tb%sYCP?}WpFCofZNgYR+tnkoPp+(&Njd`lpWW8fh`XG?M{o$wY~WvuMF|e!v1TqoQh)y+j*0P&z&DSOLtYVhEMmQu7j89B z-;P$aQbQY@Vb;34TYD&=#6BRsonZ6_+u_2*?cUDj00lLbfmZl3etRRM3M)LlzZWtf zd9BzE7nkrv14dIRbvC2#5}s&44w$rcAIjnK0FE_nfT?``@oX@Uwf}1!3((Xq@Ca9h z`h&oGa=-hoz0>_X9WJX#E|c(#mWINGE+=zKs#%s)GQe1o#|;_Ruv~p?JbpC{e52z% zf3M)}Y<>}H^1AB%fnScK)~X&cMz|cY=lSD61*hx2zS5JNRIn^DPo(JIcV0Z){|)ke V{3A8Nk>>ya002ovPDHLkV1n7x807!} literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Input/input_reply_icon.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Input/input_reply_icon.imageset/Contents.json new file mode 100644 index 000000000..5cbd96db1 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Input/input_reply_icon.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "input_reply_icon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "input_reply_icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "input_reply_icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Input/input_reply_icon.imageset/input_reply_icon.png b/Riot/Assets/Images.xcassets/Room/Input/input_reply_icon.imageset/input_reply_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..20bc5c0059f8b112baf04572a3983ef91cd0dbb8 GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&k&H|6fVg?3oVGw3ym^DWND9BhG z)x=DLSW8ML_ zbpcIw5*JO>jtKnY=qjZ~t!=iI9JKaX)TbyiIly zc5_LcQ*hur$ zMwP3Dhqii1CcoMee{b#WrWb7-H-B#M`k_B_MQPmo{4-njZf6WrXs})AT_k)~hA%wU sbn&u9+-vT6`xi`n$Mjf`r}+i*Lavbczf^o$fqr4|boFyt=akR{0MLJNh5!Hn literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Input/input_reply_icon.imageset/input_reply_icon@2x.png b/Riot/Assets/Images.xcassets/Room/Input/input_reply_icon.imageset/input_reply_icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4d45eb912dcf1b24a09912563835575c2e230d91 GIT binary patch literal 463 zcmV;=0WkiFP)Pmt zgS?K31Vuh+rG5VY?9ME}hQCIpEn2o?ly|GI<-O|r^V7o%^}Xta87JXL~~QFG4Ic-XN<$(2HUA3m~W zDI87xgOCaqLGgY>gClk7XZ`KrMYA2-kv!5k?ceh3A$MKjQKff%JOd5&R?kF0Cm4{b zM4xP_Ob3_E8b@^pev+k_SSMWrGz3pW>ML@A=h3wv|3MxTZ>#YA{)W~dY2rz=LBq&C za>H&hlR?DffaJmi4VE3=QmI(Z3c7=Aat|4x1)4$0AgpGf!{o@NC;Y_jD=urkZF-sN zsJFT`1aXs`=a1%+q{_&Sm$iUj;tPYKG0Bger1*ya!6)Khn36FX@Am)z002ovPDHLk FV1iB8$4LMH literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Input/input_reply_icon.imageset/input_reply_icon@3x.png b/Riot/Assets/Images.xcassets/Room/Input/input_reply_icon.imageset/input_reply_icon@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..d9999c5e342a87869ac75da19277fb2a9f9469a2 GIT binary patch literal 604 zcmV-i0;BzjP)I2|f_5l2r1|UNc2ox0n0xkTPu6z>y4=Y?FwakO4CZ zP09ceK#vOY2{Is7C`?9kz1|jw9%KL}lSzN>#0o7#CQ@iwGND3#nH1=m)-_-1xMzFM zalrwh`q8am3{N2w$aA=}el6-=R77BvQGq(tA5{k6U-s=^h>Qq(SQ9rDrr4W#S?oWF zA6XQ(FD|hLh})y5fu1%lw z3q?tN|HvvNfv7hoRD>i#ekt1+>SR?LaVUWyjD)2KVN-hwf_*WK2z5m6$GUYo!DXWymA` z)rKsE{!R&6y_l~}E@d?tXJs^(<|rdV_si6NnUty$knuy%?aM?Te^bkla(I_1=^ String { + return VectorL10n.tr("Vector", "room_message_replying_to", p1) + } /// Send a message… internal static var roomMessageShortPlaceholder: String { return VectorL10n.tr("Vector", "room_message_short_placeholder") diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 153708745..8c6be6078 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -155,8 +155,8 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo // Missed discussions badge NSUInteger missedDiscussionsCount; NSUInteger missedHighlightCount; - UIBarButtonItem *missedDiscussionsButton; UILabel *missedDiscussionsBadgeLabel; + UIView *missedDiscussionsDotView; // Potential encryption details view. EncryptionInfoView *encryptionInfoView; @@ -446,10 +446,6 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo self.previewHeaderContainer.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; - missedDiscussionsBadgeLabel.textColor = ThemeService.shared.theme.baseTextPrimaryColor; - missedDiscussionsBadgeLabel.font = [UIFont boldSystemFontOfSize:14]; - missedDiscussionsBadgeLabel.backgroundColor = [UIColor clearColor]; - // Check the table view style to select its bg color. self.bubblesTableView.backgroundColor = ((self.bubblesTableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.bubblesTableView.separatorColor = ThemeService.shared.theme.lineBreakColor; @@ -714,6 +710,19 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id )coordinator { + if ([self.titleView isKindOfClass:RoomTitleView.class]) + { + RoomTitleView *roomTitleView = (RoomTitleView*)self.titleView; + if (UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) + { + [roomTitleView updateLayoutForOrientation:UIInterfaceOrientationPortrait]; + } + else + { + [roomTitleView updateLayoutForOrientation:UIInterfaceOrientationLandscapeLeft]; + } + } + // Hide the expanded header or the preview in case of iPad and iPhone 6 plus. // On these devices, the display mode of the splitviewcontroller may change during screen rotation. // It may correspond to an overlay mode in portrait and a side-by-side mode in landscape. @@ -1135,7 +1144,9 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)setKeyboardHeight:(CGFloat)keyboardHeight { [super setKeyboardHeight:keyboardHeight]; - + + self.inputToolbarView.maxHeight = round(([UIScreen mainScreen].bounds.size.height - keyboardHeight) * 0.7); + // Make the activity indicator follow the keyboard // At runtime, this creates a smooth animation CGPoint activityIndicatorCenter = self.activityIndicator.center; @@ -1213,6 +1224,14 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo titleView.delegate = self; titleView.mxRoom = self.roomDataSource.room; self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:titleView]; + + if ([titleView isKindOfClass:RoomTitleView.class]) + { + RoomTitleView *roomTitleView = (RoomTitleView*)self.titleView; + missedDiscussionsBadgeLabel = roomTitleView.missedDiscussionsBadgeLabel; + missedDiscussionsDotView = roomTitleView.dotView; + [roomTitleView updateLayoutForOrientation:[UIApplication sharedApplication].statusBarOrientation]; + } [self updateViewControllerAppearanceOnRoomDataSourceState]; @@ -1305,6 +1324,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)setShowMissedDiscussionsBadge:(BOOL)showMissedDiscussionsBadge { missedDiscussionsBadgeLabel.hidden = !showMissedDiscussionsBadge; + missedDiscussionsDotView.hidden = !showMissedDiscussionsBadge; } - (void)setScrollToBottomHidden:(BOOL)scrollToBottomHidden @@ -1418,8 +1438,6 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [self.roomDataSource.room.summary setRoomAvatarImageIn:userPictureView]; } - missedDiscussionsBadgeLabel = ((RoomTitleView*)self.titleView).missedDiscussionsBadgeLabel; - [self refreshMissedDiscussionsCount:YES]; } @@ -1468,11 +1486,28 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo } } -- (void)setInputToolBarSendMode:(RoomInputToolbarViewSendMode)sendMode +- (void)setInputToolBarSendMode:(RoomInputToolbarViewSendMode)sendMode forEventWithId:(NSString *)eventId { if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:[RoomInputToolbarView class]]) { RoomInputToolbarView *roomInputToolbarView = (RoomInputToolbarView*)self.inputToolbarView; + if (eventId) + { + MXEvent *event = [self.roomDataSource eventWithEventId:eventId]; + MXRoomMember * roomMember = [self.roomDataSource.roomState.members memberWithUserId:event.sender]; + if (roomMember) + { + roomInputToolbarView.eventSenderDisplayName = roomMember.displayname; + } + else + { + roomInputToolbarView.eventSenderDisplayName = event.sender; + } + } + else + { + roomInputToolbarView.eventSenderDisplayName = nil; + } roomInputToolbarView.sendMode = sendMode; } } @@ -3161,7 +3196,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)selectEventWithId:(NSString*)eventId inputToolBarSendMode:(RoomInputToolbarViewSendMode)inputToolBarSendMode showTimestamp:(BOOL)showTimestamp { - [self setInputToolBarSendMode:inputToolBarSendMode]; + [self setInputToolBarSendMode:inputToolBarSendMode forEventWithId:eventId]; customizedRoomDataSource.showBubbleDateTimeOnSelection = showTimestamp; customizedRoomDataSource.selectedEventId = eventId; @@ -3172,7 +3207,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)cancelEventSelection { - [self setInputToolBarSendMode:RoomInputToolbarViewSendModeSend]; + [self setInputToolBarSendMode:RoomInputToolbarViewSendModeSend forEventWithId:nil]; if (currentAlert) { @@ -3627,6 +3662,11 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [self showMediaPickerAnimated:YES]; } +- (void)roomInputToolbarViewDidTapCancel:(MXKRoomInputToolbarView*)toolbarView +{ + [self cancelEventSelection]; +} + #pragma mark - RoomParticipantsViewControllerDelegate - (void)roomParticipantsViewController:(RoomParticipantsViewController *)roomParticipantsViewController mention:(MXRoomMember*)member @@ -4467,16 +4507,15 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo - (void)refreshMissedDiscussionsCount:(BOOL)force { // Ignore this action when no room is displayed - if (!self.roomDataSource || !missedDiscussionsBadgeLabel) + if (!self.roomDataSource || !missedDiscussionsBadgeLabel + || [UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPhone + || ([[UIScreen mainScreen] nativeBounds].size.height > 2532 && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation))) { + self.showMissedDiscussionsBadge = NO; return; } - if ([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPhone) - { - missedDiscussionsBadgeLabel.text = nil; - return; - } + self.showMissedDiscussionsBadge = YES; NSUInteger highlightCount = 0; NSUInteger missedCount = [[AppDelegate theDelegate].masterTabBarController missedDiscussionsCount]; @@ -4523,7 +4562,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo missedDiscussionsBadgeLabel.text = [NSString stringWithFormat:@"%tu", missedCount]; } - missedDiscussionsBadgeLabel.textColor = highlightCount ? ThemeService.shared.theme.noticeColor : ThemeService.shared.theme.tintColor; + missedDiscussionsDotView.alpha = highlightCount == 0 ? 0 : 1; } else { diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h index c7f984803..e5559e9fe 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h @@ -59,6 +59,13 @@ typedef enum : NSUInteger */ - (void)roomInputToolbarViewDidTapMediaLibrary:(MXKRoomInputToolbarView*)toolbarView; +/** + Tells the delegate that the user wants to cancel the current edition / reply. + + @param toolbarView the room input toolbar view + */ +- (void)roomInputToolbarViewDidTapCancel:(MXKRoomInputToolbarView*)toolbarView; + @end /** @@ -84,11 +91,21 @@ typedef enum : NSUInteger @property (weak, nonatomic) IBOutlet UIImageView *inputTextBackgroundView; +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *inputContextViewHeightConstraint; +@property (weak, nonatomic) IBOutlet UIImageView *inputContextImageView; +@property (weak, nonatomic) IBOutlet UILabel *inputContextLabel; +@property (weak, nonatomic) IBOutlet UIButton *inputContextButton; + /** Tell whether the filled data will be sent encrypted. NO by default. */ @property (nonatomic) BOOL isEncryptionEnabled; +/** + Sender of the event being edited / replied. + */ +@property (nonatomic, strong) NSString *eventSenderDisplayName; + /** Destination of the message in the composer. */ diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index 80c070485..dfceba978 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -27,6 +27,8 @@ #import "WidgetManager.h" #import "IntegrationManagerViewController.h" +const double RoomInputToolbarViewContextBarHeight = 30; + @interface RoomInputToolbarView() { // The intermediate action sheet @@ -61,7 +63,8 @@ [super awakeFromNib]; _sendMode = RoomInputToolbarViewSendModeSend; - + self.inputContextViewHeightConstraint.constant = 0; + [self.rightInputToolbarButton setTitle:nil forState:UIControlStateNormal]; [self.rightInputToolbarButton setTitle:nil forState:UIControlStateHighlighted]; @@ -113,6 +116,10 @@ else if (@available(iOS 12.0, *) && ThemeService.shared.theme.userInterfaceStyle == UIUserInterfaceStyleDark) { [self.attachMediaButton setImage:[UIImage imageNamed:@"upload_icon_dark"] forState:UIControlStateNormal]; } + + self.inputContextImageView.tintColor = ThemeService.shared.theme.textSecondaryColor; + self.inputContextLabel.textColor = ThemeService.shared.theme.textSecondaryColor; + self.inputContextButton.tintColor = ThemeService.shared.theme.textSecondaryColor; } #pragma mark - @@ -132,30 +139,77 @@ - (void)setSendMode:(RoomInputToolbarViewSendMode)sendMode { + RoomInputToolbarViewSendMode previousMode = _sendMode; _sendMode = sendMode; [self updatePlaceholder]; - [self updateToolbarButtonLabel]; + [self updateToolbarButtonLabelWithPreviousMode: previousMode]; } -- (void)updateToolbarButtonLabel +- (void)updateToolbarButtonLabelWithPreviousMode:(RoomInputToolbarViewSendMode)previousMode { UIImage *buttonImage; + double updatedHeight = self.mainToolbarHeightConstraint.constant; + switch (_sendMode) { case RoomInputToolbarViewSendModeReply: buttonImage = [UIImage imageNamed:@"send_icon"]; + self.inputContextImageView.image = [UIImage imageNamed:@"input_reply_icon"]; + self.inputContextLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_message_replying_to", @"Vector", nil), self.eventSenderDisplayName]; + + self.inputContextViewHeightConstraint.constant = RoomInputToolbarViewContextBarHeight; + updatedHeight += RoomInputToolbarViewContextBarHeight; + self->growingTextView.maxHeight -= RoomInputToolbarViewContextBarHeight; break; case RoomInputToolbarViewSendModeEdit: buttonImage = [UIImage imageNamed:@"save_icon"]; + self.inputContextImageView.image = [UIImage imageNamed:@"input_edit_icon"]; + self.inputContextLabel.text = NSLocalizedStringFromTable(@"room_message_editing", @"Vector", nil); + + self.inputContextViewHeightConstraint.constant = RoomInputToolbarViewContextBarHeight; + updatedHeight += RoomInputToolbarViewContextBarHeight; + self->growingTextView.maxHeight -= RoomInputToolbarViewContextBarHeight; break; default: buttonImage = [UIImage imageNamed:@"send_icon"]; + + if (previousMode != _sendMode) + { + updatedHeight -= RoomInputToolbarViewContextBarHeight; + self->growingTextView.maxHeight += RoomInputToolbarViewContextBarHeight; + } + self.inputContextViewHeightConstraint.constant = 0; break; } - + [self.rightInputToolbarButton setImage:buttonImage forState:UIControlStateNormal]; + + if (self.maxHeight && updatedHeight > self.maxHeight) + { + growingTextView.maxHeight -= updatedHeight - self.maxHeight; + updatedHeight = self.maxHeight; + } + + if (updatedHeight < self.mainToolbarMinHeightConstraint.constant) + { + updatedHeight = self.mainToolbarMinHeightConstraint.constant; + } + + if (self.mainToolbarHeightConstraint.constant != updatedHeight) + { + [UIView animateWithDuration:.3 animations:^{ + self.mainToolbarHeightConstraint.constant = updatedHeight; + [self layoutIfNeeded]; + + // Update toolbar superview + if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:heightDidChanged:completion:)]) + { + [self.delegate roomInputToolbarView:self heightDidChanged:updatedHeight completion:nil]; + } + }]; + } } - (void)updatePlaceholder @@ -213,6 +267,16 @@ self.placeholder = placeholder; } +#pragma mark - Actions + +- (IBAction)cancelAction:(id)sender +{ + if ([self.delegate respondsToSelector:@selector(roomInputToolbarViewDidTapCancel:)]) + { + [self.delegate roomInputToolbarViewDidTapCancel:self]; + } +} + #pragma mark - HPGrowingTextView delegate - (BOOL)growingTextView:(HPGrowingTextView *)growingTextView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text @@ -237,8 +301,14 @@ - (void)growingTextView:(HPGrowingTextView *)hpGrowingTextView willChangeHeight:(float)height { // Update height of the main toolbar (message composer) - CGFloat updatedHeight = height + (self.messageComposerContainerTopConstraint.constant + self.messageComposerContainerBottomConstraint.constant); + CGFloat updatedHeight = height + (self.messageComposerContainerTopConstraint.constant + self.messageComposerContainerBottomConstraint.constant) + self.inputContextViewHeightConstraint.constant; + if (self.maxHeight && updatedHeight > self.maxHeight) + { + hpGrowingTextView.maxHeight -= updatedHeight - self.maxHeight; + updatedHeight = self.maxHeight; + } + if (updatedHeight < self.mainToolbarMinHeightConstraint.constant) { updatedHeight = self.mainToolbarMinHeightConstraint.constant; diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib index c8ac8203d..fbc52e9f1 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib @@ -31,23 +31,62 @@ - + + + + + + + + + + + + + + + + + + + + + + - + - + + + - + + - + @@ -88,6 +127,10 @@ + + + + @@ -102,6 +145,8 @@ + + diff --git a/Riot/Modules/Room/Views/Title/RoomTitleView.h b/Riot/Modules/Room/Views/Title/RoomTitleView.h index a96cb2d26..9769d3999 100644 --- a/Riot/Modules/Room/Views/Title/RoomTitleView.h +++ b/Riot/Modules/Room/Views/Title/RoomTitleView.h @@ -40,6 +40,10 @@ @property (weak, nonatomic) IBOutlet UILabel *missedDiscussionsBadgeLabel; @property (weak, nonatomic) IBOutlet UILabel *typingLabel; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *displayNameCenterYConstraint; +@property (weak, nonatomic) IBOutlet UIView *dotView; +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *missedDiscussionsBadgeLabelLeadingConstraint; +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *pictureViewHeightConstraint; +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *pictureViewWidthConstraint; /** The room preview data may be used when mxRoom instance is not available @@ -61,4 +65,9 @@ */ - (void)reportTapGesture:(UITapGestureRecognizer*)tapGestureRecognizer; +/** + update the layout of the title view according to the target orientation + */ +- (void)updateLayoutForOrientation:(UIInterfaceOrientation)orientation; + @end diff --git a/Riot/Modules/Room/Views/Title/RoomTitleView.m b/Riot/Modules/Room/Views/Title/RoomTitleView.m index b9ae630f6..37390bd80 100644 --- a/Riot/Modules/Room/Views/Title/RoomTitleView.m +++ b/Riot/Modules/Room/Views/Title/RoomTitleView.m @@ -47,6 +47,8 @@ [tap setDelegate:self]; [self.titleMask addGestureRecognizer:tap]; self.titleMask.userInteractionEnabled = YES; + self.dotView.layer.masksToBounds = YES; + self.dotView.layer.cornerRadius = CGRectGetMidX(self.dotView.bounds); } } @@ -85,6 +87,8 @@ self.backgroundColor = UIColor.clearColor; self.displayNameTextField.textColor = (self.mxRoom.summary.displayname.length ? ThemeService.shared.theme.textPrimaryColor : ThemeService.shared.theme.textSecondaryColor); self.typingLabel.textColor = ThemeService.shared.theme.textSecondaryColor; + self.dotView.backgroundColor = ThemeService.shared.theme.warningColor; + self.missedDiscussionsBadgeLabel.textColor = ThemeService.shared.theme.tintColor; } - (void)setRoomPreviewData:(RoomPreviewData *)roomPreviewData @@ -133,6 +137,28 @@ } } +- (void)updateLayoutForOrientation:(UIInterfaceOrientation)orientation +{ + if (UIInterfaceOrientationIsLandscape(orientation)) + { + self.missedDiscussionsBadgeLabel.font = [UIFont systemFontOfSize:10]; + self.missedDiscussionsBadgeLabelLeadingConstraint.constant = -24; + self.pictureViewWidthConstraint.constant = 28; + self.pictureViewHeightConstraint.constant = 28; + self.displayNameTextField.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium]; + self.typingLabel.font = [UIFont systemFontOfSize:10]; + } + else + { + self.missedDiscussionsBadgeLabel.font = [UIFont systemFontOfSize:15]; + self.missedDiscussionsBadgeLabelLeadingConstraint.constant = -32; + self.pictureViewWidthConstraint.constant = 32; + self.pictureViewHeightConstraint.constant = 32; + self.displayNameTextField.font = [UIFont systemFontOfSize:17 weight:UIFontWeightMedium]; + self.typingLabel.font = [UIFont systemFontOfSize:12]; + } +} + - (void)setTypingNotificationString:(NSString *)typingNotificationString { if (typingNotificationString.length > 0) diff --git a/Riot/Modules/Room/Views/Title/RoomTitleView.xib b/Riot/Modules/Room/Views/Title/RoomTitleView.xib index 0fbeea444..8280083bd 100644 --- a/Riot/Modules/Room/Views/Title/RoomTitleView.xib +++ b/Riot/Modules/Room/Views/Title/RoomTitleView.xib @@ -4,6 +4,7 @@ + @@ -14,11 +15,19 @@ + + + + + + + + @@ -89,6 +104,9 @@ - + + + + From cf8117329e5e1e3b386898ca00b2a50b4628a8df Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Wed, 24 Mar 2021 23:16:27 +0100 Subject: [PATCH 11/78] Explore typing notifications inspired by web - Update for dark mode --- .../Views/BubbleCells/RoomTypingBubbleCell.swift | 12 ++++-------- .../Room/Views/BubbleCells/RoomTypingBubbleCell.xib | 8 +------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift b/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift index 60f9cb98e..b69541f88 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift +++ b/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift @@ -36,20 +36,16 @@ class RoomTypingBubbleCell: UITableViewCell { // MARK: - Lifecycle - override func awakeFromNib() { - super.awakeFromNib() - - additionalUsersLabel?.textColor = ThemeService.shared().theme.textSecondaryColor - dotsView?.highlightedDotColor = ThemeService.shared().theme.textTertiaryColor - dotsView?.dotColor = ThemeService.shared().theme.tabBarUnselectedItemTintColor - } - override func prepareForReuse() { super.prepareForReuse() for pictureView in userPictureViews { pictureView.removeFromSuperview() } + + additionalUsersLabel?.textColor = ThemeService.shared().theme.textSecondaryColor + dotsView?.highlightedDotColor = ThemeService.shared().theme.textTertiaryColor + dotsView?.dotColor = ThemeService.shared().theme.textSecondaryColor } override func layoutSubviews() { diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.xib b/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.xib index ec01f9b08..5cd15a92d 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.xib +++ b/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.xib @@ -4,7 +4,6 @@ - @@ -25,7 +24,7 @@ - + @@ -46,9 +45,4 @@ - - - - - From 65c67ea52b6f40cfc8baf07eaa34b452aeb5bb6d Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Thu, 25 Mar 2021 10:26:15 +0100 Subject: [PATCH 12/78] Explore typing notifications inspired by web - generated strings updated --- Riot/Generated/Strings.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index dd05e41f8..0d5515a5a 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1514,7 +1514,7 @@ internal enum VectorL10n { internal static var identityServerSettingsDisconnect: String { return VectorL10n.tr("Vector", "identity_server_settings_disconnect") } - /// Disconnecting from your identity server will mean you won’t be discoverable by other users and be able to invite others by email or phone. + /// Disconnecting from your identity server will mean you won’t be discoverable by other users and be able to invite others by email or phone. internal static var identityServerSettingsDisconnectInfo: String { return VectorL10n.tr("Vector", "identity_server_settings_disconnect_info") } From 7fb8dc95abf5dde39084ed330d1ff3cb523395f6 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Thu, 25 Mar 2021 11:53:45 +0100 Subject: [PATCH 13/78] Explore typing notifications inspired by web - bug fixing --- Riot/Modules/Room/DataSources/RoomDataSource.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 880f6dc65..210f27c93 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -275,6 +275,8 @@ if (self.currentTypingUsers.count == 0) { + self.typingCellIndex = -1; + // we may have changed the number of bubbles in this block, consider that change return bubbles.count; } From 9bc478f6343f56c5e508a1afadf43720a1d050bd Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Thu, 25 Mar 2021 22:15:18 +0100 Subject: [PATCH 14/78] Switching composer between text mode & action mode --- .../Room/Actions/Contents.json | 6 + .../action_camera.imageset/Contents.json | 26 +++ .../action_camera.imageset/action_camera.png | Bin 0 -> 469 bytes .../action_camera@2x.png | Bin 0 -> 780 bytes .../action_camera@3x.png | Bin 0 -> 1118 bytes .../action_file.imageset/Contents.json | 26 +++ .../action_file.imageset/action_file.png | Bin 0 -> 653 bytes .../action_file.imageset/action_file@2x.png | Bin 0 -> 1269 bytes .../action_file.imageset/action_file@3x.png | Bin 0 -> 1845 bytes .../Contents.json | 26 +++ .../action_media_library.png | Bin 0 -> 519 bytes .../action_media_library@2x.png | Bin 0 -> 851 bytes .../action_media_library@3x.png | Bin 0 -> 1238 bytes .../action_sticker.imageset/Contents.json | 26 +++ .../action_sticker.png | Bin 0 -> 582 bytes .../action_sticker@2x.png | Bin 0 -> 1050 bytes .../action_sticker@3x.png | Bin 0 -> 1457 bytes Riot/Generated/Images.swift | 4 + Riot/Generated/Strings.swift | 2 +- Riot/Modules/Room/RoomViewController.m | 219 ++++++++++-------- .../Views/InputToolbar/RoomActionItem.swift | 30 +++ .../Views/InputToolbar/RoomActionsBar.swift | 129 +++++++++++ .../Views/InputToolbar/RoomInputToolbarView.h | 36 +-- .../Views/InputToolbar/RoomInputToolbarView.m | 135 ++++------- .../InputToolbar/RoomInputToolbarView.xib | 12 + 25 files changed, 467 insertions(+), 210 deletions(-) create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_camera.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_camera.imageset/action_camera.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_camera.imageset/action_camera@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_camera.imageset/action_camera@3x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_file.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_file.imageset/action_file.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_file.imageset/action_file@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_file.imageset/action_file@3x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_media_library.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_media_library.imageset/action_media_library.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_media_library.imageset/action_media_library@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_media_library.imageset/action_media_library@3x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_sticker.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_sticker.imageset/action_sticker.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_sticker.imageset/action_sticker@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_sticker.imageset/action_sticker@3x.png create mode 100644 Riot/Modules/Room/Views/InputToolbar/RoomActionItem.swift create mode 100644 Riot/Modules/Room/Views/InputToolbar/RoomActionsBar.swift diff --git a/Riot/Assets/Images.xcassets/Room/Actions/Contents.json b/Riot/Assets/Images.xcassets/Room/Actions/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Actions/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_camera.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Actions/action_camera.imageset/Contents.json new file mode 100644 index 000000000..aec39f6e4 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Actions/action_camera.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "action_camera.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "action_camera@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "action_camera@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_camera.imageset/action_camera.png b/Riot/Assets/Images.xcassets/Room/Actions/action_camera.imageset/action_camera.png new file mode 100644 index 0000000000000000000000000000000000000000..799e2c02a34e0e95981ca836b05eb11146523f1d GIT binary patch literal 469 zcmV;`0V@89P)(3s!#c*;}R93a_= z+9S8fUHeN2@P&2%!yW{@>MjtJ1F_HFLV=)&o}{Z(jN1%4tYofuiSjlgSjc7TF%VC_tsnc z>X@5tVBBJ(s}ow&U=O19u(xRm?dyQMPIEDN;f#*TqLmp)Nz)=4;_31SN?hDwiUnZ00000 LNkvXXu0mjfO-#xO literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_camera.imageset/action_camera@2x.png b/Riot/Assets/Images.xcassets/Room/Actions/action_camera.imageset/action_camera@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..191b388f9587a3ecb24cbb846a02a88fff45975e GIT binary patch literal 780 zcmV+n1M~ceP))kBx-q1IYGk-+DNpBr{oPatpuE)TkR~g#%6kR=(|TLg^E@pjES9fAblZSPQ83J_Jb`_fS0Z*NHss zuSNA%1Pe4QNz!2r?!g7165tac;UuEYt}GpU4T;0VGFrebP_WVn9c+W@}$=_ekOn;B0nMJpmW)We6=aRGoAtoxa1A+{rvd_1(-76yAVBK^t@rmk$`nWTZGvVQ8qJ$; zzkcu%Ez+t$SD!uVl0qLX!UpM^TlWWOiPi+N%PP^PaN;Crmwc?k>!Y+cL<=aigw{od z_Qi3;Yo^#nOSED^&M8~#c?CK0K!soCX)i~^gtL5T{kOdi5mkofs`an6?g}Rmv1Yzu zU!S63YM0o?^Svz<&(}FIpCSG7tu);9gZ$eoi3gL+o-}@{hbQH06P#whe_@50E?r7d>@@pbX^6NK3Cm%ga2u-*(T&N|={#uJ5$G~d z(xA0s^>e`sRKrZgYMU@)zyTd&Z2mY)?7}ciIHa#go31ToI(&9lmX9-K9?p>H9Gm_TgI&f9eH7LBtc+h03?5K;o=MQ9Gu z5MdBx?w$}pN}%IKmew?G|Hei^k_f|w8QtBQW#%_a% zY!6F0>A1J4r9Z5{By+^7|Ir8 literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_file.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Actions/action_file.imageset/Contents.json new file mode 100644 index 000000000..63ec74393 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Actions/action_file.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "action_file.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "action_file@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "action_file@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_file.imageset/action_file.png b/Riot/Assets/Images.xcassets/Room/Actions/action_file.imageset/action_file.png new file mode 100644 index 0000000000000000000000000000000000000000..0d944b128424028f710a5f0b169375c475bc561d GIT binary patch literal 653 zcmV;80&@L{P)f8zenJf`o&3bDvOKq|zItC!l%a0g8KrniDiPpdx8O zjdzC~Z(JPPsonB~Ixn`@_V@4nJ39;TzlVT3gd6R<3Kjz*(FX!g4^1`b2+`g?_g|m? zlYoYMQYDeT^@{I=fEb1C=S2{15vy>Tk^+DBA`nW_M*2+9PLiu({-4GIdAJ~oExg>A zZw4p)?&9SrCMCAgED(FQ7AV5ey3{s=D_&<}P_V96;9<1i&fwaDlPb9RZntgjXxdnS z;i#nXoZxWY4SaydrSCXHRN_;LS9I7&;4hq9hR?b|-((}toNs$k9-d!#+y3>jR=|lq z^fEXR9c1w9F-702;0u%``$_`N*PU0OXly`gd)^%l^Mb~z(XhDi4FB!Fn-+PZ=%u#c z^cueTHe|RFWPin0^UjoN(J`tt$+D@gs-;zkPpbr0)?? zNOvhghphn=p#|22`+Mv2d+$NZ9%!LqJ{zcsl0bS|Q4(=)U-wf}c%?uV&hc(vABnwX nW%1rf7n>{?U-+XK?oIpweW0!btEzG400000NkvXXu0mjf4U{R% literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_file.imageset/action_file@2x.png b/Riot/Assets/Images.xcassets/Room/Actions/action_file.imageset/action_file@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4f4522ab7be943bcede71985f74aa89bd17c54af GIT binary patch literal 1269 zcmVqHdC&x}(E>5?k!AUmL}fJEHh*OnDDT|pCx7V*^N3DHzayMo)hf?HmJ;@lOqtbi=R zA_+oVdk*I~b?}Tm_BbEqf&8Va$z{d>q`aTDzj^0+trs=ml|MR=7>)?KRWfz*QP$djwR@A2c`s)`-M>Bog?AOc5~v8< z^EVeyMUy1FfcE|C%Zl));wT=4;aP<&mz!1-PUn> zOh`Pj&EJXvHh3k`dMDGu2nPCyXxZl0V8g2W!?y(AF@|IKdAm5HETL+(r$~kAp{8x_ zMm@mrprE6VBIWe(3s-nn(C!7Ub2s9FhCgaVKm{rnKB9v3-2^HquGA?B1gr6D8^G`? z3VjN^1O1cL;8{ZGHv+|$3`pQ{gYfl-s}B9svJPb~Yj3o23=n+N75>#vR|jPkl@+%e zAV=o)iZa(q1~5Eji443zd`iQ!hw8r$eQF6F_f+`%{=*MlD1wjM4Q!;x`Fc>G$_6Cx zV5ByNm)@Tmd>k98&Qqa0%DX1jjkCO7Nh#=+EyGF%l)(q>evq2?nDLN+31s29=-ogL z{QU6z21?{?fDN7-upIbYBdKxgK^DHB`7!F1T;SvDx=bp9pT0itl+80eQ~)0ke6Ofi zs%*fz@Z2`ny!qEU{BT66(G2KH?M9-~I*<#c)L?e_Z68YD*~nSSNYA{--aquA6sNl6I|deDRr94N_t?6W*Gkc8@=d*gRDzP7%t~sQtHE# zb>IU7J`>nNXr3*Sb33iM6wO>O?vvLg6}y|N*o_Ej87lLX4kw`(IaOg+Q7fUXLH@zz z8>6T3yoE=2CskDR((q=Zm%vvIY-a<)FkCJvIyLy9^Fg=%Fhn}f$*IJ6Yu1BJW+l4; zs6OZes+GK=cwiToW{I4Xn%|`w`-m#C8z973dn@HxvWz$$_~4cA8Wh1-2<%nsfz<%Y z#T=OFyWCJJTvld2HTHP76rX1<4GEGn&sDQhRTXxldFPaL%-8>;eK7z>S+f#|dr!_z z*MX0(0>wYxprZ9?7A;pJc&kX>=$q|^+32mK()|FRDDXKw7;OV-LK)^qI<&dxBFAyT z26MKOHT)8l>}J3>49{}fkuBP4!N(oW6*5m|m%0f#KOb0l0^Xa;$x#7q(g&{l+$yT{ zfa7_IbSbdUz|9-{Jm9ugo8PP(tTwImgr?ztPnpGQQOZ1GkE9yEApS!fD^EV{!R+0( fz<1hdr-l9j#u{U2?FBcK00000NkvXXu0mjf6f$98 literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_file.imageset/action_file@3x.png b/Riot/Assets/Images.xcassets/Room/Actions/action_file.imageset/action_file@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..13ed744fc0e4e92f638233252e53fda390e53e23 GIT binary patch literal 1845 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 literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_media_library.imageset/action_media_library@3x.png b/Riot/Assets/Images.xcassets/Room/Actions/action_media_library.imageset/action_media_library@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..2dfd074ab0b731e1eb15eb1e57d0eac22426c876 GIT binary patch literal 1238 zcmV;{1S$K8P)f8deH)w|dOjr4mx=dw;8{L8U+l@D`p3oUyWUO!*Wt8=eY`y;Kcx^)v7ao({HZ zAW|Ikl*t7Op#Wy%0%D8utSj;fv{kd0A_j7Qb2ORccOmxxRpu9)O`}7YK#e>Q-u1*J-$;-+C*x29v3VMyEh7 z4De%b<#S`)V7Ic-viQI;O(abMVMmrAx^L=-L9_f)gfux!o@|G87O4%$&)^5~xo;gI?qpq=ZZv zhM_>>N}w)c33RKWLKns`6j;rQ$z?X1&r0;2>2k*?`j%a)@JM979wbffda1VfSSDvo z!OS-~z>hHH=-a4@B02BFX-D72m0U;B_p}mTBm0A7^nEYkO%?AwdFl9{!-Con+b!6W*mDkr=9z;eg95|95jS3yi!THQeQ1sG@Ir*DKYtYJmhh z8}m=)s8<=Y0=Wg*?E%G~O~x%nqHA$wCkPAp6kb!expUm_3%jd z=E;T|jGO786`(x$q_e%yYC!Z43*6md-At(&YFfyk(WRyHDd?v2ePVr@LL!Ail=oSg z{Rp=bCyk0bvI!k6el`caqa?@WIXO8wIXUL=54DWl7jMf|&;S4c07*qoM6N<$g3o0> A;Q#;t literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_sticker.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Actions/action_sticker.imageset/Contents.json new file mode 100644 index 000000000..adf12f0d6 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Actions/action_sticker.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "action_sticker.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "action_sticker@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "action_sticker@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_sticker.imageset/action_sticker.png b/Riot/Assets/Images.xcassets/Room/Actions/action_sticker.imageset/action_sticker.png new file mode 100644 index 0000000000000000000000000000000000000000..cb7a04d3fedd7a64764e1531e27487662a2119e5 GIT binary patch literal 582 zcmV-M0=fN(P)dYxl00==P#{sCeR7<~H>LYJ9)pj5k`ubh3! zVCm|h1n4vY*n{a9BMc@PskGp7=kOR%oo5gid?0QucN(>{Nlc0#)tESdFJQz@9{%r? z$71g+T{kvOs0s7HwB9dN+i%0M6k!eK1yxguBVq8e`j;x16EC8ulrUg`HTWhJ!_dGT zMj8F-U6Bqm+?fA+=?zk91vULOGAjuKb3+b3foZf;*Le)Wd=O5%&xOwug68sIb+;vh z#mjx)SzK6?mD`0n>dpb@BFvY=af2RYIb^1-{%W-?m7+;YrkQjb(zm#fxs0AssN6CW{HaNQP2ekUN+ z@|54}$?8qDZDbK2F|dcixCIj($nG$T;p)-tl|j+VNj@S zGeI3@kVQPzz?u|y0wZD4WxiUkAh9o3V%5zmo(Xvg{<&(Lo8O6yy-twjBlL@S21JM0 Uvtx15!TpF literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_sticker.imageset/action_sticker@2x.png b/Riot/Assets/Images.xcassets/Room/Actions/action_sticker.imageset/action_sticker@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..13a2b75119e96e2c5819dbaa3eea71b33b314557 GIT binary patch literal 1050 zcmV+#1m*jQP)~;JRlSFEMQUp$ROgiAY{a=pGPH^;4?Q*#R88DOq`T9v-L2Hwg*m@Eh8(Y5`Eg|=t*0v2PNoBx;aCeUs zY(X}hrZpt70dw_Z?Qy82@OTt92&~*#JGCT}5*aX8KRU_K>qfMo>h)rIJHhy9IAlC> zzXCxCMgSsQ2$Ys%Z97q81;jvkZhchZ86qgQ9TtN~1KMGOP=ryV9X5A%bP_deU#fA?Ua-cuI#3Kmo>!lM`}2Y(IpCkmtD-7{7QXuavtN_MveF zI^Ms62|};JYtt$iSD@qliNba7jpvU!gb?1sgn>eAn%)l>z*!|sM(q2^ceM3kSOHan z357rf&ZD3K0vBL>@FvxD12p&r4=HySfmrfSkb$otEM!;aMB?Zor^dNY>)vZL-+#1C zgI@kPEQ%Zy34_iJNS!@9`mO(DohthvTM1Ep0k<{BszfGigOG;zo>ja~$M>cDQ@t5d z_#+ZV274V=?-oB?3>r1_{h-kOe`dysN~uQ_LS(muXEm*0jCbp5(i@a?Gayjup#w-4 zEp#_b^Gz$j7H4RHS^Q&Nty7k*CqI&Dex5va-dr|s@PGjXbb){c$OsM_zj!{7Jmqo0 z66q*XYX5e5w-WZb2u{Fr$Oyjqoz)-nv-RUO6i|=T6o?Yf1PsuNIdGH3hiw1NXMcTK z0Y#^=X2%fE1PthO%!Yr&2~p|2)Stt@tRFs`cqXzlce}K+ zr8i&$VxoLuhb|fqtSrPcv3jF9l&f5HKt^OGo{41u?}zJm$EuKlV0*^TQ zF?q98Ue5`4UgzI6|BAS-+xO&Af>xhHJd^3qtnHqX?i^4~H14dOA@ivzQ>Mh>A1bBE UH10O~g#Z8m07*qoM6N<$f+`HqF#rGn literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_sticker.imageset/action_sticker@3x.png b/Riot/Assets/Images.xcassets/Room/Actions/action_sticker.imageset/action_sticker@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..d000e8f4c8ab95168d1c282536f38a7922870313 GIT binary patch literal 1457 zcmV;i1y1^jP)Sy0`|8_!v@R{{KgK? zdxK5LNG3sn9OVxQVHsu+L^!=cXEjU6r4|(A8y?R5-!{xPF6X6gT1lw{)kw|+*S4IM zG(~2rB+X%U4E2#Cv^51EXkrozS{Uvf%xcbwg9w%vPWBEUA&Ek4{^a*H{*hNuV-WP_ zd2j2`{Cq=dn0KP^IBBn#CT-&oQ_A0g`pXeob5iXaVl;y0-|zPU=@{-2T-bI^Cb?!K z2q|%Ln{|T8v;k8E$;=$?M`+!K=Zv4;TP#xrnVPO6Chsja2YtG7eSmumSJ>ajRoJm3 zXzcraxaau(P3$T}tRTyVr*O|f+xj8)@u&xa?t}LKfp37gFpC^PzVFv=zLge56gi4w zLQHqy8$qk_!>Grc$e*%s3F?JML?izCx2z-rS@Qj@u;C_UOWUxBO0y7L;`2Ik+=_Xx zk%HvOxR{!CT%Iz#=C7l)y)h{k2{1C9L?-F^lRc?_+AxFg+RlhSyh0m>6P%dG+Aw33 zDQ1EL^}y2_cCVfcj2F7{q6UJ9yArB<-FxXm1c+vzw#|USB*OtW7&jq>Mp5vxxeM+umsRr)_wM96|gkcv$-O zs?V=Ix|||svgb%?wP69ZAn9vpEy;fBZjk6Y2(;|#s$jZ7u7p+G8T0Rd>$4bMSLRwR zMX~l#&eo2}xsg*1P1Y%tk0%M)i^)0CmjkqTeLVT75t7IeB1e#g=t9Y*v|P%gu2w+; zCjXc#=MJPKsT?741lhJdf>I$juFerTS@s^Kkt0M33f4!EE7y`BjnAVV3&N@bIYR7; zA}RP2N+7suMbstI?#{9~D~lWU3<&^e3zY04nO#{ACrS7U^l_SIoDksdyjT${! z+TD4`5vKOVWFImV2!~f_u@;OTgjAa`^O7S>6%>5B1&$)lFGpd=l^%B>)$`>iM=6)H!<3lr{QD?*N7yg$fBU+R>6@3bKuT1F%d zucG7##)7==OIJg_p+=TWXF`sX`RFN9j*w^*=T&cT$hqMU$O+Wpg3z$#RJ0r+@h;cP z$G>a@eWx6_+qe3BT*K6vdKq$rRQseYbh=XDK}M8Pp@R4Wh!s?p93hn;sjzKcx5-0N zQp%Jgq`ncurrentAlert = nil; + } + + }]]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"yes"] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) + { + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + + // Show the sticker picker settings screen + IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc] + initForMXSession:self.roomDataSource.mxSession + inRoom:self.roomDataSource.roomId + screen:[IntegrationManagerViewController screenForWidget:kWidgetTypeStickerPicker] + widgetId:nil]; + + [self presentViewController:modularVC animated:NO completion:nil]; + } + }]]; + + [currentAlert mxk_setAccessibilityIdentifier:@"RoomVCStickerPickerAlert"]; + [self presentViewController:currentAlert animated:YES completion:nil]; + } +} + +- (void)roomInputToolbarViewDidTapFileUpload +{ + MXKDocumentPickerPresenter *documentPickerPresenter = [MXKDocumentPickerPresenter new]; + documentPickerPresenter.delegate = self; + + NSArray *allowedUTIs = @[MXKUTI.data]; + [documentPickerPresenter presentDocumentPickerWith:allowedUTIs from:self animated:YES completion:nil]; + + self.documentPickerPresenter = documentPickerPresenter; +} + #pragma mark - Dialpad - (void)openDialpad @@ -3321,80 +3445,6 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil]; } -#pragma mark - RoomInputToolbarViewDelegate - -- (void)roomInputToolbarViewPresentStickerPicker:(MXKRoomInputToolbarView*)toolbarView -{ - // Search for the sticker picker widget in the user account - Widget *widget = [[WidgetManager sharedManager] userWidgets:self.roomDataSource.mxSession ofTypes:@[kWidgetTypeStickerPicker]].firstObject; - - if (widget) - { - // Display the widget - [widget widgetUrl:^(NSString * _Nonnull widgetUrl) { - - StickerPickerViewController *stickerPickerVC = [[StickerPickerViewController alloc] initWithUrl:widgetUrl forWidget:widget]; - - stickerPickerVC.roomDataSource = self.roomDataSource; - - [self.navigationController pushViewController:stickerPickerVC animated:YES]; - } failure:^(NSError * _Nonnull error) { - - NSLog(@"[RoomVC] Cannot display widget %@", widget); - [[AppDelegate theDelegate] showErrorAsAlert:error]; - }]; - } - else - { - // The Sticker picker widget is not installed yet. Propose the user to install it - __weak typeof(self) weakSelf = self; - - [currentAlert dismissViewControllerAnimated:NO completion:nil]; - - NSString *alertMessage = [NSString stringWithFormat:@"%@\n%@", - NSLocalizedStringFromTable(@"widget_sticker_picker_no_stickerpacks_alert", @"Vector", nil), - NSLocalizedStringFromTable(@"widget_sticker_picker_no_stickerpacks_alert_add_now", @"Vector", nil) - ]; - - currentAlert = [UIAlertController alertControllerWithTitle:nil message:alertMessage preferredStyle:UIAlertControllerStyleAlert]; - - [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"no"] - style:UIAlertActionStyleCancel - handler:^(UIAlertAction * action) - { - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - } - - }]]; - - [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"yes"] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) - { - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - - // Show the sticker picker settings screen - IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc] - initForMXSession:self.roomDataSource.mxSession - inRoom:self.roomDataSource.roomId - screen:[IntegrationManagerViewController screenForWidget:kWidgetTypeStickerPicker] - widgetId:nil]; - - [self presentViewController:modularVC animated:NO completion:nil]; - } - }]]; - - [currentAlert mxk_setAccessibilityIdentifier:@"RoomVCStickerPickerAlert"]; - [self presentViewController:currentAlert animated:YES completion:nil]; - } -} - #pragma mark - VoIP - (void)placeCallWithVideo:(BOOL)video @@ -3641,27 +3691,6 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo } } -- (void)roomInputToolbarViewDidTapFileUpload:(MXKRoomInputToolbarView *)toolbarView -{ - MXKDocumentPickerPresenter *documentPickerPresenter = [MXKDocumentPickerPresenter new]; - documentPickerPresenter.delegate = self; - - NSArray *allowedUTIs = @[MXKUTI.data]; - [documentPickerPresenter presentDocumentPickerWith:allowedUTIs from:self animated:YES completion:nil]; - - self.documentPickerPresenter = documentPickerPresenter; -} - -- (void)roomInputToolbarViewDidTapCamera:(MXKRoomInputToolbarView*)toolbarView -{ - [self showCameraControllerAnimated:YES]; -} - -- (void)roomInputToolbarViewDidTapMediaLibrary:(MXKRoomInputToolbarView*)toolbarView -{ - [self showMediaPickerAnimated:YES]; -} - - (void)roomInputToolbarViewDidTapCancel:(MXKRoomInputToolbarView*)toolbarView { [self cancelEventSelection]; diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomActionItem.swift b/Riot/Modules/Room/Views/InputToolbar/RoomActionItem.swift new file mode 100644 index 000000000..e22f9bda5 --- /dev/null +++ b/Riot/Modules/Room/Views/InputToolbar/RoomActionItem.swift @@ -0,0 +1,30 @@ +// +// Copyright 2021 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +@objcMembers +@objc class RoomActionItem: NSObject { + var image: UIImage! + var action: (() -> Void)! + + init(image: UIImage, andAction action: @escaping () -> Void) { + super.init() + + self.image = image + self.action = action + } +} diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomActionsBar.swift b/Riot/Modules/Room/Views/InputToolbar/RoomActionsBar.swift new file mode 100644 index 000000000..7f4bdcb98 --- /dev/null +++ b/Riot/Modules/Room/Views/InputToolbar/RoomActionsBar.swift @@ -0,0 +1,129 @@ +// +// Copyright 2021 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +@objcMembers +@objc class RoomActionsBar: UIScrollView { + // MARK: - Properties + + var itemSpacing: CGFloat = 20 { + didSet { + self.setNeedsLayout() + } + } + + var actionItems: Array = [] { + didSet { + var actionButtons: Array = [] + for (index, item) in actionItems.enumerated() { + let button = UIButton(type: .custom) + button.setImage(item.image, for: .normal) + button.addTarget(self, action: #selector(buttonAction(_:)), for: .touchUpInside) + button.tintColor = ThemeService.shared().theme.tintColor + button.tag = index + actionButtons.append(button) + addSubview(button) + } + self.actionButtons = actionButtons + self.lastBounds = .zero + self.setNeedsLayout() + } + } + + private var actionButtons: Array = [] { + willSet { + for button in actionButtons { + button.removeFromSuperview() + } + } + } + + private var lastBounds = CGRect.zero + + // MARK: - Lifecycle + + override init(frame: CGRect) { + super.init(frame: frame) + self.showsHorizontalScrollIndicator = false + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + self.showsHorizontalScrollIndicator = false + } + + override func layoutSubviews() { + super.layoutSubviews() + + guard lastBounds != self.bounds else { + return + } + + lastBounds = self.bounds + + var currentX: CGFloat = 0 + for button in actionButtons { + button.frame = CGRect(x: currentX, y: 0, width: self.bounds.height, height: self.bounds.height) + currentX = button.frame.maxX + itemSpacing + } + + self.contentSize = CGSize(width: currentX - itemSpacing, height: self.bounds.height) + } + + // MARK: - Business methods + + func customizeViewRendering() { + for button in actionButtons { + button.tintColor = ThemeService.shared().theme.tintColor + } + } + + func animate(showIn: Bool, completion: ((Bool) -> Void)? = nil) { + if showIn { + for button in actionButtons { + button.transform = CGAffineTransform(translationX: 0, y: self.bounds.height) + } + for (index, button) in actionButtons.enumerated() { + UIView.animate(withDuration: 0.32, delay: 0.05 * Double(index), usingSpringWithDamping: 0.5, initialSpringVelocity: 7, options: .curveEaseInOut) { + button.transform = CGAffineTransform.identity + } completion: { (finished) in + completion?(finished) + } + } + } else { + for (index, button) in actionButtons.enumerated() { + UIView.animate(withDuration: 0.2, delay: 0.05 * Double(index), options: .curveEaseInOut) { + button.transform = CGAffineTransform(translationX: 0, y: self.bounds.height) + } completion: { (finished) in + completion?(finished) + } + } + } + } + + // MARK: - Private methods + + @objc private func buttonAction(_ sender: UIButton) { + if let action = actionItems[sender.tag].action { + action() + } + } + + private func setupView() { + self.showsHorizontalScrollIndicator = false + } +} diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h index e5559e9fe..b2ad883b8 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h @@ -18,6 +18,8 @@ #import "MediaPickerViewController.h" +@class RoomActionsBar; + /** Destination of the message in the composer */ @@ -31,34 +33,6 @@ typedef enum : NSUInteger @protocol RoomInputToolbarViewDelegate -/** - Tells the delegate that the user wants to display the sticker picker. - - @param toolbarView the room input toolbar view. - */ -- (void)roomInputToolbarViewPresentStickerPicker:(MXKRoomInputToolbarView*)toolbarView; - -/** - Tells the delegate that the user wants to send external files. - - @param toolbarView the room input toolbar view - */ -- (void)roomInputToolbarViewDidTapFileUpload:(MXKRoomInputToolbarView*)toolbarView; - -/** - Tells the delegate that the user wants to take photo or video with camera. - - @param toolbarView the room input toolbar view - */ -- (void)roomInputToolbarViewDidTapCamera:(MXKRoomInputToolbarView*)toolbarView; - -/** - Tells the delegate that the user wants to show media library. - - @param toolbarView the room input toolbar view - */ -- (void)roomInputToolbarViewDidTapMediaLibrary:(MXKRoomInputToolbarView*)toolbarView; - /** Tells the delegate that the user wants to cancel the current edition / reply. @@ -95,6 +69,7 @@ typedef enum : NSUInteger @property (weak, nonatomic) IBOutlet UIImageView *inputContextImageView; @property (weak, nonatomic) IBOutlet UILabel *inputContextLabel; @property (weak, nonatomic) IBOutlet UIButton *inputContextButton; +@property (weak, nonatomic) IBOutlet RoomActionsBar *actionsBar; /** Tell whether the filled data will be sent encrypted. NO by default. @@ -111,4 +86,9 @@ typedef enum : NSUInteger */ @property (nonatomic) RoomInputToolbarViewSendMode sendMode; +/** + YES if action menu is opened. NO otherwise + */ +@property (nonatomic, getter=isActionMenuOpened) BOOL actionMenuOpened; + @end diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index dfceba978..cb3a55146 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -120,6 +120,7 @@ const double RoomInputToolbarViewContextBarHeight = 30; self.inputContextImageView.tintColor = ThemeService.shared.theme.textSecondaryColor; self.inputContextLabel.textColor = ThemeService.shared.theme.textSecondaryColor; self.inputContextButton.tintColor = ThemeService.shared.theme.textSecondaryColor; + [self.actionsBar customizeViewRendering]; } #pragma mark - @@ -142,6 +143,7 @@ const double RoomInputToolbarViewContextBarHeight = 30; RoomInputToolbarViewSendMode previousMode = _sendMode; _sendMode = sendMode; + self.actionMenuOpened = NO; [self updatePlaceholder]; [self updateToolbarButtonLabelWithPreviousMode: previousMode]; } @@ -329,92 +331,7 @@ const double RoomInputToolbarViewContextBarHeight = 30; { if (button == self.attachMediaButton) { - // Check whether media attachment is supported - if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:presentViewController:)]) - { - // Ask the user the kind of the call: voice or video? - actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - - __weak typeof(self) weakSelf = self; - - [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_action_camera", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->actionSheet = nil; - - [self.delegate roomInputToolbarViewDidTapCamera:self]; - } - }]]; - - - [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_action_send_photo_or_video", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->actionSheet = nil; - - [self.delegate roomInputToolbarViewDidTapMediaLibrary:self]; - } - - }]]; - - if (BuildSettings.allowSendingStickers) - { - [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_action_send_sticker", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->actionSheet = nil; - - [self.delegate roomInputToolbarViewPresentStickerPicker:self]; - } - - }]]; - } - - [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_action_send_file", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->actionSheet = nil; - - [self.delegate roomInputToolbarViewDidTapFileUpload:self]; - } - }]]; - - [actionSheet addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] - style:UIAlertActionStyleCancel - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->actionSheet = nil; - } - - }]]; - - [actionSheet popoverPresentationController].sourceView = self.attachMediaButton; - [actionSheet popoverPresentationController].sourceRect = self.attachMediaButton.bounds; - [self.window.rootViewController presentViewController:actionSheet animated:YES completion:nil]; - } - else - { - NSLog(@"[RoomInputToolbarView] Attach media is not supported"); - } + self.actionMenuOpened = !self.isActionMenuOpened; } [super onTouchUpInside:button]; @@ -443,9 +360,55 @@ const double RoomInputToolbarViewContextBarHeight = 30; self.rightInputToolbarButton.alpha = 0; self.messageComposerContainerTrailingConstraint.constant = 12; } + [self layoutIfNeeded]; } +#pragma mark - properties + +- (void)setActionMenuOpened:(BOOL)actionMenuOpened +{ + if (_actionMenuOpened != actionMenuOpened) + { + _actionMenuOpened = actionMenuOpened; + + if (_actionMenuOpened) { + self.actionsBar.hidden = NO; + [self.actionsBar animateWithShowIn:_actionMenuOpened completion:nil]; + } + else + { + [self.actionsBar animateWithShowIn:_actionMenuOpened completion:^(BOOL finished) { + self.actionsBar.hidden = YES; + }]; + } + + [UIView animateWithDuration:.4 delay:0 usingSpringWithDamping:0.45 initialSpringVelocity:5 options:UIViewAnimationOptionCurveEaseIn animations:^{ + self.attachMediaButton.transform = actionMenuOpened ? CGAffineTransformMakeRotation(M_PI * 3 / 4) : CGAffineTransformIdentity; + } completion:^(BOOL finished) { + }]; + + [UIView animateWithDuration:.2 delay:_actionMenuOpened ? 0 : .1 options:UIViewAnimationOptionCurveEaseIn animations:^{ + self->messageComposerContainer.alpha = actionMenuOpened ? 0 : 1; + self.rightInputToolbarButton.alpha = self->growingTextView.text.length == 0 || actionMenuOpened ? 0 : 1; + } completion:^(BOOL finished) { + }]; + + [UIView animateWithDuration:.3 animations:^{ + if (actionMenuOpened) + { + self.mainToolbarHeightConstraint.constant = self.mainToolbarMinHeightConstraint.constant; + } + else + { + [self->growingTextView refreshHeight]; + } + [self layoutIfNeeded]; + [self.delegate roomInputToolbarView:self heightDidChanged:self.mainToolbarHeightConstraint.constant completion:nil]; + }]; + } +} + #pragma mark - Clipboard - Handle image/data paste from general pasteboard - (void)paste:(id)sender diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib index fbc52e9f1..e99b0ef66 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib @@ -27,6 +27,14 @@ + @@ -105,11 +113,14 @@ + + + @@ -125,6 +136,7 @@ + From 15f2f14ee720c2cd3cddd34132218fc63d236cc1 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Fri, 26 Mar 2021 11:18:05 +0100 Subject: [PATCH 15/78] Switching composer between text mode & action mode - Deselect text if user taps action button to avoid overlaps --- Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index cb3a55146..a0b5aceb8 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -372,6 +372,8 @@ const double RoomInputToolbarViewContextBarHeight = 30; { _actionMenuOpened = actionMenuOpened; + self->growingTextView.internalTextView.selectedTextRange = nil; + if (_actionMenuOpened) { self.actionsBar.hidden = NO; [self.actionsBar animateWithShowIn:_actionMenuOpened completion:nil]; From ee9aa1714c3e77c092d1b9047a837772ec3c1f39 Mon Sep 17 00:00:00 2001 From: RainSlide Date: Thu, 25 Mar 2021 15:02:48 +0000 Subject: [PATCH 16/78] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (33 of 33 strings) Translation: Element iOS/Element iOS (Push) Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-push/zh_Hans/ --- Riot/Assets/zh_Hans.lproj/Localizable.strings | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/zh_Hans.lproj/Localizable.strings b/Riot/Assets/zh_Hans.lproj/Localizable.strings index aea281b69..7ff0fb738 100644 --- a/Riot/Assets/zh_Hans.lproj/Localizable.strings +++ b/Riot/Assets/zh_Hans.lproj/Localizable.strings @@ -1,5 +1,5 @@ /* New message from a specific person, not referencing a room */ -"MSG_FROM_USER" = "%@发送了一条消息"; +"MSG_FROM_USER" = "%@ 发送了一条消息"; /* New message from a specific person in a named room */ "MSG_FROM_USER_IN_ROOM" = "%@ 在 %@ 发送了消息"; /* New message from a specific person, not referencing a room. Content included. */ @@ -27,11 +27,11 @@ /* Multiple unread messages from three people */ "MSGS_FROM_THREE_USERS" = "%@ 条未读消息 (来自 %@、%@ 和 %@)"; /* Multiple unread messages from two plus people (ie. for 4+ people: 'others' replaces the third person) */ -"MSGS_FROM_TWO_PLUS_USERS" = "%@ 条未读消息 (来自 %@、%@ 及更多)"; +"MSGS_FROM_TWO_PLUS_USERS" = "%@ 条未读消息 (来自 %@、%@ 与其他)"; /* Multiple messages in two rooms */ "MSGS_IN_TWO_ROOMS" = "%@ 条未读消息 (来自聊天室 %@ 和 %@)"; /* Look, stuff's happened, alright? Just open the app. */ -"MSGS_IN_TWO_PLUS_ROOMS" = "%@ 条未读消息 (来自聊天室 %@、%@ 和更多)"; +"MSGS_IN_TWO_PLUS_ROOMS" = "%@ 条未读消息 (来自 %@、%@ 与其他)"; /* A user has invited you to a chat */ "USER_INVITE_TO_CHAT" = "%@ 邀请您加入私聊"; /* A user has invited you to an (unamed) group chat */ @@ -54,4 +54,16 @@ "MSG_FROM_USER_IN_ROOM_TITLE" = "%@(来自 %@)"; /* Sticker from a specific person, not referencing a room. */ "STICKER_FROM_USER" = "%@发送了一张贴图"; -"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@想要验证"; +"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@ 想要验证"; +"MESSAGE_PROTECTED" = "未读消息"; + +/* New message indicator on a room */ +"MESSAGE_IN_X" = "%@ 中的消息"; + +/* New message indicator from a DM */ +"MESSAGE_FROM_X" = "来自 %@ 的消息"; + +/** Notification messages **/ + +/* New message indicator on unknown room */ +"MESSAGE" = "消息"; From cd7d533edd16da6c54db60fdb03fb7c40d2edabf Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 26 Mar 2021 17:23:48 +0100 Subject: [PATCH 17/78] Prepare for new sprint --- CHANGES.rst | 24 ++++++++++++++++++++++++ Config/AppIdentifiers.xcconfig | 4 ++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index cb519d96a..09d797340 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,27 @@ +Changes to be released in next version +================================================= + +✨ Features + * + +🙌 Improvements + * + +🐛 Bugfix + * + +⚠️ API Changes + * + +🗣 Translations + * + +🧱 Build + * + +Others + * + Changes in 1.2.8 (2021-03-26) ================================================= diff --git a/Config/AppIdentifiers.xcconfig b/Config/AppIdentifiers.xcconfig index ebcc71705..9c0a99957 100644 --- a/Config/AppIdentifiers.xcconfig +++ b/Config/AppIdentifiers.xcconfig @@ -22,8 +22,8 @@ APPLICATION_GROUP_IDENTIFIER = group.im.vector APPLICATION_SCHEME = element // Version -MARKETING_VERSION = 1.2.8 -CURRENT_PROJECT_VERSION = 1.2.8 +MARKETING_VERSION = 1.2.9 +CURRENT_PROJECT_VERSION = 1.2.9 // Team From 286faff5ea6a3183f450bfeb3f59d7aaf9fa09a4 Mon Sep 17 00:00:00 2001 From: jelv Date: Thu, 25 Mar 2021 11:08:22 +0000 Subject: [PATCH 18/78] Translated using Weblate (Dutch) Currently translated at 100.0% (1186 of 1186 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/nl/ --- Riot/Assets/nl.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index 53182a22a..9f0ecb1a4 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -1312,7 +1312,7 @@ "identity_server_settings_alert_no_terms" = "De door u gekozen identiteitsserver heeft geen dienstvoorwaarden. Ga alleen door, wanneer u de eigenaar van de server vertrouwd."; "identity_server_settings_alert_no_terms_title" = "De identiteitsserver heeft geen dienstvoorwaarden"; "identity_server_settings_disconnect" = "Verbinding verbreken"; -"identity_server_settings_disconnect_info" = "De verbinding met uw identiteitsserver verbreken zal ertoe leiden dat u niet door andere gebruikers gevonden zal kunnen worden, en dat u anderen niet via e-mail of telefoon zal kunnen uitnodigen."; +"identity_server_settings_disconnect_info" = "De verbinding met uw identiteitsserver verbreken zal ertoe leiden dat u niet door andere mensen gevonden zal kunnen worden, en dat u anderen niet via e-mail of telefoon zal kunnen uitnodigen."; "identity_server_settings_change" = "Wijzigen"; "identity_server_settings_add" = "Toevoegen"; "identity_server_settings_place_holder" = "Voer een identiteitsserver in"; From f1352a4681116b937b938ae1a40b18434922693d Mon Sep 17 00:00:00 2001 From: RainSlide Date: Thu, 25 Mar 2021 16:36:07 +0000 Subject: [PATCH 19/78] Translated using Weblate (Chinese (Simplified)) Currently translated at 92.2% (1094 of 1186 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/zh_Hans/ --- Riot/Assets/zh_Hans.lproj/Vector.strings | 237 ++++++++++++++++++----- 1 file changed, 185 insertions(+), 52 deletions(-) diff --git a/Riot/Assets/zh_Hans.lproj/Vector.strings b/Riot/Assets/zh_Hans.lproj/Vector.strings index 46cf235d1..65d372752 100644 --- a/Riot/Assets/zh_Hans.lproj/Vector.strings +++ b/Riot/Assets/zh_Hans.lproj/Vector.strings @@ -11,7 +11,7 @@ "continue" = "继续"; "create" = "新建"; "start" = "开始"; -"leave" = "退出"; +"leave" = "离开"; "remove" = "移除"; "invite" = "邀请"; "retry" = "重试"; @@ -27,7 +27,7 @@ "voice" = "语音"; "video" = "视频"; "active_call" = "当前通话"; -"active_call_details" = "当前通话 (%@)"; +"active_call_details" = "当前通话(%@)"; "later" = "稍后再说"; "rename" = "重命名"; // Authentication @@ -47,7 +47,7 @@ "auth_phone_placeholder" = "手机号码"; "auth_repeat_password_placeholder" = "重复密码"; "auth_repeat_new_password_placeholder" = "确认您的新密码"; -"auth_invalid_login_param" = "无效的用户名和/或密码"; +"auth_invalid_login_param" = "用户名和/或密码无效"; "auth_invalid_user_name" = "用户名只能包含字母、数字、短横(-)与下划线(_)"; "auth_invalid_password" = "密码太短(至少 6 个字符)"; "auth_invalid_email" = "这不像是一个有效的邮箱地址"; @@ -142,8 +142,8 @@ "room_participants_add_participant" = "添加参加者"; "room_participants_one_participant" = "1 个参加者"; "room_participants_multi_participants" = "%d 个参加者"; -"room_participants_leave_prompt_title" = "退出聊天室"; -"room_participants_leave_prompt_msg" = "您确定要退出此聊天室吗?"; +"room_participants_leave_prompt_title" = "离开聊天室"; +"room_participants_leave_prompt_msg" = "您确定要离开此聊天室吗?"; "room_participants_remove_prompt_title" = "确认"; "room_participants_remove_prompt_msg" = "您确定要将 %@ 从此聊天室里移除?"; "room_participants_remove_third_party_invite_msg" = "在 API 实现之前还不支持移除第三方邀请"; @@ -165,18 +165,18 @@ "room_participants_action_section_devices" = "会话"; "room_participants_action_section_other" = "选项"; "room_participants_action_invite" = "邀请"; -"room_participants_action_leave" = "退出此聊天室"; +"room_participants_action_leave" = "离开此聊天室"; "room_participants_action_remove" = "从此聊天室移除"; "room_participants_action_ban" = "从此聊天室封禁"; "room_participants_action_unban" = "解封"; "room_participants_action_ignore" = "隐藏此用户的所有信息"; "room_participants_action_unignore" = "显示此用户的所有信息"; "room_participants_action_set_default_power_level" = "重置到正常用户"; -"room_participants_action_set_moderator" = "使其成为主持人"; +"room_participants_action_set_moderator" = "使其成为协管员"; "room_participants_action_set_admin" = "使其成为管理员"; "room_participants_action_start_new_chat" = "发起新的聊天"; -"room_participants_action_start_voice_call" = "开始语音通话"; -"room_participants_action_start_video_call" = "开始视频通话"; +"room_participants_action_start_voice_call" = "发起语音通话"; +"room_participants_action_start_video_call" = "发起视频通话"; "room_participants_action_mention" = "提及"; // Chat "room_jump_to_first_unread" = "跳到第一条未读信息"; @@ -185,7 +185,7 @@ "room_one_user_is_typing" = "%@ 正在输入…"; "room_two_users_are_typing" = "%@ 和 %@ 正在输入…"; "room_many_users_are_typing" = "%@、%@ 和 %@ 正在输入…"; -"room_message_placeholder" = "发送消息(非加密)…"; +"room_message_placeholder" = "发送消息(未加密)…"; "encrypted_room_message_placeholder" = "发送加密消息…"; "room_message_short_placeholder" = "发送消息…"; "room_offline_notification" = "到服务器的连接已经丢失。"; @@ -252,7 +252,7 @@ "settings_labs" = "实验室"; "settings_devices" = "会话"; "settings_cryptography" = "加密"; -"settings_sign_out" = "退出"; +"settings_sign_out" = "离开"; "settings_sign_out_confirmation" = "你确定?"; "settings_sign_out_e2e_warn" = "您将丢失所有端对端加密密钥。这意味着在此设备上您将再也无法阅读已加密聊天室里的旧消息。"; "settings_profile_picture" = "档案图片"; @@ -326,7 +326,7 @@ "room_details_history_section_prompt_msg" = "改成谁都可以阅读历史只会应用于此聊天室未来的消息。已经存在的历史消息的可见性将不会改变。"; "room_details_new_address" = "添加新地址"; "room_details_new_address_placeholder" = "添加新地址(例如 #foo%@)"; -"room_details_addresses_invalid_address_prompt_title" = "无效的别名格式"; +"room_details_addresses_invalid_address_prompt_title" = "别名格式无效"; "room_details_addresses_invalid_address_prompt_msg" = "%@ 不是一个有效的别名格式"; "room_details_addresses_disable_main_address_prompt_title" = "主地址警告"; "room_details_addresses_disable_main_address_prompt_msg" = "您没有指定主地址。此聊天室的默认主地址会随机选取"; @@ -413,7 +413,7 @@ // Events formatter "event_formatter_member_updates" = "%tu 的成员身份变化"; "auth_home_server_placeholder" = "URL(例如 https://matrix.org)"; -"auth_identity_server_placeholder" = "网址(例如 https://vector.im)"; +"auth_identity_server_placeholder" = "URL(例如 https://vector.im)"; "contacts_user_directory_section" = "用户目录"; "contacts_user_directory_offline_section" = "用户目录(离线)"; "room_ongoing_conference_call_with_close" = "收到会议通话。以 %@ 或 %@.%@ 加入。"; @@ -454,8 +454,8 @@ "group_home_multi_rooms_format" = "%tu 个聊天室"; // Group participants "group_participants_add_participant" = "添加成员"; -"group_participants_leave_prompt_title" = "退出群组"; -"group_participants_leave_prompt_msg" = "你确定要退出此群组吗?"; +"group_participants_leave_prompt_title" = "离开群组"; +"group_participants_leave_prompt_msg" = "你确定要离开此群组吗?"; "group_participants_remove_prompt_title" = "选项"; "group_participants_remove_prompt_msg" = "你确定要从此群组中移除 %@ 吗?"; "group_participants_invite_prompt_title" = "选项"; @@ -477,7 +477,7 @@ "widget_integration_room_not_recognised" = "无法识别此房间。"; "widget_integration_positive_power_level" = "权限级别必须是整数。"; "widget_integration_must_be_in_room" = "您不在此聊天室中。"; -"e2e_room_key_request_start_verification" = "开始验证……"; +"e2e_room_key_request_start_verification" = "开始验证…"; "e2e_room_key_request_share_without_verifying" = "在不验证的情况下分享"; "e2e_room_key_request_ignore_request" = "忽略请求"; "room_event_action_kick_prompt_reason" = "移除此用户的原因"; @@ -527,20 +527,20 @@ "room_resource_usage_limit_reached_message_contact_3" = " 以提高限制。"; // String for App Store "store_short_description" = "安全、去中心化的聊天及 VoIP 应用"; -"store_full_description" = "沟通,由你掌控。\n\n一个聊天应用,由你掌控且完全灵活。Element 让你以你的方式沟通。为 [matrix] - 开放、去中心化的沟通而打造。\n\n获取一个免费的 matrix.org 账号,在 https://ems.element.io 获取一个你自己的服务器,或者使用其他 Matrix 服务器。\n\n为什么选择 Element?\n\n完整的沟通:围绕你的团队、你的朋友、你的社区创建聊天室 - 随你喜欢!聊天、分享文件、添加插件和语音视频通话 - 全部免费。\n\n强大的集成度:通过你了解和喜欢的工具来使用 Element 。你甚至可以在 Element 上与其他聊天应用的用户和群组聊天。\n\n隐私和安全:保守你对话的秘密。最先进的端到端加密技术可确保私密通信保持私密性。\n\n开放,而非封闭:开源,基于 Matrix。通过自己开设的服务器来拥有你自己的数据,或者选择你信赖的服务器。\n\n随处可及:在你所有的设备和线上页面 https://app.element.io 上通过完整的历史信息同步让你随处可及。"; +"store_full_description" = "Element 是一种新型的通讯与协作应用:\n\n1. 使您可以掌控您的隐私\n2. 使您与 Matrix 网络中的任何人交流,甚至可以通过集成功能与如 Slack 之类的其他应用通讯\n3. 保护您免受广告,大数据挖掘和封闭服务的侵害\n4. 通过端到端加密保证安全,通过交叉签名验证其他人\n\nElement 与其他通讯与协作应用完全不同,因为它是去中心化且开源的。\n\nElement 允许您自托管——或者选择托管商——因此,您能拥有数据和会话的隐私权,所有权和控制权。它允许您访问开放网络;因此,您可以与 Element 用户以外的人交流。并且它非常安全。\n\nElement 之所以可以做到这些,是因为它在 Matrix 上运行——开放,去中心化通讯的标准。\n\n通过让您选择由谁来托管您的会话,Element 让您掌控一切。在 Element 应用中,您可以选择不同的托管方式:\n\n1. 在由 Matrix 开发者托管的 matrix.org 公共服务器上获取免费帐户,或从志愿者托管的上千个公共服务器中选择\n2. 在您自己的硬件上运行服务器,自托管您的会话\n3. 通过订阅 Element Matrix Services 托管平台,简单地在自定义服务器上注册账户\n\n为什么选择 Element?\n\n掌控您的数据:您来决定存放您的数据和消息的位置。拥有并控制它的是您,而不是挖掘您的数据或与第三方分享的巨型企业。\n\n开放通讯与协作:您可以与 Matrix 网络中的任何人聊天,不论他们使用 Element 还是其他 Matrix 应用,甚至/即使他们在使用不同的通讯系统,例如 Slack,IRC 或 XMPP。\n\n超级安全:支持真正的端到端加密(仅有会话中的人可以解密消息),还有能够验证会话参与方的设备的交叉签名。\n\n完善的通讯方式:消息,语音和视频通话,文件共享,屏幕共享和大量集成功能,机器人和小挂件。建立房间与社区,保持联系并完成工作。\n\n随时随地:消息历史可在您的全部设备和 https://app.element.io 网页端之间完全同步,无论您在哪里,都可以保持联系。"; "auth_accept_policies" = "请查看并接受此主页服务器的服务条款:"; "room_replacement_information" = "这个聊天室已被替换,不再有效。"; "settings_flair" = "在允许的地方显示个性徽章"; "settings_key_backup" = "密钥备份"; "settings_key_backup_info" = "消息已被端对端安全加密。只有您和持有密钥的接收方可以阅读这些消息。"; -"settings_key_backup_info_checking" = "检查中……"; +"settings_key_backup_info_checking" = "正在检查…"; "settings_key_backup_info_none" = "您的密钥未从此会话备份。"; "settings_key_backup_info_signout_warning" = "在登出账号之前把此会话关联到密钥备份以免丢失仅在此设备上的所有密钥。"; "settings_key_backup_info_version" = "密钥备份版本:%@"; "settings_key_backup_info_algorithm" = "算法:%@"; "settings_key_backup_info_valid" = "此会话正在备份密钥。"; "settings_key_backup_info_not_valid" = "此会话未在备份你的密钥,但你的确一个有可以恢复和继续添加的现有备份。"; -"settings_key_backup_info_progress" = "%@ 个密钥备份中……"; +"settings_key_backup_info_progress" = "正在备份 %@ 个密钥…"; "settings_key_backup_info_progress_done" = "所有密钥都已备份"; "settings_key_backup_info_trust_signature_unknown" = "备份具有一个 ID:%@ 的会话的签名"; "settings_key_backup_info_trust_signature_valid" = "备份具有此会话的一个有效签名"; @@ -558,8 +558,8 @@ "room_details_fail_to_update_room_direct" = "此聊天室的直接标记更新失败"; "event_formatter_jitsi_widget_removed" = "VoIP 会议已被 %@ 移除"; "room_does_not_exist" = "%@ 聊天室不存在"; -"call_incoming_voice" = "来电……"; -"call_incoming_video" = "视频通话来电……"; +"call_incoming_voice" = "收到来电…"; +"call_incoming_video" = "收到视频通话…"; // Key backup wrong version "e2e_key_backup_wrong_version_title" = "新建密钥备份"; "e2e_key_backup_wrong_version" = "一个新的安全消息密钥备份已被删除。\n\n如果这不是您的操作,请在设置中设定一个新的密码。"; @@ -591,7 +591,7 @@ "rerequest_keys_alert_message" = "请在另一台可以解密消息的设备上启动Element,这样它就可以将密钥发送到此会话。"; "key_backup_setup_title" = "密钥备份"; "key_backup_setup_skip_alert_title" = "您确定吗?"; -"key_backup_setup_skip_alert_message" = "如果您退出或丢失设备,可能会丢失安全消息。"; +"key_backup_setup_skip_alert_message" = "如果您登出或丢失设备,可能会丢失安全消息。"; "key_backup_setup_skip_alert_skip_action" = "跳过"; "key_backup_setup_intro_title" = "永不丢失加密消息"; "key_backup_setup_intro_info" = "加密聊天室中的消息使用端到端加密进行保护。 只有您和拥有密钥的收件人才能阅读这些消息。\n\n安全备份密钥以避免丢失密钥。"; @@ -659,7 +659,7 @@ "sign_out_key_backup_in_progress_alert_cancel_action" = "等待"; "auth_login_single_sign_on" = "使用单点登录方式登入"; "room_message_unable_open_link_error_message" = "无法打开链接。"; -"auth_autodiscover_invalid_response" = "无效的主服务器探测响应"; +"auth_autodiscover_invalid_response" = "主服务器探测响应无效"; "close" = "关闭"; // Accessibility "accessibility_checkbox_label" = "多选框"; @@ -670,7 +670,7 @@ "auth_phone_is_required" = "未设置身份认证服务器,所以你不能添加电话号码来重设你的密码。"; "auth_forgot_password_error_no_configured_identity_server" = "未设置身份认证服务器:添加服务器以重设你的密码。"; "auth_reset_password_error_is_required" = "未设置身份认证服务器:在服务器选项中添加以便重设你的密码。"; -"auth_softlogout_signed_out" = "你已经登出"; +"auth_softlogout_signed_out" = "你已登出"; "auth_softlogout_sign_in" = "登录"; "auth_softlogout_reason" = "你的主服务器(%1$@)管理员已将你的账号%2$@(%3$@)登出。"; "auth_softlogout_recover_encryption_keys" = "登录以恢复单独保存在此设备上的加密密钥。你需要它们才能阅读任何设备上的安全消息。"; @@ -682,7 +682,7 @@ "auth_softlogout_clear_data_sign_out_msg" = "你确定希望清空所有当前保存在此设备上数据吗?再次登录可以获取你的账号数据和消息。"; "auth_softlogout_clear_data_sign_out" = "登出"; // Errors -"error_user_already_logged_in" = "看上去你试图连接另一个主服务器。你想要登出吗?"; +"error_user_already_logged_in" = "您似乎正在尝试连接另一个主服务器。您想要登出吗?"; "room_creation_error_invite_user_by_email_without_identity_server" = "未设置身份认证服务器,所以你不能用邮箱添加参与者。"; "contacts_address_book_no_identity_server" = "未设置身份认证服务器"; "room_participants_remove_third_party_invite_prompt_msg" = "你确定想撤回这个邀请吗?"; @@ -691,9 +691,9 @@ "room_participants_action_security_status_verified" = "已验证"; "room_participants_action_security_status_verify" = "验证"; "room_participants_action_security_status_warning" = "警告"; -"room_participants_security_loading" = "载入中……"; -"room_participants_security_information_room_not_encrypted" = "本聊天室中的消息不是端到端加密的。"; -"room_participants_security_information_room_encrypted" = "本聊天室中的消息是端到端加密的。\n\n你的消息被加锁保护,并且只有你和收信人有唯一的解密密钥。"; +"room_participants_security_loading" = "正在载入…"; +"room_participants_security_information_room_not_encrypted" = "此聊天室中的消息未经端对端加密。"; +"room_participants_security_information_room_encrypted" = "本聊天室中的消息已被端对端加密。\n\n您的消息受加密保护,并且只有您和消息接收者拥有唯一解密密钥。"; "room_accessiblity_scroll_to_bottom" = "滚动到底部"; "room_event_action_reply" = "回复"; "room_event_action_edit" = "编辑"; @@ -731,7 +731,7 @@ "settings_add_3pid_password_title_email" = "添加邮箱地址"; "settings_add_3pid_password_title_msidsn" = "添加电话号码"; "settings_add_3pid_password_message" = "请填写你的密码以继续"; -"settings_add_3pid_invalid_password_message" = "无效密码"; +"settings_add_3pid_invalid_password_message" = "验证信息无效"; "settings_key_backup_button_connect" = "关联此会话到密钥备份"; "settings_devices_description" = "会话的公开名字会对你联络的人可见"; "settings_discovery_no_identity_server" = "你现在没有在用身份认证服务器。想要被你认识的现有联系人发现,请添加。"; @@ -754,7 +754,7 @@ // Security settings "security_settings_title" = "安全"; "security_settings_crypto_sessions" = "我的会话"; -"security_settings_crypto_sessions_loading" = "载入会话中……"; +"security_settings_crypto_sessions_loading" = "正在载入会话…"; "security_settings_crypto_sessions_description" = "信任会话以给与获得端到端加密的消息的权限。如果你没有认出某个会话,请更改你的登录密码并且重设你用于消息备的消息密码。"; "security_settings_backup" = "消息备份"; "security_settings_advanced" = "高级"; @@ -775,14 +775,14 @@ "identity_server_settings_place_holder" = "输入身份认证服务器"; "identity_server_settings_add" = "添加"; "identity_server_settings_change" = "更改"; -"identity_server_settings_disconnect_info" = "从你的身份认证服务器断开连接意味着你不会被其他用户所发现并且不会被其他人通过邮箱或者电话邀请。"; +"identity_server_settings_disconnect_info" = "从你的身份认证服务器断开连接意味着你无法被其他用户所发现,并且不会被其他人通过邮箱或者电话邀请。"; "identity_server_settings_disconnect" = "断开连接"; "identity_server_settings_alert_no_terms_title" = "身份认证服务器没有服务条款"; "identity_server_settings_alert_no_terms" = "你所选择的身份认证服务器并没有任何服务条款。请仅当你信任此服务器所有人时再继续。"; "identity_server_settings_alert_change_title" = "更改身份认证服务器"; -"identity_server_settings_alert_change" = "断开身份认证服务器%1$@的连接,而连接到%2$@?"; +"identity_server_settings_alert_change" = "断开与身份认证服务器 %1$@ 的连接,转而连接到 %2$@?"; "identity_server_settings_alert_disconnect_title" = "断开连接身份认证服务器"; -"identity_server_settings_alert_disconnect" = "断开到身份认证服务器%@的连接?"; +"identity_server_settings_alert_disconnect" = "断开到身份认证服务器 %@ 的连接?"; "identity_server_settings_alert_disconnect_button" = "断开连接"; "identity_server_settings_alert_disconnect_still_sharing_3pid" = "你仍在身份认证服务器%@分享你的个人数据。\n\n我们建议你在断开连接前从此身份认证服务器删除你的邮箱地址和电话号码。"; "identity_server_settings_alert_disconnect_still_sharing_3pid_button" = "仍然断开连接"; @@ -810,7 +810,7 @@ "widget_integration_manager_disabled" = "你需要在设置中打开“集成管理器”"; // Widget Picker "widget_picker_title" = "集成"; -"widget_picker_manage_integrations" = "管理集成……"; +"widget_picker_manage_integrations" = "管理集成…"; // Room widget permissions "room_widget_permission_title" = "载入小插件"; "room_widget_permission_creator_info_title" = "这个小插件的添加者是:"; @@ -849,13 +849,13 @@ "device_verification_incoming_description_2" = "验证此会话将标记它为已信任,而且同伴也会标记你的会话为已信任。"; // MARK: Start "device_verification_start_title" = "比较一段短文字来验证"; -"device_verification_start_wait_partner" = "等待同伴接受……"; +"device_verification_start_wait_partner" = "正在等待同伴接受…"; "device_verification_start_use_legacy" = "啥都没出现?并非所有客户端都支持交互式验证。使用旧的验证方法。"; "device_verification_start_verify_button" = "开始验证"; "device_verification_start_use_legacy_action" = "使用老式验证"; "device_verification_verify_title_emoji" = "通过确认以下emoji表情出现在同伴的屏幕上来验证此会话"; "device_verification_verify_title_number" = "通过确认以下数字出现在同伴的屏幕上来验证此会话"; -"device_verification_verify_wait_partner" = "等待同伴确认……"; +"device_verification_verify_wait_partner" = "正在等待同伴确认…"; "key_verification_verify_user_title_emoji" = "通过确认以下唯一的emoji表情,以同样顺序,出现在同伴的屏幕上来验证此用户。"; "key_verification_verify_user_title_number" = "通过确认以下数字,以同样顺序,出现在同伴的屏幕上来验证此用户。"; "device_verification_verified_title" = "已验证!"; @@ -949,8 +949,8 @@ "error_not_supported_on_mobile" = "你不能从移动版%@中进行此操作。"; "key_verification_tile_request_incoming_title" = "验证请求"; "key_verification_tile_request_outgoing_title" = "验证已发送"; -"key_verification_tile_request_status_data_loading" = "数据载入中……"; -"key_verification_tile_request_status_waiting" = "等待中……"; +"key_verification_tile_request_status_data_loading" = "正在载入数据…"; +"key_verification_tile_request_status_waiting" = "正在等待…"; "key_verification_tile_request_status_expired" = "已过期"; "key_verification_tile_request_status_cancelled_by_me" = "您已取消"; "key_verification_tile_request_status_cancelled" = "%@已取消"; @@ -963,7 +963,7 @@ "user_verification_start_verify_action" = "开始验证"; "user_verification_start_information_part1" = "为了额外的安全性,请验证: "; "user_verification_start_information_part2" = " 检查在你的两个设备上的一次性代码。"; -"user_verification_start_waiting_partner" = "等待%@……"; +"user_verification_start_waiting_partner" = "正在等待 %@…"; "user_verification_start_additional_information" = "为了安全,请当面或者使用其他通信方式进行此操作。"; "user_verification_sessions_list_user_trust_level_trusted_title" = "已信任"; "user_verification_sessions_list_user_trust_level_warning_title" = "警告"; @@ -989,16 +989,16 @@ "client_ios_name" = "Element iOS版"; "client_android_name" = "Element 安卓版"; "room_participants_action_security_status_complete_security" = "完整安全性"; -"room_participants_action_security_status_loading" = "加载中……"; -"room_member_power_level_admin_in" = "在 %@ 的管理员"; -"room_member_power_level_moderator_in" = "在 %@ 的主持人"; -"room_member_power_level_short_admin" = "管理员"; -"room_member_power_level_short_moderator" = "主持人"; +"room_participants_action_security_status_loading" = "正在加载…"; +"room_member_power_level_admin_in" = "管理员(%@)"; +"room_member_power_level_moderator_in" = "协管员(%@)"; +"room_member_power_level_short_admin" = "管理"; +"room_member_power_level_short_moderator" = "协管"; "security_settings_crosssigning" = "交叉签名"; "security_settings_crosssigning_info_not_bootstrapped" = "交叉签名还没有被设置。"; "security_settings_crosssigning_info_exists" = "您的帐户有一个交叉签名身份,但是还没有被这个会话信任。完全安全的会话。"; "skip" = "跳过"; -"room_member_power_level_custom_in" = "自定义(%@) 到%@"; +"room_member_power_level_custom_in" = "自定义(%@)(%@)"; "room_member_power_level_short_custom" = "自定义"; "security_settings_crosssigning_info_trusted" = "已启用交叉签名。您可以基于交叉签名信任其他用户和其他会话,但不能从此会话交叉签名,因为它没有交叉签名私钥。此会话完全安全。"; "security_settings_crosssigning_info_ok" = "交叉签名还没有被设置。"; @@ -1008,7 +1008,7 @@ "security_settings_cryptography" = "加密"; "security_settings_complete_security_alert_title" = "绝对安全"; "security_settings_complete_security_alert_message" = "您应该先完成当前会话的安全防护。"; -"security_settings_coming_soon" = "对不起。这个操作在Element iOS版本上还不可用。请使用另一个Matrix客户端来设置它。Elment iOS会使用它。"; +"security_settings_coming_soon" = "对不起。这个操作在 Element iOS 版本上还不可用。请使用另一个 Matrix 客户端来设置它。Elment iOS 会沿用其他客户端的设置。"; // Recover from private key "key_backup_recover_from_private_key_info" = "备份恢复中…"; // MARK: - Device Verification @@ -1023,7 +1023,7 @@ "device_verification_self_verify_alert_validate_action" = "验证"; "device_verification_self_verify_start_verify_action" = "开始验证"; "device_verification_self_verify_start_information" = "使用此会话验证您的新会话,并授予其访问加密信息的权限。"; -"device_verification_self_verify_start_waiting" = "等待中……"; +"device_verification_self_verify_start_waiting" = "正在等待…"; "key_verification_self_verify_current_session_alert_title" = "验证此会话"; "key_verification_self_verify_current_session_alert_message" = "其他用户可能不信任它。"; "key_verification_self_verify_current_session_alert_validate_action" = "验证"; @@ -1104,12 +1104,145 @@ "switch" = "开关"; "joined" = "已加入"; "store_promotional_text" = "在开放网络上保护隐私的聊天和协作应用程序。分散权力让你掌控一切。没有数据挖掘,没有后门,也没有第三方访问。"; -"social_login_button_title_sign_up" = "以 %@ 方式登录"; -"social_login_button_title_sign_in" = "以 %@ 方式登录"; -"social_login_button_title_continue" = "继续 %@ 以其他方式登录"; -"social_login_list_title_sign_in" = "利用社交媒体登录"; -"social_login_list_title_sign_up" = "利用社交媒体注册"; +"social_login_button_title_sign_up" = "使用 %@ 注册"; +"social_login_button_title_sign_in" = "使用 %@ 登录"; +"social_login_button_title_continue" = "使用 %@ 继续"; +"social_login_list_title_sign_in" = "或"; +"social_login_list_title_sign_up" = "或"; // Social login "social_login_list_title_continue" = "以其他方式登录"; +"security_settings_secure_backup_delete" = "删除"; +"security_settings_secure_backup_synchronise" = "同步"; +"security_settings_secure_backup_setup" = "设置"; +"security_settings_secure_backup_description" = "通过在您的服务器上备份加密密钥,防止失去对加密信息和数据的访问。"; +"security_settings_crypto_sessions_description_2" = "如果您未曾发起登录,请更改密码并重置安全备份。"; +"settings_show_NSFW_public_rooms" = "显示 NSFW 公共聊天室"; +"external_link_confirmation_message" = "此链接 %@ 会将您带至另一个网站:%@\n\n是否前往?"; +"external_link_confirmation_title" = "双击此链接"; +"room_open_dialpad" = "拨号键盘"; +"room_place_voice_call" = "语音通话"; +"room_event_action_delete_confirmation_message" = "您确定要删除这条未发送的消息吗?"; +"room_event_action_delete_confirmation_title" = "删除未发送的消息"; +"room_unsent_messages_cancel_message" = "您确定要删除此聊天室中所有未发送的消息吗?"; +"room_unsent_messages_cancel_title" = "删除未发送的消息"; +"room_participants_security_information_room_encrypted_for_dm" = "这里的消息已被端对端加密。\n\n您的消息受加密保护,并且只有您和消息接收者拥有唯一解密密钥。"; +"room_participants_security_information_room_not_encrypted_for_dm" = "此处的消息未经端对端加密。"; +"room_participants_filter_room_members_for_dm" = "筛选成员"; +"room_participants_leave_prompt_msg_for_dm" = "您确定要离开吗?"; +"room_participants_leave_prompt_title_for_dm" = "离开"; +"callbar_active_and_multiple_paused" = "1 个进行中通话(%@)· %@ 个已暂停通话"; +"callbar_active_and_single_paused" = "1 个进行中通话(%@)· 1 个已暂停通话"; + +// Call Bar +"callbar_only_single_active" = "进行中通话(%@)"; +"callbar_only_multiple_paused" = "%@ 个已暂停通话"; +"callbar_only_single_paused" = "已暂停通话"; +"callbar_return" = "返回"; +"room_intro_cell_information_room_sentence1_part3" = "。 "; +"room_intro_cell_information_dm_sentence1_part3" = "。 "; +"room_intro_cell_information_room_without_topic_sentence2_part1" = "添加话题"; +"room_intro_cell_information_room_with_topic_sentence2" = "话题:%@"; + +// Mark: - Room creation introduction cell + +"room_intro_cell_add_participants_action" = "添加人员"; +"room_avatar_view_accessibility_hint" = "修改聊天室头像"; + +// Mark: - Room avatar view + +"room_avatar_view_accessibility_label" = "头像"; + +// MARK: - Invite friends + +"invite_friends_action" = "邀请好友至 %@"; + +// MARK: - Favourites + +"favourites_empty_view_title" = "收藏聊天室与对话"; + +// MARK: - Home + +"home_empty_view_title" = "欢迎使用 %@,\n%@"; +"call_transfer_error_message" = "呼叫转移失败"; +"call_transfer_error_title" = "错误"; +"call_transfer_contacts_all" = "全部"; +"call_transfer_contacts_recent" = "最近"; +"call_transfer_dialpad" = "拨号键盘"; +"call_transfer_users" = "用户"; + +// MARK: - Call Transfer +"call_transfer_title" = "转移"; + +// MARK: - Dial Pad +"dialpad_title" = "拨号键盘"; +"room_info_list_section_other" = "其他"; +"create_room_section_footer_encryption" = "加密一经启用,便无法禁用。"; +"create_room_placeholder_address" = "#testroom:matrix.org"; +"create_room_section_header_address" = "聊天室地址"; +"create_room_section_header_type" = "聊天室类型"; +"create_room_enable_encryption" = "启用加密"; +"create_room_section_header_encryption" = "聊天室加密"; +"create_room_placeholder_topic" = "话题"; +"create_room_section_header_topic" = "聊天室话题(可选)"; +"create_room_placeholder_name" = "名称"; +"create_room_section_header_name" = "聊天室名称"; + +// MARK: - Create Room + +"create_room_title" = "新聊天室"; +"searchable_directory_search_placeholder" = "名称或 ID"; +"searchable_directory_x_network" = "%@ 网络"; + +// MARK: - Searchable Directory View Controller + +"searchable_directory_create_new_room" = "创建新聊天室"; +"biometrics_cant_unlocked_alert_message_retry" = "重试"; +"biometrics_desetup_disable_button_title_x" = "禁用 %@"; +"biometrics_desetup_title_x" = "禁用 %@"; +"biometrics_setup_enable_button_title_x" = "启用 %@"; +"biometrics_setup_title_x" = "启用 %@"; +"biometrics_settings_enable_x" = "启用 %@"; +"biometrics_mode_face_id" = "Face ID"; + +// MARK: - Biometrics Protection + +"biometrics_mode_touch_id" = "Touch ID"; +"pin_protection_mismatch_error_message" = "请再试一次"; +"pin_protection_reset_alert_action_reset" = "重置"; +"pin_protection_choose_pin_welcome_after_register" = "欢迎。"; + +// MARK: - PIN Protection + +"pin_protection_choose_pin_welcome_after_login" = "欢迎回来。"; +"major_update_learn_more_action" = "了解更多"; + +// MARK: - Major update + +"major_update_title" = "Riot 现已成为 Element"; +"secrets_reset_reset_action" = "重置"; +"secrets_setup_recovery_passphrase_summary_title" = "保存您的安全密语"; +"secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "确认安全密语"; +"secrets_setup_recovery_passphrase_confirm_passphrase_title" = "确认"; +"secrets_setup_recovery_passphrase_additional_information" = "不要使用你的账号密码。"; +"secrets_setup_recovery_passphrase_validate_action" = "完成"; + +// Recovery passphrase + +"secrets_setup_recovery_passphrase_title" = "设置安全密语"; +"secrets_setup_recovery_key_done_action" = "完成"; +"secrets_setup_recovery_key_export_action" = "保存"; +"secrets_setup_recovery_key_loading" = "正在加载…"; + +// MARK: - Secrets set up + +// Recovery Key + +"secrets_setup_recovery_key_title" = "保存您的安全密钥"; +"room_info_list_several_members" = "%@ 位成员"; + +// MARK: - Room Info + +"room_info_list_one_member" = "1 位成员"; +"security_settings_secure_backup" = "安全备份"; From 28e34873a382bd85876b817042fdf58420abc3f1 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Wed, 24 Mar 2021 16:31:34 +0000 Subject: [PATCH 20/78] Translated using Weblate (Albanian) Currently translated at 99.6% (1182 of 1186 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 77 ++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index e6057953a..b8ea07cc1 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -476,7 +476,7 @@ "room_participants_remove_third_party_invite_msg" = "Heqja e ftesave nga palë të treta nuk mbulohet ende, derisa të kihet API"; "room_participants_invite_malformed_id" = "ID e keqformuar. Duhet të jetë një adresë email ose ID Matrix, si '@localpart:domain'"; "room_participants_action_ban" = "Dëboje nga kjo dhomë"; -"room_unsent_messages_unknown_devices_notification" = "Mesazhi s’u dërgua, për shkak të pranisë së sesioneve të panjohur. %@ ose %@ tani?"; +"room_unsent_messages_unknown_devices_notification" = "Mesazhi s’u dërgua, për shkak të pranisë së sesioneve të panjohur."; "room_ongoing_conference_call" = "Thirrje konference që po zhvillohet. Merrni pjesë si %@ ose %@."; "room_ongoing_conference_call_with_close" = "Thirrje konference që po zhvillohet. Merrni pjesë si %@ ose %@. %@."; "room_conference_call_no_power" = "Ju duhen leje për të administruar thirrje konferencë në këtë dhomë"; @@ -853,7 +853,7 @@ "settings_add_3pid_password_title_email" = "Shtoni adresë email"; "settings_add_3pid_password_title_msidsn" = "Shtoni numër telefoni"; "settings_add_3pid_password_message" = "Që të vazhdohet, ju lutemi, jepni fjalëkalimin tuaj"; -"settings_add_3pid_invalid_password_message" = "Fjalëkalim i pavlefshëm"; +"settings_add_3pid_invalid_password_message" = "Kredenciale të pavlefshme"; "settings_devices_description" = "Emri publik i një sesioni është i dukshëm për persona me të cilët komunikoni"; "settings_discovery_no_identity_server" = "S’po përdorni ndonjë shërbyes identitetesh. Që të jeni i zbulueshëm nga kontakte ekzistuese që njihni, shtoni një të tillë."; "settings_discovery_terms_not_signed" = "Pajtohuni me Kushtet e Shërbimit të Shërbyesit të Identiteteve që t’i lejoni vetes të jeni i zbulueshëm përmes adrese email ose numri telefoni."; @@ -1266,3 +1266,76 @@ "rooms_empty_view_title" = "Dhoma"; "people_empty_view_information" = "Fjalosuni në mënyrë të sigurt me këdo. Prekni + që të filloni të shtoni persona."; "people_empty_view_title" = "Njerëz"; +"room_intro_cell_information_multiple_dm_sentence2" = "Në këtë bisedë jeni vetëm ju, veç nëse cilido prej jush fton dikë tjetër të vijë."; +"room_intro_cell_information_dm_sentence2" = "Në këtë bisedë gjenden vetëm ju të dy, s’mund të vijë tjetërkush."; +"room_intro_cell_information_dm_sentence1_part3" = ". "; +"room_intro_cell_information_dm_sentence1_part1" = "Ky është fillimi i mesazhit tuaj të drejtpërdrejtë me "; +"room_intro_cell_information_room_without_topic_sentence2_part2" = " që t’u bëni me dije njerëzve se për çfarë është kjo dhomë."; +"room_intro_cell_information_room_without_topic_sentence2_part1" = "Shtoni një temë"; +"room_intro_cell_information_room_with_topic_sentence2" = "Temë: %@"; +"room_intro_cell_information_room_sentence1_part3" = ". "; +"room_intro_cell_information_room_sentence1_part1" = "Ky është fillimi i "; + +// Mark: - Room creation introduction cell + +"room_intro_cell_add_participants_action" = "Shtoni persona"; +"room_avatar_view_accessibility_hint" = "Ndryshoni avatar dhome"; + +// Mark: - Room avatar view + +"room_avatar_view_accessibility_label" = "avatar"; +"invite_friends_share_text" = "Hej, bisedoni me mua në %@: %@"; + +// MARK: - Invite friends + +"invite_friends_action" = "Ftoni shokë te %@"; +"call_transfer_error_message" = "S’u arrit të shpërngulej thirrje"; +"call_transfer_error_title" = "Gabim"; +"call_transfer_contacts_all" = "Krejt"; +"call_transfer_contacts_recent" = "Së fundi"; +"call_transfer_dialpad" = "Numërator"; +"call_transfer_users" = "Përdorues"; + +// MARK: - Call Transfer +"call_transfer_title" = "Shpërngule"; + +// MARK: - Dial Pad +"dialpad_title" = "Numërator"; +"pin_protection_settings_change_pin" = "Ndryshoni PIN-in"; +"pin_protection_confirm_pin_to_change" = "Ripohoni PIN-in që të ndryshohet PIN"; +"secrets_recovery_with_key_recovery_key_title" = "Jepeni"; +"secrets_recovery_with_passphrase_passphrase_title" = "Jepeni"; +"bug_report_background_mode" = "Vazhdo në prapaskenë"; +"call_actions_unhold" = "Rimerre"; +"event_formatter_call_back" = "Ktheji thirrjen"; +"event_formatter_call_you_declined" = "Hodhët poshtë këtë thirrje"; +"event_formatter_call_you_currently_in" = "Gjendeni në këtë thirrje"; +"event_formatter_call_has_ended" = "Kjo thirrje ka përfunduar"; +"event_formatter_call_video" = "Thirrje video"; +"event_formatter_call_voice" = "Thirrje audio"; +"security_settings_crosssigning_reset" = "Rikthe te parazgjedhjet cross-signing"; +"security_settings_crosssigning" = "CROSS-SIGNING"; +"settings_show_NSFW_public_rooms" = "Shfaq dhoma publike NSFW"; +"room_open_dialpad" = "Numërator"; +"room_place_voice_call" = "Thirrje audio"; +"room_event_action_delete_confirmation_message" = "Jeni i sigurt se doni të fshihet ky mesazh i padërguar?"; +"room_event_action_delete_confirmation_title" = "Fshi mesazh të padërguar"; +"room_unsent_messages_cancel_message" = "Jeni i sigurt se doni të fshihen krejt mesazhet e padërguar në këtë dhomë?"; +"room_unsent_messages_cancel_title" = "Fshi mesazhet e padërguar"; +"social_login_button_title_sign_up" = "Regjistrohuni me %@"; +"social_login_button_title_sign_in" = "Hyni me %@"; +"social_login_button_title_continue" = "Vazhdo me %@"; +"social_login_list_title_sign_up" = "Ose"; +"social_login_list_title_sign_in" = "Ose"; + +// Social login + +"social_login_list_title_continue" = "Vazhdoni me"; +"callbar_return" = "Rimerre"; +"callbar_only_multiple_paused" = "%@ thirrje të ndalura"; +"callbar_only_single_paused" = "Thirrje e ndalur"; +"callbar_active_and_multiple_paused" = "1 thirrje aktive (%@) · %@ thirrje të ndalura"; +"callbar_active_and_single_paused" = "1 thirrje aktive (%@) · 1 thirrje e ndalur"; + +// Call Bar +"callbar_only_single_active" = "Thirrje aktive (%@)"; From 32768b6f4b56ce2a8c40c41d26d0fcfbb5a4a1e1 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Wed, 24 Mar 2021 20:42:24 +0000 Subject: [PATCH 21/78] Translated using Weblate (Ukrainian) Currently translated at 21.6% (257 of 1186 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ --- Riot/Assets/uk.lproj/Vector.strings | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index 0eb951189..667094056 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -288,3 +288,13 @@ "security_settings_secure_backup_delete" = "Видалити"; "security_settings_secure_backup_synchronise" = "Синхронізувати"; "security_settings_secure_backup_setup" = "Налаштувати"; +"people_empty_view_information" = "Безпечно спілкуйтеся з будь-ким. Торкніться +, щоб додати співрозмовників."; +"callbar_return" = "Повернути"; +"callbar_only_multiple_paused" = "%@ викликів у очікуванні"; +"callbar_only_single_paused" = "Виклики в очікуванні"; +"callbar_active_and_multiple_paused" = "1 активний виклик (%@) · %@ викликів у очікуванні"; +"callbar_active_and_single_paused" = "1 активний виклик (%@) · 1 виклик в очікуванні"; + +// Call Bar +"callbar_only_single_active" = "Активний виклик (%@)"; +"switch" = "Перемкнути"; From 0927154b023b85d5e5b94ad75dbcf5cb606ab52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Wed, 24 Mar 2021 15:13:39 +0000 Subject: [PATCH 22/78] Translated using Weblate (Estonian) Currently translated at 100.0% (1186 of 1186 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ --- Riot/Assets/et.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index f150ffbcf..e97d22d99 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -559,7 +559,7 @@ "identity_server_settings_place_holder" = "Sisesta isikutuvastusserver"; "identity_server_settings_add" = "Lisa"; "identity_server_settings_change" = "Muuda"; -"identity_server_settings_disconnect_info" = "Isikutuvastusserveri kasutamise lõpetamine tähendab, et sa ei ole leitav teiste kasutajate poolt ega sulle ei saa telefoninumbri või e-posti aadressi alusel kutset saata. Küll aga saab kutset saata Matrix'i kasutajatunnuse alusel."; +"identity_server_settings_disconnect_info" = "Isikutuvastusserveri kasutamise lõpetamine tähendab, et sa ei ole leitav nende kasutajate poolt, kes tahavad sulle telefoninumbri või e-posti aadressi alusel kutset saata. Küll aga saab alati kutset saata Matrix'i kasutajatunnuse alusel."; "room_details_history_section_prompt_msg" = "Kui muudad seda, kes saavad selle jututoa ajalugu lugeda, siis kehtib see vaid tulevaste sõnumite kohta. Senise ajaloo nähtavus sellega ei muutu."; // Read Receipts "read_receipts_list" = "Lugemisteatiste loend"; From 99dda6e3ebf767178af07448c8e403e00d43b2d1 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Sat, 27 Mar 2021 07:51:51 +0100 Subject: [PATCH 23/78] Switching composer between text mode & action mode - Fixed: if you start typing while the new attachment sending mode is on, the send button appears --- .../Room/Views/InputToolbar/RoomInputToolbarView.m | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index a0b5aceb8..5997ce8bb 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -350,6 +350,8 @@ const double RoomInputToolbarViewContextBarHeight = 30; - (void)updateSendButtonWithMessage:(NSString *)textMessage { + self.actionMenuOpened = NO; + if (textMessage.length) { self.rightInputToolbarButton.alpha = 1; @@ -372,7 +374,13 @@ const double RoomInputToolbarViewContextBarHeight = 30; { _actionMenuOpened = actionMenuOpened; - self->growingTextView.internalTextView.selectedTextRange = nil; + if (self->growingTextView.internalTextView.selectedRange.length > 0) + { + NSRange range = self->growingTextView.internalTextView.selectedRange; + range.location = range.location + range.length; + range.length = 0; + self->growingTextView.internalTextView.selectedRange = range; + } if (_actionMenuOpened) { self.actionsBar.hidden = NO; From 55b47da61b6e103229e0c3f8f1fcdbfce4f2638e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexia=20=C5=ACerner?= Date: Sun, 28 Mar 2021 20:49:29 +0000 Subject: [PATCH 24/78] Translated using Weblate (Esperanto) Currently translated at 98.9% (1174 of 1186 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/eo/ --- Riot/Assets/eo.lproj/Vector.strings | 226 +++++++++++++++++++++++++++- 1 file changed, 223 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/eo.lproj/Vector.strings b/Riot/Assets/eo.lproj/Vector.strings index 2a7d6d0fd..7ae1f4577 100644 --- a/Riot/Assets/eo.lproj/Vector.strings +++ b/Riot/Assets/eo.lproj/Vector.strings @@ -128,8 +128,8 @@ "room_conference_call_no_power" = "Vi bezonas permeson administri grupvokojn en ĉi tiu ĉambro"; "room_ongoing_conference_call_close" = "Fermi"; "room_ongoing_conference_call" = "Daŭranta grupa voko. Aliĝi kiel %@ aŭ %@."; -"room_unsent_messages_unknown_devices_notification" = "Mesaĝoj ne sendiĝis pro ĉeesto de nekonataj salutaĵoj. Ĉu %@ aŭ %@ nun?"; -"room_unsent_messages_notification" = "Mesaĝoj ne sendiĝis. Ĉu %@ aŭ %@ nun?"; +"room_unsent_messages_unknown_devices_notification" = "Mesaĝoj ne sendiĝis pro ĉeesto de nekonataj salutaĵoj."; +"room_unsent_messages_notification" = "Mesaĝoj ne sendiĝis."; "room_offline_notification" = "Konekto al la servilo perdiĝis."; "room_accessiblity_scroll_to_bottom" = "Moviĝi ĝis planko"; "encrypted_room_message_placeholder" = "Sendi mesaĝon ĉifritan…"; @@ -964,7 +964,7 @@ //"settings_join_leave_rooms" = "When people join or leave rooms"; //"settings_call_invitations" = "Call invitations"; -"settings_enable_callkit" = ""; +"settings_enable_callkit" = "Integraj vokoj"; "settings_ui_theme_picker_title" = "Elektu haŭton"; "settings_ui_theme_black" = "Nigra"; "settings_ui_theme_dark" = "Malluma"; @@ -1075,3 +1075,223 @@ "room_resource_limit_exceeded_message_contact_3" = " por ke vi daŭre uzu la servilon."; "room_resource_limit_exceeded_message_contact_2_link" = "kontaktu vian serviladministranton"; "room_resource_limit_exceeded_message_contact_1" = " Bonvole "; +"room_intro_cell_information_dm_sentence1_part3" = ". "; +"room_intro_cell_information_dm_sentence2" = "Sole la paro de vi ĉeestas ĉi tiu ĉambro, neniu alia povas eniri."; +"room_intro_cell_information_multiple_dm_sentence2" = "Sole vi ĉeestas ĉi tiu ĉambro, krom se oni invitas aliulon."; +"device_verification_security_advice_emoji" = "Komparu la unikajn bildosignojn, certigante, ke ili aperas samorde."; +"key_verification_user_title" = "Kontrolu ĝin"; +"key_verification_this_session_title" = "Kontrolu ĉi tiun salutaĵon"; +"key_verification_new_session_title" = "Kontrolu vian novan salutaĵon"; + +// MARK: - Device Verification +"key_verification_other_session_title" = "Kontrolu salutaĵon"; +"sign_out_key_backup_in_progress_alert_cancel_action" = "Mi atendos"; +"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Mi ne volas miajn ĉifritajn mesaĝojn"; +"sign_out_key_backup_in_progress_alert_title" = "Savkopiado progresas. Se vi adiaŭos nun, vi perdos aliron al viaj ĉifritaj mesaĝoj."; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "Savkopii"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "Adiaŭi"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "Vi perdos aliron al viaj ĉifritaj mesaĝoj, se vi ne savkopios viajn ŝlosilojn antaŭ adiaŭo."; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Vi perdos viajn ĉifritajn mesaĝojn"; +"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Mi ne volas miajn ĉifritajn mesaĝojn"; +"sign_out_non_existing_key_backup_alert_setup_secure_backup_action" = "Ekuzi sekuran savkopiadon"; +"sign_out_non_existing_key_backup_alert_title" = "Vi perdos aliron al viaj ĉifritaj mesaĝoj se vi nun adiaŭos"; +"sign_out_existing_key_backup_alert_sign_out_action" = "Adiaŭi"; + +// MARK: Sign out warning + +"sign_out_existing_key_backup_alert_title" = "Ĉu vi certe volas adiaŭi?"; +"key_backup_recover_done_action" = "Finite"; + +// Success + +"key_backup_recover_success_info" = "Savkopio rehaviĝis!"; +"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Ĉu vi perdis vian rehavan ŝlosilon? Vi povas agordi novan per la Agordoj."; +"key_backup_recover_from_recovery_key_recover_action" = "Malŝlosi historion"; +"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Enigu rehavan ŝlosilon"; +"key_backup_recover_from_recovery_key_recovery_key_title" = "Enigu"; + +// Recover from recovery key + +"key_backup_recover_from_recovery_key_info" = "Uzu vian rehavan ŝlosilon por malŝlosi vian historion de sekuraj mesaĝoj"; +"large_badge_value_k_format" = "%.1fK"; +"image_picker_action_library" = "Elekti el vidaŭdaĵujo"; +"media_picker_library" = "Vidaŭdaĵujo"; +"room_details_advanced_section" = "Altnivelaj"; +"room_details_banned_users_section" = "Forbaritaj uzantoj"; +"room_details_flair_invalid_id_prompt_msg" = "%@ ne estas valida identigilo por komunumo"; +"room_details_flair_invalid_id_prompt_title" = "Nevalida formo"; +"room_details_new_flair_placeholder" = "Aldoni novan identigilon de komunumo (ekz. +io%@)"; +"room_details_flair_section" = "Montri insignon por komunumoj"; +"room_details_addresses_disable_main_address_prompt_msg" = "Vi havos neniun specifitan ĉefan adreson. La implicita ĉefadreso por ĉi tiu ĉambro elektiĝos hazarde"; +"room_details_access_section_for_dm" = "Kiu rajtas aliri ĉi tion?"; +"room_details_access_section" = "Kiu rajtas aliri ĉi tiun ĉambron?"; +"room_details_direct_chat" = "Rekta babilo"; +"room_details_mute_notifs" = "Silentigi sciigojn"; +"room_details_low_priority_tag" = "Malalta prioritato"; +"room_details_favourite_tag" = "Elstarigita"; +"room_details_topic" = "Temo"; +"room_details_room_name_for_dm" = "Nomo"; +"room_details_room_name" = "Nomo de ĉambro"; +"room_details_photo_for_dm" = "Bildo"; +"room_details_photo" = "Bildo de ĉambro"; +"room_details_settings" = "Agordoj"; +"room_details_files" = "Alŝutoj"; +"room_details_people" = "Ĉambranoj"; +"room_details_title_for_dm" = "Detaloj"; + + +// Room Details +"room_details_title" = "Detaloj pri ĉambro"; +"identity_server_settings_alert_error_invalid_identity_server" = "%@ ne estas valida identiga servilo."; +"identity_server_settings_alert_error_terms_not_accepted" = "Vi devas akcepti la uzokondiĉojn de %@, por uzi ĝin kiel identiga servilo."; +"identity_server_settings_alert_disconnect" = "Ĉu malkonektu de la identiga servilo %@?"; +"identity_server_settings_alert_disconnect_title" = "Malkonektu identigan servilon"; +"identity_server_settings_alert_change" = "Ĉu malkonektu de identiga servilo %1$@ kaj anstataŭe konektu al %@$A?"; +"identity_server_settings_alert_change_title" = "Ŝanĝu identigan servilon"; +"identity_server_settings_alert_no_terms" = "Via elektita identiga servilo ne havas uzokondiĉojn. Daŭrigu nur se vi fidas la posedanton de tiu servilo."; +"identity_server_settings_alert_no_terms_title" = "Identiga servilo ne havas uzokondiĉojn"; +"identity_server_settings_disconnect" = "Malkonekti"; +"identity_server_settings_disconnect_info" = "Malkonektinte de via identiga servilo, vi ne troviĝus al aliaj uzantoj per, nek povos inviti uzantojn per, retpoŝtadresoj aŭ telefonnumeroj."; +"identity_server_settings_change" = "Ŝanĝi"; +"identity_server_settings_add" = "Aldoni"; +"identity_server_settings_place_holder" = "Enigu identigan servilon"; +"identity_server_settings_no_is_description" = "Vi nun ne uzas identigan servilon. Por troviĝi de kaj trovi jamajn kontaktojn, aldonu tian servilon ĉi-supre."; +"identity_server_settings_description" = "Vi uzas %@ por trovi kaj troviĝi de jamaj kontaktoj, kiujn vi konas."; + +// Identity server settings +"identity_server_settings_title" = "Identiga Servilo"; + +// AuthenticatedSessionViewControllerFactory +"authenticated_session_flow_not_supported" = "Ĉi tiu programo ne subtenas la identigan metodon de via hejmservilo."; +"manage_session_sign_out" = "Adiaŭi al ĉi tiu salutaĵo"; +"manage_session_not_trusted" = "Nefidataj"; +"manage_session_trusted" = "Fidataj de vi"; +"manage_session_name" = "Nomo de salutaĵo"; +"manage_session_info" = "INFORMOJ DE SALUTAĴO"; + +// Manage session +"manage_session_title" = "Administru salutaĵon"; +"security_settings_user_password_description" = "Konfirmi vian identon per enigo de via pasvorto"; +"security_settings_coming_soon" = "Bedaŭron. Ĉi tiu funkcio ne jam estas subtenata por Element iOS. Bonvolu uzi alian Matrix klienton por agordi ĝin. Element iOS sekvos ĝin."; +"security_settings_complete_security_alert_message" = "Vi kompletigu sekurigon je ĉi tiu salutaĵo unue."; +"security_settings_complete_security_alert_title" = "Kompletigi sekurigon"; +"security_settings_blacklist_unverified_devices_description" = "Kontroli ĉiujn salutaĵojn de uzanto, por marki ilin fidata kaj sendi mesaĝojn al ĝi."; +"security_settings_blacklist_unverified_devices" = "Neniam sendi mesaĝojn al nefidatajn salutaĵojn"; +"security_settings_advanced" = "ALTNIVELAJ"; +"security_settings_export_keys_manually" = "Elporti ŝlosilojn permane"; +"security_settings_cryptography" = "KRIPTOGRAFIO"; +"security_settings_crosssigning_complete_security" = "Kompletigi sekurigon"; +"security_settings_crosssigning_reset" = "Restarigi delegan subskribadon"; +"security_settings_crosssigning_bootstrap" = "Praŝargi delegan subskribadon"; +"security_settings_crosssigning_info_ok" = "Delega subskribado estas ŝaltita."; +"security_settings_crosssigning_info_trusted" = "Delega subskribado estas ŝaltita. Vi povas fidi aliajn uzantojn kaj viajn salutaĵojn per delegaj subskriboj, sed vi ne povas delege subskribi de ĉi tiu salutaĵo, ĉar mankas al ĝi ŝlosiloj privataj por delegaj subskriboj."; +"security_settings_crosssigning_info_exists" = "Via konto havas identon de delega subskribado, sed ne jam estas fidata de ĉi tiu salutaĵo. Kompletigu la sekurigon de ĉi tiu salutaĵo."; +"security_settings_crosssigning_info_not_bootstrapped" = "Delega subskribado ne jam agordiĝis."; +"security_settings_crosssigning" = "DELEGAJ SUBSKRIBOJ"; +"security_settings_backup" = "SAVKOPIADO DE MESAĜOJ"; +"security_settings_secure_backup_delete" = "Forigi"; +"security_settings_secure_backup_synchronise" = "Speguli"; +"security_settings_secure_backup_setup" = "Agordi"; +"security_settings_secure_backup_description" = "Malhelpu perdon de aliro al ĉifritaj mesaĝoj kaj datumoj per savkopiado de ĉifraj ŝlosiloj al via servilo."; +"security_settings_secure_backup" = "SAVKOPIADO"; +"security_settings_crypto_sessions_description_2" = "Se vi ne konas salutaĵojn, ŝanĝu vian pasvorton kaj restarigu savkopiadon."; +"security_settings_crypto_sessions_loading" = "Enlegante salutaĵojn…"; +"security_settings_crypto_sessions" = "MIAJ SALUTAĴOJ"; + +// Security settings +"security_settings_title" = "Sekureco"; +"settings_show_NSFW_public_rooms" = "Montru publikajn ĉambrojn konsternajn"; +"settings_identity_server_no_is_description" = "Vi uzas neniun identigan servilon. Por trovi kaj troviĝi de jamaj kontaktoj, kiujn vi konas, aldonu servilon supre."; +"settings_identity_server_no_is" = "Neniu identiga servilo estas agordita"; +"settings_identity_server_description" = "Uzante la identigan servilon supre agorditan, vi povas trovi kaj troviĝi de jamaj kontaktoj, kiujn vi konas."; +"settings_discovery_three_pid_details_enter_sms_code_action" = "Enigu SMS aktivigan kodon"; +"settings_discovery_three_pid_details_cancel_email_validation_action" = "Nuligi retpoŝtan kontrolon"; +"settings_discovery_three_pid_details_revoke_action" = "Senvalidigi"; +"settings_discovery_three_pid_details_share_action" = "Kunhavigi"; +"settings_discovery_three_pid_details_information_phone_number" = "Agordu ĉi tiun telefonnumeron, per kiu aliaj uzantoj povas trovi kaj inviti vin al ĉambroj. Aldonu aŭ forigu telefonnumerojn ĉe Kontoj."; +"settings_discovery_three_pid_details_title_phone_number" = "Agordu telefonnumeron"; +"settings_discovery_three_pid_details_information_email" = "Agordu ĉi tiun retpoŝtadreson, per kiu aliaj uzantoj povas trovi kaj inviti vin al ĉambroj. Aldonu aŭ forigu retpoŝtadresojn ĉe Kontoj."; +"settings_discovery_three_pid_details_title_email" = "Agordu retpoŝtadreson"; +"settings_discovery_error_message" = "Eraris iel. Bonvolu reprovi."; +"settings_discovery_three_pids_management_information_part3" = "."; +"settings_discovery_three_pids_management_information_part2" = "Agordoj de uzanto"; +"settings_discovery_three_pids_management_information_part1" = "Agordu la retpoŝtadresojn kaj telefonnumerojn per kiuj aliaj uzantoj povas trovi kaj inviti vin al ĉambroj. Aldonu aŭ forigu retpoŝtadresojn aŭ telefonnumerojn de ĉi tiu listo en "; +"settings_discovery_terms_not_signed" = "Konsentu la uzokondiĉojn de la identiga servilo (%@) por trovebligi vin per retpoŝtadreso aŭ telefonnumero."; +"settings_discovery_no_identity_server" = "Vi nun ne uzas identigan servilon. Por trovi kaj troviĝi de jamaj kontaktoj, kiujn vi konas, aldonu servilon."; +"settings_devices_description" = "Prezenta nomo de salutaĵo videblas al personoj kunbabilataj"; +"settings_key_backup_delete_confirmation_prompt_msg" = "Ĉu vi certas? Vi perdos aliron al viaj ĉifritaj mesaĝoj, se vi ne savkopios viajn ŝlosilojn."; +"settings_key_backup_delete_confirmation_prompt_title" = "Forigi savkopion"; +"settings_key_backup_button_connect" = "Konekti ĉi tiu salutaĵo savkopionte"; +"settings_key_backup_button_delete" = "Forigi savkopion"; +"settings_key_backup_button_restore" = "Rehavigi per savkopio"; +"settings_key_backup_button_create" = "Komenci savkopiojn"; +"settings_key_backup_info_trust_signature_invalid_device_unverified" = "Savkopio havas nevalidan subskribon de %@"; +"settings_key_backup_info_trust_signature_invalid_device_verified" = "Savkopio havas nevalidan subskribon de %@"; +"settings_key_backup_info_trust_signature_valid_device_unverified" = "Savkopio havas subskribon de %@"; +"settings_key_backup_info_trust_signature_valid_device_verified" = "Savkopio havas validan subskribon de %@"; +"settings_key_backup_info_trust_signature_valid" = "Savkopio havas validan subskribon de ĉi salutaĵo"; +"settings_key_backup_info_trust_signature_unknown" = "Savkopio havas subskribon de salutaĵo kun la identigilo: %@"; +"settings_key_backup_info_progress_done" = "Ĉiuj ŝlosiloj estas savkopiitaj"; +"settings_key_backup_info_progress" = "Savkopiante %@ ŝlosilojn…"; +"settings_key_backup_info_not_valid" = "Ĉi tiu salutaĵo ne savkopias viajn ŝlosilojn, sed vi havas restantan kopion kiun vi povas alreveni kaj uzi ekde nun."; +"settings_key_backup_info_valid" = "Ĉi tiu salutaĵo savkopias viajn ŝlosilojn."; +"settings_key_backup_info_algorithm" = "Algoritmo: %@"; +"settings_key_backup_info_version" = "Savkopia Versio: %@"; +"settings_key_backup_info_signout_warning" = "Konektu ĉi tiun salutaĵon savkopie antaŭ ol vi adiaŭas, por eviti la perdiĝon de ŝlosiloj nurlokaj."; +"settings_key_backup_info_none" = "Viaj ŝlosiloj estas nesavkopiataj por ĉi tiu salutaĵo."; +"settings_key_backup_info_checking" = "Kontrolante…"; +"settings_key_backup_info" = "Mesaĝoj en ĉifritaj ĉambroj estas sekurigitaj per tutvoja ĉifrado. Nur vi kaj la adresato(j) havas la ŝlosilojn por malĉifri tiujn ĉi mesaĝojn."; +"settings_deactivate_my_account" = "Malŝalti mian konton"; +"settings_crypto_blacklist_unverified_devices" = "Ĉifri nur al kontrolitaj salutaĵoj"; +"settings_crypto_export" = "Elporti ŝlosilojn"; +"settings_crypto_device_key" = "\nŜlosilo de salutaĵo:\n"; +"settings_crypto_device_id" = "\nIdentigilo de salutaĵo: "; +"settings_crypto_device_name" = "Nomo de salutaĵo: "; +"settings_add_3pid_invalid_password_message" = "Nevalida pasvorto"; +"settings_add_3pid_password_message" = "Por daŭrigi, bonvolu enigi vian pasvorton"; +"settings_add_3pid_password_title_msidsn" = "Aldoni telefonnumeron"; +"settings_add_3pid_password_title_email" = "Aldoni retpoŝtadreson"; +"settings_password_updated" = "Via pasvorto ĝisdatiĝis"; +"settings_fail_to_update_password" = "Eraris ĝisdatigante pasvorton"; +"settings_confirm_password" = "Konfirmu pasvorton"; +"settings_global_settings_info" = "Ĉieaj sciigoj uzeblas per via retkliento %@"; +"settings_fail_to_update_profile" = "Eraris ĝisdatigante profilon"; +"room_open_dialpad" = "Ciferilo"; +"room_place_voice_call" = "Voĉvoko"; +"room_event_action_delete_confirmation_message" = "Ĉu vi certas, ke vi volas forigi ĉi tiun nesenditan mesaĝon?"; +"room_event_action_delete_confirmation_title" = "Forigi nesenditan mesaĝon"; +"room_ongoing_conference_call_with_close" = "Daŭranta grupa voko. Aliĝi kiel %@ aŭ %@. %@."; +"room_unsent_messages_cancel_message" = "Ĉu vi certas, ke vi volas forigi ĉiujn nesenditajn mesaĝojn en ĉi tiu ĉambro?"; +"room_unsent_messages_cancel_title" = "Forigi nesenditajn mesaĝojn"; +"room_member_power_level_short_moderator" = "Reg"; +"room_participants_security_loading" = "Enlegante…"; +"room_participants_action_security_status_loading" = "Enlegante…"; +"room_participants_filter_room_members_for_dm" = "Filtri ĉambranojn"; +"room_participants_add_participant" = "Aldoni partoprenanton"; +"contacts_user_directory_offline_section" = "KATALOGO DE UZANTOJ (nefunkcia)"; +"contacts_address_book_permission_denied" = "Vi ne permesis al Element aliri viajn lokajn kontaktojn"; +"rooms_empty_view_information" = "Ĉambroj taŭgas por ajna grupbabilo, privata aŭ publika. Premu la + por trovi ekzistantaj ĉambroj, aŭ fari novajn."; +"rooms_empty_view_title" = "Ĉambroj"; +"people_empty_view_information" = "Sekure babili kun iu ajn.Premu la + por inviti personojn."; +"people_empty_view_title" = "Homoj"; +"social_login_button_title_sign_up" = "Registriĝi per %@"; +"social_login_button_title_sign_in" = "Saluti per %@"; +"social_login_button_title_continue" = "Saluti per %@"; +"social_login_list_title_sign_up" = "Aŭ registriĝi per"; +"social_login_list_title_sign_in" = "Aŭ saluti per"; + +// Social login + +"social_login_list_title_continue" = "Saluti per"; +"callbar_return" = "Reveni"; +"callbar_only_multiple_paused" = "%@ paŭzigitaj vokoj"; +"callbar_only_single_paused" = "Paŭzigita voko"; +"callbar_active_and_multiple_paused" = "1 aktiva voko (%@) · %@ paŭzigitaj vokoj"; +"callbar_active_and_single_paused" = "1 aktiva voko (%@) · 1 paŭzigita voko"; + +// Call Bar +"callbar_only_single_active" = "Aktiva voko (%@)"; +"less" = "Malpli"; +"more" = "Pli"; +"switch" = "Baskuli"; +"store_promotional_text" = "Privatecgardanta babila kaj kunlabora programo, uzanta malferman reton. Ĝi estas malcentra, do vi estas la stiranto. Neniu datumamasanto nek kaŝenirejo."; From eb3b2631bde278a99b52031c8bd378fb93c03f42 Mon Sep 17 00:00:00 2001 From: Tirifto Date: Sun, 28 Mar 2021 20:48:55 +0000 Subject: [PATCH 25/78] Translated using Weblate (Esperanto) Currently translated at 98.9% (1174 of 1186 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/eo/ --- Riot/Assets/eo.lproj/Vector.strings | 178 ++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/Riot/Assets/eo.lproj/Vector.strings b/Riot/Assets/eo.lproj/Vector.strings index 7ae1f4577..90ecb7d1f 100644 --- a/Riot/Assets/eo.lproj/Vector.strings +++ b/Riot/Assets/eo.lproj/Vector.strings @@ -1295,3 +1295,181 @@ "more" = "Pli"; "switch" = "Baskuli"; "store_promotional_text" = "Privatecgardanta babila kaj kunlabora programo, uzanta malferman reton. Ĝi estas malcentra, do vi estas la stiranto. Neniu datumamasanto nek kaŝenirejo."; +"room_intro_cell_information_room_with_topic_sentence2" = "Temo: %@"; +"room_intro_cell_information_room_sentence1_part3" = ". "; +"room_intro_cell_information_room_sentence1_part1" = "Ĉi tio estas la komenco de "; + +// Mark: - Room creation introduction cell + +"room_intro_cell_add_participants_action" = "Aldoni personojn"; +"room_avatar_view_accessibility_hint" = "Ŝanĝi bildon de ĉambro"; + +// Mark: - Room avatar view + +"room_avatar_view_accessibility_label" = "bildo de ĉambro"; +"invite_friends_share_text" = "Saluton! Parolu kun mi per %@: %@"; + +// MARK: - Invite friends + +"invite_friends_action" = "Inviti amikojn al %@"; +"favourites_empty_view_information" = "Vi povas elstarigi kelkmaniere – plej rapide estas simple premi kaj teni. Tuŝetu la stelon por aperigi la elstarigaton ĉi tie."; + +// MARK: - Favourites + +"favourites_empty_view_title" = "Elstarigitaj ĉambroj kaj personoj"; +"home_empty_view_information" = "La ĉionhava sekura babililo por skipoj, amikoj, kaj organizaĵoj. Tuŝetu la ĉi-suban butonon + por aldoni personojn kaj ĉambrojn."; + +// MARK: - Home + +"home_empty_view_title" = "bonvenu al %@,\n%@"; +"call_transfer_error_title" = "Eraro"; +"call_transfer_contacts_all" = "Ĉiuj"; +"call_transfer_contacts_recent" = "Freŝdataj"; +"call_transfer_users" = "Uzantoj"; +"pin_protection_settings_change_pin" = "Ŝanĝi personan identigan numeron"; +"pin_protection_confirm_pin_to_change" = "Konfirmu personan identigan numeron por ĝin ŝanĝi"; +"key_verification_bootstrap_not_setup_message" = "Vi devas praŝargi delegan subskribadon unue."; +"error_not_supported_on_mobile" = "Vi ne povas tion fari per %@ je portebla aparato."; + +// Unverified sessions + +"key_verification_self_verify_unverified_sessions_alert_title" = "Kontrolu, kie vi salutis"; +"device_verification_incoming_description_1" = "Kontrolu ĉi tiun salutaĵon por marki ĝin fidata. Fidi salutaĵojn de kunuloj helpos vin resti pli trankvila kiam vi sendas tutvoje ĉifritajn mesaĝojn."; + +// Mark: Incoming +"device_verification_incoming_title" = "Envena kontrolpeto"; +"device_verification_error_cannot_load_device" = "Ne povas enlegi informojn pri salutaĵo."; +"device_verification_cancelled_by_me" = "La kontrolo nuliĝis. Kialo: %@"; +"device_verification_cancelled" = "La aliulo nuligis la kontrolon."; +"device_verification_security_advice_number" = "Komparu la numerojn, certigante, ke ili aperas samorde."; +"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "."; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "uzi vian rehavan ŝlosilon"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Ĉu vi ne konas vian rehavan pasfrazon? Vi povas "; +"key_backup_recover_from_passphrase_recover_action" = "Malŝlosi historion"; +"key_backup_recover_from_passphrase_passphrase_placeholder" = "Enigu pasfrazon"; +"key_backup_recover_from_passphrase_passphrase_title" = "Enigu"; + +// Recover from passphrase + +"key_backup_recover_from_passphrase_info" = "Uzu vian rehavan pasfrazon por malŝlosi vian historion de sekuraj mesaĝoj"; + +// Recover from private key +"key_backup_recover_from_private_key_info" = "Rehavante savkopion…"; +"key_backup_recover_invalid_recovery_key" = "Ne povis malĉifri savkopion per tiu ĉi ŝlosilo: bonvolu kontroli, ke vi enigis la ĝustan rehavan ŝlosilon."; +"key_backup_recover_invalid_recovery_key_title" = "Malakordo de rehava ŝlosilo"; +"key_backup_recover_invalid_passphrase" = "Ne povis malĉifri savkopion per ĉi tiu pasfrazo: bonvolu kontroli, ĉu vi ĝuste enigis la rehavan pasfrazon."; +"key_backup_recover_invalid_passphrase_title" = "Malĝusta rehava pasfrazo"; + +// MARK: Key backup recover + +"key_backup_recover_title" = "Sekuraj mesaĝoj"; +"key_backup_setup_success_from_recovery_key_made_copy_action" = "Mi faris kopion"; +"key_backup_setup_success_from_recovery_key_make_copy_action" = "Fari kopion"; +"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Rehava ŝlosilo"; + +// Success from recovery key +"key_backup_setup_success_from_recovery_key_info" = "Viaj ŝlosiloj estas savkopiataj.\n\nFaru kopion de tiu ĉi rehava ŝlosilo kaj bone ĝin sekurigu."; +"key_backup_setup_success_from_passphrase_done_action" = "Finite"; +"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Konservi rehavan ŝlosilon"; + +// Success from passphrase +"key_backup_setup_success_from_passphrase_info" = "Viaj ŝlosiloj estas savkopiataj.\n\nVia rehava ŝlosilo estas formo de asekuro – vi povas ĝin uzi por rehavi aliron al viaj ĉifritaj mesaĝoj, se vi forgesos vian pasfrazon.\n\nTenu vian rehavan ŝlosilon en loko tre sekura, kiel ekzemple administrilo de pasvortoj (aŭ sekurkesto)."; + +// Success + +"key_backup_setup_success_title" = "Sukceso!"; +"key_backup_setup_passphrase_setup_recovery_key_action" = "(Altnivele) Agordi per rehava ŝlosilo"; +"key_backup_setup_passphrase_setup_recovery_key_info" = "Aŭ sekurigu vian savkopion per rehava ŝlosilo, konservante ĝin en sekura loko."; +"key_backup_setup_passphrase_set_passphrase_action" = "Agordi pasfrazon"; +"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Pasfrazoj ne akordas"; +"key_backup_setup_passphrase_confirm_passphrase_valid" = "Bonege!"; +"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Konfirmu pasfrazon"; +"key_backup_setup_passphrase_confirm_passphrase_title" = "Konfirmi"; +"key_backup_setup_passphrase_passphrase_invalid" = "Provu aldoni vorton"; +"key_backup_setup_passphrase_passphrase_title" = "Enigi"; + +// Passphrase + +"key_backup_setup_passphrase_title" = "Sekurigu vian savkopion per pasfrazo"; +"key_backup_setup_intro_manual_export_info" = "(Altnivele)"; +"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Konektu ĉi tiun aparaton al savkopio de ŝlosiloj"; +"key_backup_setup_intro_setup_action_without_existing_backup" = "Ekuzu savkopiadon de ŝlosiloj"; + + +// MARK: Key backup setup + +"key_backup_setup_title" = "Savkopiado de ŝlosiloj"; + +// MARK: Secure backup setup + +// Intro + +"secure_key_backup_setup_intro_title" = "Sekura savkopiado"; +"room_widget_permission_information_title" = "Uzado povas havigi datumojn al %@:\n"; +"room_widget_permission_webview_information_title" = "Uzado povas meti kuketojn kaj havigi datumojn al %@:\n"; +"bug_report_background_mode" = "Daŭrigi fone"; +"e2e_key_backup_wrong_version" = "Nova savkopio de ŝlosiloj de sekuraj mesaĝoj troviĝis.\n\nSe vi ne faris ĝin, agordu novan pasfrazon per la Agordoj."; + +// Key backup wrong version +"e2e_key_backup_wrong_version_title" = "Nova savkopio de ŝlosiloj"; +"call_actions_unhold" = "Daŭrigi"; +"photo_library_access_not_granted" = "%@ ne havas permeson aliri la fotujon; bonvolu ŝanĝi agordojn de privateco"; +"event_formatter_call_back" = "Revoki"; +"event_formatter_call_you_declined" = "Vi rifuzis ĉi tiun vokon"; +"event_formatter_call_you_currently_in" = "Vi nun partoprenas ĉi tiun vokon"; +"event_formatter_call_has_ended" = "Ĉi tiu voko finiĝis"; +"event_formatter_call_video" = "Vidvoko"; +"event_formatter_call_voice" = "Voĉvoko"; + +// Media picker +"media_picker_title" = "Vidaŭdaĵoj"; +"room_details_set_main_address" = "Agordi kiel ĉefadreson"; +"room_details_save_changes_prompt" = "Ĉu vi volas konservi ŝanĝojn?"; +"room_details_fail_to_enable_encryption" = "Malsukcesis ŝalti ĉifradon en ĉi tiu ĉambro"; +"room_details_fail_to_update_room_direct" = "Malsukcesis ĝisdatigi la rektan markon de ĉi tiu ĉambro"; +"room_details_fail_to_update_room_communities" = "Malsukcesis ĝisdatigi la ritalajn komunumojn"; +"room_details_fail_to_update_room_canonical_alias" = "Malsukcesis ĝisdatigi la ĉefadreson"; +"room_details_fail_to_remove_room_aliases" = "Malsukcesis forigi la adresojn de ĉambro"; +"room_details_fail_to_add_room_aliases" = "Malsukcesis aldoni novajn adresojn de ĉambro"; +"room_details_fail_to_update_history_visibility" = "Malsukcesis ĝisdatigi videblecon de la historio"; +"room_details_fail_to_update_room_directory_visibility" = "Malsukcesis ĝisdatigi videblecon en la katalogo de ĉambroj"; +"room_details_fail_to_update_room_join_rule" = "Malsukcesis ĝisdatigi la regulon pri aliĝoj"; +"room_details_fail_to_update_room_guest_access" = "Malsukcesis ĝisdatigi aliron de gastoj al la ĉambro"; +"room_details_fail_to_update_topic" = "Malsukcesis ĝisdatigi la temon"; +"room_details_fail_to_update_room_name" = "Malsukcesis ĝisdatigi la nomon de ĉambro"; +"room_details_fail_to_update_avatar" = "Malsukcesis ĝisdatigi la bildon de ĉambro"; +"room_details_advanced_e2e_encryption_blacklist_unverified_devices" = "Ĉifri nur por kontrolitaj salutaĵoj"; +"room_details_advanced_e2e_encryption_disabled_for_dm" = "Ĉifrado ne estas ŝaltita ĉi tie."; +"room_details_advanced_e2e_encryption_disabled" = "Ĉifrado ne estas ŝaltita en ĉi tiu ĉambro."; +"room_details_advanced_e2e_encryption_enabled_for_dm" = "Ĉifrado estas ŝaltita ĉi tie"; +"room_details_advanced_e2e_encryption_enabled" = "Ĉifrado estas ŝaltita en ĉi tiu ĉambro"; +"room_details_advanced_enable_e2e_encryption" = "Ŝalti ĉifradon (averto: ne eblas malŝalti!)"; +"room_details_advanced_room_id_for_dm" = "Identigilo:"; +"room_details_advanced_room_id" = "Identigilo de ĉambro:"; +"identity_server_settings_alert_disconnect_still_sharing_3pid" = "Vi ankoraŭ kunhavigas personajn informojn per la identiga servilo %@.\n\nNi rekomendas, ke vi forigu viajn retpoŝtadresojn kaj telefonnumerojn de la identiga servilo, antaŭ malkonekto."; +"identity_server_settings_alert_disconnect_still_sharing_3pid_button" = "Malkonekti malgraŭe"; +"identity_server_settings_alert_disconnect_button" = "Malkonekti"; +"room_details_addresses_disable_main_address_prompt_title" = "Averto je la ĉefadreso"; +"room_details_addresses_invalid_address_prompt_msg" = "%@ ne estas valida formo por kromnomo"; +"room_details_addresses_invalid_address_prompt_title" = "Nevalido formo de kromnomo"; +"room_details_new_address_placeholder" = "Aldoni novan adreson (ekz. #io%@)"; +"room_details_new_address" = "Aldoni novan adreson"; +"room_details_no_local_addresses_for_dm" = "Ĉi tio ne havas lokajn adresojn"; +"room_details_no_local_addresses" = "Ĉi tiu ĉambro ne havas lokajn adresojn"; +"room_details_addresses_section" = "Adresoj"; +"room_details_history_section_prompt_msg" = "Ŝanĝoj al legebleco de historio nur aplikiĝos al venontaj mesaĝoj en ĉi tiu ĉambro. La videbleco de jama historio restos senŝanĝe."; +"room_details_history_section_prompt_title" = "Averto de privateco"; +"room_details_history_section_members_only_since_joined" = "Nur ĉambranoj (ekde sia aliĝo)"; +"room_details_history_section_members_only_since_invited" = "Nur ĉambranoj (ekde sia invitiĝo)"; +"room_details_history_section_members_only" = "Nur ĉambranoj (ekde ĉi tiu elekto)"; +"room_details_history_section_anyone" = "Ĉiu ajn"; +"room_details_history_section" = "Kiu rajtas legi historion?"; +"room_details_access_section_directory_toggle_for_dm" = "Listigi en katalogo de ĉambroj"; +"room_details_access_section_directory_toggle" = "Listigi ĉi tiun ĉambron en la katalogo de ĉambroj"; +"room_details_access_section_no_address_warning" = "Por ligi al ĉambro, ĝi bezonas adreson"; +"room_details_access_section_anyone_for_dm" = "Ĉiu, kiu konas la ligilon, inkluzive gastojn"; +"room_details_access_section_anyone" = "Ĉiu, kiu konas la ligilon de la ĉambro, inkluzive gastojn"; +"room_details_access_section_anyone_apart_from_guest_for_dm" = "Ĉiu, kiu konas la ligilon, krom gastoj"; +"room_details_access_section_anyone_apart_from_guest" = "Ĉiu, kiu konas la ligilon de la ĉambro, krom gastoj"; +"room_details_access_section_invited_only" = "Nur personoj, kiuj invitiĝis"; +"store_full_description" = "Element estas nova speco de mesaĝilo kaj kunlabora aplikaĵo, kiu:\n\n1. Stirigas vin por konservi vian privatecon\n2. Lasas vin komuniki kun ĉiu en la reto de Matrix, kaj eĉ ekstere, per kuniĝo kun aliaj aplikaĵoj, kiel ekzemple Slack\n3. Protektas vin de reklamoj, datumkolektado, kaŝenirejoj, kaj muritaj ĝardenoj\n4. Sekurigas vin per tutvoja ĉifrado, kun delegaj subskriboj por kontroli aliulojn\n\nElement tute malsamas de aliaj mesaĝiloj kaj kunlaboriloj, ĉar ĝi estas federa kaj malfermitkoda.\n\nElement lasas vin gastigi vin mem – aŭ elekti alian gastiganton – por ke vi havu privatecon, regon kaj kontrolon de viaj datumoj kaj interparoloj. Ĝi donas al vi aliron al malfermita reto, por ke via komunikado ne limiĝu al nur aliaj uzantoj de Element. Kaj ĝi estas tre sekura.\n\nElement povas fari ĉi ĉion, ĉar ĝi funkcias per Matrix – publika normo por malfermita, sencentra komunikado. \n\n\nElement lasas vi elekti, kiu gastigos viajn interparolojn. Per la aplikaĵo Element, vi povas elekti diversajn specojn de gastigado:\n\n1. Akiri senpagan konton ĉe la publika servilo matrix.org\n2. Memgastiĝi per via propra servilo ĉe via propra aparato\n3. Registriĝi ĉe propra servilo per simpla pagaliĝo al la gastiga platformo «Element Matrix Services»\n\nKial Element?\n\nPOSEDU VIAJN DATUMOJN: Vi decidu, kie vi tenu viajn datumojn kaj mesaĝojn. Vi posedas kaj regas ilin, ne iu granda komerca firmao, kiu kolektas viajn datumojn aŭ donas aliron al aliuloj.\n\nMALFERMAJ MESAĜADO KAJ KUNLABORADO: Vi povas babili kun ĉiu alia en la reto de Matrix, ĉu ĝi uzas Elementon aŭ alian aplikaĵon de Matrix, kaj eĉ se ĝi uzas tute alian mesaĝilon, kiel ekzemple Slack, IRC, aŭ XMPP.\n\nTRE SEKURA: Vera tutvoja ĉifrado (nur la interparolantoj povas malĉifri siajn mesaĝojn), kaj delegaj subskriboj por kontroli la aparatojn de partoprenantoj.\n\nSENMANKA KOMUNIKADO: Mesaĝoj, voĉvokoj kaj vidvokoj, havigado de dosieroj, ekrano, kaj multaj diversaj kunigoj, robotoj kaj fenestraĵoj. Kreu ĉambrojn, komunumojn, komuniku kaj kunlaboru.\n\nĈIE KUN VI: Tenu vin ĝisdata per historio de mesaĝoj plene spegulita trans ĉiuj viaj aparatoj, kaj sur la reto per https://app.element.io."; From dcc5b9cccb3f563817b74d71d175d204905c82ff Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 29 Mar 2021 22:26:03 +0200 Subject: [PATCH 26/78] Explore typing notifications inspired by web - prevent timeline from going up and down by keeping the space allocated for the typing notification --- Riot/Modules/Room/DataSources/RoomDataSource.m | 4 ++-- Riot/Modules/Room/RoomViewController.m | 17 +++++++++++++++-- .../BubbleCells/RoomTypingBubbleCell.swift | 2 ++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 210f27c93..8cef6f9b2 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -273,7 +273,7 @@ [self updateStatusInfo]; } - if (self.currentTypingUsers.count == 0) + if (!self.currentTypingUsers) { self.typingCellIndex = -1; @@ -285,7 +285,7 @@ return bubbles.count + 1; } - if (self.currentTypingUsers.count == 0) + if (!self.currentTypingUsers) { self.typingCellIndex = -1; diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index abac470da..6e5173a6f 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -4109,9 +4109,22 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo if (needsUpdate) { + BOOL needsReload = roomDataSource.currentTypingUsers == nil; roomDataSource.currentTypingUsers = typingUsers; - [self.bubblesTableView reloadData]; - if (self.isScrollToBottomHidden) + if (needsReload) + { + [self.bubblesTableView reloadData]; + } + else + { + NSInteger count = [self.roomDataSource tableView:self.bubblesTableView numberOfRowsInSection:0]; + NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:count - 1 inSection:0]; + [self.bubblesTableView reloadRowsAtIndexPaths:@[lastIndexPath] withRowAnimation:UITableViewRowAnimationFade]; + } + + if (self.isScrollToBottomHidden + && !self.bubblesTableView.isDragging + && !self.bubblesTableView.isDecelerating) { NSInteger count = [self.roomDataSource tableView:self.bubblesTableView numberOfRowsInSection:0]; if (count) diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift b/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift index b69541f88..0fdbc7a8e 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift +++ b/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift @@ -51,6 +51,8 @@ class RoomTypingBubbleCell: UITableViewCell { override func layoutSubviews() { super.layoutSubviews() + self.dotsView?.isHidden = userPictureViews.count == 0 + guard userPictureViews.count > 0 else { return } From 5c1b741ad43e5d7e74e026364b7c044f13dd8268 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 29 Mar 2021 23:04:01 +0200 Subject: [PATCH 27/78] Composer update - UI enhancements - Fixed red dot misplacement in the header --- Riot/Modules/Room/Views/Title/RoomTitleView.h | 2 ++ Riot/Modules/Room/Views/Title/RoomTitleView.m | 6 +++++- Riot/Modules/Room/Views/Title/RoomTitleView.xib | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/Room/Views/Title/RoomTitleView.h b/Riot/Modules/Room/Views/Title/RoomTitleView.h index 9769d3999..d4295093f 100644 --- a/Riot/Modules/Room/Views/Title/RoomTitleView.h +++ b/Riot/Modules/Room/Views/Title/RoomTitleView.h @@ -44,6 +44,8 @@ @property (weak, nonatomic) IBOutlet NSLayoutConstraint *missedDiscussionsBadgeLabelLeadingConstraint; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *pictureViewHeightConstraint; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *pictureViewWidthConstraint; +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *dotViewCenterXConstraint; +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *dotViewCenterYConstraint; /** The room preview data may be used when mxRoom instance is not available diff --git a/Riot/Modules/Room/Views/Title/RoomTitleView.m b/Riot/Modules/Room/Views/Title/RoomTitleView.m index 37390bd80..6a338590e 100644 --- a/Riot/Modules/Room/Views/Title/RoomTitleView.m +++ b/Riot/Modules/Room/Views/Title/RoomTitleView.m @@ -147,6 +147,8 @@ self.pictureViewHeightConstraint.constant = 28; self.displayNameTextField.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium]; self.typingLabel.font = [UIFont systemFontOfSize:10]; + self.dotViewCenterXConstraint.constant = 3; + self.dotViewCenterYConstraint.constant = -2; } else { @@ -156,7 +158,9 @@ self.pictureViewHeightConstraint.constant = 32; self.displayNameTextField.font = [UIFont systemFontOfSize:17 weight:UIFontWeightMedium]; self.typingLabel.font = [UIFont systemFontOfSize:12]; - } + self.dotViewCenterXConstraint.constant = 0; + self.dotViewCenterYConstraint.constant = -1; + } } - (void)setTypingNotificationString:(NSString *)typingNotificationString diff --git a/Riot/Modules/Room/Views/Title/RoomTitleView.xib b/Riot/Modules/Room/Views/Title/RoomTitleView.xib index 8280083bd..0d6fc7ca1 100644 --- a/Riot/Modules/Room/Views/Title/RoomTitleView.xib +++ b/Riot/Modules/Room/Views/Title/RoomTitleView.xib @@ -71,7 +71,7 @@ - + @@ -92,6 +92,8 @@ + + From 2c83494d223069ca1f9d1bae02432829368e1539 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 29 Mar 2021 23:45:47 +0200 Subject: [PATCH 28/78] Switching composer between text mode & action mode - Fixed: The final frames of the appearance animation of the new composer buttons are missing --- Riot/Modules/Room/Views/InputToolbar/RoomActionsBar.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomActionsBar.swift b/Riot/Modules/Room/Views/InputToolbar/RoomActionsBar.swift index 7f4bdcb98..0cf11e292 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomActionsBar.swift +++ b/Riot/Modules/Room/Views/InputToolbar/RoomActionsBar.swift @@ -77,6 +77,7 @@ import UIKit var currentX: CGFloat = 0 for button in actionButtons { + button.transform = CGAffineTransform.identity button.frame = CGRect(x: currentX, y: 0, width: self.bounds.height, height: self.bounds.height) currentX = button.frame.maxX + itemSpacing } @@ -98,7 +99,7 @@ import UIKit button.transform = CGAffineTransform(translationX: 0, y: self.bounds.height) } for (index, button) in actionButtons.enumerated() { - UIView.animate(withDuration: 0.32, delay: 0.05 * Double(index), usingSpringWithDamping: 0.5, initialSpringVelocity: 7, options: .curveEaseInOut) { + UIView.animate(withDuration: 0.38, delay: 0.05 * Double(index), usingSpringWithDamping: 0.5, initialSpringVelocity: 7, options: .curveEaseInOut) { button.transform = CGAffineTransform.identity } completion: { (finished) in completion?(finished) @@ -106,7 +107,7 @@ import UIKit } } else { for (index, button) in actionButtons.enumerated() { - UIView.animate(withDuration: 0.2, delay: 0.05 * Double(index), options: .curveEaseInOut) { + UIView.animate(withDuration: 0.3, delay: 0.05 * Double(index), options: .curveEaseInOut) { button.transform = CGAffineTransform(translationX: 0, y: self.bounds.height) } completion: { (finished) in completion?(finished) From bce89d5ab45aee34cd9f2b56c46d85a301e87014 Mon Sep 17 00:00:00 2001 From: zer0-x <1rn0kmrwo@relay.firefox.com> Date: Sun, 28 Mar 2021 09:52:10 +0000 Subject: [PATCH 29/78] Translated using Weblate (Arabic) Currently translated at 8.6% (102 of 1186 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ar/ --- Riot/Assets/ar.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/ar.lproj/Vector.strings b/Riot/Assets/ar.lproj/Vector.strings index bdf811f06..fa1146281 100644 --- a/Riot/Assets/ar.lproj/Vector.strings +++ b/Riot/Assets/ar.lproj/Vector.strings @@ -118,3 +118,4 @@ "store_full_description" = "Element هُوَ نَوعٌ جَديدٌ مِن تَطبيقاتِ المُراسَلَة وَالتَّعَاوُن الَّذي:\n\n1. يَمنَحُكَ التَّحَكُّم فِي لِلحِفاظِ عَلَى خُصُوصِيَّتِك\n2. يُتيحُ لَكَ التَّواصُلُ مَعَ أيّ شَخص عَلَى شَبَكَةِ Matrix ، وَحَتَّى خارِجَهَا مِن خِلال التَّكامُل مَعَ التَّطبيقات مِثل Slack\n3. يَحميكَ مِنَ الإعلانات، التَّنقيبُ عَنِ البَيانات، الأبوابُ الخَلفِيَّة وَعَمَلِيَّاتُ الحَدائِقِ المُسَوَّرَة\n4. يُؤمِنُكَ مِن خِلاِل تَعمِيَةِ النِّهايَة-إلى-النِّهايَة، مَعَ التَّوقيعِ المُتَبادَلِ لِلتَحَقُّقِ مِنَ الآخَرَين\n\nيَختَلِفُ Element تَمامًا عَن تَطبيقاتِ المُراسَلَةِ وَالتَّعاوُن الأُخرَى لأنَّهُ لا مَركَزي وَمَفتُوح المَصدَر.\n\nيُتيحُ لَكَ Element إمكانيةُ الاِستِضافَة الذّاتيَّة -أو اِختيارُ مُضيف- بِحَيث تَتَمَتَّع بِالخُصُوصِيَّة وَالمُلكيَّة وَالتَّحَكُّم فِي بَيانَاتك وَمُحَادَثاتك. يُتيحُ لَكَ الوُصُول إلى شَبَكَة مَفتُوحَة؛ لِذَلِكَ لا يَقتَصِرُ الأمر عَلَى التَّحدُث إلى مُستَخدِمي Element الآخَرين فَقَط. كَما اَنهُ آمِنٌ لِلغايَة.\n\nإنَّ Element قادِرٌ عَلَى إتاحَةِ كُلِ ذَلِك لِأنَهُ يَعمَلُ عَلَى Matrix -مِعيار التَّواصُل المَفتُوح اللَّامَركَزي.\n\nإنَّ Element يَمنَحُكَ زِمَامَ التَّحَكُم مِن خِلال السَّماح لَك بِاختيار مَن يَستَضيفُ المُحادَثاتِ الخَاصَّةِ بِك. مِن تَطبيقِ Element يُمكِنُكَ اِختيار الاِستِضافَة بِطُرُقٍ مُختَلِفَة:\n\n1. الحُصُول عَلَى حِساب مَجانيّ عَلَى الخادِم العَامّ matrix.org\n2. اِستِضافَة حِسابك بِنَفسِك عَن طَريق تَشغيل خادِم عَلَى أجهِزَتِكَ الخَاصَّة\n3. التَّسجيل لِلحُصُولِ عَلَى حِساب عَلَى خادِم مُخصص بمُجرد الاِشتِراك فِي مِنَصَّة اِستِضافَة Element Matrix Services\n\nلِمَاذَا تَختارُ Element؟\n\nتَملَّك بَياناتَك: أنتَ مَن تُقرر أين تَحتَفِظ ببياناتك ورسائلك. أنت تمتلكها وتتحكم فيها، وليس بعض الشركات الكُبرى الإحتكارية التي تُنقِّب عن بياناتك أو تُتيح الوصول إلى أطراف ثالثة.\n\n\nتَراسُل وَتَعَاوُن مَفتُوح يُمكِنُكَ مُحادَثَة أي شَخص آخَر عَلَى شَبَكَة Matrix، سَواءً كانُوا يَستَخدِمُونَ Element أو تَطبيقُ Matrix آخَر، وَحَتّى إذا كانُوا يَستَخدِمُونَ نِظامَ مُراسلةٍ مُختَلِف مِثل Slack أو IRC أو XMPP.\n\nالأمان-الخارِق: تَعميَة حَقيقيَة مِنَ النِّهايَة إلى النِّهايَة (فَقَط أطراف المُحادَثَة مَن يُمكِنَهُم فَكّ تَعميَة الرَّسائِل)، وَالتَّوقِيع المُتَبادَل لِلتحقق من أجهزة المُشاركين في المُحادثة.\n\nالتواصل الكامل: المُراسلة، المُكالمات الصوتية والمرئية، مُشاركة الملفات، مُشاركة الشاشة، مجموعة كاملة وكبيرة من عمليات التكامُل، الروبوتات والأدوات. بناء الغُرف، المُجتمعات، ابق على اتصال وأنجز المهام.\n\nأين ما كُنت: ابق على اتصال أينما كنت مع سجل الرسائل المتزامن بالكامل عبر جميع أجهزتك وفي الويب على https://app.element.io."; // String for App Store "store_short_description" = "مُحادَثَةٌ/VoIP آمنةٌ لَا مَركَزِيَّة"; +"auth_missing_email" = "عنوان البريد ناقص"; From d242b9f5908bfbfc43dde9a98c03ffda64ee1eab Mon Sep 17 00:00:00 2001 From: libexus Date: Mon, 29 Mar 2021 15:16:49 +0000 Subject: [PATCH 30/78] Translated using Weblate (German) Currently translated at 99.6% (1182 of 1186 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index a2fe5d961..5b4213b72 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -755,7 +755,7 @@ "widget_integrations_server_failed_to_connect" = "Verbindung zum Integrationsserver fehlgeschlagen"; "device_verification_security_advice" = "Für maximale Sicherheit empfehlen wir, dies persönlich zu tun oder ein anderes vertrauenswürdiges Kommunikationsmittel zu verwenden"; "device_verification_incoming_description_1" = "Überprüfe diese Sitzung, um sie als vertrauenswürdig zu markieren. Sitzungen von Partnern zu vertrauen gibt dir zusätzliche Sicherheit bei der Verwendung von Ende-zu-Ende verschlüsselten Nachrichten."; -"device_verification_incoming_description_2" = "Wenn du diese Sitzung verifizierst, wird es sie vertrauenswürdig und für dein Gegenüber als vertrauenswürdig gekennzeichnet."; +"device_verification_incoming_description_2" = "Wenn du diese Sitzung verifizierst, wird sie für dich und für dein Gegenüber als vertrauenswürdig gekennzeichnet."; // MARK: Start "device_verification_start_title" = "Verifizieren durch Vergleichen eines kurzen Textes"; "device_verification_start_wait_partner" = "Warten auf Annahme durch Partner…"; @@ -1058,7 +1058,7 @@ "key_verification_other_session_title" = "Sitzung verifizieren"; "key_verification_new_session_title" = "Neue Sitzung verifizieren"; "key_verification_this_session_title" = "Verifiziere diese Sitzung"; -"device_verification_security_advice_emoji" = "Vergleiche die einzigartigen Emoji und stell sicher, dass sie in derselben Reihenfolge angezeigt werden."; +"device_verification_security_advice_emoji" = "Vergleiche die einzigartigen Emojis und kontrolliere, dass sie in derselben Reihenfolge angezeigt werden."; "device_verification_security_advice_number" = "Vergleiche die Zahlen und stell sicher, dass sie in derselben Reihenfolge angezeigt werden."; "key_verification_self_verify_current_session_alert_title" = "Verifiziere diese Sitzung"; "key_verification_self_verify_current_session_alert_message" = "Andere Benutzer vertrauen ihr vielleicht nicht."; From 6cc65e4fb4842ab4fec67321c9fb40b1d688210a Mon Sep 17 00:00:00 2001 From: RainSlide Date: Sun, 28 Mar 2021 15:16:45 +0000 Subject: [PATCH 31/78] Translated using Weblate (Chinese (Simplified)) Currently translated at 92.2% (1094 of 1186 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/zh_Hans/ --- Riot/Assets/zh_Hans.lproj/Vector.strings | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Riot/Assets/zh_Hans.lproj/Vector.strings b/Riot/Assets/zh_Hans.lproj/Vector.strings index 65d372752..858143d2f 100644 --- a/Riot/Assets/zh_Hans.lproj/Vector.strings +++ b/Riot/Assets/zh_Hans.lproj/Vector.strings @@ -466,13 +466,13 @@ "group_participants_invited_section" = "已邀请"; // Group rooms "group_rooms_filter_rooms" = "过滤社区聊天室"; -"event_formatter_widget_added" = "小部件 %@ 已被 %@ 添加"; -"event_formatter_widget_removed" = "小部件 %@ 已被 %@ 移除"; +"event_formatter_widget_added" = "挂件 %@ 已被 %@ 添加"; +"event_formatter_widget_removed" = "挂件 %@ 已被 %@ 移除"; "do_not_ask_again" = "不再询问"; "call_already_displayed" = "正在通话。"; // Widget Integration Manager "widget_integration_need_to_be_able_to_invite" = "你需要有邀请用户的权限才能进行此操作。"; -"widget_integration_unable_to_create" = "无法创建小部件。"; +"widget_integration_unable_to_create" = "无法创建挂件。"; "widget_integration_failed_to_send_request" = "发送请求失败。"; "widget_integration_room_not_recognised" = "无法识别此房间。"; "widget_integration_positive_power_level" = "权限级别必须是整数。"; @@ -489,8 +489,8 @@ "group_invitation_format" = "%@ 邀请您加入此社区"; "group_participants_invite_malformed_id" = "ID 格式错误。一个 Matrix ID 看起来应该像是 “@localpart:domain”"; // Widget -"widget_no_power_to_manage" = "你需要相关权限以管理此聊天室的小部件"; -"widget_creation_failure" = "小部件创建失败"; +"widget_no_power_to_manage" = "你需要相关权限以管理此聊天室的挂件"; +"widget_creation_failure" = "挂件创建失败"; // Room key request dialog "e2e_room_key_request_title" = "密钥共享请求"; "room_recents_server_notice_section" = "系统警告"; @@ -527,7 +527,7 @@ "room_resource_usage_limit_reached_message_contact_3" = " 以提高限制。"; // String for App Store "store_short_description" = "安全、去中心化的聊天及 VoIP 应用"; -"store_full_description" = "Element 是一种新型的通讯与协作应用:\n\n1. 使您可以掌控您的隐私\n2. 使您与 Matrix 网络中的任何人交流,甚至可以通过集成功能与如 Slack 之类的其他应用通讯\n3. 保护您免受广告,大数据挖掘和封闭服务的侵害\n4. 通过端到端加密保证安全,通过交叉签名验证其他人\n\nElement 与其他通讯与协作应用完全不同,因为它是去中心化且开源的。\n\nElement 允许您自托管——或者选择托管商——因此,您能拥有数据和会话的隐私权,所有权和控制权。它允许您访问开放网络;因此,您可以与 Element 用户以外的人交流。并且它非常安全。\n\nElement 之所以可以做到这些,是因为它在 Matrix 上运行——开放,去中心化通讯的标准。\n\n通过让您选择由谁来托管您的会话,Element 让您掌控一切。在 Element 应用中,您可以选择不同的托管方式:\n\n1. 在由 Matrix 开发者托管的 matrix.org 公共服务器上获取免费帐户,或从志愿者托管的上千个公共服务器中选择\n2. 在您自己的硬件上运行服务器,自托管您的会话\n3. 通过订阅 Element Matrix Services 托管平台,简单地在自定义服务器上注册账户\n\n为什么选择 Element?\n\n掌控您的数据:您来决定存放您的数据和消息的位置。拥有并控制它的是您,而不是挖掘您的数据或与第三方分享的巨型企业。\n\n开放通讯与协作:您可以与 Matrix 网络中的任何人聊天,不论他们使用 Element 还是其他 Matrix 应用,甚至/即使他们在使用不同的通讯系统,例如 Slack,IRC 或 XMPP。\n\n超级安全:支持真正的端到端加密(仅有会话中的人可以解密消息),还有能够验证会话参与方的设备的交叉签名。\n\n完善的通讯方式:消息,语音和视频通话,文件共享,屏幕共享和大量集成功能,机器人和小挂件。建立房间与社区,保持联系并完成工作。\n\n随时随地:消息历史可在您的全部设备和 https://app.element.io 网页端之间完全同步,无论您在哪里,都可以保持联系。"; +"store_full_description" = "Element 是一种新型的通讯与协作应用:\n\n1. 使您可以掌控您的隐私\n2. 使您与 Matrix 网络中的任何人交流,甚至可以通过集成功能与如 Slack 之类的其他应用通讯\n3. 保护您免受广告,大数据挖掘和封闭服务的侵害\n4. 通过端到端加密保证安全,通过交叉签名验证其他人\n\nElement 与其他通讯与协作应用完全不同,因为它是去中心化且开源的。\n\nElement 允许您自托管——或者选择托管商——因此,您能拥有数据和会话的隐私权,所有权和控制权。它允许您访问开放网络;因此,您可以与 Element 用户以外的人交流。并且它非常安全。\n\nElement 之所以可以做到这些,是因为它在 Matrix 上运行——开放,去中心化通讯的标准。\n\n通过让您选择由谁来托管您的会话,Element 让您掌控一切。在 Element 应用中,您可以选择不同的托管方式:\n\n1. 在由 Matrix 开发者托管的 matrix.org 公共服务器上获取免费帐户,或从志愿者托管的上千个公共服务器中选择\n2. 在您自己的硬件上运行服务器,自托管您的会话\n3. 通过订阅 Element Matrix Services 托管平台,简单地在自定义服务器上注册账户\n\n为什么选择 Element?\n\n掌控您的数据:您来决定存放您的数据和消息的位置。拥有并控制它的是您,而不是挖掘您的数据或与第三方分享的巨型企业。\n\n开放通讯与协作:您可以与 Matrix 网络中的任何人聊天,不论他们使用 Element 还是其他 Matrix 应用,甚至/即使他们在使用不同的通讯系统,例如 Slack,IRC 或 XMPP。\n\n超级安全:支持真正的端到端加密(仅有会话中的人可以解密消息),还有能够验证会话参与方的设备的交叉签名。\n\n完善的通讯方式:消息,语音和视频通话,文件共享,屏幕共享和大量集成功能,机器人和挂件。建立房间与社区,保持联系并完成工作。\n\n随时随地:消息历史可在您的全部设备和 https://app.element.io 网页端之间完全同步,无论您在哪里,都可以保持联系。"; "auth_accept_policies" = "请查看并接受此主页服务器的服务条款:"; "room_replacement_information" = "这个聊天室已被替换,不再有效。"; "settings_flair" = "在允许的地方显示个性徽章"; From bb130d82263cb8efa856a9287b02e2fbf200a848 Mon Sep 17 00:00:00 2001 From: Tirifto Date: Sun, 28 Mar 2021 21:11:20 +0000 Subject: [PATCH 32/78] Translated using Weblate (Esperanto) Currently translated at 99.8% (1184 of 1186 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/eo/ --- Riot/Assets/eo.lproj/Vector.strings | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/eo.lproj/Vector.strings b/Riot/Assets/eo.lproj/Vector.strings index 90ecb7d1f..4b7604cde 100644 --- a/Riot/Assets/eo.lproj/Vector.strings +++ b/Riot/Assets/eo.lproj/Vector.strings @@ -305,7 +305,7 @@ "auth_untrusted_id_server" = "La identiga servilo ne estas fidata"; "auth_phone_is_required" = "Neniu identiga servilo estas agordita, do vi ne povas agordi telefonnumeron por laŭbezona rehavo de konto."; "auth_email_is_required" = "Neniu identiga servilo estas agordita, do vi ne povas agordi retpoŝtadreson por laŭbezona rehavo de konto."; -"auth_phone_in_use" = "Ĉi tiu telefonnumero jam estas uzata."; +"auth_phone_in_use" = "Ĉi tiu telefonnumero jam estas uzata"; "auth_email_in_use" = "Ĉi tiu retpoŝtadreso jam estas uzata"; "auth_add_email_phone_message_2" = "Agordu retpoŝtadreson por rehavo de konto. Uzu poste retpoŝtadreson aŭ telefonon por esti laŭplaĉe trovebla de personoj, kiuj vin konas."; "auth_add_phone_message_2" = "Agordu telefonon, por via (laŭplaĉa) trovebleco de personoj, kiuj vin konas."; @@ -313,7 +313,7 @@ "joined" = "Aliĝita"; "device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Uzi rehavajn pasfrazon aŭ ŝlosilon"; "device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Uzi rehavan ŝlosilon"; -"device_verification_self_verify_wait_additional_information" = "Ĉi tio funkcias por Element kaj aliaj klientoj kapablaj je delegaj subskriboj"; +"device_verification_self_verify_wait_additional_information" = "Ĉi tio funkcias por Element kaj aliaj klientoj kapablaj je delegaj subskriboj."; "device_verification_self_verify_wait_information" = "Kontrolu ĉi tiun saluton per unu el viaj aliaj salutaĵoj, dononte al ĝi aliron al ĉifritaj mesaĝoj.\n\nUzu la plej freŝan version de Element per viaj aliaj aparatoj:"; "device_verification_self_verify_wait_new_sign_in_title" = "Kontrolu ĉi tiun saluton"; @@ -1077,7 +1077,7 @@ "room_resource_limit_exceeded_message_contact_1" = " Bonvole "; "room_intro_cell_information_dm_sentence1_part3" = ". "; "room_intro_cell_information_dm_sentence2" = "Sole la paro de vi ĉeestas ĉi tiu ĉambro, neniu alia povas eniri."; -"room_intro_cell_information_multiple_dm_sentence2" = "Sole vi ĉeestas ĉi tiu ĉambro, krom se oni invitas aliulon."; +"room_intro_cell_information_multiple_dm_sentence2" = "Sole vi ĉeestas ĉi tiun ĉambron, krom se oni invitas aliulon."; "device_verification_security_advice_emoji" = "Komparu la unikajn bildosignojn, certigante, ke ili aperas samorde."; "key_verification_user_title" = "Kontrolu ĝin"; "key_verification_this_session_title" = "Kontrolu ĉi tiun salutaĵon"; @@ -1473,3 +1473,9 @@ "room_details_access_section_anyone_apart_from_guest" = "Ĉiu, kiu konas la ligilon de la ĉambro, krom gastoj"; "room_details_access_section_invited_only" = "Nur personoj, kiuj invitiĝis"; "store_full_description" = "Element estas nova speco de mesaĝilo kaj kunlabora aplikaĵo, kiu:\n\n1. Stirigas vin por konservi vian privatecon\n2. Lasas vin komuniki kun ĉiu en la reto de Matrix, kaj eĉ ekstere, per kuniĝo kun aliaj aplikaĵoj, kiel ekzemple Slack\n3. Protektas vin de reklamoj, datumkolektado, kaŝenirejoj, kaj muritaj ĝardenoj\n4. Sekurigas vin per tutvoja ĉifrado, kun delegaj subskriboj por kontroli aliulojn\n\nElement tute malsamas de aliaj mesaĝiloj kaj kunlaboriloj, ĉar ĝi estas federa kaj malfermitkoda.\n\nElement lasas vin gastigi vin mem – aŭ elekti alian gastiganton – por ke vi havu privatecon, regon kaj kontrolon de viaj datumoj kaj interparoloj. Ĝi donas al vi aliron al malfermita reto, por ke via komunikado ne limiĝu al nur aliaj uzantoj de Element. Kaj ĝi estas tre sekura.\n\nElement povas fari ĉi ĉion, ĉar ĝi funkcias per Matrix – publika normo por malfermita, sencentra komunikado. \n\n\nElement lasas vi elekti, kiu gastigos viajn interparolojn. Per la aplikaĵo Element, vi povas elekti diversajn specojn de gastigado:\n\n1. Akiri senpagan konton ĉe la publika servilo matrix.org\n2. Memgastiĝi per via propra servilo ĉe via propra aparato\n3. Registriĝi ĉe propra servilo per simpla pagaliĝo al la gastiga platformo «Element Matrix Services»\n\nKial Element?\n\nPOSEDU VIAJN DATUMOJN: Vi decidu, kie vi tenu viajn datumojn kaj mesaĝojn. Vi posedas kaj regas ilin, ne iu granda komerca firmao, kiu kolektas viajn datumojn aŭ donas aliron al aliuloj.\n\nMALFERMAJ MESAĜADO KAJ KUNLABORADO: Vi povas babili kun ĉiu alia en la reto de Matrix, ĉu ĝi uzas Elementon aŭ alian aplikaĵon de Matrix, kaj eĉ se ĝi uzas tute alian mesaĝilon, kiel ekzemple Slack, IRC, aŭ XMPP.\n\nTRE SEKURA: Vera tutvoja ĉifrado (nur la interparolantoj povas malĉifri siajn mesaĝojn), kaj delegaj subskriboj por kontroli la aparatojn de partoprenantoj.\n\nSENMANKA KOMUNIKADO: Mesaĝoj, voĉvokoj kaj vidvokoj, havigado de dosieroj, ekrano, kaj multaj diversaj kunigoj, robotoj kaj fenestraĵoj. Kreu ĉambrojn, komunumojn, komuniku kaj kunlaboru.\n\nĈIE KUN VI: Tenu vin ĝisdata per historio de mesaĝoj plene spegulita trans ĉiuj viaj aparatoj, kaj sur la reto per https://app.element.io."; +"directory_server_all_native_rooms" = "Ĉiuj ĉambroj, propraj al Matrix"; +"call_transfer_error_message" = "Malsukcesis transdono de voko"; + +// MARK: - Call Transfer +"call_transfer_title" = "Transdono"; +"room_intro_cell_information_dm_sentence1_part1" = "Ĉi tie komencas historio de viaj rektaj mesaĝoj kun "; From 3da3ecd45c42ead75650a0b065a60c64f9c4ee06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexia=20=C5=ACerner?= Date: Sun, 28 Mar 2021 20:57:51 +0000 Subject: [PATCH 33/78] Translated using Weblate (Esperanto) Currently translated at 99.8% (1184 of 1186 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/eo/ --- Riot/Assets/eo.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/eo.lproj/Vector.strings b/Riot/Assets/eo.lproj/Vector.strings index 4b7604cde..71c9a87a2 100644 --- a/Riot/Assets/eo.lproj/Vector.strings +++ b/Riot/Assets/eo.lproj/Vector.strings @@ -1479,3 +1479,7 @@ // MARK: - Call Transfer "call_transfer_title" = "Transdono"; "room_intro_cell_information_dm_sentence1_part1" = "Ĉi tie komencas historio de viaj rektaj mesaĝoj kun "; +"call_transfer_dialpad" = "Ciferplato"; + +// MARK: - Dial Pad +"dialpad_title" = "Ciferplato"; From 550c4033ea14cff4cf8e171a8534c1716819271f Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Wed, 31 Mar 2021 19:04:00 +0200 Subject: [PATCH 34/78] Make the application settings more configurable --- Config/BuildSettings.swift | 2 + Riot/Generated/Strings.swift | 2 +- Riot/Managers/Settings/RiotSettings.swift | 27 ++++++ .../Security/SecurityViewController.m | 83 +++++++++++-------- .../Modules/Settings/SettingsViewController.m | 29 +++++-- 5 files changed, 101 insertions(+), 42 deletions(-) diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index d06d29747..df69674d0 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -109,6 +109,8 @@ final class BuildSettings: NSObject { // MARK: - Legal URLs + + // Note: Set empty strings to hide the related entry in application settings static let applicationCopyrightUrlString = "https://element.io/copyright" static let applicationPrivacyPolicyUrlString = "https://element.io/privacy" static let applicationTermsConditionsUrlString = "https://element.io/terms-of-service" diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 218303eaf..5de375e70 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1514,7 +1514,7 @@ internal enum VectorL10n { internal static var identityServerSettingsDisconnect: String { return VectorL10n.tr("Vector", "identity_server_settings_disconnect") } - /// Disconnecting from your identity server will mean you won’t be discoverable by other users and be able to invite others by email or phone. + /// Disconnecting from your identity server will mean you won’t be discoverable by other users and be able to invite others by email or phone. internal static var identityServerSettingsDisconnectInfo: String { return VectorL10n.tr("Vector", "identity_server_settings_disconnect_info") } diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index 5627d7369..7dbbc84d7 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -37,6 +37,9 @@ final class RiotSettings: NSObject { static let hideReviewSessionsAlert = "hideReviewSessionsAlert" static let matrixApps = "matrixApps" static let showNSFWPublicRooms = "showNSFWPublicRooms" + static let accountManagedExternally = "accountManagedExternally" + static let inviteFriendsNotAllowed = "inviteFriendsNotAllowed" + static let callsSettingsManagedExternally = "callsSettingsManagedExternally" } static let shared = RiotSettings() @@ -212,4 +215,28 @@ final class RiotSettings: NSObject { defaults.set(newValue, forKey: UserDefaultsKeys.matrixApps) } } + + var inviteFriendsNotAllowed: Bool { + get { + return defaults.bool(forKey: UserDefaultsKeys.inviteFriendsNotAllowed) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.inviteFriendsNotAllowed) + } + } + + var accountManagedExternally: Bool { + get { + return defaults.bool(forKey: UserDefaultsKeys.accountManagedExternally) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.accountManagedExternally) + } + } + + var callsSettingsManagedExternally: Bool { + get { + return defaults.bool(forKey: UserDefaultsKeys.callsSettingsManagedExternally) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.callsSettingsManagedExternally) + } + } } diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index 291a02d73..48dfc2e87 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -329,41 +329,53 @@ TableViewSectionsDelegate> // Crypto sessions section - Section *sessionsSection = [Section sectionWithTag:SECTION_CRYPTO_SESSIONS]; - - sessionsSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_crypto_sessions", @"Vector", nil); - - NSUInteger sessionsSectionRowsCount; - - if (self.showLoadingDevicesInformation) + if (!RiotSettings.shared.accountManagedExternally) { - sessionsSectionRowsCount = 2; - } else { - sessionsSectionRowsCount = devicesArray.count + 1; - } + Section *sessionsSection = [Section sectionWithTag:SECTION_CRYPTO_SESSIONS]; + + sessionsSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_crypto_sessions", @"Vector", nil); + + NSUInteger sessionsSectionRowsCount; + + if (self.showLoadingDevicesInformation) + { + sessionsSectionRowsCount = 2; + } + else + { + sessionsSectionRowsCount = devicesArray.count + 1; + } - [sessionsSection addRowsWithCount:sessionsSectionRowsCount]; - - [sections addObject:sessionsSection]; + [sessionsSection addRowsWithCount:sessionsSectionRowsCount]; + + [sections addObject:sessionsSection]; + } // Secure backup - - Section *secureBackupSection = [Section sectionWithTag:SECTION_SECURE_BACKUP]; - secureBackupSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_secure_backup", @"Vector", nil); - - [secureBackupSection addRowsWithCount:[self numberOfRowsInSecureBackupSection]]; - - [sections addObject:secureBackupSection]; + if (!RiotSettings.shared.accountManagedExternally) + { + Section *secureBackupSection = [Section sectionWithTag:SECTION_SECURE_BACKUP]; + secureBackupSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_secure_backup", @"Vector", nil); + + [secureBackupSection addRowsWithCount:[self numberOfRowsInSecureBackupSection]]; + + [sections addObject:secureBackupSection]; + } // Cryptograhpy - Section *cryptograhpySection = [Section sectionWithTag:SECTION_CRYPTOGRAPHY]; - cryptograhpySection.headerTitle = NSLocalizedStringFromTable(@"security_settings_cryptography", @"Vector", nil); - - [cryptograhpySection addRowsWithCount:CRYPTOGRAPHY_COUNT]; - - [sections addObject:cryptograhpySection]; + if (!RiotSettings.shared.accountManagedExternally) + { + Section *cryptograhpySection = [Section sectionWithTag:SECTION_CRYPTOGRAPHY]; + cryptograhpySection.headerTitle = NSLocalizedStringFromTable(@"security_settings_cryptography", @"Vector", nil); + + [cryptograhpySection addRowWithTag:CRYPTOGRAPHY_INFO]; + + [cryptograhpySection addRowWithTag:CRYPTOGRAPHY_EXPORT]; + + [sections addObject:cryptograhpySection]; + } #ifdef CROSS_SIGNING_AND_BACKUP_DEV @@ -389,13 +401,16 @@ TableViewSectionsDelegate> // Advanced - Section *advancedSection = [Section sectionWithTag:SECTION_ADVANCED]; - advancedSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_advanced", @"Vector", nil); - - [advancedSection addRowWithTag:ADVANCED_BLACKLIST_UNVERIFIED_DEVICES]; - [advancedSection addRowWithTag:ADVANCED_BLACKLIST_UNVERIFIED_DEVICES_DESCRIPTION]; - - [sections addObject:advancedSection]; + if (!RiotSettings.shared.accountManagedExternally) + { + Section *advancedSection = [Section sectionWithTag:SECTION_ADVANCED]; + advancedSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_advanced", @"Vector", nil); + + [advancedSection addRowWithTag:ADVANCED_BLACKLIST_UNVERIFIED_DEVICES]; + [advancedSection addRowWithTag:ADVANCED_BLACKLIST_UNVERIFIED_DEVICES_DESCRIPTION]; + + [sections addObject:advancedSection]; + } // Update sections diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index fdac6daff..923c6717d 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -300,7 +300,10 @@ TableViewSectionsDelegate> Section *sectionUserSettings = [Section sectionWithTag:SECTION_TAG_USER_SETTINGS]; [sectionUserSettings addRowWithTag:USER_SETTINGS_PROFILE_PICTURE_INDEX]; [sectionUserSettings addRowWithTag:USER_SETTINGS_DISPLAYNAME_INDEX]; - [sectionUserSettings addRowWithTag:USER_SETTINGS_CHANGE_PASSWORD_INDEX]; + if (!RiotSettings.shared.accountManagedExternally) + { + [sectionUserSettings addRowWithTag:USER_SETTINGS_CHANGE_PASSWORD_INDEX]; + } if (BuildSettings.settingsScreenShowUserFirstName) { [sectionUserSettings addRowWithTag:USER_SETTINGS_FIRST_NAME_INDEX]; @@ -332,8 +335,10 @@ TableViewSectionsDelegate> { [sectionUserSettings addRowWithTag:USER_SETTINGS_THREEPIDS_INFORMATION_INDEX]; } - - [sectionUserSettings addRowWithTag:USER_SETTINGS_INVITE_FRIENDS_INDEX]; + if (!RiotSettings.shared.inviteFriendsNotAllowed) + { + [sectionUserSettings addRowWithTag:USER_SETTINGS_INVITE_FRIENDS_INDEX]; + } sectionUserSettings.headerTitle = NSLocalizedStringFromTable(@"settings_user_settings", @"Vector", nil); [tmpSections addObject:sectionUserSettings]; @@ -352,7 +357,8 @@ TableViewSectionsDelegate> sectionNotificationSettings.headerTitle = NSLocalizedStringFromTable(@"settings_notifications_settings", @"Vector", nil); [tmpSections addObject:sectionNotificationSettings]; - if (BuildSettings.allowVoIPUsage && BuildSettings.stunServerFallbackUrlString) + if (BuildSettings.allowVoIPUsage && BuildSettings.stunServerFallbackUrlString + && !RiotSettings.shared.callsSettingsManagedExternally) { Section *sectionCalls = [Section sectionWithTag:SECTION_TAG_CALLS]; [sectionCalls addRowWithTag:CALLS_ENABLE_STUN_SERVER_FALLBACK_INDEX]; @@ -432,9 +438,18 @@ TableViewSectionsDelegate> Section *sectionOther = [Section sectionWithTag:SECTION_TAG_OTHER]; [sectionOther addRowWithTag:OTHER_VERSION_INDEX]; [sectionOther addRowWithTag:OTHER_OLM_VERSION_INDEX]; - [sectionOther addRowWithTag:OTHER_COPYRIGHT_INDEX]; - [sectionOther addRowWithTag:OTHER_TERM_CONDITIONS_INDEX]; - [sectionOther addRowWithTag:OTHER_PRIVACY_INDEX]; + if (BuildSettings.applicationCopyrightUrlString.length) + { + [sectionOther addRowWithTag:OTHER_COPYRIGHT_INDEX]; + } + if (BuildSettings.applicationTermsConditionsUrlString.length) + { + [sectionOther addRowWithTag:OTHER_TERM_CONDITIONS_INDEX]; + } + if (BuildSettings.applicationPrivacyPolicyUrlString.length) + { + [sectionOther addRowWithTag:OTHER_PRIVACY_INDEX]; + } [sectionOther addRowWithTag:OTHER_THIRD_PARTY_INDEX]; [sectionOther addRowWithTag:OTHER_SHOW_NSFW_ROOMS_INDEX]; From 13aded46b93b802de7f2aa0a3754c288f29566eb Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Thu, 1 Apr 2021 07:55:04 +0200 Subject: [PATCH 35/78] Updated CHANGES.rst --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 09d797340..49117bff7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,7 @@ Changes to be released in next version * 🙌 Improvements - * + * Make the application settings more configurable (#4171) 🐛 Bugfix * From 9b4cb2f94f21d6baabbbe61898861ef73a8a3048 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Thu, 1 Apr 2021 14:16:06 +0200 Subject: [PATCH 36/78] Make the application settings more configurable - Update after review --- Config/BuildSettings.swift | 12 +- Riot/Managers/Settings/RiotSettings.swift | 127 ++++++++++++++++-- Riot/Modules/Application/LegacyAppDelegate.m | 4 + .../Security/SecurityViewController.m | 83 +++++++----- .../Modules/Settings/SettingsViewController.m | 21 ++- 5 files changed, 192 insertions(+), 55 deletions(-) diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index df69674d0..5612061e0 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -227,7 +227,17 @@ final class BuildSettings: NSObject { static let settingsScreenAllowChangingCrashUsageDataSettings: Bool = true static let settingsScreenAllowBugReportingManually: Bool = true static let settingsScreenAllowDeactivatingAccount: Bool = true - + static let settingsScreenShowChangePassword:Bool = true + static let settingsScreenShowInviteFriends:Bool = true + static let settingsScreenShowEnableStunServerFallback: Bool = true + static let settingsSecurityScreenShowSessions:Bool = true + static let settingsSecurityScreenShowSetupBackup:Bool = true + static let settingsSecurityScreenShowRestoreBackup:Bool = true + static let settingsSecurityScreenShowDeleteBackup:Bool = true + static let settingsSecurityScreenShowCryptographyInfo:Bool = true + static let settingsSecurityScreenShowCryptographyExport:Bool = true + static let settingsSecurityScreenShowAdvancedUnverifiedDevices:Bool = true + // MARK: - Timeline settings static let roomInputToolbarCompressionMode = MXKRoomInputToolbarCompressionModePrompt diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index 7dbbc84d7..014f4502d 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -37,9 +37,16 @@ final class RiotSettings: NSObject { static let hideReviewSessionsAlert = "hideReviewSessionsAlert" static let matrixApps = "matrixApps" static let showNSFWPublicRooms = "showNSFWPublicRooms" - static let accountManagedExternally = "accountManagedExternally" - static let inviteFriendsNotAllowed = "inviteFriendsNotAllowed" - static let callsSettingsManagedExternally = "callsSettingsManagedExternally" + static let settingsScreenShowChangePassword = "settingsScreenShowChangePassword" + static let settingsScreenShowInviteFriends = "settingsScreenShowInviteFriends" + static let settingsScreenShowEnableStunServerFallback = "settingsScreenShowEnableStunServerFallback" + static let settingsSecurityScreenShowSessions = "settingsSecurityScreenShowSessions" + static let settingsSecurityScreenShowSetupBackup = "settingsSecurityScreenShowSetupBackup" + static let settingsSecurityScreenShowRestoreBackup = "settingsSecurityScreenShowRestoreBackup" + static let settingsSecurityScreenShowDeleteBackup = "settingsSecurityScreenShowDeleteBackup" + static let settingsSecurityScreenShowCryptographyInfo = "settingsSecurityScreenShowCryptographyInfo" + static let settingsSecurityScreenShowCryptographyExport = "settingsSecurityScreenShowCryptographyExport" + static let settingsSecurityScreenShowAdvancedUnverifiedDevices = "settingsSecurityScreenShowAdvancedBlacklistUnverifiedDevices" } static let shared = RiotSettings() @@ -54,6 +61,19 @@ final class RiotSettings: NSObject { // MARK: - Public + func reset() { + defaults.removeObject(forKey: UserDefaultsKeys.settingsScreenShowChangePassword) + defaults.removeObject(forKey: UserDefaultsKeys.settingsScreenShowInviteFriends) + defaults.removeObject(forKey: UserDefaultsKeys.settingsScreenShowEnableStunServerFallback) + defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowSessions) + defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowSetupBackup) + defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowRestoreBackup) + defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowDeleteBackup) + defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowCryptographyInfo) + defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowCryptographyExport) + defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowAdvancedUnverifiedDevices) + } + // MARK: Servers var homeserverUrlString: String { @@ -216,27 +236,106 @@ final class RiotSettings: NSObject { } } - var inviteFriendsNotAllowed: Bool { + // MARK: General Settings + + var settingsScreenShowChangePassword: Bool { get { - return defaults.bool(forKey: UserDefaultsKeys.inviteFriendsNotAllowed) + guard defaults.object(forKey: UserDefaultsKeys.settingsScreenShowChangePassword) != nil else { + return BuildSettings.settingsScreenShowChangePassword + } + return defaults.bool(forKey: UserDefaultsKeys.settingsScreenShowChangePassword) } set { - defaults.set(newValue, forKey: UserDefaultsKeys.inviteFriendsNotAllowed) + defaults.set(newValue, forKey: UserDefaultsKeys.settingsScreenShowChangePassword) } } - - var accountManagedExternally: Bool { + var settingsScreenShowInviteFriends: Bool { get { - return defaults.bool(forKey: UserDefaultsKeys.accountManagedExternally) + guard defaults.object(forKey: UserDefaultsKeys.settingsScreenShowInviteFriends) != nil else { + return BuildSettings.settingsScreenShowInviteFriends + } + return defaults.bool(forKey: UserDefaultsKeys.settingsScreenShowInviteFriends) } set { - defaults.set(newValue, forKey: UserDefaultsKeys.accountManagedExternally) + defaults.set(newValue, forKey: UserDefaultsKeys.settingsScreenShowInviteFriends) } } - - var callsSettingsManagedExternally: Bool { + var settingsScreenShowEnableStunServerFallback: Bool { get { - return defaults.bool(forKey: UserDefaultsKeys.callsSettingsManagedExternally) + guard defaults.object(forKey: UserDefaultsKeys.settingsScreenShowInviteFriends) != nil else { + return BuildSettings.settingsScreenShowEnableStunServerFallback + } + return defaults.bool(forKey: UserDefaultsKeys.settingsScreenShowEnableStunServerFallback) } set { - defaults.set(newValue, forKey: UserDefaultsKeys.callsSettingsManagedExternally) + defaults.set(newValue, forKey: UserDefaultsKeys.settingsScreenShowEnableStunServerFallback) + } + } + var settingsSecurityScreenShowSessions: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.settingsSecurityScreenShowSessions) != nil else { + return BuildSettings.settingsSecurityScreenShowSessions + } + return defaults.bool(forKey: UserDefaultsKeys.settingsSecurityScreenShowSessions) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.settingsSecurityScreenShowSessions) + } + } + var settingsSecurityScreenShowSetupBackup: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.settingsSecurityScreenShowSetupBackup) != nil else { + return BuildSettings.settingsSecurityScreenShowSetupBackup + } + return defaults.bool(forKey: UserDefaultsKeys.settingsSecurityScreenShowSetupBackup) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.settingsSecurityScreenShowSetupBackup) + } + } + var settingsSecurityScreenShowRestoreBackup: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.settingsSecurityScreenShowRestoreBackup) != nil else { + return BuildSettings.settingsSecurityScreenShowRestoreBackup + } + return defaults.bool(forKey: UserDefaultsKeys.settingsSecurityScreenShowRestoreBackup) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.settingsSecurityScreenShowRestoreBackup) + } + } + var settingsSecurityScreenShowDeleteBackup: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.settingsSecurityScreenShowDeleteBackup) != nil else { + return BuildSettings.settingsSecurityScreenShowDeleteBackup + } + return defaults.bool(forKey: UserDefaultsKeys.settingsSecurityScreenShowDeleteBackup) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.settingsSecurityScreenShowDeleteBackup) + } + } + var settingsSecurityScreenShowCryptographyInfo: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.settingsSecurityScreenShowCryptographyInfo) != nil else { + return BuildSettings.settingsSecurityScreenShowCryptographyInfo + } + return defaults.bool(forKey: UserDefaultsKeys.settingsSecurityScreenShowCryptographyInfo) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.settingsSecurityScreenShowCryptographyInfo) + } + } + var settingsSecurityScreenShowCryptographyExport: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.settingsSecurityScreenShowCryptographyExport) != nil else { + return BuildSettings.settingsSecurityScreenShowCryptographyExport + } + return defaults.bool(forKey: UserDefaultsKeys.settingsSecurityScreenShowCryptographyExport) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.settingsSecurityScreenShowCryptographyExport) + } + } + var settingsSecurityScreenShowAdvancedUnverifiedDevices: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.settingsSecurityScreenShowAdvancedUnverifiedDevices) != nil else { + return BuildSettings.settingsSecurityScreenShowAdvancedUnverifiedDevices + } + return defaults.bool(forKey: UserDefaultsKeys.settingsSecurityScreenShowAdvancedUnverifiedDevices) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.settingsSecurityScreenShowAdvancedUnverifiedDevices) } } } diff --git a/Riot/Modules/Application/LegacyAppDelegate.m b/Riot/Modules/Application/LegacyAppDelegate.m index 4c4ff28f3..501021ddc 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.m +++ b/Riot/Modules/Application/LegacyAppDelegate.m @@ -2136,6 +2136,10 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni [self logoutSendingRequestServer:YES completion:^(BOOL isLoggedOut) { if (completion) { + if (isLoggedOut) + { + [RiotSettings.shared reset]; + } completion (YES); } }]; diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index 48dfc2e87..c087bdfb6 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -329,7 +329,7 @@ TableViewSectionsDelegate> // Crypto sessions section - if (!RiotSettings.shared.accountManagedExternally) + if (RiotSettings.shared.settingsSecurityScreenShowSessions) { Section *sessionsSection = [Section sectionWithTag:SECTION_CRYPTO_SESSIONS]; @@ -353,30 +353,36 @@ TableViewSectionsDelegate> // Secure backup - if (!RiotSettings.shared.accountManagedExternally) + Section *secureBackupSection = [Section sectionWithTag:SECTION_SECURE_BACKUP]; + secureBackupSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_secure_backup", @"Vector", nil); + + [secureBackupSection addRowsWithCount:[self numberOfRowsInSecureBackupSection]]; + + if (secureBackupSection.rows.count) { - Section *secureBackupSection = [Section sectionWithTag:SECTION_SECURE_BACKUP]; - secureBackupSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_secure_backup", @"Vector", nil); - - [secureBackupSection addRowsWithCount:[self numberOfRowsInSecureBackupSection]]; - [sections addObject:secureBackupSection]; } // Cryptograhpy - if (!RiotSettings.shared.accountManagedExternally) + Section *cryptograhpySection = [Section sectionWithTag:SECTION_CRYPTOGRAPHY]; + cryptograhpySection.headerTitle = NSLocalizedStringFromTable(@"security_settings_cryptography", @"Vector", nil); + + if (RiotSettings.shared.settingsSecurityScreenShowCryptographyInfo) { - Section *cryptograhpySection = [Section sectionWithTag:SECTION_CRYPTOGRAPHY]; - cryptograhpySection.headerTitle = NSLocalizedStringFromTable(@"security_settings_cryptography", @"Vector", nil); - [cryptograhpySection addRowWithTag:CRYPTOGRAPHY_INFO]; - - [cryptograhpySection addRowWithTag:CRYPTOGRAPHY_EXPORT]; - - [sections addObject:cryptograhpySection]; } + if (RiotSettings.shared.settingsSecurityScreenShowCryptographyExport) + { + [cryptograhpySection addRowWithTag:CRYPTOGRAPHY_EXPORT]; + } + + if (cryptograhpySection.rows.count) + { + [sections addObject:cryptograhpySection]; + } + #ifdef CROSS_SIGNING_AND_BACKUP_DEV // Cross-Signing @@ -401,17 +407,20 @@ TableViewSectionsDelegate> // Advanced - if (!RiotSettings.shared.accountManagedExternally) + Section *advancedSection = [Section sectionWithTag:SECTION_ADVANCED]; + advancedSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_advanced", @"Vector", nil); + + if (RiotSettings.shared.settingsSecurityScreenShowAdvancedUnverifiedDevices) { - Section *advancedSection = [Section sectionWithTag:SECTION_ADVANCED]; - advancedSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_advanced", @"Vector", nil); - [advancedSection addRowWithTag:ADVANCED_BLACKLIST_UNVERIFIED_DEVICES]; [advancedSection addRowWithTag:ADVANCED_BLACKLIST_UNVERIFIED_DEVICES_DESCRIPTION]; - + } + + if (advancedSection.rows.count) + { [sections addObject:advancedSection]; } - + // Update sections self.tableViewSections.sections = sections; @@ -868,28 +877,36 @@ TableViewSectionsDelegate> - (void)refreshSecureBackupSectionData { MXRecoveryService *recoveryService = self.mainSession.crypto.recoveryService; + NSMutableArray *secureBackupSectionState = [NSMutableArray new]; if (recoveryService.hasRecovery) { - secureBackupSectionState = @[ - @(SECURE_BACKUP_RESTORE), - @(SECURE_BACKUP_DELETE), - @(SECURE_BACKUP_DESCRIPTION), - //@(SECURE_BACKUP_MANAGE_MANUALLY), - ]; + if (RiotSettings.shared.settingsSecurityScreenShowRestoreBackup) + { + [secureBackupSectionState addObject:@(SECURE_BACKUP_RESTORE)]; + } + if (RiotSettings.shared.settingsSecurityScreenShowDeleteBackup) + { + [secureBackupSectionState addObject:@(SECURE_BACKUP_DELETE)]; + } } else { - secureBackupSectionState = @[ - @(SECURE_BACKUP_SETUP), - @(SECURE_BACKUP_DESCRIPTION), - //@(SECURE_BACKUP_MANAGE_MANUALLY), - ]; + if (RiotSettings.shared.settingsSecurityScreenShowSetupBackup) + { + [secureBackupSectionState addObject:@(SECURE_BACKUP_SETUP)]; + } } + if (secureBackupSectionState.count) + { + [secureBackupSectionState addObject:@(SECURE_BACKUP_DESCRIPTION)]; + } + #ifdef CROSS_SIGNING_AND_BACKUP_DEV - secureBackupSectionState = [@[@(SECURE_BACKUP_INFO)] arrayByAddingObjectsFromArray:secureBackupSectionState]; + [secureBackupSectionState addObject:@(SECURE_BACKUP_INFO)]; #endif + self->secureBackupSectionState = secureBackupSectionState; } - (NSUInteger)secureBackupSectionEnumForRow:(NSUInteger)row diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 923c6717d..12adb0364 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -300,7 +300,7 @@ TableViewSectionsDelegate> Section *sectionUserSettings = [Section sectionWithTag:SECTION_TAG_USER_SETTINGS]; [sectionUserSettings addRowWithTag:USER_SETTINGS_PROFILE_PICTURE_INDEX]; [sectionUserSettings addRowWithTag:USER_SETTINGS_DISPLAYNAME_INDEX]; - if (!RiotSettings.shared.accountManagedExternally) + if (RiotSettings.shared.settingsScreenShowChangePassword) { [sectionUserSettings addRowWithTag:USER_SETTINGS_CHANGE_PASSWORD_INDEX]; } @@ -335,7 +335,7 @@ TableViewSectionsDelegate> { [sectionUserSettings addRowWithTag:USER_SETTINGS_THREEPIDS_INFORMATION_INDEX]; } - if (!RiotSettings.shared.inviteFriendsNotAllowed) + if (RiotSettings.shared.settingsScreenShowInviteFriends) { [sectionUserSettings addRowWithTag:USER_SETTINGS_INVITE_FRIENDS_INDEX]; } @@ -357,14 +357,21 @@ TableViewSectionsDelegate> sectionNotificationSettings.headerTitle = NSLocalizedStringFromTable(@"settings_notifications_settings", @"Vector", nil); [tmpSections addObject:sectionNotificationSettings]; - if (BuildSettings.allowVoIPUsage && BuildSettings.stunServerFallbackUrlString - && !RiotSettings.shared.callsSettingsManagedExternally) + if (BuildSettings.allowVoIPUsage && BuildSettings.stunServerFallbackUrlString) { Section *sectionCalls = [Section sectionWithTag:SECTION_TAG_CALLS]; - [sectionCalls addRowWithTag:CALLS_ENABLE_STUN_SERVER_FALLBACK_INDEX]; - [sectionCalls addRowWithTag:CALLS_STUN_SERVER_FALLBACK_DESCRIPTION_INDEX]; sectionCalls.headerTitle = NSLocalizedStringFromTable(@"settings_calls_settings", @"Vector", nil); - [tmpSections addObject:sectionCalls]; + + if (RiotSettings.shared.settingsScreenShowEnableStunServerFallback) + { + [sectionCalls addRowWithTag:CALLS_ENABLE_STUN_SERVER_FALLBACK_INDEX]; + [sectionCalls addRowWithTag:CALLS_STUN_SERVER_FALLBACK_DESCRIPTION_INDEX]; + } + + if (sectionCalls.rows.count) + { + [tmpSections addObject:sectionCalls]; + } } if (BuildSettings.settingsScreenShowDiscoverySettings) From a5526415fbe53f4c3a170dc171395ee41a7e07d2 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Thu, 1 Apr 2021 15:02:43 +0200 Subject: [PATCH 37/78] Enable / disable external friends invite --- Config/BuildSettings.swift | 2 ++ Riot/Managers/Settings/RiotSettings.swift | 15 +++++++++++++++ .../Contacts/DataSources/ContactsDataSource.m | 2 +- Riot/Modules/StartChat/StartChatViewController.m | 6 ++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 5612061e0..ef6fc2a87 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -195,6 +195,8 @@ final class BuildSettings: NSObject { static let allowLocalContactsAccess: Bool = true + static let allowInviteExernalUsers: Bool = true + // MARK: - Feature Specifics /// Not allowed pin codes. User won't be able to select one of the pin in the list. diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index 014f4502d..a984eda88 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -47,6 +47,7 @@ final class RiotSettings: NSObject { static let settingsSecurityScreenShowCryptographyInfo = "settingsSecurityScreenShowCryptographyInfo" static let settingsSecurityScreenShowCryptographyExport = "settingsSecurityScreenShowCryptographyExport" static let settingsSecurityScreenShowAdvancedUnverifiedDevices = "settingsSecurityScreenShowAdvancedBlacklistUnverifiedDevices" + static let allowInviteExernalUsers = "allowInviteExernalUsers" } static let shared = RiotSettings() @@ -72,6 +73,7 @@ final class RiotSettings: NSObject { defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowCryptographyInfo) defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowCryptographyExport) defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowAdvancedUnverifiedDevices) + defaults.removeObject(forKey: UserDefaultsKeys.allowInviteExernalUsers) } // MARK: Servers @@ -236,6 +238,19 @@ final class RiotSettings: NSObject { } } + // MARK: Features + + var allowInviteExernalUsers: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.allowInviteExernalUsers) != nil else { + return BuildSettings.allowInviteExernalUsers + } + return defaults.bool(forKey: UserDefaultsKeys.allowInviteExernalUsers) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.allowInviteExernalUsers) + } + } + // MARK: General Settings var settingsScreenShowChangePassword: Bool { diff --git a/Riot/Modules/Contacts/DataSources/ContactsDataSource.m b/Riot/Modules/Contacts/DataSources/ContactsDataSource.m index 72d2e4616..dea3437ff 100644 --- a/Riot/Modules/Contacts/DataSources/ContactsDataSource.m +++ b/Riot/Modules/Contacts/DataSources/ContactsDataSource.m @@ -499,7 +499,7 @@ if (section == searchInputSection) { - count = 1; + count = RiotSettings.shared.allowInviteExernalUsers ? 1 : 0; } else if (section == filteredLocalContactsSection && !(shrinkedSectionsBitMask & CONTACTSDATASOURCE_LOCALCONTACTS_BITWISE)) { diff --git a/Riot/Modules/StartChat/StartChatViewController.m b/Riot/Modules/StartChat/StartChatViewController.m index eb35cd1fb..421ac343e 100644 --- a/Riot/Modules/StartChat/StartChatViewController.m +++ b/Riot/Modules/StartChat/StartChatViewController.m @@ -133,6 +133,12 @@ - (void)setupInviteFriendsHeaderView { + if (!RiotSettings.shared.allowInviteExernalUsers) + { + self.contactsTableView.tableHeaderView = nil; + return; + } + InviteFriendsHeaderView *inviteFriendsHeaderView = [InviteFriendsHeaderView instantiate]; inviteFriendsHeaderView.delegate = self; self.contactsTableView.tableHeaderView = inviteFriendsHeaderView; From b23579ca305bc8a3dccea6ae8925af9da192e4d1 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Fri, 2 Apr 2021 10:09:04 +0200 Subject: [PATCH 38/78] Update Riot/Modules/Room/RoomViewController.m Co-authored-by: manuroe --- Riot/Modules/Room/RoomViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 8c6be6078..9bd047643 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -1495,7 +1495,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { MXEvent *event = [self.roomDataSource eventWithEventId:eventId]; MXRoomMember * roomMember = [self.roomDataSource.roomState.members memberWithUserId:event.sender]; - if (roomMember) + if (roomMember.displayname.length) { roomInputToolbarView.eventSenderDisplayName = roomMember.displayname; } From e26acaaffbafc2c589aa876eed5c23e0a0c8fd8a Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Fri, 2 Apr 2021 11:49:02 +0200 Subject: [PATCH 39/78] Updated CHANGES.rst --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8855e8f83..6c1534a28 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,7 @@ Changes to be released in next version 🙌 Improvements * Make the application settings more configurable (#4171) + * Enable / disable external friends invite (#4173) 🐛 Bugfix * From 16937adc62a168eff42e9306072efcb51e7bceb4 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Fri, 2 Apr 2021 15:54:21 +0200 Subject: [PATCH 40/78] Possibility to lock some room creation parameters from settings --- CHANGES.rst | 1 + Config/BuildSettings.swift | 17 +++++ Riot/Managers/Settings/RiotSettings.swift | 22 +++++++ .../EnterNewRoomDetailsViewController.swift | 62 +++++++++++-------- .../EnterNewRoomDetailsViewModel.swift | 3 +- 5 files changed, 79 insertions(+), 26 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 8855e8f83..846f03166 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,7 @@ Changes to be released in next version 🙌 Improvements * Make the application settings more configurable (#4171) + * Possibility to lock some room creation parameters from settings (#4181) 🐛 Bugfix * diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 5612061e0..57285d9eb 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -241,6 +241,23 @@ final class BuildSettings: NSObject { // MARK: - Timeline settings static let roomInputToolbarCompressionMode = MXKRoomInputToolbarCompressionModePrompt + // MARK: - Room Creation Screen + + enum ForcedEncryptionMode: String { + case none = "none" + case disabled = "disabled" + case enabled = "enabled" + } + + enum ForcedRoomType: String { + case none = "none" + case privateRoom = "privateRoom" + case publicRoom = "publicRoom" + } + + static let roomCreationScreenForcedEncryptionMode: ForcedEncryptionMode = .none + static let roomCreationScreenForcedRoomType: ForcedRoomType = .none + // MARK: - Room Settings Screen static let roomSettingsScreenShowLowPriorityOption: Bool = true diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index 014f4502d..f2b43fa5f 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -47,6 +47,8 @@ final class RiotSettings: NSObject { static let settingsSecurityScreenShowCryptographyInfo = "settingsSecurityScreenShowCryptographyInfo" static let settingsSecurityScreenShowCryptographyExport = "settingsSecurityScreenShowCryptographyExport" static let settingsSecurityScreenShowAdvancedUnverifiedDevices = "settingsSecurityScreenShowAdvancedBlacklistUnverifiedDevices" + static let roomCreationScreenForcedEncryptionMode = "roomCreationScreenForcedEncryptionMode" + static let roomCreationScreenForcedRoomType = "roomCreationScreenForcedRoomType" } static let shared = RiotSettings() @@ -236,6 +238,26 @@ final class RiotSettings: NSObject { } } + // MARK: - Room Creation Screen + + var roomCreationScreenForcedEncryptionMode: BuildSettings.ForcedEncryptionMode { + get { + let rawValue = defaults.string(forKey: UserDefaultsKeys.roomCreationScreenForcedEncryptionMode) ?? BuildSettings.roomCreationScreenForcedEncryptionMode.rawValue + return BuildSettings.ForcedEncryptionMode(rawValue: rawValue) ?? .none + } set { + defaults.set(newValue.rawValue, forKey: UserDefaultsKeys.roomCreationScreenForcedEncryptionMode) + } + } + + var roomCreationScreenForcedRoomType: BuildSettings.ForcedRoomType { + get { + let rawValue = defaults.string(forKey: UserDefaultsKeys.roomCreationScreenForcedRoomType) ?? BuildSettings.roomCreationScreenForcedRoomType.rawValue + return BuildSettings.ForcedRoomType(rawValue: rawValue) ?? .none + } set { + defaults.set(newValue.rawValue, forKey: UserDefaultsKeys.roomCreationScreenForcedRoomType) + } + } + // MARK: General Settings var settingsScreenShowChangePassword: Bool { diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift index 7ec1cf9e8..5e0dc933e 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift @@ -113,39 +113,51 @@ final class EnterNewRoomDetailsViewController: UIViewController { rows: [row_2_0], footer: nil) - let row_3_0 = Row(type: .withSwitch(isOn: viewModel.roomCreationParameters.isEncrypted, onValueChanged: { (theSwitch) in - self.viewModel.roomCreationParameters.isEncrypted = theSwitch.isOn - }), text: VectorL10n.createRoomEnableEncryption, accessoryType: .none) { - // no-op - } - let section3 = Section(header: VectorL10n.createRoomSectionHeaderEncryption, - rows: [row_3_0], - footer: VectorL10n.createRoomSectionFooterEncryption) - - let row_4_0 = Row(type: .default, text: VectorL10n.createRoomTypePrivate, accessoryType: viewModel.roomCreationParameters.isPublic ? .none : .checkmark) { - self.viewModel.roomCreationParameters.isPublic = false - self.updateSections() - } - let row_4_1 = Row(type: .default, text: VectorL10n.createRoomTypePublic, accessoryType: viewModel.roomCreationParameters.isPublic ? .checkmark : .none) { - self.viewModel.roomCreationParameters.isPublic = true - self.updateSections() - // scroll bottom to show user new fields - DispatchQueue.main.async { - self.mainTableView.scrollToRow(at: IndexPath(row: 0, section: 6), at: .bottom, animated: true) + var section3: Section? + if RiotSettings.shared.roomCreationScreenForcedEncryptionMode == .none { + let row_3_0 = Row(type: .withSwitch(isOn: viewModel.roomCreationParameters.isEncrypted, onValueChanged: { (theSwitch) in + self.viewModel.roomCreationParameters.isEncrypted = theSwitch.isOn + }), text: VectorL10n.createRoomEnableEncryption, accessoryType: .none) { + // no-op } + section3 = Section(header: VectorL10n.createRoomSectionHeaderEncryption, + rows: [row_3_0], + footer: VectorL10n.createRoomSectionFooterEncryption) + } + + var section4: Section? + if RiotSettings.shared.roomCreationScreenForcedRoomType == .none { + let row_4_0 = Row(type: .default, text: VectorL10n.createRoomTypePrivate, accessoryType: viewModel.roomCreationParameters.isPublic ? .none : .checkmark) { + self.viewModel.roomCreationParameters.isPublic = false + self.updateSections() + } + let row_4_1 = Row(type: .default, text: VectorL10n.createRoomTypePublic, accessoryType: viewModel.roomCreationParameters.isPublic ? .checkmark : .none) { + self.viewModel.roomCreationParameters.isPublic = true + self.updateSections() + // scroll bottom to show user new fields + DispatchQueue.main.async { + self.mainTableView.scrollToRow(at: IndexPath(row: 0, section: 6), at: .bottom, animated: true) + } + } + section4 = Section(header: VectorL10n.createRoomSectionHeaderType, + rows: [row_4_0, row_4_1], + footer: VectorL10n.createRoomSectionFooterType) } - let section4 = Section(header: VectorL10n.createRoomSectionHeaderType, - rows: [row_4_0, row_4_1], - footer: VectorL10n.createRoomSectionFooterType) var tmpSections: [Section] = [ section0, section1, - section2, - section3, - section4 + section2 ] + if let section3 = section3 { + tmpSections.append(section3) + } + + if let section4 = section4 { + tmpSections.append(section4) + } + if viewModel.roomCreationParameters.isPublic { let row_5_0 = Row(type: .withSwitch(isOn: viewModel.roomCreationParameters.showInDirectory, onValueChanged: { (theSwitch) in self.viewModel.roomCreationParameters.showInDirectory = theSwitch.isOn diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift index 37ed2bed9..b4f919239 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift @@ -39,7 +39,8 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType { init(session: MXSession) { self.session = session - roomCreationParameters.isEncrypted = session.vc_isE2EByDefaultEnabledByHSAdmin() + roomCreationParameters.isEncrypted = session.vc_isE2EByDefaultEnabledByHSAdmin() && RiotSettings.shared.roomCreationScreenForcedEncryptionMode != .disabled + roomCreationParameters.isPublic = RiotSettings.shared.roomCreationScreenForcedRoomType == .publicRoom } deinit { From ef7347f0bcdb749b3b59995873785745edaaebf0 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Sat, 3 Apr 2021 00:03:57 +0200 Subject: [PATCH 41/78] Switching composer between text mode & action mode - Tweaked animation speed --- Riot/Modules/Room/Views/InputToolbar/RoomActionsBar.swift | 8 +++++--- .../Room/Views/InputToolbar/RoomInputToolbarView.m | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomActionsBar.swift b/Riot/Modules/Room/Views/InputToolbar/RoomActionsBar.swift index 0cf11e292..3f78b9cb3 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomActionsBar.swift +++ b/Riot/Modules/Room/Views/InputToolbar/RoomActionsBar.swift @@ -99,7 +99,7 @@ import UIKit button.transform = CGAffineTransform(translationX: 0, y: self.bounds.height) } for (index, button) in actionButtons.enumerated() { - UIView.animate(withDuration: 0.38, delay: 0.05 * Double(index), usingSpringWithDamping: 0.5, initialSpringVelocity: 7, options: .curveEaseInOut) { + UIView.animate(withDuration: 0.3, delay: 0.05 * Double(index), usingSpringWithDamping: 0.45, initialSpringVelocity: 11, options: .curveEaseInOut) { button.transform = CGAffineTransform.identity } completion: { (finished) in completion?(finished) @@ -107,10 +107,12 @@ import UIKit } } else { for (index, button) in actionButtons.enumerated() { - UIView.animate(withDuration: 0.3, delay: 0.05 * Double(index), options: .curveEaseInOut) { + UIView.animate(withDuration: 0.25, delay: 0.05 * Double(index), options: .curveEaseInOut) { button.transform = CGAffineTransform(translationX: 0, y: self.bounds.height) } completion: { (finished) in - completion?(finished) + if index == self.actionButtons.count - 1 { + completion?(finished) + } } } } diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index 5997ce8bb..c76bbd038 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -201,7 +201,7 @@ const double RoomInputToolbarViewContextBarHeight = 30; if (self.mainToolbarHeightConstraint.constant != updatedHeight) { - [UIView animateWithDuration:.3 animations:^{ + [UIView animateWithDuration:.15 animations:^{ self.mainToolbarHeightConstraint.constant = updatedHeight; [self layoutIfNeeded]; From 90412e33937d15068a573a240ff036c816a131bc Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Tue, 6 Apr 2021 10:47:54 +0200 Subject: [PATCH 42/78] Possibility to lock some room creation parameters from settings - Update after review --- Config/BuildSettings.swift | 18 ++----- Riot/Managers/Settings/RiotSettings.swift | 49 ++++++++++++++----- .../EnterNewRoomDetailsViewController.swift | 4 +- .../EnterNewRoomDetailsViewModel.swift | 4 +- 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 57285d9eb..0d46526d1 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -243,20 +243,10 @@ final class BuildSettings: NSObject { // MARK: - Room Creation Screen - enum ForcedEncryptionMode: String { - case none = "none" - case disabled = "disabled" - case enabled = "enabled" - } - - enum ForcedRoomType: String { - case none = "none" - case privateRoom = "privateRoom" - case publicRoom = "publicRoom" - } - - static let roomCreationScreenForcedEncryptionMode: ForcedEncryptionMode = .none - static let roomCreationScreenForcedRoomType: ForcedRoomType = .none + static let roomCreationScreenAllowEncryptionConfiguration: Bool = true + static let roomCreationScreenEncryptionEnabled: Bool = true + static let roomCreationScreenAllowRoomTypeConfiguration: Bool = true + static let roomCreationScreenRoomIsPublic: Bool = false // MARK: - Room Settings Screen diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index f2b43fa5f..314541939 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -47,8 +47,10 @@ final class RiotSettings: NSObject { static let settingsSecurityScreenShowCryptographyInfo = "settingsSecurityScreenShowCryptographyInfo" static let settingsSecurityScreenShowCryptographyExport = "settingsSecurityScreenShowCryptographyExport" static let settingsSecurityScreenShowAdvancedUnverifiedDevices = "settingsSecurityScreenShowAdvancedBlacklistUnverifiedDevices" - static let roomCreationScreenForcedEncryptionMode = "roomCreationScreenForcedEncryptionMode" - static let roomCreationScreenForcedRoomType = "roomCreationScreenForcedRoomType" + static let roomCreationScreenAllowEncryptionConfiguration = "roomCreationScreenAllowEncryptionConfiguration" + static let roomCreationScreenEncryptionEnabled = "roomCreationScreenEncryptionEnabled" + static let roomCreationScreenAllowRoomTypeConfiguration = "roomCreationScreenAllowRoomTypeConfiguration" + static let roomCreationScreenRoomIsPublic = "roomCreationScreenRoomIsPublic" } static let shared = RiotSettings() @@ -239,22 +241,45 @@ final class RiotSettings: NSObject { } // MARK: - Room Creation Screen - - var roomCreationScreenForcedEncryptionMode: BuildSettings.ForcedEncryptionMode { + + var roomCreationScreenAllowEncryptionConfiguration: Bool { get { - let rawValue = defaults.string(forKey: UserDefaultsKeys.roomCreationScreenForcedEncryptionMode) ?? BuildSettings.roomCreationScreenForcedEncryptionMode.rawValue - return BuildSettings.ForcedEncryptionMode(rawValue: rawValue) ?? .none + guard defaults.object(forKey: UserDefaultsKeys.roomCreationScreenAllowEncryptionConfiguration) != nil else { + return BuildSettings.roomCreationScreenAllowEncryptionConfiguration + } + return defaults.bool(forKey: UserDefaultsKeys.roomCreationScreenAllowEncryptionConfiguration) } set { - defaults.set(newValue.rawValue, forKey: UserDefaultsKeys.roomCreationScreenForcedEncryptionMode) + defaults.set(newValue, forKey: UserDefaultsKeys.roomCreationScreenAllowEncryptionConfiguration) } } - - var roomCreationScreenForcedRoomType: BuildSettings.ForcedRoomType { + var roomCreationScreenEncryptionEnabled: Bool { get { - let rawValue = defaults.string(forKey: UserDefaultsKeys.roomCreationScreenForcedRoomType) ?? BuildSettings.roomCreationScreenForcedRoomType.rawValue - return BuildSettings.ForcedRoomType(rawValue: rawValue) ?? .none + guard defaults.object(forKey: UserDefaultsKeys.roomCreationScreenEncryptionEnabled) != nil else { + return BuildSettings.roomCreationScreenEncryptionEnabled + } + return defaults.bool(forKey: UserDefaultsKeys.roomCreationScreenEncryptionEnabled) } set { - defaults.set(newValue.rawValue, forKey: UserDefaultsKeys.roomCreationScreenForcedRoomType) + defaults.set(newValue, forKey: UserDefaultsKeys.roomCreationScreenEncryptionEnabled) + } + } + var roomCreationScreenAllowRoomTypeConfiguration: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.roomCreationScreenAllowRoomTypeConfiguration) != nil else { + return BuildSettings.roomCreationScreenAllowRoomTypeConfiguration + } + return defaults.bool(forKey: UserDefaultsKeys.roomCreationScreenAllowRoomTypeConfiguration) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.roomCreationScreenAllowRoomTypeConfiguration) + } + } + var roomCreationScreenRoomIsPublic: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.roomCreationScreenRoomIsPublic) != nil else { + return BuildSettings.roomCreationScreenRoomIsPublic + } + return defaults.bool(forKey: UserDefaultsKeys.roomCreationScreenRoomIsPublic) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.roomCreationScreenRoomIsPublic) } } diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift index 5e0dc933e..d177dcd52 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift @@ -114,7 +114,7 @@ final class EnterNewRoomDetailsViewController: UIViewController { footer: nil) var section3: Section? - if RiotSettings.shared.roomCreationScreenForcedEncryptionMode == .none { + if RiotSettings.shared.roomCreationScreenAllowEncryptionConfiguration { let row_3_0 = Row(type: .withSwitch(isOn: viewModel.roomCreationParameters.isEncrypted, onValueChanged: { (theSwitch) in self.viewModel.roomCreationParameters.isEncrypted = theSwitch.isOn }), text: VectorL10n.createRoomEnableEncryption, accessoryType: .none) { @@ -126,7 +126,7 @@ final class EnterNewRoomDetailsViewController: UIViewController { } var section4: Section? - if RiotSettings.shared.roomCreationScreenForcedRoomType == .none { + if RiotSettings.shared.roomCreationScreenAllowRoomTypeConfiguration { let row_4_0 = Row(type: .default, text: VectorL10n.createRoomTypePrivate, accessoryType: viewModel.roomCreationParameters.isPublic ? .none : .checkmark) { self.viewModel.roomCreationParameters.isPublic = false self.updateSections() diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift index b4f919239..709623c18 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift @@ -39,8 +39,8 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType { init(session: MXSession) { self.session = session - roomCreationParameters.isEncrypted = session.vc_isE2EByDefaultEnabledByHSAdmin() && RiotSettings.shared.roomCreationScreenForcedEncryptionMode != .disabled - roomCreationParameters.isPublic = RiotSettings.shared.roomCreationScreenForcedRoomType == .publicRoom + roomCreationParameters.isEncrypted = session.vc_isE2EByDefaultEnabledByHSAdmin() && RiotSettings.shared.roomCreationScreenEncryptionEnabled + roomCreationParameters.isPublic = RiotSettings.shared.roomCreationScreenRoomIsPublic } deinit { From 57517d6aacdde49d839db13d8351d3460831b818 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Tue, 6 Apr 2021 10:51:07 +0200 Subject: [PATCH 43/78] Possibility to lock some room creation parameters from settings - Update after review --- Config/BuildSettings.swift | 2 +- Riot/Managers/Settings/RiotSettings.swift | 12 ++++++------ .../EnterNewRoomDetailsViewModel.swift | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 0d46526d1..100bf20ee 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -244,7 +244,7 @@ final class BuildSettings: NSObject { // MARK: - Room Creation Screen static let roomCreationScreenAllowEncryptionConfiguration: Bool = true - static let roomCreationScreenEncryptionEnabled: Bool = true + static let roomCreationScreenRoomIsEncrypted: Bool = true static let roomCreationScreenAllowRoomTypeConfiguration: Bool = true static let roomCreationScreenRoomIsPublic: Bool = false diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index 314541939..13910303d 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -48,7 +48,7 @@ final class RiotSettings: NSObject { static let settingsSecurityScreenShowCryptographyExport = "settingsSecurityScreenShowCryptographyExport" static let settingsSecurityScreenShowAdvancedUnverifiedDevices = "settingsSecurityScreenShowAdvancedBlacklistUnverifiedDevices" static let roomCreationScreenAllowEncryptionConfiguration = "roomCreationScreenAllowEncryptionConfiguration" - static let roomCreationScreenEncryptionEnabled = "roomCreationScreenEncryptionEnabled" + static let roomCreationScreenRoomIsEncrypted = "roomCreationScreenRoomIsEncrypted" static let roomCreationScreenAllowRoomTypeConfiguration = "roomCreationScreenAllowRoomTypeConfiguration" static let roomCreationScreenRoomIsPublic = "roomCreationScreenRoomIsPublic" } @@ -252,14 +252,14 @@ final class RiotSettings: NSObject { defaults.set(newValue, forKey: UserDefaultsKeys.roomCreationScreenAllowEncryptionConfiguration) } } - var roomCreationScreenEncryptionEnabled: Bool { + var roomCreationScreenRoomIsEncrypted: Bool { get { - guard defaults.object(forKey: UserDefaultsKeys.roomCreationScreenEncryptionEnabled) != nil else { - return BuildSettings.roomCreationScreenEncryptionEnabled + guard defaults.object(forKey: UserDefaultsKeys.roomCreationScreenRoomIsEncrypted) != nil else { + return BuildSettings.roomCreationScreenRoomIsEncrypted } - return defaults.bool(forKey: UserDefaultsKeys.roomCreationScreenEncryptionEnabled) + return defaults.bool(forKey: UserDefaultsKeys.roomCreationScreenRoomIsEncrypted) } set { - defaults.set(newValue, forKey: UserDefaultsKeys.roomCreationScreenEncryptionEnabled) + defaults.set(newValue, forKey: UserDefaultsKeys.roomCreationScreenRoomIsEncrypted) } } var roomCreationScreenAllowRoomTypeConfiguration: Bool { diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift index 709623c18..a9a48bdde 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift @@ -39,7 +39,7 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType { init(session: MXSession) { self.session = session - roomCreationParameters.isEncrypted = session.vc_isE2EByDefaultEnabledByHSAdmin() && RiotSettings.shared.roomCreationScreenEncryptionEnabled + roomCreationParameters.isEncrypted = session.vc_isE2EByDefaultEnabledByHSAdmin() && RiotSettings.shared.roomCreationScreenRoomIsEncrypted roomCreationParameters.isPublic = RiotSettings.shared.roomCreationScreenRoomIsPublic } From 96408aea02782b2b0c820c8ec5a285ae2b79bec4 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Tue, 6 Apr 2021 13:09:30 +0200 Subject: [PATCH 44/78] Limit typing notifications timeline jumps --- Riot/Modules/Room/DataSources/RoomDataSource.h | 2 ++ Riot/Modules/Room/DataSources/RoomDataSource.m | 4 ++++ Riot/Modules/Room/RoomViewController.m | 17 +++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.h b/Riot/Modules/Room/DataSources/RoomDataSource.h index ced9e687a..d4ec45c91 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.h +++ b/Riot/Modules/Room/DataSources/RoomDataSource.h @@ -98,6 +98,8 @@ success:(void(^)(void))success failure:(void(^)(NSError*))failure; +- (void)resetTypingNotification; + @end @protocol RoomDataSourceDelegate diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 8cef6f9b2..2122a398d 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -950,6 +950,10 @@ }]; } +- (void)resetTypingNotification { + self.currentTypingUsers = nil; +} + #pragma - Accessibility - (void)setupAccessibilityForCell:(MXKRoomBubbleTableViewCell *)cell withCellData:(RoomBubbleCellData*)cellData diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 6e5173a6f..9ac281f7d 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -511,6 +511,12 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo [self refreshRoomInputToolbar]; } + // Reset typing notification in order to remove the allocated space + if ([self.roomDataSource isKindOfClass:RoomDataSource.class]) + { + [((RoomDataSource*)self.roomDataSource) resetTypingNotification]; + } + [self listenTypingNotifications]; [self listenCallNotifications]; [self listenWidgetNotifications]; @@ -1338,6 +1344,17 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo _scrollToBottomHidden = scrollToBottomHidden; } + if (!_scrollToBottomHidden && [self.roomDataSource isKindOfClass:RoomDataSource.class]) + { + RoomDataSource *roomDataSource = (RoomDataSource *) self.roomDataSource; + if (roomDataSource.currentTypingUsers && !roomDataSource.currentTypingUsers.count) + { + [roomDataSource resetTypingNotification]; + NSInteger count = [roomDataSource tableView:self.bubblesTableView numberOfRowsInSection:0]; + [self.bubblesTableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:count inSection:0]] withRowAnimation:UITableViewRowAnimationNone]; + } + } + [UIView animateWithDuration:.2 animations:^{ self.scrollToBottomBadgeLabel.alpha = (scrollToBottomHidden || !self.scrollToBottomBadgeLabel.text) ? 0 : 1; self.scrollToBottomButton.alpha = scrollToBottomHidden ? 0 : 1; From f222f468104aca0690f10685fd8e3ca67f2f4485 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Tue, 6 Apr 2021 14:32:25 +0200 Subject: [PATCH 45/78] Consider displaying names in typing notifications --- Riot/Assets/en.lproj/Vector.strings | 2 ++ Riot/Generated/Strings.swift | 4 +++ .../BubbleCells/RoomTypingBubbleCell.swift | 26 +++++++++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 40ab6cd46..6b58da5c1 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -406,6 +406,8 @@ Tap the + to start adding people."; "external_link_confirmation_title" = "Double-check this link"; "external_link_confirmation_message" = "The link %@ is taking you to another site: %@\n\nAre you sure you want to continue?"; +"room_multiple_typing_notification" = "%@ and others"; + // Unknown devices "unknown_devices_alert_title" = "Room contains unknown sessions"; "unknown_devices_alert" = "This room contains unknown sessions which have not been verified.\nThis means there is no guarantee that the sessions belong to the users they claim to.\nWe recommend you go through the verification process for each session before continuing, but you can resend the message without verifying if you prefer."; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 0d5515a5a..ab9947434 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -2938,6 +2938,10 @@ internal enum VectorL10n { internal static var roomMessageUnableOpenLinkErrorMessage: String { return VectorL10n.tr("Vector", "room_message_unable_open_link_error_message") } + /// %@ and others + internal static func roomMultipleTypingNotification(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_multiple_typing_notification", p1) + } /// %d new message internal static func roomNewMessageNotification(_ p1: Int) -> String { return VectorL10n.tr("Vector", "room_new_message_notification", p1) diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift b/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift index 0fdbc7a8e..ae06ed057 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift +++ b/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift @@ -32,10 +32,17 @@ class RoomTypingBubbleCell: UITableViewCell { // MARK: - members private var userPictureViews: Array = Array() - private var typingUsers: Array = Array() // MARK: - Lifecycle + override func awakeFromNib() { + super.awakeFromNib() + + additionalUsersLabel?.textColor = ThemeService.shared().theme.textSecondaryColor + dotsView?.highlightedDotColor = ThemeService.shared().theme.textTertiaryColor + dotsView?.dotColor = ThemeService.shared().theme.textSecondaryColor + } + override func prepareForReuse() { super.prepareForReuse() @@ -97,7 +104,22 @@ class RoomTypingBubbleCell: UITableViewCell { self.contentView.addSubview(pictureView) } - additionalUsersLabel?.text = typingUsers.count <= 4 ? nil : "+\(typingUsers.count - 4)" + switch typingUsers.count { + case 0: + additionalUsersLabel?.text = nil + case 1: + additionalUsersLabel?.text = firstUserNameFor(typingUsers) + default: + additionalUsersLabel?.text = VectorL10n.roomMultipleTypingNotification(firstUserNameFor(typingUsers) ?? "") + } self.setNeedsLayout() } + + private func firstUserNameFor(_ typingUsers: Array) -> String? { + guard let firstUser = typingUsers.first else { + return nil + } + + return firstUser.displayname.isEmptyOrNil ? firstUser.userId : firstUser.displayname + } } From 88cf035fe6271ef7267cf871c9b860c2c021726e Mon Sep 17 00:00:00 2001 From: Hivaa Date: Wed, 7 Apr 2021 04:51:25 +0000 Subject: [PATCH 46/78] Added translation using Weblate (Persian) --- Riot/Assets/fa.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) create mode 100644 Riot/Assets/fa.lproj/Vector.strings diff --git a/Riot/Assets/fa.lproj/Vector.strings b/Riot/Assets/fa.lproj/Vector.strings new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/Riot/Assets/fa.lproj/Vector.strings @@ -0,0 +1 @@ + From 0a5e16965cd91c41777be29fe242e0c8c59601b7 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Wed, 7 Apr 2021 09:32:57 +0200 Subject: [PATCH 47/78] Switching composer between text mode & action mode - Tweaked animation speed --- Riot/Modules/Room/RoomViewController.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 87f9f09a8..99d67c067 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -286,6 +286,8 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo { [super finalizeInit]; + self.resizeComposerAnimationDuration = 0.15; + // Setup `MXKViewControllerHandling` properties self.enableBarTintColorStatusChange = NO; self.rageShakeManager = [RageShakeManager sharedManager]; From fb5dca4c24ce5619412ab9f1a28939fd21a5177d Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Wed, 7 Apr 2021 10:28:35 +0200 Subject: [PATCH 48/78] Switching composer between text mode & action mode - Tweaked animation speed --- Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index c76bbd038..aef222b14 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -393,7 +393,7 @@ const double RoomInputToolbarViewContextBarHeight = 30; }]; } - [UIView animateWithDuration:.4 delay:0 usingSpringWithDamping:0.45 initialSpringVelocity:5 options:UIViewAnimationOptionCurveEaseIn animations:^{ + [UIView animateWithDuration:.4 delay:0 usingSpringWithDamping:0.45 initialSpringVelocity:7 options:UIViewAnimationOptionCurveEaseIn animations:^{ self.attachMediaButton.transform = actionMenuOpened ? CGAffineTransformMakeRotation(M_PI * 3 / 4) : CGAffineTransformIdentity; } completion:^(BOOL finished) { }]; From eb9678e30beba5b7ad37ce7b747e7e0938a82fda Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Wed, 7 Apr 2021 11:14:08 +0200 Subject: [PATCH 49/78] Crash in [RoomViewController setupActions] --- Riot/Modules/Room/RoomViewController.m | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 99d67c067..6a14a78c2 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -1702,6 +1702,10 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo } - (void)setupActions { + if (![self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { + return; + } + RoomInputToolbarView *roomInputView = ((RoomInputToolbarView *) self.inputToolbarView); __weak typeof(self) weakSelf = self; roomInputView.actionsBar.actionItems = @[ @@ -1709,7 +1713,9 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo if (weakSelf) { typeof(self) self = weakSelf; - roomInputView.actionMenuOpened = NO; + if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { + ((RoomInputToolbarView *) self.inputToolbarView).actionMenuOpened = NO; + } [self showCameraControllerAnimated:YES]; } }], @@ -1717,7 +1723,9 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo if (weakSelf) { typeof(self) self = weakSelf; - roomInputView.actionMenuOpened = NO; + if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { + ((RoomInputToolbarView *) self.inputToolbarView).actionMenuOpened = NO; + } [self showMediaPickerAnimated:YES]; } }], @@ -1725,7 +1733,9 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo if (weakSelf) { typeof(self) self = weakSelf; - roomInputView.actionMenuOpened = NO; + if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { + ((RoomInputToolbarView *) self.inputToolbarView).actionMenuOpened = NO; + } [self roomInputToolbarViewPresentStickerPicker]; } }], @@ -1733,7 +1743,9 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo if (weakSelf) { typeof(self) self = weakSelf; - roomInputView.actionMenuOpened = NO; + if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { + ((RoomInputToolbarView *) self.inputToolbarView).actionMenuOpened = NO; + } [self roomInputToolbarViewDidTapFileUpload]; } }], From 8433048451a66ea02059da5f111be15217229d0f Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Wed, 7 Apr 2021 12:26:51 +0200 Subject: [PATCH 50/78] Crash in [RoomViewController refreshTypingNotification] --- .../Modules/Room/DataSources/TypingUserInfo.h | 34 ++++++++++++++ .../Modules/Room/DataSources/TypingUserInfo.m | 46 +++++++++++++++++++ Riot/Modules/Room/RoomViewController.m | 15 +++++- .../BubbleCells/RoomTypingBubbleCell.swift | 8 ++-- Riot/SupportingFiles/Riot-Bridging-Header.h | 1 + 5 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 Riot/Modules/Room/DataSources/TypingUserInfo.h create mode 100644 Riot/Modules/Room/DataSources/TypingUserInfo.m diff --git a/Riot/Modules/Room/DataSources/TypingUserInfo.h b/Riot/Modules/Room/DataSources/TypingUserInfo.h new file mode 100644 index 000000000..54914dc6e --- /dev/null +++ b/Riot/Modules/Room/DataSources/TypingUserInfo.h @@ -0,0 +1,34 @@ +// +// Copyright 2021 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TypingUserInfo : NSObject + +@property (nonatomic, strong) NSString *userId; +@property (nonatomic, strong, nullable) NSString *displayName; +@property (nonatomic, strong, nullable) NSString *avatarUrl; + +- (instancetype) initWithMember:(MXRoomMember*)member; + +- (instancetype) initWithUserId:(NSString*)userId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Riot/Modules/Room/DataSources/TypingUserInfo.m b/Riot/Modules/Room/DataSources/TypingUserInfo.m new file mode 100644 index 000000000..fa59cde01 --- /dev/null +++ b/Riot/Modules/Room/DataSources/TypingUserInfo.m @@ -0,0 +1,46 @@ +// +// Copyright 2021 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "TypingUserInfo.h" + +@implementation TypingUserInfo + +- (instancetype) initWithMember:(MXRoomMember*)member +{ + self = [self initWithUserId:member.userId]; + + if (self) + { + self.displayName = member.displayname; + self.avatarUrl = member.avatarUrl; + } + + return self; +} + +- (instancetype) initWithUserId:(NSString*)userId +{ + self = [super init]; + + if (self) + { + self.userId = userId; + } + + return self; +} + +@end diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index e3379981e..e7c07ec93 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -124,6 +124,8 @@ #import "SettingsViewController.h" #import "SecurityViewController.h" +#import "TypingUserInfo.h" + #import "Riot-Swift.h" NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNotification"; @@ -4134,8 +4136,17 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo for (NSUInteger i = 0 ; i < currentTypingUsers.count ; i++) { NSString *userId = currentTypingUsers[i]; MXRoomMember* member = [self.roomDataSource.roomState.members memberWithUserId:userId]; - [typingUsers addObject:member]; - needsUpdate = needsUpdate || member.userId != ((MXRoomMember *) roomDataSource.currentTypingUsers[i]).userId; + TypingUserInfo *userInfo; + if (member) + { + userInfo = [[TypingUserInfo alloc] initWithMember: member]; + } + else + { + userInfo = [[TypingUserInfo alloc] initWithUserId: userId]; + } + [typingUsers addObject:userInfo]; + needsUpdate = needsUpdate || userInfo.userId != ((MXRoomMember *) roomDataSource.currentTypingUsers[i]).userId; } if (needsUpdate) diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift b/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift index ae06ed057..551dca621 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift +++ b/Riot/Modules/Room/Views/BubbleCells/RoomTypingBubbleCell.swift @@ -82,7 +82,7 @@ class RoomTypingBubbleCell: UITableViewCell { // MARK: - Business methods - func updateTypingUsers(_ typingUsers: Array, mediaManager: MXMediaManager) { + func updateTypingUsers(_ typingUsers: Array, mediaManager: MXMediaManager) { for pictureView in userPictureViews { pictureView.removeFromSuperview() } @@ -97,7 +97,7 @@ class RoomTypingBubbleCell: UITableViewCell { pictureView.layer.masksToBounds = true pictureView.layer.cornerRadius = pictureView.bounds.midX - let defaultavatarImage = AvatarGenerator.generateAvatar(forMatrixItem: user.userId, withDisplayName: user.displayname) + let defaultavatarImage = AvatarGenerator.generateAvatar(forMatrixItem: user.userId, withDisplayName: user.displayName) pictureView.setImageURI(user.avatarUrl, withType: nil, andImageOrientation: .up, toFitViewSize: pictureView.bounds.size, with: MXThumbnailingMethodCrop, previewImage: defaultavatarImage, mediaManager: mediaManager) userPictureViews.append(pictureView) @@ -115,11 +115,11 @@ class RoomTypingBubbleCell: UITableViewCell { self.setNeedsLayout() } - private func firstUserNameFor(_ typingUsers: Array) -> String? { + private func firstUserNameFor(_ typingUsers: Array) -> String? { guard let firstUser = typingUsers.first else { return nil } - return firstUser.displayname.isEmptyOrNil ? firstUser.userId : firstUser.displayname + return firstUser.displayName.isEmptyOrNil ? firstUser.userId : firstUser.displayName } } diff --git a/Riot/SupportingFiles/Riot-Bridging-Header.h b/Riot/SupportingFiles/Riot-Bridging-Header.h index f8ff71ea5..7daa40759 100644 --- a/Riot/SupportingFiles/Riot-Bridging-Header.h +++ b/Riot/SupportingFiles/Riot-Bridging-Header.h @@ -31,3 +31,4 @@ #import "AuthFallBackViewController.h" #import "CallViewController.h" #import "MatrixContactsDataSource.h" +#import "TypingUserInfo.h" From e31707d97113415b29b21fe8bf26d30ef94e9d1a Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Wed, 7 Apr 2021 13:11:07 +0200 Subject: [PATCH 51/78] too much vertical whitespace when replying --- .../Room/Views/InputToolbar/RoomInputToolbarView.m | 2 +- .../Room/Views/InputToolbar/RoomInputToolbarView.xib | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index aef222b14..ec9afdc99 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -27,7 +27,7 @@ #import "WidgetManager.h" #import "IntegrationManagerViewController.h" -const double RoomInputToolbarViewContextBarHeight = 30; +const double RoomInputToolbarViewContextBarHeight = 20; @interface RoomInputToolbarView() { diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib index e99b0ef66..403a83a59 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib @@ -45,16 +45,16 @@ - +