From c89e1f8fa70836ba7996a8ae1d51cdf910965c5d Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Tue, 3 Mar 2020 14:31:09 +1100 Subject: [PATCH 1/8] minor fix for syncing groups --- SignalServiceKit/src/Messages/OWSMessageHandler.m | 4 ++++ SignalServiceKit/src/Protos/Generated/SignalService.pb.swift | 1 + 2 files changed, 5 insertions(+) diff --git a/SignalServiceKit/src/Messages/OWSMessageHandler.m b/SignalServiceKit/src/Messages/OWSMessageHandler.m index eb0853815..d56e30afc 100644 --- a/SignalServiceKit/src/Messages/OWSMessageHandler.m +++ b/SignalServiceKit/src/Messages/OWSMessageHandler.m @@ -172,6 +172,10 @@ NSString *envelopeAddress(SSKProtoEnvelope *envelope) [description appendString:verifiedString]; } else if (syncMessage.contacts) { [description appendString:@"Contacts"]; + } else if (syncMessage.groups) { + [description appendString:@"ClosedGroups"]; + } else if (syncMessage.openGroups) { + [description appendString:@"OpenGroups"]; } else { [description appendString:@"Unknown"]; } diff --git a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift index 44f689eed..0ae7a2464 100644 --- a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift +++ b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift @@ -4348,6 +4348,7 @@ extension SignalServiceProtos_SyncMessage: SwiftProtobuf.Message, SwiftProtobuf. 7: .same(proto: "verified"), 9: .same(proto: "configuration"), 8: .same(proto: "padding"), + 100: .same(proto: "openGroups"), ] fileprivate class _StorageClass { From fcaff5dac7ab9b5630a416ce9b0b5380726148a6 Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Tue, 3 Mar 2020 14:32:16 +1100 Subject: [PATCH 2/8] Merge branch 'dev' of https://github.com/loki-project/loki-messenger-ios into dev --- Signal.xcodeproj/project.pbxproj | 12 +- .../Loki V2/Group.imageset/Contents.json | 12 + .../Loki V2/Group.imageset/Group.pdf | Bin 0 -> 30357 bytes .../Loki V2/Message.imageset/Contents.json | 12 + .../Loki V2/Message.imageset/Message.pdf | Bin 0 -> 5742 bytes .../Loki V2/Plus.imageset/Contents.json | 12 + .../Loki V2/Plus.imageset/Plus.pdf | Bin 0 -> 4018 bytes Signal/Signal-Info.plist | 2 +- Signal/src/AppDelegate.m | 2 +- Signal/src/Loki/Components/Button.swift | 2 +- .../Loki/Components/ConversationCell.swift | 9 +- .../Components/NewConversationButtonSet.swift | 310 ++++++++++++++++++ Signal/src/Loki/Style Guide/Colors.swift | 1 + Signal/src/Loki/Style Guide/Gradients.swift | 3 +- Signal/src/Loki/Style Guide/Values.swift | 3 +- .../src/Loki/Utilities/GeneralUtilities.swift | 29 -- .../src/Loki/Utilities/UIImage+Scaling.swift | 17 + .../Loki/View Controllers/DeviceLinksVC.swift | 2 +- Signal/src/Loki/View Controllers/HomeVC.swift | 66 ++-- .../View Controllers/NewClosedGroupVC.swift | 11 +- Signal/src/Loki/View Controllers/SeedVC.swift | 5 +- .../ConversationViewController.m | 8 - 22 files changed, 421 insertions(+), 97 deletions(-) create mode 100644 Signal/Images.xcassets/Loki V2/Group.imageset/Contents.json create mode 100644 Signal/Images.xcassets/Loki V2/Group.imageset/Group.pdf create mode 100644 Signal/Images.xcassets/Loki V2/Message.imageset/Contents.json create mode 100644 Signal/Images.xcassets/Loki V2/Message.imageset/Message.pdf create mode 100644 Signal/Images.xcassets/Loki V2/Plus.imageset/Contents.json create mode 100644 Signal/Images.xcassets/Loki V2/Plus.imageset/Plus.pdf create mode 100644 Signal/src/Loki/Components/NewConversationButtonSet.swift delete mode 100644 Signal/src/Loki/Utilities/GeneralUtilities.swift create mode 100644 Signal/src/Loki/Utilities/UIImage+Scaling.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 297b2117f..fccbb059f 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -573,6 +573,8 @@ B82B408E239DC00D00A248E7 /* DisplayNameVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */; }; B82B4090239DD75000A248E7 /* RestoreVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408F239DD75000A248E7 /* RestoreVC.swift */; }; B82B4094239DF15900A248E7 /* ConversationTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B4093239DF15900A248E7 /* ConversationTitleView.swift */; }; + B83F2B86240C7B8F000A54AB /* NewConversationButtonSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B83F2B85240C7B8F000A54AB /* NewConversationButtonSet.swift */; }; + B83F2B88240CB75A000A54AB /* UIImage+Scaling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */; }; B846365B22B7418B00AF1514 /* Identicon+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */; }; B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84664F4235022F30083A1CD /* MentionUtilities.swift */; }; B847570323D5698100759540 /* LokiPushNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B847570223D5698100759540 /* LokiPushNotificationManager.swift */; }; @@ -587,7 +589,6 @@ B8544E3523D5201400299F14 /* UIView+Constraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */; }; B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; }; B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; }; - B8783E9C23EB8DDE00404FB8 /* GeneralUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8783E9B23EB8DDE00404FB8 /* GeneralUtilities.swift */; }; B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */; }; B885D5F4233491AB00EE0D8E /* DeviceLinkingModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */; }; B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B886B4A62398B23E00211ABE /* QRCodeVC.swift */; }; @@ -1416,6 +1417,8 @@ B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayNameVC.swift; sourceTree = ""; }; B82B408F239DD75000A248E7 /* RestoreVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreVC.swift; sourceTree = ""; }; B82B4093239DF15900A248E7 /* ConversationTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationTitleView.swift; sourceTree = ""; }; + B83F2B85240C7B8F000A54AB /* NewConversationButtonSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationButtonSet.swift; sourceTree = ""; }; + B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Scaling.swift"; sourceTree = ""; }; B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Identicon+ObjC.swift"; sourceTree = ""; }; B84664F4235022F30083A1CD /* MentionUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionUtilities.swift; sourceTree = ""; }; B847570023D568EB00759540 /* SignalServiceKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SignalServiceKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1429,7 +1432,6 @@ B8544E3223D50E4900299F14 /* AppearanceUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceUtilities.swift; sourceTree = ""; }; B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = ""; }; B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = ""; }; - B8783E9B23EB8DDE00404FB8 /* GeneralUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralUtilities.swift; sourceTree = ""; }; B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Interaction.swift"; sourceTree = ""; }; B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceLinkingModal.swift; sourceTree = ""; }; B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Constraints.swift"; sourceTree = ""; }; @@ -2786,6 +2788,7 @@ B8162F0422892C5F00D46544 /* FriendRequestViewDelegate.swift */, B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */, B8B26C90234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift */, + B83F2B85240C7B8F000A54AB /* NewConversationButtonSet.swift */, B8BB82B02390C37000BA5194 /* SearchBar.swift */, B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */, B85357C023A1B81900AAF6CD /* SeedReminderViewDelegate.swift */, @@ -2802,11 +2805,11 @@ children = ( B8544E3223D50E4900299F14 /* AppearanceUtilities.swift */, B8544E3023D16CA500299F14 /* DeviceUtilities.swift */, - B8783E9B23EB8DDE00404FB8 /* GeneralUtilities.swift */, B847570223D5698100759540 /* LokiPushNotificationManager.swift */, B84664F4235022F30083A1CD /* MentionUtilities.swift */, B886B4A82398BA1500211ABE /* QRCode.swift */, B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */, + B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */, ); path = Utilities; sourceTree = ""; @@ -3846,6 +3849,7 @@ 340FC8B8204DAC8D007AEB0F /* AddToGroupViewController.m in Sources */, 341F2C0F1F2B8AE700D07D6B /* DebugUIMisc.m in Sources */, B893063F2383961A005EAA8E /* ScanQRCodeWrapperVC.swift in Sources */, + B83F2B86240C7B8F000A54AB /* NewConversationButtonSet.swift in Sources */, 340FC8AF204DAC8D007AEB0F /* OWSLinkDeviceViewController.m in Sources */, 34E3EF0D1EFC235B007F6822 /* DebugUIDiskUsage.m in Sources */, 454A84042059C787008B8C75 /* MediaTileViewController.swift in Sources */, @@ -3883,6 +3887,7 @@ B885D5F4233491AB00EE0D8E /* DeviceLinkingModal.swift in Sources */, 45DF5DF21DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift in Sources */, 451166C01FD86B98000739BA /* AccountManager.swift in Sources */, + B83F2B88240CB75A000A54AB /* UIImage+Scaling.swift in Sources */, 3430FE181F7751D4000EC51B /* GiphyAPI.swift in Sources */, 4C2F454F214C00E1004871FF /* AvatarTableViewCell.swift in Sources */, 346E9D5421B040B700562252 /* RegistrationController.swift in Sources */, @@ -3900,7 +3905,6 @@ 3403B95D20EA9527001A1F44 /* OWSContactShareButtonsView.m in Sources */, 34B0796D1FCF46B100E248C2 /* MainAppContext.m in Sources */, 34E3EF101EFC2684007F6822 /* DebugUIPage.m in Sources */, - B8783E9C23EB8DDE00404FB8 /* GeneralUtilities.swift in Sources */, B80C6B572384A56D00FDBC8B /* DeviceLinksVC.swift in Sources */, 34A8B3512190A40E00218A25 /* MediaAlbumCellView.swift in Sources */, 34D1F0AE1F867BFC0066283D /* OWSMessageCell.m in Sources */, diff --git a/Signal/Images.xcassets/Loki V2/Group.imageset/Contents.json b/Signal/Images.xcassets/Loki V2/Group.imageset/Contents.json new file mode 100644 index 000000000..10de2f9a6 --- /dev/null +++ b/Signal/Images.xcassets/Loki V2/Group.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Group.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/Loki V2/Group.imageset/Group.pdf b/Signal/Images.xcassets/Loki V2/Group.imageset/Group.pdf new file mode 100644 index 0000000000000000000000000000000000000000..be41ce92df6735b7f431981cdb1d5fbb96bb73e5 GIT binary patch literal 30357 zcma&NWmud|vo4Ce2iFO~-6gm~g1fuBySqCK7Tn$4-66QUI|SE5-gkXxt$p^su6_Q@ z(_P(N)m>dZbKlj^kje^+(lXL9!;$vy-|U|j{mGv0AB1B8FaT`y&EdGY0rZka)+Uao z0M<{DJb+%*%+k@w{_|<6=V&BsWME@x1mNX`b8xgb(zAkd1tpCVfa_&I7kn}MqD;awS)0| z5ya-QPS73+Y9nxb4rx=W@oy^cupp8iVrKhO@15fP#{|a;&dA#Ezf69X{?7F8^!`2> z8Cd={{w*^7k3}fD*%|@pW%W${`Ls8(b_6j0yC8WZ2OB4Q10x3j+rI)K8*9hUb_c*; zC&>R@+<)o&+x{P?$lBW&C>S{cG(MFIivs8sja(f8S^#<>8%rB|1zSA>BfwwZ2{|wV z*#Aq-r_Vk$|07m3U;=Rd6~poJ{tp9;4FAKzf4QBJ@t@)J(x3CC7x?RY>rcl6=!K1( z%?ym>#RUKFTkkbQ;;S$GykPDOWl?4k9By$#q6h%j+ zR5n!X1{Dh-HV{FF5AfGtguOwL9qf@47TI5qw1B;=VEwa^_BN!pxqR5PxNLIiwb%+G z`|1zT>!=Fq%aWstesS8PiS}iz{{aje1r$jYw0qmw7((n(7;NsfgC{k$SbVVV{({ce zux*v5OP0)G;ln46m|chj3ItAMo{ig^3~L`OQK4=Chv@@i^frl-DVUK7GY2-eoOrjt z%JRPGfVyoY1xU7+gR3@))6S9^*(SS@M!KCB36Z0jLb_#==Lv1pp(v3Cvj0FaJNuX+ z3BrJ$sxy8_mSY*lB!Z4}5q1Lx0+ZO!*=~;%@x*DG3F2C4vKI~!CZS)5x|m|Ne>80T zd|jvTq|Zp5*eA6e>MA*PTaOUUfaP0dlPm&Cs%DjH8h2iSe}1mEaA5k>wY*8_t?Ll|tS z9pAr$Kn@>qc4p}Snq@&peZ>Bf|E70mgNI(G4*%vFfR=l4wlgV{)Azp$wAr||eV z0upe_@a+NRW4D{rY+|I3CL^mG>pJL2N$w~SHZaVzEr{DsHI~j0F3@Lw+XtJC92&>_ zmj$}1lMaYi5T|Uj1oW^C4~Wn=a1v`icQCJ04HQPW;m&r`inuC=7$*1k!)GFR&2Z~& zNdaPogL{M+LwnL(8l1e%K+7I5v{-1zY&aG_ItDNrT||6eW?S$L07#&(I0|?&IGs3{ z89zj=0PhkAoB-nzsB?h1Ey$do`!s~KKbtL-CWJ~iqAfgvuNXgEaL-Q$&<#H`82>92 zj9h`zSj>FnRKW!lJarg1ApuzqbnpS8uvnCM{-+3kTth2-d^U}FyA?1rdC zZtE-SE$IJXm`^cCQI^CyU^7Nx_IvU#(4Vg4OSJsWu)u;y6fG_Et zap5Bkb>r;d+j=)+Ycg50Rzfr(FGjZcp7tSblU=*EQ*EGM`Xl!^T2#aqHLAr>u!y5@?seCzDP< zmk1`ok|&`|t@l%uFela`<00$#nlSR+P|O}%jbB@)k|YbDLy8>FC!0t?hA#61Uqo zLzhLa^VR3ad*N#{m|PEkkAh!bwQ`Q=c6~2Tkc3EDZjyeeewu!me!vdvfE;OiykC41 znoJ4*arF#o!B`xN{5`?&jc zqw-L_47!iZ@;C*N36X7)F@IPNTBdfVR@th|v_IM@lb)!osXUX2lZ=x|{Gt_4r@=1Jq}in4A}I5dUX@;_O71d;S1zj}E5A#uOT%Zhdm6B! z8zGS1Q=Ai?)1{ZGm$z;3tu(IeH&vc`9@ij;L8D#xHSewlS#{ViiFk>q5h;bGxeZ0# z)~Wg_1d9VxGL|ydwxQhWH8r(z(S>ZzKJH=9Jh${GYIvRq)reo1>zFz;=QIkmr!+)r zlxmQwR`n;<&wVTXX%<=rUPHm=y9RZWh3f|m6(zKr&RyqTTxnX>>zHlQZBzG1_rxz` zFO1MUA+8}ENDoVn&J=BdUh}A@N88Pl*O9x>CH{;vK(HYW3>bTnG%qguV?W&(Xk4r5^9Xj`0^{+4SEWb6sP$U0DjuVR*N-Rn^ z#GQiI4h2@J#Hmy@#Xsm@nqL<2e&-G2jpF^%>D*c7MgOSx2KMIsy#8nL>hO8-MFxTc zq8#=JE+0Y^LK&JDo(UEW(g~uk2ehZnpNM`&heUG@6HM?`$T^4`mK8w@_pD3iBtXi|B~hfXKX@Pc%|J z5>Uv4NrZ>tLE>y*@-VahGi3K{2X6O*j#)Fg?rg=rVZUT-sxnrmN6n6k5RZsp6Xhf9 zOK-`(WaX_+i_1R5VnSQUOZ?#=^!{iMh0>Q?{@-zu z6b@>}Z7I?3l6T^fBhBdue;tV+13AMqa#GjaglsOgf$4 zgI{}CFD#HMf)*+@S6ifAw0c^%?>bLotN>Qx%U5HfV^KzUo7UkQb8^{o)t$Pgb(aT& zf-Qn_+a0u%8c3D{mKc`j>&A5z=Bq$r?caC$V>{tHGNfzqr<-bCDo^X<_krgf1@{Fn zE?dqpE^F(@t$$Qg_0_bOrz^L#D)z^3a&Ma2RL(k;^)&2skWy)l=bBq&FJ@0<>y3&Ztl702vU%IO6Y}^{b}Y-B*YXI# z@s;y>XXugWG82J&*)80w^qBlD>%MVXJzPC!VQnF-F`v!ML(*gO*mw39!6X4KU;2mV zZRCAwTHUD0&#AacjEr>7EEn$^!NH*Gh%l^>?<$VGt@V2|zbdOMH^%&cRod4r_9d$w zFRx0#GLuS9EiSKLULEJbZ&3?`ZJEhBXWnMV4VP9O$F_BE$-5QNoseFK&ss0^8~3eV z`_Ui2@W3OWo-gP+JwCM8c#gd`-hM-uAZzoDyK}smz8ReD=90t7P-T|#RlLnVE*h6~ z+@57G>Q2>#n#2S*3(G#pmJ)`+DO_W@UV^`4IT$<$s6Q|G?0HKs6g9(|-W*Uu64N_6dR|goFh3 z9E=PB|6o{ofYyKd{6*RS0nPsn!W{tgQbvYmdV)5t0FA$>mzfE`#QYC_cKXD~i~#zd zW`+)*==v{)|J3$BLGu3&>xJ|j^(<{n{>AqW{{`&-8@K;O+W+em{sH3j3Qqct|ImWJ z^ZGRTcLD#ls7i`U{}7@6TM=;iDHUB6?vnV`HOF-tm`cXaJZ$!Mu}$k-ft|OZ%h`HcHlJf4LMK z#;#1B&qF zb2v|72%yNsfPxZ~C_ef78;A%lo(b^w{= zD_%q)r3e<0F5_FmQ77N+vFp~C`y)~XT614E_FH6Dc@=!WeYMN>Jrsg}2@g$=$2 zlNUFdwcb0PpfA1QVHB|VkS!~bgv{NUdtcv09wPUnM9A>)eY6@3{T~AOKLnA9f&D+R z`On7v?~(D}Tl?=;OY2#E60ZMSOjzofd{RcZe}Y5spSY%FWd+cF?h$~Uos%8F!Or>_ z-x8mZYGxo{ZDMKk8I|+`4hDaz=x5tsM)z;!pCR-@dbZ+5W+tYNa2!lb0D1*SBP(S9 z$7f0PUon8~bN2s;#Q|&_pPHOa{)vmv1IX~HSW3_J-|DC7&whU+_LI3AF~c$b%X0tw zbo?Xuj}!hwI{)RY|DO{g^na(C=U(tBS|Ul-zev`#_DHz3`|B&>&W2-gR)7*VlNRt$p; z2yDs-Uf)~$_*nGLsGD8qG@(wl>%`+#UR-V?EK{yfx0o+gYJj(1!&ad{M|#`a^gPNV zdg_+*@!G~R>GZjGJIC_gZvP_P_ShSf!WmF30g(C%`j%g*68zV+1r8X3FEzIVw+ z8r$W_)n)70BMSpULSImW;kq|Qa$`5G$9~N4p^t~G&1e1eV#MIX%7(pCmSbzF>uCwr zhUcKV#E0+He=6$*E2QH*&_IUcf`GjJ{LvuVur5U8oY1*f&-AIf_&9EJ+??-n$N5lG zFBLC{NTp8O3_uDf1W+feW{;Zc%SXL0MC7^o_1)Wm!CxFc}B*5$X zg{y*BL)FJv4=|u~H7^iopn6OQ!EgWNMBm=$pbRO)tU(!i%qWYjEP6sE%fu|3WmN|o z^B(fxv~(W0yhiw$x@qy0Lde>mUGTfX*PUPF%Ts>?Un%AdE}BOuu{Xn>33U$iP&@9jLQAshGa!IXJl*SvXl zsmWv7%sn3~uXbPVqc`88FGlCfGS4qy)%hB8CoY|j9&1!&%(yIitxw4#KMo!*(YxID zlT_%2K2)@q+su0dqdKVDJTfyF?5;{B0(3SXF1+Bf@FK$Y*0d)(23kJ$2l3Aay=S7l zdZN-V)p-wAG9BK%n^yT+ul?{o3{>!U$<{8MHz&^Dshfr-tfzdOJ)BxQ_>S*6C#6=# zq}*4|JsMYS8dopdE_~y;wy<5gSJ(a8TEKnwky@7tZoj!)IlBzUUBt??32h#0vyXIK zaJnpcUEFzHTuHn1FJB;zt>YE7v3o5^rJfb8Z56&Ev)@s;>`7JDFAz9&di;^NpsjMS zahvbB174(*t*^2*8}MC5a}5xD*gf34T@VztxDak{+_64jIjpy}t$SG8`aJPGY`m<8 zbvaymjJ(aey3FWZ z$g*-VnR4;@&~uptuC>6gyKOpt@Yo(^?eQ+|(mh%{AbYgnTQ7L5l_NK+5jpAHSl?e*-)p*zq*_m|t`)4V8DTZM5jpl9Cz{_rFARPaBpqXk2_UAO@3>GUudJIcOK*0 zQpVp>Zr^JCDAI7N(C8Sr%RH{kJgJ0T2eP-FZtPUPU^O)kRd7vd`Q*2Gm%WUVHEk0& zZJSu{UR&>)H<=Z#I+m>7PF)1swBU6ebha+7J2t+oc&sfVJ9_b4;CVFLcuu_S_FNEQ zwc2Q(S@X`XzfN6bxvw{^tT*wUSi5z!?c-f6KwfYWxik@+cCPMpzQ8|pct1@2SwDzr zxukb#im_|*nOx_cUB4D`8S=WA6k12-yYyCd`gjIj=ny%&T%KRN`@J;%TsEZIb6B5RU8h%Hr>}JSIGnin zu(bVX>U+DXy8y1PS6$d5zb)|GLO+~Nza4yiNDudTl)Bh8?9ptJX+O2Go7pzc;}hVK)7SB6 z_7Jk!I@(fs6N%S8)$z*u@S_Nx?^OFOa`oL?#p_oYuZ}GrMe_^X#UkXxi{N%;Z%R-Q zV;Q^wpE9Z!HfPI&m24t0*JoZcLIOzj8$?YlNCq|FN3N6Mf+E_ksudDp(6 zAa+6}*IC{mb}7pCW0xRGa*VeYJmK zT1(b|9QS3oUDg;w2(oyP3Oq2H?4-Q$8Q;QiGHp)5E^40EGJJj^pD+(hzhPdi7~~Au ziE336xFsk3X#{qasRwQqDm!VncEdziQ1;DPu&3dwypL@T`H}TJYn6 z8pu^Uk2I9+e+w?cnyN${+URV9IyBCr@C^SgYKB8b9Y8?#qfy{at>6(G8>_+Qk-n8VShRjHdDity6n~T ze%;ixIq!_(+7YA~Tf995wcF8DNJG#N8f;F(hkNo;kvpRm*|uK(aiKK|UtQr`3WOib z$^%4deF+EL6Y`F*c}IO>oOb=NOxk$$HgCCi6{hAi)T_K8dqW7m(X}S?{8FWX(O5sS zXHlyE910uLFzp77rs-8ID0Kfwg&fYw$Rc=m1n?_N(F_<^}>dzAf%Bv&vA%9+sxb%n6*Sd&{P@jwbq7pKV8IWzJ zEc$VM<0{|xBbiAe_p}7L; ziTUVOv6#AG_vQPVN@e^gd+66}V;*0o4T8}2&2W~H?ZlE9lB#c3vvZe}-;wgU-)Ykm zoFx>Hlb%`E4j~WyYF~}u5;#?1=X^5CmDQ3!m396Is^!bbft!D)pUErbnay3n4+^nCdOkDw4}0C)95&m5cT-fR}^ z6!gHhFI9O_=~g>-ErW$PVfH-*o_d+G8YoHBe86{sHF=*Iw3UbzEHYT*?wA6CYACM? z=)VI5?=%<<_3laCy4}?{sJh`iuZ&?lyo=oXrbN*qP>s3RSJ=`|K&bAlVX=K@TV(ES6AfJ$10u&l}I zEBxVk6MVryTD1NVoNQL1)@;g3_4=I~-5g}(5wp+RBokr9Y3A%)xc%suFY}xlf#WLysG_~976`0)Ue(*QEJz^L#f2}L`M8A#Kn4whbT8S(& z{9ZM{oJxr^29bd3#sk@qu!LD}$Nat|wu#-{FGcS5_9Bh}*Pv>h-4{#Nc>{HWEngi) zxi{BQ-(*(Wx%u9*pfB5U6v;eoP9?H;6N?@<$9YjK#xPhC%AAVvC6rj9EhnSswoy<9 z84daPg5?gn@?~fj(#cAuC~*!trGL77rPM_6VP+A>N-`G~O0Gcp}B?Dc?OX)2^nxD{`VJ%l)h96mEp^D`P?s zPCn(tWIx3|nt#UnT5ncfeojo0)wyDH5g$kBLQQ^T7)Zdeqhyh3TIu3FAi!9Frqn!@ zFSDirNp8~I;HPfNXt1Joeu-+Os)}dL+lK9B_FJN@K1yH{5+M;pl7>b&N>?%JN%|MD zUrJ5$0~`%Ag`5q8#6IV4G5mXR`4mZMOHsww8V)PWoWF=M8i599M6e5<0SCyffojNY zP_rG7#Ih*&S1Eo>EGl;3R6`38nelD+WZCuQ66ewbWU+2J<5aw*{y1RS&-f{gixmnnDg=p`btWmLB-XbX*5)W1TktoG^eTO zt%mC8m}jZxqxxlpu`FlvP_gD-)y~%qe=2`9J|7eBs=R@2IKcn5j&IW94s!ff+@) z3Aij_4l7lr4(=fnUMbv`3)gy{9hs81qxlLy9_cA{jmtlfOEcUa@uIBOX7Pe42T|I{ zhufa$$x4af8;6qz$!_rmQe2%X!fG&RhKgKcPNjow1`<4tKSZlf zkC&FVee&%D;<}pnTdl$rs+OH*Bz%p#R4hWs&DT^0BL^Ybdfq~>Xz^ImpFGqiv5{#I z$C8wV-*gqImfIzu=x}}$6q^cHWEEpf=NrT1%56RgfBs4;oIqu7cdn<)z^td&Su|v< zF+{r^`>64fe94qFRuozbdcwpZC%AxHGEj%d#@Fb*-J=2-`*8vKAdfz#kDp6HNFvj{ zcNw`2u+UXD%!^xq)!|H3XhI(1M)53%l+n|n+g8uD$)REjWsqd3TP~iGlMnhpD%2g_ zm@e+W!A$n=A!P~!eAOj<>VH=)De4$CLE-g+Ls~Xh1$2k`yW{kS=X&;GdIW_CsOpw* zkvyszje|4vG^48p5n!hVX3zWRB_MQA#~%4IG^6U3ZF2b@LApf6uB&u@p7BJ;jMD0b zp!-Pbeh$dD?p{y4$D9&u!$O#xR*bB%H7ZT;T&#NNtCbK-nX28 zQxt|=sNxIJ2=n5=b}P#$P0a*cF5fh5e-q)K+5ySGWWE(*9TxG^tA0}%kr3c@Dy5qZ zOv3=)~`uzAQPEWDriNch0VjDz;>it*(fvvFQ(*J)E>DvlQml=F#0M^xn2+V8 zMSxo@RKRyK)55T25satSZM|t;N7f4qlB(aBYD8I3*Nw)lXStc|*~)F3=biHMS*?5< zvf3CIMTU0Ah6)7)!BbIHvo-K>;K_+@VfS;Ylyw;l&7kjXImTXnjk}_3X9nLlBQ7b} zuk*w#+LG}Jw*kzew~$DH`Jd(xqpNw{kz=pVZmi6}s;O*W0)K+2V@xwE0(X>wL{d;F zk~e(0;082N#L2`R)dFy_ZlJkp0(mX;@2kvPsR61QVY>7)u1wV6a;W}L2ijFegzfsC zQ-q@US^03_I~;JBfCvBL>-!tyaX_+-H(*Kr8mrv;)ZfWm73+96^%<)A&L ztrNFW&Pqi*MUGltYu}!TsfzL(IFomW2tq)H@b}~A#p^Yx{N^;ReiSSeLUC|_Ck|!o z#NY$zJ8Ot$$wOAR;>1?$lXj;&)EvT7h(^5U2hq64i$aWdk-US!B1J>|5U`rnaNp>O+|cUx18!yvwfohY6+$YGf3C;=M3_R#h!)gX0mx`PfAqBK1F zLM?P#_v;}S6W}Zbu*iaGdUlFn&JE(`qr%t;_x6SV%&rv%0Yedk(}QtM3;b$%EYtzb zpn?0UyN%K_!P~s7Phy@&Mq?@vhw6liStT|ODM$$huTxxt$eQQ9om>xxK!Z=rv5EC( zkcW)=?V!+pqj)mI!Qz&`Hx*B#m8KCE|2wo@{T zb)u*RsBj1}SRqRx(O4s_ZXY@q#gDPQ>(~Oc zitl@Ygu|IjQTKuBw-iQY6)dbsT1j=rame4}kfkUhaG<7lS1t$aBf*u{6%P9AQoiN<=mlT?^K&FF%{hFT+T@hXXvuC0N`S>{!o#h1e3_-$ z6)!0rWCOTm6fowbLAgH^{~}>Bh(Ng59VpTdY;E(68d*#f`rPQq;Yl7Mi}G9+BpZEz5Hfam2yCHznWuOL93_ zMFP4L5T5+8ZfW|w3RyW+ne4x$*QLlP$#T~Dvep6$!;OF*CN$J?PN`&bDJUpZ{NV~g zKE%)vKjG48rr{IoD^1sz+DTRuW4xz6?V(MKyliLcmL7mp#dSWx(`&nAU)6;8y6jeqP1R&c&3jh@U)IZ2eLe$92Lhbwo8 z{25kHcUQl#f)gv+YTGSaDNV={Y1B)}#t;>~&K#C#S*iKE6$cUi+*eeTt`Eqjn;-Io zJCjHOXWTn25EjsMt5^ro=qYVRfhQ2T{#=%LI?L<#M*$ZwWR{PY0jR&XB^3-&-VqR2=BYUTh0 z_?d!E=8VV!zW9`Uh}DKr3=mf5*i%ovUccuP%VO$8|3p^|deQA4o%{AGh|AY4>I1lT z%aa59Sp%Ruo@<`cyz^PI`7NpHKBk_A)+5+#M`@gj`4tTz2^R?Yl;_Xl)TTCiD+Eu< z26rT3kJi|g z<=ChUuE%6BO~PJhda1#g4eX)>s_@G?aXez%Im8NxGmbLj*95bpX9 zc<}<2TN+ zRem3)KT83s)Fdo4R%k5tLcsGg{q3mi$)sP{u1t92N{=QI&I<#-;=+)ZFq6FovyAzt0`B}yBhK1i2qfg zY0QJzm0GcrO$`*q=zXraoldAt^yi@DR0Wqv3Jn)8laAknQ+^-L(%%jU1uVk97-QpO>O)1n(T68ka{3;@Ti^{mZ)yuyD&Yfe{# z#Qxpz+cmk_&^)~Gz$^_7s(aun7s_fQHL{k%(4thXeK#Ln*%$~7%u}gRY5$_Y9$gES zPLgRmY1Z7^gW(#;AZ^G5;}l;H=`I(;sExQhn@-mlmx}|C3ja)1p7C-Ad_gH0W|#@W zW`UAyE27lmA#<0oU`nLa0_$4%6Jw{&_AV8rAN0#2lNF<=;q*JS_BRPsU(i3JmpBj&9Q-L>rgLkPWTj(Ch!Qt%LesbsgnEoS0*1KNz zE2J8MF`_b&NH&;(r>-xxDNcdzFFj`7v@LD}Ya4A)hxR|(mMkK8LvyS%^zm2$d$BB} zUU8zmeOndDvir`Uh85ORAf5clbl8m%8f{=>a9xUt^U=w5u?4Dl;0^)%x*7nI4k)ss z;BS~}v9R7<4~y8c_bPbzgLAuMCd7jwRKGWLaae&pKdX^Za^;+$fdZUkGCx|cg>i00 zutNppe?`y-f_@8L0TAy$fF_^5ttVp7eV5N{jNSbn9at-dZ=-|LlmLDH0CY zcMvARg;)o5m(%pf=p9)&q$JwG=_)=!>KnaX?cSk{-MzG?oyjz@hDX|JjgRC_+7ho zo7ZY-&yeIp2A*Z(G2k)GgemT96R+Izr-o=bZY4=vJIUw!vNmrT8ZO>VC$=hOCu!dO zI6J+{{N+~SowO{86^G|V@Sri;*$Gww(*UJn1G^6xYg8v?Y z5wVyYC9?C3bo)8v4w&1n?(52o^zk>CECz{U; z0tcGLTy2sIh>vH6L}n6b`D1W0)ZSqY31&Z4{s|QNYC8V+P?PTp3SO9bDhcMht5#b& z8%)`juaJEp%eDOUvR`VUs36YAc2cR_9EWg)XM3PxxG)lBQi_jh0O8}k@Ngs$vR>b( zLK494yx|Rt_Dn~uP`Q+k#;f-`9qpioA!C0a#nzdLMbd-ui0Z1~$WGv$k-SbuZ1;J> z#d2pG)>~5f<|=~%;JE^ovx;;r0;nuem(WWk0POIK!n)v26c$O*#4QgB*^NCyKV2a~ zfOa<=h>w*mXGrt-ICrFGVK8;Jfv{BKR|eRw?q)>dzi%7tCx|Qsv71E#7Y9SfoFj(U`_sN@VqNND;srJo>5qVOi(;bdVRKMi6UoS4_4{yJV7S2cxWJyG=NOAd zYi03xEG;=571`r#)H3d2p+E3F$+`-52(@k--2^>3YVQYb?IA}IvUPdiAL;!`3TywS z`isF=dVZY@pAqq>h8i`QyJiq%AEZs^g9({3=xS zCpYYYRPB0cu^KS8+%uNkGq!Z!jFh{KsPlVkw8v4&;Et*q>_>Uo%FeJ>@!N%LDg&YW z?a!%7R6Z0%<-yFHg{yb~LxOw_x2$ZQ@Ka(gb}sn493MqdnR0xiA9VFLkI~P>-hCKX z6WP>nvE2RC=&DQ_V=&x^TWChvU9awHzF5GVu5mJ$*-p*Q@#u$?Z?=Mp=+S%&f}TA> zfjY_uM-(V^%Q=7o*@(uHukJKWKk}ln#D|)t3#QYeNl~D60u@va^Ib1}t!4s-4rsMH zZ$}GW^*05xId=vs8$sO9waAZtzn=xu=S;m`%J4W5Bw3`k6PzYDqkVh1}&>S6aKny=|*5HfF-OMw=+}q z?!_=?|i7(RPn;30;qwdHBE(ALWHlH4dhAr(|Q*gB{tscaNpnbpnO3Lt`h~Y`yl1ISwKo}%qYXb zxh(o1ogdArRfbR5SS@7;N2PEpG4s>!GO*crw$yXSW1V`LmI<_^8Xd_wGU`uP)FEKs z$_p?40MNi?zgJ!~<2qo^M(Lx@^-X3}y@omyJ{-}ALfQl)YLvXfv_dY&;;1PimY!D2 zzXHBd_#eD-vNtVH6uq-wn%1n!cpa^VC;8aG1L$|PL?dz2=k69*Dd)!H4l?&+;(5kQ z^S(UQd1PE8nNA12T7e4kSa~GO8$-zKr*`tgv0FzO zY|l~jL}}=0CcbZqds7Xh*+!wJ_$S%sO=E3pEw`OldBa)DG)bc06o{ZVDS2K$Y~#Jd z;q{>CW15`Y8#Qq7k0YZ9%7EHOHuE4PMOWwr%z#xJa}=6iy3L9qVC}Mfwf#LKA?mQw zDsN8Us=MVIfK0UQnB$@%0yzCj%;LcWxWj_K`hxG(QV{3d?Q(feP$cNT|39^atZys22b5-c*5~ zam|V;DIv~b#4iXcRHL|&t350oWc<|1e9Xi@>F?{q;`*cgo*U2$DTWMEd}$L&FjQfv zW{-l@zuvwGFb0S=fqvH&pb6-<2Z@Z69mBfhe;c~WTX%$;XtoL#Vfa(a7NKsub{}Q0 zp$C5hit_m7=%h;GRmHYK!_c>H34;00wgLpb;Bz|0b)@%=RoaTEuny(dO=WrJHKa}h zIHt5fv#&UeUXr?JSh>$w` zZmWAZl;z46@L6Sq$Q{a zJHVp*oTG*{Fx^v`Hnk(4q1{bhfOhPp9{E&XpVPBR3L7Yy7#WicqG<#pB2h>pqcye) z5Qy5tRBt#(kJGcuzcXF&xP#ProVz+#yeIs?_6^)c30+K>c`$1YeER3 zLhyw!ZnWh#kHm2DwP_NPba^sk;V0H8K;Iw7r5P@vVX&3v>u4x>okjDYV6tEG92{n3 zfkx@uzx>`&k(xr^m3okBt{gM*ZhY53>6omh%e(HRVxjQF-oz#{HoF=Q;Hg(7LC1&} zpQm*qf3qr4)Kc);#Y^1UQ;bXR{h{QY#zYEqMV-Za%r`&QS<9hhr57goRWS1*DeZJE z+K&IF$RF?VW;|o3*n=gBayyPeV9>S!cEEou8|=dSE5>#xyZiZ~PRtY+)2=wtDvdIk z;?>8|Dp}oP-&x)vQT3e9K1L5ltb;i1>@1y*<^r1~^)-RgcH-#VZ;LJK0xFHc(h?X* zJ6f#tfe@;Q?Ld?dw z4%CYarj_7<-|WmSW<~}OFe3pynuX|XJp;o`S13cRok@F0HKTK*iN7tx2W|2RBN~%- zjvfjM3|PJ^g@41FYCsdX$Nl`55$?`lh?|SajcC&T+Am7|Tvj3#X4Vu3EZ6Y3R_D%I z<}07&6&xCjZ+>@rGcTM)GW*h=t!OOi;{I&lhK!`tGO^We2F?CT~ zB$wA}IH!~#uua5dO|C<-#!Old3AyTlVJMrpT#SF*(WG3C z2u5)r=$E7zh4<2T4&$L+&EVaseRKUTOYmScv=VMu5ArNqKd;(9-&$Ny4Jyp$jFdl> zqwI(^OgNVCp_bVpW+uR}og0y90-ui9!w=DcSfnV96Qdyl#q&&fdRrs8&+-sZ%f_-r zv8>r$Y$BxFs0ICNMk4eCFpXObt@;Sv4tI1-JaHz~c2_Rx46pD@Eqe!yPcas`ZCMPt` zqvluw>r#h#UA201fB_~af^%zdI`;A`s};UXM>i*)p|JC&gS*BX4d~{a7vFfH)SqX9 zS^Air1(wOYcAf4StSq?fuC*oP)f-G4U9s|8h4NsUbL?Mc`AeWP_bUcgw0_@r&R~o4 z$QsCb{J>JNM$7dZ@d8FE>?Bmu%kBx{$5W}J{R``?SUQT2t*TQc2*sI%+mvXEe-~nP zu?>JLIlo4<`&C%Jo5hPvldk$j6^tY8ghVZf7h3|$^Kiz6p%%o6aefoekkWkT2Cw^> zY!pJLCZps~$ZF5SSYO%gSBT=_D4YZuaWaODS>zhc!Wt;gJp$hkI7i5}BAyr{PvU{U z6;z8SrWh`2wWM4P#5K4ln0!_D)}z>~1@Ih!1ShX9;aV0_#tMB2Ca|h6;LeXg=4TP% zQlod2Wx8B%%I;;gCD^^F!Wj@xbGwXf3~vSBXWNm+R$EO2?Ugz5-oLfx16zEWhY8WHeg$*Kz}M5qn`KeY(=49Zfc^bBMphxy=$6P@bJ&!m(E|1Gt& z)TUUd`Jy?1qxV5D8mRUm%lqX}%d8N0T1a9}#dKR`Det`W%N-WqB9jt{ z<>47i518+_lB|b4C;NtHRZ8 z9v_pD`(zz9Ctk;DrF6apaBboIV_(l9M|AK$rIU9Y=lZLrN^)*hI3+?$q+7ez$tCy3 zXir;^-Dz({Bwuh(%V{vL7=y2+`9d-_ujlw6eU^U9Pq4Z%Y=I|19uZ+TOsz0{lkV0&8 zh8)E}0Q!KXEOJV3B;5c_oB4dC1C2HLYo`%3pAG1mv7eF%b-|<~n$h70s-7)zBF7`0 z!1%yj$cN92f5{*^8UAt5IQ5kbEKe_d`~Bj6-Uf8n{Iipk(Glh?JkfTyYwiD2*jWI@ z(Qs>eaCdii8*GpOVQ>%bZb1jP;O_43P6!^{A!u-d2X_swS-$_?mHXe_+ODatp105G zo@%LWwRLg+{nAb*W0hAsGpI?kzLjl7$1LrJsDslZf5#i=p*i)MDzBz22K+_Hcf z3{`!MC9GwD0c+Vp%YJMoEPQj7v{bYiFHG&c!(0q#=I3X<`o$>=QYLXm7;P*M7UlA` zApnigXs@Mr)cPEgC_}o5D1+9SxeCH0uK8USt~WXj-(8?yWyQbF+OJ43Gcm1BoZ@;5 z0Qg;GO8aeOATa|`qw?)i^t$jG4Hvd{kVh+L`q7ijj`Yw()g=kv;B&HDI8XX$-l6V! z{6>St_~D0XZa&BR825+-o<&*Z!MhqqnJ@L%WJkix9X~(C3LAOw1|mpz4o%H!V}GtNsg3npuxFcSH9Pk zd^kY|!ndO`Z-~i?!bO=up;8;mI$)LBYDfyAcvpIAE)E-S4=?M4W}k_pz|8Qo4oJ zpdFRJa4l(1Tdxkk!yvDDjL)i?9TZU(2)#?i+|$sp3e%(V5zAw@C9U+3nYjnspj;^0 z21-Sl#p%;B^#JOYLuG4rzV_Bv?Fi*I86;>8cg6t^8+By91*71#i>bcT)T#a`uCGjA z%WZZHAGaevVkp@$X+plPeSy6ULJ2lF5K8hYo#+QDj_%6Q#2F#Rmp{VLM+C&=B(`p` z4*Dh%j5yt(cP@^9zr1P$Cc2wnkE}pfOOGV1Q}A1>p9$xXi*YLAU8^}AqmGw--7xOEHMSyFFuw@K^Q! zo(Xxse_=3Cu>7F%p%b)Lc~B*g+}Qy7O- z6AU&SQFRL42+#KiaWoBnlK?K-d`5RHeU($Tn~PhEZQ5!b*DHKM32@& zX%tN`oD~?m41tnZMMMK75JrgjGP80Kw(Ca$_oN`Arr)q?=nT&-x3~8b&~J`^phVO} zaDMe|i$P@sX`?a9?&-xrcs8p@_Sz53yb&(YX2;c~)^W5&l#(Rjzy%J;(HYiQ$0Rfps)lDAo16Iot2r@Bo?J&UwD}2 zfFQ&V_7oT_A6A$8ReaEsK#rSHL)6o&v&oe%S_v3SStLSql`YH7yj>a&NM#C@zOhj? z3k=Go0%{O|dB%m6mWq}Ncp_9!M+@$#jqTy6H?TJ&uD$!HUwlGGyxXP_I+a;5 zLw(?ChjkS%2d7FPhmMZXO9SMUMtpGU>I} z(LYH#MU%A784WJW8+zEoRKzJB5`(z|?v01ILIrvVBQ7;25rSK}!4ulY7Z^{0oc?`2 zyKLxezOV3G78-LtLo0T?8D&e%GvF)gX(nWs7nyh`i&N%TSQus$t{^C1Y-;6FKl`Pi zuNX4Q%O-3|F|$9t(zTr$@qZ{H*e%pTp$a1tU6YkCWh%+0xG7a1g)ZvvPFeFfdBK#y z{pJ149oy+iY~hpv{{T908$HRK>fmQ3md{371~TL)$ES=k{n(E=c`^tHvp*@CrwIFi z7NXKR_S3Y3-)tgMI#6D60Zo=^hz29fHO_E!CaLTOzroGRX)vNkPjtppRH zt$pK$xk{XfRpl44zrS_;m(c-G_K(qF=jXT3#h7%`a+0YR=Rea}J6VVlUVmB}G2uwX z4uf_KU49g}qK?S!NU=*6myBdWZ);GXMvzOE<~*ey#lD}2=C5gEkk_$Rfm`mgIxI@M z;JRumZMngWI#EQc8GPEVuYAW^D~UX*?imI%jIU9pju^cMSItd7#w>zcjU!9?z;`Hu zuRji10KsPFsu#hmg%b^VUlL|whszwpRQ`;!p2-DADtlrsH!lhGiE?e*h6b9J&((b6LSP#@HQp%*WKDREnZ zRu;As{DAMziI1MxgU&FIxLL)CW`oe)rdUF!s*~y3!sSc#ReU}AbfRdNm-#bkNZf+0hhi7P->0SL#ip&PuR9zNudd1 zlt(B&!MX?iufdF!L+P;2sE?OPAYCi2+&tEFr&x0KI?jkC!zw5rsD1jcW3SfbCJP+- z7?xBk-_QIfrO(A(Ec+FWi4zsN1q_^_u=eAivCiL|ET%Tkd&v6y%bnNe_TNC$pl5BD z@W~ALupK0(Q)u@4^4CxNB=pxFBt{ogOcmdHWYEtCK3mHbk)~6?XOGme`5yRAL%;qVi46A^0X;^h%+At!1n?E>0?&@Nc6t*d7*{d^plyO{0cYRX~m?< zyT4^x!&susMz#Q57Ojj)=_E@#6S=E~RzbohHe{Fy=0>bIa3Z0w{Dy+}HKnjxnOvgQ zcC}89Q&G*XBdn!RsPnWsYs^Z`Bm?jaMlQ zFsAlF&V*l#wzTD@9MdGTlwHjQ+>#t*hd!EAwr>e3lF^ zjw1df!27}^Q0w(Gvn;wKi-X{;2{IoGZJj@>p0*S589i>bGt>DaLb*+dC@~wfG@|!_ zZDFOhfH^7qu)01nuBk7Noe$H7t$qc6rb{co5c*}UUI?2#+Rx#{P@_X{=bl~E5M)7w z#Gk&tM$-&Ej$UUcV-})5z*7{}zyo6#7=e1UK-IqG?hgxe+9Z;4VZ7FYjxE~9pBimY z!+W(mAVj~`iG2_}pgwK2#CpRV@Wlk^NuY(i1aX~6(updco)m4cEuDasPA3c0FUfjb z)2a1gB{NZ|Q^Mg?UmOZ0%VS4xSE`F0RigGZtq+nD zqh`7@XB~1!Qify!QYZvfWTGv#-g#~iVwCy6Qxnt z$JLL{x81-|L*8__rWacwcZNLe0~Fg$97)Q%x5r2xz1Tdu>AD%` z?Bk4y{pgx+sMD&jZ+tX`e;=n#UNX?qS>7^3E_O*ni`cHDS`TV`4st!>$Z5T-U8P(~ zJWrh4d=BcsIkGP!rRj0xRDKLqgnsu=$k8bGwqD`^D~A$ZH_8h|V=k{zvSU>- z)=A4+e^p9!NTkprlTmxs5ya%49PRk`Yc6y%6AyWCRQ6NI7^#;7OD5yYFpn}2!RXGEk}B=QH+1eZ)p5^P^U)FGt> zWfwzG{ysIqM91J4IE=gBG!eY1r}Iz=rvt<*;>pIDeRT}$@&yBki~g=xD@s}bZKOB* z6ApiUfjsFig<1hTZetPQlTjna$Des<%XuSqt`4t%YB4!)@iqex$`}S)rFboMRlF;MmtWW_e@Vhgj*8W- zn7-P9KU11Gwj!B)1Iz4FV-?*xQrIT0r9`Dum0q$HNmcQQEC-qn;B+xLA*7I`yA3Is z`&$IS!q#=$L>qQ#9&2r&)pr3`Yl37kGc%(mnA_O z&z)Y$s>Bq-02AfDbMzhO>=L!jRK_fk7s$Y(4F?y7HROb116=I@yMsd8Ld)9LxUHZsF_2m!W+n(d2+k7nAg54pZHGX+JmOtep>*wcE%V?)nr_9a^a>sPkT@(1}c@;KF zf>;$*Y%+R_=PU4n(Jnm0)B99d zg;Ba60(|%|O@_+Y5%d;U;?5x!jr@|K>lBDg!HDeJbF!lOMl;h+ERlv0P}aG;h&uLW z&oO*K4sa==c*JNuMIS1Bu#6&_Odq}=FS$K@Dt`Z~c$*h-aVzYVRSwh7X-IX6yvAs! z3WT+@8qUAjhnPQtO&gbhG~Yk94Dqg#wn9YvE*{ajtFH$w4o3ZcR?a#i^9p_ip5{>q z_P1eGmv4FeiHcQUkWsWjB~s)-`^{U~lzWS&FbCL<<@tq3hvk-2wT&gKQ!_y!D3bH3 zUa6zAt#Kz_7;5Q{f$Hzgq0KDemTLmi#rV6%@c$DVeDIG})>Fd3pd3U)! zR<*ha9|0@2?cQNPszIFRigOF_O^|osVdF&(3{+%@A4mlP?ylGSC7UmIZT6Nsxpoqg z1Fa#IO21VsajJt(@$q1Aio^bcJjPxsgJ6T`v=w*Xr6AHYc&3@kW0v`llo`vAAtt-Y z5oI$xksKsB?$EEHoTnHrZBDFtW!l-cMEow5^Klq@%yyB?71ang;SgZOExtR5HsUJn z_cPUESxt$!iu_Ho{Sr!va>UnaW+NyiIg<}EAWpa3r-By+D&)8>HF=2?*s7esc%0n8 z3*0Rh$xMzBT8-n(rUA(v z*B4;C;^%LmpQP*eAxb%d?SGMfBaMWRxB^i4Wqdqg%V}!lE8!Y}$zT2T3vkJ2v3)%g z%luQ2;Evj$R)Gsxv0%H1r^|#fssh=URph?1?JrK$IzjeX<&t({jd7N=BIs^17F=J7 z@V)^&zQv|1W4ger+_6S%H^3E+ep$eDwc3ktbhdEzl)xm%RzmNmVGakx(_9u-0mA3M zlIe<7EOtA=X|o3YzC@O<+Ktxsl&cSE&Dscc>B5X*Ww z2uQ;vfT2dLB``Id2$V9jmqD$WctWg|!|9ipa45_u8@I}>l={bpBf{KAGwb);{ZMx{{K9gsjZhJEwky$U_X zrkZb|op13?wrk=$W1~#@z=J$o3Jr=jPOeTLwZB~UAdjsE+Tj%XZM9;`Q6Do2afFxR z!zOX}h$=RuAv0w*H$wI4hWgNZQ324`8iLBYZHqVZQS_zIP#xXjrO8MvjwwEuM*KCA z!UG_K98X9Te=&kK8Pc0Kz34Buw_EVna6+w(G6AcNO;xDlb)efOxm;N+pHc2rQjfW` zcn7ESP7}}EqiUDje3Exn!>H&m*&-nCAg9s( zQ+GoGWQwR(Q4Rc!*@zO7OACt)@X3=N`CDt6>11@WSF zqf}xB!(VuC<(%^E`bHj+HR8W8ZJ0)s3;iOcLQ?Qu!f#>Agr}jG(z~fcO0=IYNjWYM zwiy&3&(_jhVYUZSEV;tUWQs+41GLEqo5~*NbO<8 zCtqqX4UdaLHb^EhCpPqSwU)-BUNaXY1Q5uibwJek$ppxoFqt!_R~nqoGP$KRtKUOa z8J(szwHn)=sEN*;5|m`hK~4^nFXfhgwOLA;ByvsVav9WB7c27xkFVg;r<$10mZY(e zkOy{t-HT}(lb5e6-S=>z9UW&F)%m#(FLc4mco+Q9O;|F!w!HTh$0!sfrMQbDQSApA&I(|-`m6Dc=OXG)FtT|BR6&sZks6? zE~AOQw8UX_r=|vQ-yxfk2RFE$k)^l9uDd$>4-IS$TZbWKFkZ#f%}mt{$`aGv5*sFN z5BuPzWCwcGp1ynG=2pY*orixT<^JKZ0+>oP2+{WRqLSUs_G5$|l5iIf&65-V)Ez^- zFlCQYe|e=WZL=q&?vuCh95OR!rO^}{*{qVjgA(Ug^;w%|a*c(#umDzI(E-=o;_t)r zLU8H$xzOT8Ih}k8)Ny;oG#7QnzrSdYNdSXAXu_^h4b5*zWFWu9O{x0#unX4Z{FV}|UHY;O%ApG}y1e)E(&QG5#KT-s{?`Q4U1W9i73-4_t9w3dt z3bNk*Ap7j9D0VZlF!MlCSX66l#s85U?AIXTjxZeSiZk7n8sD^ztG*s6I@VltvZneY zDg5Ky{KiWgUoG{z=b4oLXFuag3Q6?E5<${$j$NNVPF#IujVuSoe2=PFK*&n|!h$jypowoM-HR-e@5Va;6D zujuILPfL`zdq>;xZYtqvCk~Z8Q3ox=%548p``&H`E0AXTVnL5&)`n=#+4j=4#gwtI z8Ux4wSep4XCBx*m<*b;|eksRJPI z`K0NE$K2{z`FHj-O(|Kg;H`KD)=Iii%w`D*oouFZsLUZ%L9P&!I%bO#TlAlpeS!buZW%y| zyaWZ`cR3txH$@!5EZ+QqXThN>LZzYE7=6eS8y*l7fS;YT7QMZv>asr{{J54T8AG&- zP!W{5T6dds|7Re_cig*~MlfBxYb`ZC>AUfobHbk+AAPRVTQD;5ASXL48nl>*3$+6g zBLepe(Ju4qLvMCfzjtXi7iH}89)5&Nr9B=K zswrKDkp$L;u7Dn4)y$@U6K7CEYs2p3qz4vS>5|To4h4W?ex2VvxO81b}-A$ zf*v@rMs6+;wV8#x!5U4EdD`509?<+~Y0?V%&4apZh)$#?QHdNWdf&ZnGrZJ)JYFK3 zRrQzDi1hJF*WUVqRbS@UW^bZ$3wxA0RftRi^yPSC|kw zJauXe(!`g!t%?A1E9i(Bp6srw)qP5@!SJ~;VP0!NN_TfaWk|9EjKXwE;HMxhBAlc% zrp`+kwJuDV^2{aQ1&)s`Ya?} z=nhI8YIrtXKWLBN8C>ZX>1`M7Y@bN^Q8?7?Dp4~8)G^^i1yDL)HNd5__13%RxC58B z2QY$96y0`Ihh?UjT@AK*6-Q;RN)n+jF^_i#7RMs3;5fB^1rB=`A`!XccPSlnEuj1+ zW7$adnFKHekxTH&ww?VfpLKL((}AEvc`a~^kxW(px|Vxm4z@Ws=;vKyi) z)8{}d;xy3^W5F-NgR|LU!e3~03J+RYrKpDXQ8sLL)ZkfPirGR)MiX;B+qOM0_~39` z+Gpoy{g&8L;Bj;;x&FYR-Gs~1In*a6HDCoxhw#>(Zy|=LY zY9h;kzvuQ5ei$FDQfBR`abmza#{V6=M&%nCjodRr+C_2rQ=wwv>eRi?G%`DG{+-%aNZQH1qCVC5%#*uIb zk#%6wBhkT5^5F1Jzx;{&qCou^)(Aygyh03MK zL_zLMWD$Yk!V(Y{n0nE8FS%|tk}%Di_!iVIp~4Oo+@!Q0?kW0)1!V zV2pZmg^X!E8FV;(>>>e^1(v7MHqgd|`ok0}q^;p^j7^syanuIH>UXNRnAwl>%;C=M z5N_Dn)C<#OLGyl?2>&tQC=N20!k1~nLNB<@pTp0$iH*T7l4U7|A6!IsgSrg$CALLS zSCAUjrGsIN6BFWc7N@S&ka=tjXqJRmaKWw_6|dWB-KW(~miCOk;(WAJ>M#o+r)lPG zBNjdtafO#67*ggJo|cn;`{WoN$ z4|||mOF{Cp;NTunVR8PK!2S`dWvJpP_0qK>Yi_wx?%cydt=NgUF^v?(W8zN_0~eqd zq)B9>1Z1Y(nl`$v;Uds^8!6R(px z_(NfgP*C$Dp52XZUJPlZ%CKo<39Pm`l){3f*(agzh4=!GtKn7Z&H8jdfkFFK*`ajO0H&)wWRM@71MfvIXU%&J7qhfxIKVxe()& zQN`_}IHBFY@jSpqdj>mqVx-l1evW-p)NWhdi{(eHP~;N2I0>KnVr6Q^x=dM)ret-7 zR;xSHIQH?`g&zg@ch@;$t#xO&IhX{X&l0{+4fCR)837p;SI(Zb#MX!&O^`?$xeNLM zBclKQ=1v#i&CqX1Ok$KYMSo7~!UTlyt#hQ(p=}kx9*Pw659K&#dMi`vT!0}ZN}Tcw z;Wff+n3X(BmENH42D%GGhNxxssxkxjQFeBI{Ev0g!x~L}aONsc-xU(SOLW#5YW4Ep z*Bg)PIZuBD~2Y-{C`pj({+{{8j!_&VYjld=gM@{@fyW zK_bDqHnE@`L&!!h6nPi|0TsI*IBRYLqu=$O zVFaF|9s{G~PGYPzNllIH@P?s#VOZ~@4skH02YEw51h|>6ETnr*$F9zn&d~wu0xD=9 zc1KjNJ+I^S{-V5CaSqdAn$Nz~hG!19-j%Jin=UI9J`je52@o+TG=Ux&Si$FLQU39W ztyjw%&V9S}o4*K`@zufa-x-0x^?epZOv&w`bQvm$Of*YI>LC&1IYPzmh%PRMF#$+^ z@tQ%{L_+*~zzVW&A4UpbT_8weUaF416v``9Z@8oqY%+~xjq>zF;e&_@Z4Ee1@+E2u zk2B#xb7#ZaA$l%ELzlf>fHGfmDo{s~x%mCee<`7~Im%f&EgHXaStb@{jO-B5U<+q# zh8YDvs!0kv0p;bqkf&-ajvCh4l^F-oqO&TV3pchpbBRc5>I2GKfJ~NNd@uUn@HeVo zDxz(Fa_~%c`bO+-!=BzzkcfWw#HbSqsJxI>?T6+2a_x9p-0j?QD&R;jF?ilifP$H9 z4-j1ysw@cL)8;Wk&~3oHm@BlZP&}HGxkQ1&LJr1RUEGv{mSMc)C#1#H1H?%)!Q0%i zAHnfmkLP6N?{~mIKOp>W{}LGM!XvO*xBqB{+Ubv3Y~h@%$Id)?`C}vXMU%$1rQjow zQnx`fuy2+(`ejNW;~4&m=M$lKU!EREM*nw|5{Zn&V7dB;wy2^Qv04a)s9`}cdc{{- z4aRyk?AUqV14r}3I&2>kI`ta-OE?yb{oefGL~}8Xz>O;0X7g+wo|12YIiHGB)WtHY z2GIcqb*!gJ97-Q-xOq@4IotV;#hlnX8eODF``eS3mplqYiqgx$E*95|*8a0k@Fe>9 zd`qjQJk7l)#@B()A4F*Ht&DoW37a$BB0f~;pHNNx(p!&4Pj3MjS5@>ZC|1P6y6laD zqOGPgu*gXyy*Y>n5y^}O><+p$n1;cGKZD;LfIfG|X$W7ZoPr86l)6C z`C%RUF5qk$Ep-$85)&Rw9UzfEvZlpYl=d}IN(iYf-SHuyJ@A9w zKBy&QP40>=^RwP5Zhs9BWtN{>b(HVmI%9Dyi$$nS0AYWBAHQUBD^6#1LPV~y)#w-P zIoha3NDRsMKJ9?5<>jN#+xxj@6?a=^krI$#bRvrWZ}l24*(4f&?|ERJX>l+nQw0K5US|mD=q8&gLL4xigMXwR4mn~s z1K_%Spf1@>$@udmTMKIBIZ5*!QtIhLLs`?~67$!!Hkn(V`-+%bCwkqAdm|URN8Xzc zDEM!PgQmju>3EHg_zq_)3j_(nb!(?F%2P3Zskt#x{%Fo+?b{R4e+*(9DGukKflZM? z3JnNbmu_MWElPKzXF%05ZCR94Ybg;+PwU1v=y$U!Aqu{UiQBiOk!0h|Gnh?n7!%_Y zx7dr;ZX4@DU>nCpi0-L;bjrpV1d|V_dF&l<3N7b3VOsqral-Ayczf3KoKH1mJSp`= zIcI6Fg4J^~jPb)Z+G-Zx=j>BE9i&0X;f5d5BIFm`)V`RZ1hBva7ekubF@?*dn;FG; zJU?dr5J4Ohjuo^@zIcH_D#C2wd(sI8FFMe`!E^*p5o<%O$o=6=vKd(4L?1j!Yn-s- zyB*#jb495>s0fzHJR54haijoU2UmQvy#2X%S4Y9e0?6i^UU-+TRz(z~4g@ zhv6ark+RQk`gPD);KR3bp z(ZjyB;+f`&pwHzL2QVYT<nK)$ways-Bny+q1HB_)x7LN`v9>zdjpone zk-ek>=2yMa@2z;|?FvrMvo>FI8W8hR#^eUzC9))5K&V zOiS*i-EcEM*<79MkD1ghgJ9ygeK3P+KcXT~WFIaMGqH1cW{J7gu>+y3@eDtItyfd6 z^GrR8{vtW=^@ICm6B7^qD&P~!%`;c>?iWS56+#c)eyL)KuI*rZL53@znZV;%XwSwYUME7fSpoynr*+$E6r!zMC9`i^ ze^3viQB*9Lu#zOQ2RT?_dV6YGPz+s^gseFs7Uc)L3`PN6w1EpqRp0pKkr5guCi`{gg>iy|B zQ6+b!Zg6Yyr*NK_PxOGtQUGKnTw^O9A{dHI;D?1^Dqxj;aY%Efr5sA?Fpaka)1XOR z@Uqg(wqBpcXYmuc6mu{*u|{%Alob~7R(#c10$Nn_z3F6Ko{Tpq0D+((t3C9!cLsqh zs)*k(Ks@~}puSXDV3{*s6euyySUg-nZ&8Ddo^mxxZzt$#${%{fAJfU?%=|YiWw)i} z_cC<34aVKf40C%7r?dyN{nxXCcoAgOLbuU$j=qKMDW~?+SjlD)8atrr@vA^&2_ZV{ zwZ7J)tsO8U78al2X|!BBHToC8F+o(2ROwL4(f}3EU}_i82{xztPP_8Uh~UkwM5?L> zVV|Cqg5wtaA`I?y^$)$=eY})s?|`-S@a-prE+vc00?A=N!B>@3DKb6uLnpV?Cmfyh z2qihWG7I^f4#XF!$bn$6<2p<>^vU$e#&fZ4lBWWft>y#QJ+U?Gp?BXbvS0$-YtkN> zviDkAH(yX7VIunuB+jWNho-VM$Y@n1{jUj)IBQ8}Ebt*=oOozxG=ofW?tm{;D8E=a zf~`db#a2v&ClGExKA63YfYm;=>Sh*Ua@0g}hEWnoh2f<_D>?xGE?emlh8V|i; zT1|JT=p@sQ5j#o1qq+$au_334Via$`aY-WVhIBL*!6154Ck05%mpY^V8Ic8R53GcC zCUbJQ-+~!u^K)*k1jot~@3^J9Cx*eO-3mU`0jc7{V^;as|zYuW)(lJ}Agv!LN`WR$Gy+OQR1+Cj|^hl3ZdF5>7eRRBRz`BCBchC=~9!ZQF9P zj^N2=6W~(dj8pxEBTXG15rZwzMBQMW^tCIuGR(r3TD|0kUw=Ef1lN@yVa~(ur1K_! ze~t07LSS>xytQ1C`8!&*we=QvLKE5TFsL*Abu$ugef$;8NXoh(&YQl3=i4p8HIla| z`5+&!K+C= zmP7)MSlkq-jG#wsWW_CMl7z!+0;aOXY*_sbU~XL4vy>*1I>mfJBqiDJq*b-Zva~|O zm7W4>mt^IG6-*E(c5axaPO=W7|6+`nJ0<*IU?=~R%)`aY1LFQ)xRd{bgY%BFF?Tb2 z2YE8uo0xr4qttw7(O5e`-svIioV@JZl=_TfZq|11I3EECK9D{WrHY%0v+Ebich=>< z@R**iOz&{ZcjTw*dy>REkP?|uLXZo{1q5>cGvegr;@1TN8Qwqdaf%M+|AbKfzeD~v zjl|R0!V(!s$;prWjxqVqffB^a%gsw^N%#gTp(8=^xUP>N~RrV(IXXWcrV3iFfXh5Rlu9n_Iw=%fb}IZ^~oFZEnsB t0-9J_0?p0LfLwe)5#;}W$bW!~TwLFetAFiuE&+aS0c1Kl$xl+q{{scLy(0hs literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/Loki V2/Message.imageset/Contents.json b/Signal/Images.xcassets/Loki V2/Message.imageset/Contents.json new file mode 100644 index 000000000..22681b32e --- /dev/null +++ b/Signal/Images.xcassets/Loki V2/Message.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Message.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/Loki V2/Message.imageset/Message.pdf b/Signal/Images.xcassets/Loki V2/Message.imageset/Message.pdf new file mode 100644 index 0000000000000000000000000000000000000000..46a40dafdb84ec47b20c20bac5bf85d2321aa0de GIT binary patch literal 5742 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 literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/Loki V2/Plus.imageset/Contents.json b/Signal/Images.xcassets/Loki V2/Plus.imageset/Contents.json new file mode 100644 index 000000000..e34919cc9 --- /dev/null +++ b/Signal/Images.xcassets/Loki V2/Plus.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Plus.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/Loki V2/Plus.imageset/Plus.pdf b/Signal/Images.xcassets/Loki V2/Plus.imageset/Plus.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7d6855b3b49d5c0db0301c027b0ab4fd9fc61632 GIT binary patch literal 4018 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 E1K~p7P5=M^ literal 0 HcmV?d00001 diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index 378a5f3ca..342d37adb 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -5,7 +5,7 @@ BuildDetails CarthageVersion - 0.33.0 + 0.34.0 OSXVersion 10.15.3 WebRTCCommit diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 25a8de660..64721d20c 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -884,7 +884,7 @@ static NSTimeInterval launchStartedAt; return; } - [SignalApp.sharedApp.homeViewController createPrivateChat]; + [SignalApp.sharedApp.homeViewController createNewPrivateChat]; completionHandler(YES); }]; diff --git a/Signal/src/Loki/Components/Button.swift b/Signal/src/Loki/Components/Button.swift index d94d18b89..f8b0ceb1d 100644 --- a/Signal/src/Loki/Components/Button.swift +++ b/Signal/src/Loki/Components/Button.swift @@ -63,7 +63,7 @@ final class Button : UIButton { layer.borderColor = borderColor.cgColor layer.borderWidth = Values.borderThickness let fontSize = (size == .small) ? Values.smallFontSize : Values.mediumFontSize - titleLabel!.font = Fonts.spaceMono(ofSize: fontSize) + titleLabel!.font = .boldSystemFont(ofSize: fontSize) setTitleColor(textColor, for: UIControl.State.normal) } } diff --git a/Signal/src/Loki/Components/ConversationCell.swift b/Signal/src/Loki/Components/ConversationCell.swift index cb9ce6629..f66dc38dc 100644 --- a/Signal/src/Loki/Components/ConversationCell.swift +++ b/Signal/src/Loki/Components/ConversationCell.swift @@ -42,7 +42,9 @@ final class ConversationCell : UITableViewCell { private lazy var statusIndicatorView: UIImageView = { let result = UIImageView() - result.contentMode = .center + result.contentMode = .scaleAspectFit + result.layer.cornerRadius = Values.conversationCellStatusIndicatorSize / 2 + result.layer.masksToBounds = true return result }() @@ -167,6 +169,7 @@ final class ConversationCell : UITableViewCell { typingIndicatorView.isHidden = true typingIndicatorView.stopAnimation() } + statusIndicatorView.backgroundColor = nil let lastMessage = threadViewModel.lastMessageForInbox if let lastMessage = lastMessage as? TSOutgoingMessage { let image: UIImage @@ -174,7 +177,9 @@ final class ConversationCell : UITableViewCell { switch status { case .calculatingPoW, .uploading, .sending: image = #imageLiteral(resourceName: "CircleDotDotDot") case .sent, .skipped, .delivered: image = #imageLiteral(resourceName: "CircleCheck") - case .read: image = #imageLiteral(resourceName: "FilledCircleCheck") + case .read: + statusIndicatorView.backgroundColor = .white + image = #imageLiteral(resourceName: "FilledCircleCheck") case .failed: image = #imageLiteral(resourceName: "message_status_failed").asTintedImage(color: Colors.text)! } statusIndicatorView.image = image diff --git a/Signal/src/Loki/Components/NewConversationButtonSet.swift b/Signal/src/Loki/Components/NewConversationButtonSet.swift new file mode 100644 index 000000000..8510a2a51 --- /dev/null +++ b/Signal/src/Loki/Components/NewConversationButtonSet.swift @@ -0,0 +1,310 @@ + +final class NewConversationButtonSet : UIView { + private var isUserDragging = false + private var horizontalButtonConstraints: [NewConversationButton:NSLayoutConstraint] = [:] + private var verticalButtonConstraints: [NewConversationButton:NSLayoutConstraint] = [:] + private var expandedButton: NewConversationButton? + var delegate: NewConversationButtonSetDelegate? + + // MARK: Settings + private let spacing = Values.largeSpacing + private let iconSize = CGFloat(24) + private let maxDragDistance = CGFloat(56) + private let dragMargin = CGFloat(16) + + // MARK: Components + private lazy var mainButton = NewConversationButton(isMainButton: true, icon: #imageLiteral(resourceName: "Plus").scaled(to: CGSize(width: iconSize, height: iconSize))) + private lazy var createNewPrivateChatButton = NewConversationButton(isMainButton: false, icon: #imageLiteral(resourceName: "Message").scaled(to: CGSize(width: iconSize, height: iconSize))) + private lazy var createNewClosedGroupButton = NewConversationButton(isMainButton: false, icon: #imageLiteral(resourceName: "Group").scaled(to: CGSize(width: iconSize, height: iconSize))) + private lazy var joinOpenGroupButton = NewConversationButton(isMainButton: false, icon: #imageLiteral(resourceName: "Globe").scaled(to: CGSize(width: iconSize, height: iconSize))) + + // MARK: Initialization + override init(frame: CGRect) { + super.init(frame: frame) + setUpViewHierarchy() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setUpViewHierarchy() + } + + private func setUpViewHierarchy() { + let inset = (Values.newConversationButtonExpandedSize - Values.newConversationButtonCollapsedSize) / 2 + addSubview(joinOpenGroupButton) + horizontalButtonConstraints[joinOpenGroupButton] = joinOpenGroupButton.pin(.left, to: .left, of: self, withInset: inset) + verticalButtonConstraints[joinOpenGroupButton] = joinOpenGroupButton.pin(.bottom, to: .bottom, of: self, withInset: -inset) + addSubview(createNewPrivateChatButton) + createNewPrivateChatButton.center(.horizontal, in: self) + verticalButtonConstraints[createNewPrivateChatButton] = createNewPrivateChatButton.pin(.top, to: .top, of: self, withInset: inset) + addSubview(createNewClosedGroupButton) + horizontalButtonConstraints[createNewClosedGroupButton] = createNewClosedGroupButton.pin(.right, to: .right, of: self, withInset: -inset) + verticalButtonConstraints[createNewClosedGroupButton] = createNewClosedGroupButton.pin(.bottom, to: .bottom, of: self, withInset: -inset) + addSubview(mainButton) + mainButton.center(.horizontal, in: self) + mainButton.pin(.bottom, to: .bottom, of: self) + let width = 3 * Values.newConversationButtonExpandedSize + 2 * spacing + set(.width, to: width) + let height = 2 * Values.newConversationButtonExpandedSize + spacing + set(.height, to: height) + collapse(withAnimation: false) + isUserInteractionEnabled = true + let mainButtonTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMainButtonTapped)) + mainButton.addGestureRecognizer(mainButtonTapGestureRecognizer) + let joinOpenGroupButtonTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleJoinOpenGroupButtonTapped)) + joinOpenGroupButton.addGestureRecognizer(joinOpenGroupButtonTapGestureRecognizer) + let createNewPrivateChatButtonTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleCreateNewPrivateChatButtonTapped)) + createNewPrivateChatButton.addGestureRecognizer(createNewPrivateChatButtonTapGestureRecognizer) + let createNewClosedGroupButtonTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleCreateNewClosedGroupButtonTapped)) + createNewClosedGroupButton.addGestureRecognizer(createNewClosedGroupButtonTapGestureRecognizer) + } + + // MARK: Interaction + @objc private func handleMainButtonTapped() { expand(isUserDragging: false) } + @objc private func handleJoinOpenGroupButtonTapped() { delegate?.joinOpenGroup() } + @objc private func handleCreateNewPrivateChatButtonTapped() { delegate?.createNewPrivateChat() } + @objc private func handleCreateNewClosedGroupButtonTapped() { delegate?.createNewClosedGroup() } + + private func expand(isUserDragging: Bool) { + let buttons = [ joinOpenGroupButton, createNewPrivateChatButton, createNewClosedGroupButton ] + UIView.animate(withDuration: 0.25, animations: { + buttons.forEach { $0.alpha = 1 } + let inset = (Values.newConversationButtonExpandedSize - Values.newConversationButtonCollapsedSize) / 2 + let size = Values.newConversationButtonCollapsedSize + self.joinOpenGroupButton.frame = CGRect(origin: CGPoint(x: inset, y: self.height() - size - inset), size: CGSize(width: size, height: size)) + self.createNewPrivateChatButton.frame = CGRect(center: CGPoint(x: self.bounds.center.x, y: inset + size / 2), size: CGSize(width: size, height: size)) + self.createNewClosedGroupButton.frame = CGRect(origin: CGPoint(x: self.width() - size - inset, y: self.height() - size - inset), size: CGSize(width: size, height: size)) + }, completion: { _ in + self.isUserDragging = isUserDragging + }) + } + + private func collapse(withAnimation isAnimated: Bool) { + isUserDragging = false + let buttons = [ joinOpenGroupButton, createNewPrivateChatButton, createNewClosedGroupButton ] + UIView.animate(withDuration: isAnimated ? 0.25 : 0) { + buttons.forEach { button in + button.alpha = 0 + let size = Values.newConversationButtonCollapsedSize + button.frame = CGRect(center: self.mainButton.center, size: CGSize(width: size, height: size)) + } + } + } + + private func reset() { + let mainButtonLocationInSelfCoordinates = CGPoint(x: width() / 2, y: height() - Values.newConversationButtonExpandedSize / 2) + let mainButtonSize = mainButton.frame.size + UIView.animate(withDuration: 0.25) { + self.mainButton.frame = CGRect(center: mainButtonLocationInSelfCoordinates, size: mainButtonSize) + self.mainButton.alpha = 1 + } + if let expandedButton = expandedButton { collapse(expandedButton) } + expandedButton = nil + Timer.scheduledTimer(withTimeInterval: 0.25, repeats: false) { _ in + self.collapse(withAnimation: true) + } + } + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + guard let touch = touches.first, mainButton.contains(touch), !isUserDragging else { return } + UIImpactFeedbackGenerator(style: .medium).impactOccurred() + expand(isUserDragging: true) + } + + override func touchesMoved(_ touches: Set, with event: UIEvent?) { + guard let touch = touches.first, isUserDragging else { return } + let mainButtonSize = mainButton.frame.size + let mainButtonLocationInSelfCoordinates = CGPoint(x: width() / 2, y: height() - Values.newConversationButtonExpandedSize / 2) + let touchLocationInSelfCoordinates = touch.location(in: self) + mainButton.frame = CGRect(center: touchLocationInSelfCoordinates, size: mainButtonSize) + mainButton.alpha = 1 - (touchLocationInSelfCoordinates.distance(to: mainButtonLocationInSelfCoordinates) / maxDragDistance) + let buttons = [ joinOpenGroupButton, createNewPrivateChatButton, createNewClosedGroupButton ] + let buttonToExpand = buttons.first { button in + var hasUserDraggedBeyondButton = false + if button == joinOpenGroupButton && touch.isLeft(of: joinOpenGroupButton, with: dragMargin) { hasUserDraggedBeyondButton = true } + if button == createNewPrivateChatButton && touch.isAbove(createNewPrivateChatButton, with: dragMargin) { hasUserDraggedBeyondButton = true } + if button == createNewClosedGroupButton && touch.isRight(of: createNewClosedGroupButton, with: dragMargin) { hasUserDraggedBeyondButton = true } + return button.contains(touch) || hasUserDraggedBeyondButton + } + if let buttonToExpand = buttonToExpand { + guard buttonToExpand != expandedButton else { return } + if let expandedButton = expandedButton { collapse(expandedButton) } + expand(buttonToExpand) + expandedButton = buttonToExpand + } else { + if let expandedButton = expandedButton { collapse(expandedButton) } + expandedButton = nil + } + } + + override func touchesEnded(_ touches: Set, with event: UIEvent?) { + guard let touch = touches.first, isUserDragging else { return } + if joinOpenGroupButton.contains(touch) || touch.isLeft(of: joinOpenGroupButton, with: dragMargin) { delegate?.joinOpenGroup() } + else if createNewPrivateChatButton.contains(touch) || touch.isAbove(createNewPrivateChatButton, with: dragMargin) { delegate?.createNewPrivateChat() } + else if createNewClosedGroupButton.contains(touch) || touch.isRight(of: createNewClosedGroupButton, with: dragMargin) { delegate?.createNewClosedGroup() } + reset() + } + + override func touchesCancelled(_ touches: Set, with event: UIEvent?) { + guard isUserDragging else { return } + reset() + } + + private func expand(_ button: NewConversationButton) { + if let horizontalConstraint = horizontalButtonConstraints[button] { horizontalConstraint.constant = 0 } + if let verticalConstraint = verticalButtonConstraints[button] { verticalConstraint.constant = 0 } + let size = Values.newConversationButtonExpandedSize + let frame = CGRect(center: button.center, size: CGSize(width: size, height: size)) + button.widthConstraint.constant = size + button.heightConstraint.constant = size + UIView.animate(withDuration: 0.25) { + self.layoutIfNeeded() + button.frame = frame + button.layer.cornerRadius = size / 2 + button.addGlow(ofSize: size) + button.backgroundColor = Colors.accent + } + } + + private func collapse(_ button: NewConversationButton) { + let inset = (Values.newConversationButtonExpandedSize - Values.newConversationButtonCollapsedSize) / 2 + if joinOpenGroupButton == expandedButton { + horizontalButtonConstraints[joinOpenGroupButton]!.constant = inset + verticalButtonConstraints[joinOpenGroupButton]!.constant = -inset + } else if createNewPrivateChatButton == expandedButton { + verticalButtonConstraints[createNewPrivateChatButton]!.constant = inset + } else if createNewClosedGroupButton == expandedButton { + horizontalButtonConstraints[createNewClosedGroupButton]!.constant = -inset + verticalButtonConstraints[createNewClosedGroupButton]!.constant = -inset + } + let size = Values.newConversationButtonCollapsedSize + let frame = CGRect(center: button.center, size: CGSize(width: size, height: size)) + button.widthConstraint.constant = size + button.heightConstraint.constant = size + UIView.animate(withDuration: 0.25) { + self.layoutIfNeeded() + button.frame = frame + button.layer.cornerRadius = size / 2 + button.removeGlow() + button.backgroundColor = Colors.newConversationButtonCollapsedBackground + } + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if !bounds.contains(point), isUserDragging { collapse(withAnimation: true) } + return super.hitTest(point, with: event) + } +} + +// MARK: Delegate +protocol NewConversationButtonSetDelegate { + + func joinOpenGroup() + func createNewPrivateChat() + func createNewClosedGroup() +} + +// MARK: Button +private final class NewConversationButton : UIImageView { + private let isMainButton: Bool + private let icon: UIImage + var widthConstraint: NSLayoutConstraint! + var heightConstraint: NSLayoutConstraint! + + // Initialization + init(isMainButton: Bool, icon: UIImage) { + self.isMainButton = isMainButton + self.icon = icon + super.init(frame: CGRect.zero) + setUpViewHierarchy() + } + + override init(frame: CGRect) { + preconditionFailure("Use init(isMainButton:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(isMainButton:) instead.") + } + + private func setUpViewHierarchy() { + backgroundColor = isMainButton ? Colors.accent : Colors.newConversationButtonCollapsedBackground + let size = isMainButton ? Values.newConversationButtonExpandedSize : Values.newConversationButtonCollapsedSize + layer.cornerRadius = size / 2 + if isMainButton { addGlow(ofSize: size) } + layer.masksToBounds = false + image = icon + contentMode = .center + widthConstraint = set(.width, to: size) + heightConstraint = set(.height, to: size) + } + + // General + func addGlow(ofSize size: CGFloat) { + layer.shadowPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: size, height: size))).cgPath + layer.shadowColor = Colors.newConversationButtonShadow.cgColor + layer.shadowOffset = CGSize(width: 0, height: 0.8) + layer.shadowOpacity = 1 + layer.shadowRadius = 6 + } + + func removeGlow() { + layer.shadowPath = nil + layer.shadowColor = nil + layer.shadowOffset = CGSize.zero + layer.shadowOpacity = 0 + layer.shadowRadius = 0 + } +} + +// MARK: Convenience +private extension UIView { + + func contains(_ touch: UITouch) -> Bool { + return bounds.contains(touch.location(in: self)) + } +} + +private extension UITouch { + + func isLeft(of view: UIView, with margin: CGFloat = 0) -> Bool { + return isContainedVertically(in: view, with: margin) && location(in: view).x < view.bounds.minX + } + + func isAbove(_ view: UIView, with margin: CGFloat = 0) -> Bool { + return isContainedHorizontally(in: view, with: margin) && location(in: view).y < view.bounds.minY + } + + func isRight(of view: UIView, with margin: CGFloat = 0) -> Bool { + return isContainedVertically(in: view, with: margin) && location(in: view).x > view.bounds.maxX + } + + func isBelow(_ view: UIView, with margin: CGFloat = 0) -> Bool { + return isContainedHorizontally(in: view, with: margin) && location(in: view).y > view.bounds.maxY + } + + private func isContainedHorizontally(in view: UIView, with margin: CGFloat = 0) -> Bool { + return ((view.bounds.minX - margin)...(view.bounds.maxX + margin)) ~= location(in: view).x + } + + private func isContainedVertically(in view: UIView, with margin: CGFloat = 0) -> Bool { + return ((view.bounds.minY - margin)...(view.bounds.maxY + margin)) ~= location(in: view).y + } +} + +private extension CGPoint { + + func distance(to otherPoint: CGPoint) -> CGFloat { + return sqrt(pow(self.x - otherPoint.x, 2) + pow(self.y - otherPoint.y, 2)) + } +} + +private extension CGRect { + + init(center: CGPoint, size: CGSize) { + let originX = center.x - size.width / 2 + let originY = center.y - size.height / 2 + let origin = CGPoint(x: originX, y: originY) + self.init(origin: origin, size: size) + } +} diff --git a/Signal/src/Loki/Style Guide/Colors.swift b/Signal/src/Loki/Style Guide/Colors.swift index ee7d5c8e8..eba314018 100644 --- a/Signal/src/Loki/Style Guide/Colors.swift +++ b/Signal/src/Loki/Style Guide/Colors.swift @@ -35,4 +35,5 @@ final class Colors : NSObject { @objc static let composeViewTextFieldBackground = UIColor(hex: 0x141414) @objc static let receivedMessageBackground = UIColor(hex: 0x222325) @objc static let sentMessageBackground = UIColor(hex: 0x3F4146) + @objc static let newConversationButtonCollapsedBackground = UIColor(hex: 0x1F1F1F) } diff --git a/Signal/src/Loki/Style Guide/Gradients.swift b/Signal/src/Loki/Style Guide/Gradients.swift index c65be7b36..0060bf9e6 100644 --- a/Signal/src/Loki/Style Guide/Gradients.swift +++ b/Signal/src/Loki/Style Guide/Gradients.swift @@ -26,5 +26,6 @@ final class Gradient : NSObject { @objc(LKGradients) final class Gradients : NSObject { - @objc static let defaultLokiBackground = Gradient(start: UIColor(hex: 0x171717), end: UIColor(hex:0x121212)) + @objc static let defaultLokiBackground = Gradient(start: UIColor(hex: 0x171717), end: UIColor(hex: 0x121212)) + @objc static let transparentToBlack75 = Gradient(start: UIColor(red: 0, green: 0, blue: 0, alpha: 0), end: UIColor(red: 0, green: 0, blue: 0, alpha: 0.75)) } diff --git a/Signal/src/Loki/Style Guide/Values.swift b/Signal/src/Loki/Style Guide/Values.swift index 587f55a10..257e98d5e 100644 --- a/Signal/src/Loki/Style Guide/Values.swift +++ b/Signal/src/Loki/Style Guide/Values.swift @@ -30,7 +30,8 @@ final class Values : NSObject { @objc static let borderThickness = CGFloat(1) @objc static let conversationCellStatusIndicatorSize = CGFloat(14) @objc static let searchBarHeight = CGFloat(36) - @objc static let newConversationButtonSize = CGFloat(45) + @objc static let newConversationButtonCollapsedSize = CGFloat(48) + @objc static let newConversationButtonExpandedSize = CGFloat(60) @objc static let textFieldHeight = isSmallScreen ? CGFloat(48) : CGFloat(80) @objc static let textFieldCornerRadius = CGFloat(8) @objc static let separatorLabelHeight = CGFloat(24) diff --git a/Signal/src/Loki/Utilities/GeneralUtilities.swift b/Signal/src/Loki/Utilities/GeneralUtilities.swift deleted file mode 100644 index 12d761798..000000000 --- a/Signal/src/Loki/Utilities/GeneralUtilities.swift +++ /dev/null @@ -1,29 +0,0 @@ - -@objc(LKGeneralUtilities) -final class GeneralUtilities : NSObject { - - private override init() { } - - @objc static func getSessionPublicChatNotice() -> String { - return """ - Welcome to the Session public chat! In order for this forum to be a fun environment, full of robust and constructive discussion and inclusive of everyone, please read and follow the rules below. - - 1. Please Keep Talk Relevant to Topic and Add Value to the Discussion. - (No Referral Links, Spamming, Off Topic Discussion) - - 2. You don't have to love everyone, but be civil. - (No Baiting, Excessively Partisan Arguments, Threats, and so on. Use common sense.) - - 3. Do not be a shill. - Comparison and criticism is reasonable, but blatant shilling of anything you work for, work on, or own is not. - - 4. Don't post explicit content - be it excessively offensive language, sexual, or violent. Any form of bigotry including racism, sexism, transphobia, homophobia, ableism, fatphobia, classism will NOT be tolerated. - - If you break these rules, you’ll be warned by an admin. If your behaviour doesn’t improve, you will be removed from the public chat. Admins reserve the right to remove anyone violating these rules. - - We want to keep this group a pleasant and supportive space for everyone. - - If you experience any anti-social behaviour or have an issue with these rules, please contact an admin. - """ - } -} diff --git a/Signal/src/Loki/Utilities/UIImage+Scaling.swift b/Signal/src/Loki/Utilities/UIImage+Scaling.swift new file mode 100644 index 000000000..2645c3d43 --- /dev/null +++ b/Signal/src/Loki/Utilities/UIImage+Scaling.swift @@ -0,0 +1,17 @@ + +extension UIImage { + + func scaled(to size: CGSize) -> UIImage { + var rect = CGRect.zero + let aspectRatio = min(size.width / self.size.width, size.height / self.size.height) + rect.size.width = self.size.width * aspectRatio + rect.size.height = self.size.height * aspectRatio + rect.origin.x = (size.width - rect.size.width) / 2 + rect.origin.y = (size.height - rect.size.height) / 2 + UIGraphicsBeginImageContextWithOptions(size, false, 0) + draw(in: rect) + let result = UIGraphicsGetImageFromCurrentImageContext()! + UIGraphicsEndImageContext() + return result + } +} diff --git a/Signal/src/Loki/View Controllers/DeviceLinksVC.swift b/Signal/src/Loki/View Controllers/DeviceLinksVC.swift index 3b54c62bc..10a232e70 100644 --- a/Signal/src/Loki/View Controllers/DeviceLinksVC.swift +++ b/Signal/src/Loki/View Controllers/DeviceLinksVC.swift @@ -24,7 +24,7 @@ final class DeviceLinksVC : UIViewController, UITableViewDataSource, UITableView explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.textAlignment = .center explanationLabel.text = NSLocalizedString("You haven't linked any devices yet", comment: "") - let linkNewDeviceButton = Button(style: .prominentOutline, size: .medium) + let linkNewDeviceButton = Button(style: .prominentOutline, size: .large) linkNewDeviceButton.setTitle(NSLocalizedString("Link a Device", comment: ""), for: UIControl.State.normal) linkNewDeviceButton.addTarget(self, action: #selector(linkNewDevice), for: UIControl.Event.touchUpInside) linkNewDeviceButton.set(.width, to: 160) diff --git a/Signal/src/Loki/View Controllers/HomeVC.swift b/Signal/src/Loki/View Controllers/HomeVC.swift index 554f65498..22fbffedc 100644 --- a/Signal/src/Loki/View Controllers/HomeVC.swift +++ b/Signal/src/Loki/View Controllers/HomeVC.swift @@ -1,5 +1,5 @@ -final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate, UIViewControllerPreviewingDelegate, SeedReminderViewDelegate { +final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate, UIViewControllerPreviewingDelegate, NewConversationButtonSetDelegate, SeedReminderViewDelegate { private var threadViewModelCache: [String:ThreadViewModel] = [:] private var isObservingDatabase = true private var isViewVisible = false { didSet { updateIsObservingDatabase() } } @@ -45,23 +45,17 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat return result }() - private lazy var newConversationButton: UIButton = { - let result = UIButton() - result.setTitle("+", for: UIControl.State.normal) - result.titleLabel!.font = .systemFont(ofSize: 35) - result.setTitleColor(UIColor(hex: 0x121212), for: UIControl.State.normal) - result.titleEdgeInsets = UIEdgeInsets(top: 0, left: 1, bottom: 4, right: 0) // Slight adjustment to make the plus exactly centered - result.backgroundColor = Colors.accent - let size = Values.newConversationButtonSize - result.layer.cornerRadius = size / 2 - result.layer.shadowPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: size, height: size))).cgPath - result.layer.shadowColor = Colors.newConversationButtonShadow.cgColor - result.layer.shadowOffset = CGSize(width: 0, height: 0.8) - result.layer.shadowOpacity = 1 - result.layer.shadowRadius = 6 - result.layer.masksToBounds = false - result.set(.width, to: size) - result.set(.height, to: size) + private lazy var newConversationButtonSet: NewConversationButtonSet = { + let result = NewConversationButtonSet() + result.delegate = self + return result + }() + + private lazy var fadeView: UIView = { + let result = UIView() + let gradient = Gradients.transparentToBlack75 + result.setGradient(gradient) + result.isUserInteractionEnabled = false return result }() @@ -109,15 +103,16 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat } tableView.pin(.trailing, to: .trailing, of: view) tableView.pin(.bottom, to: .bottom, of: view) + view.addSubview(fadeView) + fadeView.pin(to: view) // Set up search bar // tableView.tableHeaderView = searchBar // searchBar.sizeToFit() // tableView.contentOffset = CGPoint(x: 0, y: searchBar.frame.height) - // Set up new conversation button - newConversationButton.addTarget(self, action: #selector(createPrivateChat), for: UIControl.Event.touchUpInside) - view.addSubview(newConversationButton) - newConversationButton.center(.horizontal, in: view) - newConversationButton.pin(.bottom, to: .bottom, of: view, withInset: -Values.newConversationButtonBottomOffset) // Negative due to how the constraint is set up + // Set up new conversation button set + view.addSubview(newConversationButtonSet) + newConversationButtonSet.center(.horizontal, in: view) + newConversationButtonSet.pin(.bottom, to: .bottom, of: view, withInset: -Values.newConversationButtonBottomOffset) // Negative due to how the constraint is set up // Set up previewing if (traitCollection.forceTouchCapability == .available) { registerForPreviewing(with: self, sourceView: tableView) @@ -261,17 +256,6 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(openSettings)) profilePictureView.addGestureRecognizer(tapGestureRecognizer) navigationItem.leftBarButtonItem = UIBarButtonItem(customView: profilePictureView) - let newClosedGroupButton = UIButton(type: .custom) - newClosedGroupButton.setImage(#imageLiteral(resourceName: "btnGroup--white"), for: UIControl.State.normal) - newClosedGroupButton.addTarget(self, action: #selector(createClosedGroup), for: UIControl.Event.touchUpInside) - newClosedGroupButton.tintColor = Colors.text - let joinPublicChatButton = UIButton(type: .custom) - joinPublicChatButton.setImage(#imageLiteral(resourceName: "Globe"), for: UIControl.State.normal) - joinPublicChatButton.addTarget(self, action: #selector(joinPublicChat), for: UIControl.Event.touchUpInside) - joinPublicChatButton.tintColor = Colors.text - let buttonStackView = UIStackView(arrangedSubviews: [ newClosedGroupButton, joinPublicChatButton ]) - buttonStackView.axis = .horizontal - navigationItem.rightBarButtonItem = UIBarButtonItem(customView: buttonStackView) } // MARK: Interaction @@ -371,21 +355,21 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat present(navigationController, animated: true, completion: nil) } - @objc private func joinPublicChat() { + @objc func joinOpenGroup() { let joinPublicChatVC = JoinPublicChatVC() let navigationController = OWSNavigationController(rootViewController: joinPublicChatVC) present(navigationController, animated: true, completion: nil) } - @objc private func createClosedGroup() { - let newClosedGroupVC = NewClosedGroupVC() - let navigationController = OWSNavigationController(rootViewController: newClosedGroupVC) + @objc func createNewPrivateChat() { + let newPrivateChatVC = NewPrivateChatVC() + let navigationController = OWSNavigationController(rootViewController: newPrivateChatVC) present(navigationController, animated: true, completion: nil) } - @objc func createPrivateChat() { - let newPrivateChatVC = NewPrivateChatVC() - let navigationController = OWSNavigationController(rootViewController: newPrivateChatVC) + @objc func createNewClosedGroup() { + let newClosedGroupVC = NewClosedGroupVC() + let navigationController = OWSNavigationController(rootViewController: newClosedGroupVC) present(navigationController, animated: true, completion: nil) } diff --git a/Signal/src/Loki/View Controllers/NewClosedGroupVC.swift b/Signal/src/Loki/View Controllers/NewClosedGroupVC.swift index 4a7008b48..5d849b8ca 100644 --- a/Signal/src/Loki/View Controllers/NewClosedGroupVC.swift +++ b/Signal/src/Loki/View Controllers/NewClosedGroupVC.swift @@ -65,7 +65,8 @@ final class NewClosedGroupVC : UIViewController, UITableViewDataSource, UITableV let titleLabel = UILabel() titleLabel.text = NSLocalizedString("New Closed Group", comment: "") titleLabel.textColor = Colors.text - titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize) + let titleLabelFontSize = isSmallScreen ? Values.largeFontSize : Values.veryLargeFontSize + titleLabel.font = .boldSystemFont(ofSize: titleLabelFontSize) navigationItem.titleView = titleLabel // Set up content if !contacts.isEmpty { @@ -104,9 +105,9 @@ final class NewClosedGroupVC : UIViewController, UITableViewDataSource, UITableV explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.textAlignment = .center explanationLabel.text = NSLocalizedString("You don't have any contacts yet", comment: "") - let createNewPrivateChatButton = Button(style: .prominentOutline, size: .medium) + let createNewPrivateChatButton = Button(style: .prominentOutline, size: .large) createNewPrivateChatButton.setTitle(NSLocalizedString("Start a Session", comment: ""), for: UIControl.State.normal) - createNewPrivateChatButton.addTarget(self, action: #selector(createPrivateChat), for: UIControl.Event.touchUpInside) + createNewPrivateChatButton.addTarget(self, action: #selector(createNewPrivateChat), for: UIControl.Event.touchUpInside) createNewPrivateChatButton.set(.width, to: 160) let stackView = UIStackView(arrangedSubviews: [ explanationLabel, createNewPrivateChatButton ]) stackView.axis = .vertical @@ -200,9 +201,9 @@ final class NewClosedGroupVC : UIViewController, UITableViewDataSource, UITableV } } - @objc private func createPrivateChat() { + @objc private func createNewPrivateChat() { presentingViewController?.dismiss(animated: true, completion: nil) - SignalApp.shared().homeViewController!.createPrivateChat() + SignalApp.shared().homeViewController!.createNewPrivateChat() } } diff --git a/Signal/src/Loki/View Controllers/SeedVC.swift b/Signal/src/Loki/View Controllers/SeedVC.swift index e0fd439a0..0dd6d4cc2 100644 --- a/Signal/src/Loki/View Controllers/SeedVC.swift +++ b/Signal/src/Loki/View Controllers/SeedVC.swift @@ -71,7 +71,8 @@ final class SeedVC : UIViewController { let navigationBarTitleLabel = UILabel() navigationBarTitleLabel.text = NSLocalizedString("Your Recovery Phrase", comment: "") navigationBarTitleLabel.textColor = Colors.text - navigationBarTitleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize) + let titleLabelFontSize = isSmallScreen ? Values.largeFontSize : Values.veryLargeFontSize + navigationBarTitleLabel.font = .boldSystemFont(ofSize: titleLabelFontSize) navigationItem.titleView = navigationBarTitleLabel // Set up navigation bar buttons let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close)) @@ -145,7 +146,7 @@ final class SeedVC : UIViewController { let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, copyButtonContainer ]) mainStackView.axis = .vertical mainStackView.alignment = .fill - mainStackView.layoutMargins = UIEdgeInsets(top: 0, leading: 0, bottom: isSmallScreen ? Values.smallSpacing : Values.mediumSpacing, trailing: 0) + mainStackView.layoutMargins = UIEdgeInsets(top: 0, leading: 0, bottom: Values.mediumSpacing, trailing: 0) mainStackView.isLayoutMarginsRelativeArrangement = true view.addSubview(mainStackView) mainStackView.pin(.leading, to: .leading, of: view) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 68eace1e0..ee47a7191 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -1440,14 +1440,6 @@ typedef enum : NSUInteger { [self updateInputToolbarLayout]; [self ensureScrollDownButton]; - - NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults; - if ([@"Session Public Chat" isEqual:self.thread.name] && ![userDefaults boolForKey:@"hasSeenSessionPublicChatNotice"]) { - UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"The Rules" message:[LKGeneralUtilities getSessionPublicChatNotice] preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; - [self presentViewController:alert animated:YES completion:nil]; - [userDefaults setBool:YES forKey:@"hasSeenSessionPublicChatNotice"]; - } } // `viewWillDisappear` is called whenever the view *starts* to disappear, From 82bbc5369d7c410935f6d933141613df80672f84 Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Tue, 3 Mar 2020 16:05:04 +1100 Subject: [PATCH 3/8] deal with confilcts --- Signal/Signal-Info.plist | 2 +- Signal/src/Loki/View Controllers/HomeVC.swift | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index 342d37adb..9d7c02e10 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -7,7 +7,7 @@ CarthageVersion 0.34.0 OSXVersion - 10.15.3 + 10.15.2 WebRTCCommit 1445d719bf05280270e9f77576f80f973fd847f8 M73 diff --git a/Signal/src/Loki/View Controllers/HomeVC.swift b/Signal/src/Loki/View Controllers/HomeVC.swift index 75817ac42..22fbffedc 100644 --- a/Signal/src/Loki/View Controllers/HomeVC.swift +++ b/Signal/src/Loki/View Controllers/HomeVC.swift @@ -104,15 +104,7 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat tableView.pin(.trailing, to: .trailing, of: view) tableView.pin(.bottom, to: .bottom, of: view) view.addSubview(fadeView) -<<<<<<< HEAD fadeView.pin(to: view) -======= - fadeView.pin(.leading, to: .leading, of: view) - let topInset = 0.15 * view.height() - fadeView.pin(.top, to: .top, of: view, withInset: topInset) - fadeView.pin(.trailing, to: .trailing, of: view) - fadeView.pin(.bottom, to: .bottom, of: view) ->>>>>>> b56fc0d01031a276b79be70736da8a48d720ebb6 // Set up search bar // tableView.tableHeaderView = searchBar // searchBar.sizeToFit() From 5bf9d0a68b42a8bf95b212de34748c003f2d5185 Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Tue, 3 Mar 2020 16:17:46 +1100 Subject: [PATCH 4/8] deal with the conflicts the right way.... --- Signal/src/Loki/View Controllers/HomeVC.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Signal/src/Loki/View Controllers/HomeVC.swift b/Signal/src/Loki/View Controllers/HomeVC.swift index 22fbffedc..5bc989837 100644 --- a/Signal/src/Loki/View Controllers/HomeVC.swift +++ b/Signal/src/Loki/View Controllers/HomeVC.swift @@ -104,7 +104,11 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat tableView.pin(.trailing, to: .trailing, of: view) tableView.pin(.bottom, to: .bottom, of: view) view.addSubview(fadeView) - fadeView.pin(to: view) + fadeView.pin(.leading, to: .leading, of: view) + let topInset = 0.15 * view.height() + fadeView.pin(.top, to: .top, of: view, withInset: topInset) + fadeView.pin(.trailing, to: .trailing, of: view) + fadeView.pin(.bottom, to: .bottom, of: view) // Set up search bar // tableView.tableHeaderView = searchBar // searchBar.sizeToFit() From 8a8634984a803c428ca944f78ce1e1316d30927a Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Tue, 3 Mar 2020 16:19:24 +1100 Subject: [PATCH 5/8] deal with the conflicts the right way... --- Signal/src/Loki/Style Guide/Gradients.swift | 2 +- Signal/src/Loki/View Controllers/HomeVC.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Signal/src/Loki/Style Guide/Gradients.swift b/Signal/src/Loki/Style Guide/Gradients.swift index 0060bf9e6..5cc573476 100644 --- a/Signal/src/Loki/Style Guide/Gradients.swift +++ b/Signal/src/Loki/Style Guide/Gradients.swift @@ -27,5 +27,5 @@ final class Gradient : NSObject { final class Gradients : NSObject { @objc static let defaultLokiBackground = Gradient(start: UIColor(hex: 0x171717), end: UIColor(hex: 0x121212)) - @objc static let transparentToBlack75 = Gradient(start: UIColor(red: 0, green: 0, blue: 0, alpha: 0), end: UIColor(red: 0, green: 0, blue: 0, alpha: 0.75)) + @objc static let transparentToBlack = Gradient(start: UIColor(red: 0, green: 0, blue: 0, alpha: 0), end: UIColor(red: 0, green: 0, blue: 0, alpha: 0.75)) } diff --git a/Signal/src/Loki/View Controllers/HomeVC.swift b/Signal/src/Loki/View Controllers/HomeVC.swift index 5bc989837..a509a24a0 100644 --- a/Signal/src/Loki/View Controllers/HomeVC.swift +++ b/Signal/src/Loki/View Controllers/HomeVC.swift @@ -53,7 +53,7 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat private lazy var fadeView: UIView = { let result = UIView() - let gradient = Gradients.transparentToBlack75 + let gradient = Gradients.transparentToBlack result.setGradient(gradient) result.isUserInteractionEnabled = false return result From abdbeda8db229f2938a1e7af9834103910bc6a5b Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Tue, 3 Mar 2020 16:20:17 +1100 Subject: [PATCH 6/8] finally right this time.... --- Signal/src/Loki/Style Guide/Gradients.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Signal/src/Loki/Style Guide/Gradients.swift b/Signal/src/Loki/Style Guide/Gradients.swift index 5cc573476..0f2ac23bb 100644 --- a/Signal/src/Loki/Style Guide/Gradients.swift +++ b/Signal/src/Loki/Style Guide/Gradients.swift @@ -27,5 +27,5 @@ final class Gradient : NSObject { final class Gradients : NSObject { @objc static let defaultLokiBackground = Gradient(start: UIColor(hex: 0x171717), end: UIColor(hex: 0x121212)) - @objc static let transparentToBlack = Gradient(start: UIColor(red: 0, green: 0, blue: 0, alpha: 0), end: UIColor(red: 0, green: 0, blue: 0, alpha: 0.75)) + @objc static let transparentToBlack = Gradient(start: UIColor(red: 0, green: 0, blue: 0, alpha: 0), end: UIColor(red: 0, green: 0, blue: 0, alpha: 1)) } From 3490f9a2b3dbecc65e80ff68dc295bb8be72f45f Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Wed, 4 Mar 2020 14:33:01 +1100 Subject: [PATCH 7/8] hide the threads with background friend requests after receiving sync contacts messages on slave device --- SignalServiceKit/src/Messages/OWSMessageManager.m | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index bf39441cd..16ea7fd0b 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -1089,8 +1089,19 @@ NS_ASSUME_NONNULL_BEGIN case LKThreadFriendRequestStatusNone: { OWSMessageSender *messageSender = SSKEnvironment.shared.messageSender; LKFriendRequestMessage *automatedFriendRequestMessage = [messageSender getMultiDeviceFriendRequestMessageForHexEncodedPublicKey:hexEncodedPublicKey transaction:transaction]; - [automatedFriendRequestMessage saveWithTransaction:transaction]; - [self.messageSenderJobQueue addMessage:automatedFriendRequestMessage transaction:transaction]; + thread.isForceHidden = true; + [thread saveWithTransaction:transaction]; + [messageSender sendMessage:automatedFriendRequestMessage + success:^{ + [automatedFriendRequestMessage remove]; + thread.isForceHidden = false; + [thread save]; + } + failure:^(NSError *error) { + [automatedFriendRequestMessage remove]; + thread.isForceHidden = false; + [thread save]; + }]; break; } case LKThreadFriendRequestStatusRequestReceived: { From 9d2cdef7507aaa7b5dd174b706600c33a4c09b6f Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Wed, 4 Mar 2020 15:12:22 +1100 Subject: [PATCH 8/8] fix up unlinking --- Signal/src/Loki/View Controllers/DeviceLinkingModal.swift | 3 +++ SignalServiceKit/src/Messages/OWSMessageSender.m | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Signal/src/Loki/View Controllers/DeviceLinkingModal.swift b/Signal/src/Loki/View Controllers/DeviceLinkingModal.swift index a2853b9da..31a33d0ab 100644 --- a/Signal/src/Loki/View Controllers/DeviceLinkingModal.swift +++ b/Signal/src/Loki/View Controllers/DeviceLinkingModal.swift @@ -168,6 +168,9 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate { SSKEnvironment.shared.syncManager.syncAllContacts() SSKEnvironment.shared.syncManager.syncAllGroups() SSKEnvironment.shared.syncManager.syncAllOpenGroups() + let thread = TSContactThread.getOrCreateThread(contactId: deviceLink.slave.hexEncodedPublicKey) + thread.friendRequestStatus = .friends + thread.save() }) { _ in print("[Loki] Failed to send device link authorization message.") } diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 388229e78..6b2e8096d 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -988,9 +988,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; BOOL isGroupMessage = messageSend.thread.isGroupThread; BOOL isPublicChatMessage = isGroupMessage && ((TSGroupThread *)messageSend.thread).isPublicChat; BOOL isDeviceLinkMessage = [message isKindOfClass:LKDeviceLinkMessage.class]; - BOOL isUnlinkDeviceMessage = [message isKindOfClass:LKUnlinkDeviceMessage.class]; - // FIXME: Clean this up - if (isPublicChatMessage || isDeviceLinkMessage || isUnlinkDeviceMessage) { + if (isPublicChatMessage || isDeviceLinkMessage) { [self sendMessage:messageSend]; } else { BOOL isSilentMessage = message.isSilent || [message isKindOfClass:LKEphemeralMessage.class] || [message isKindOfClass:OWSOutgoingSyncMessage.class];