Progressing on theming functionality
Created the ThemeManager and the system to control the dynamic theming Started updating the main settings screens Added the AppearanceViewController and connected it to the ThemeManager Started adding theme values Started applying theme values throughout
This commit is contained in:
parent
b40752dc78
commit
d56cee8234
4
Podfile
4
Podfile
|
@ -82,10 +82,10 @@ abstract_target 'GlobalDependencies' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# No dependencies for this
|
# No extra dependencies for this
|
||||||
target 'SessionUIKit'
|
target 'SessionUIKit'
|
||||||
|
end
|
||||||
|
|
||||||
# Actions to perform post-install
|
# Actions to perform post-install
|
||||||
post_install do |installer|
|
post_install do |installer|
|
||||||
|
|
|
@ -242,6 +242,6 @@ SPEC CHECKSUMS:
|
||||||
YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331
|
YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331
|
||||||
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
||||||
|
|
||||||
PODFILE CHECKSUM: f0857369c4831b2e5c1946345e76e493f3286805
|
PODFILE CHECKSUM: 3423ad813952f2c56952aa7118df68e7878c3c65
|
||||||
|
|
||||||
COCOAPODS: 1.11.3
|
COCOAPODS: 1.11.3
|
||||||
|
|
|
@ -202,7 +202,6 @@
|
||||||
B84A89BC25DE328A0040017D /* ProfilePictureVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84A89BB25DE328A0040017D /* ProfilePictureVC.swift */; };
|
B84A89BC25DE328A0040017D /* ProfilePictureVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84A89BB25DE328A0040017D /* ProfilePictureVC.swift */; };
|
||||||
B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */; };
|
B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */; };
|
||||||
B85357C323A1BD1200AAF6CD /* SeedVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85357C223A1BD1200AAF6CD /* SeedVC.swift */; };
|
B85357C323A1BD1200AAF6CD /* SeedVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85357C223A1BD1200AAF6CD /* SeedVC.swift */; };
|
||||||
B8544E3323D50E4900299F14 /* SNAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8544E3223D50E4900299F14 /* SNAppearance.swift */; };
|
|
||||||
B8569AC325CB5D2900DBA3DB /* ConversationVC+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AC225CB5D2900DBA3DB /* ConversationVC+Interaction.swift */; };
|
B8569AC325CB5D2900DBA3DB /* ConversationVC+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AC225CB5D2900DBA3DB /* ConversationVC+Interaction.swift */; };
|
||||||
B8569AE325CBB19A00DBA3DB /* DocumentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AE225CBB19A00DBA3DB /* DocumentView.swift */; };
|
B8569AE325CBB19A00DBA3DB /* DocumentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AE225CBB19A00DBA3DB /* DocumentView.swift */; };
|
||||||
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; };
|
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; };
|
||||||
|
@ -324,7 +323,7 @@
|
||||||
C331FFB92558FA8D00070591 /* UIView+Constraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */; };
|
C331FFB92558FA8D00070591 /* UIView+Constraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */; };
|
||||||
C331FFE02558FB0000070591 /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B02390C37000BA5194 /* SearchBar.swift */; };
|
C331FFE02558FB0000070591 /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B02390C37000BA5194 /* SearchBar.swift */; };
|
||||||
C331FFE32558FB0000070591 /* TabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF638239721E20091D419 /* TabBar.swift */; };
|
C331FFE32558FB0000070591 /* TabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF638239721E20091D419 /* TabBar.swift */; };
|
||||||
C331FFE42558FB0000070591 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B5BCEB2394D869003823C9 /* Button.swift */; };
|
C331FFE42558FB0000070591 /* OutlineButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B5BCEB2394D869003823C9 /* OutlineButton.swift */; };
|
||||||
C331FFE72558FB0000070591 /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B423947F2D00BA5194 /* TextField.swift */; };
|
C331FFE72558FB0000070591 /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B423947F2D00BA5194 /* TextField.swift */; };
|
||||||
C331FFE82558FB0000070591 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C3CF8824D8EED300E1CCE7 /* TextView.swift */; };
|
C331FFE82558FB0000070591 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C3CF8824D8EED300E1CCE7 /* TextView.swift */; };
|
||||||
C331FFE92558FB0000070591 /* Separator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B82394911B00BA5194 /* Separator.swift */; };
|
C331FFE92558FB0000070591 /* Separator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B82394911B00BA5194 /* Separator.swift */; };
|
||||||
|
@ -644,6 +643,19 @@
|
||||||
FD245C6B2850667400B966DD /* VisibleMessage+Profile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C300A5B12554AF9800555489 /* VisibleMessage+Profile.swift */; };
|
FD245C6B2850667400B966DD /* VisibleMessage+Profile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C300A5B12554AF9800555489 /* VisibleMessage+Profile.swift */; };
|
||||||
FD245C6C2850669200B966DD /* MessageReceiveJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352A31225574F5200338F3E /* MessageReceiveJob.swift */; };
|
FD245C6C2850669200B966DD /* MessageReceiveJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352A31225574F5200338F3E /* MessageReceiveJob.swift */; };
|
||||||
FD245C6D285066A400B966DD /* NotifyPushServerJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352A32E2557549C00338F3E /* NotifyPushServerJob.swift */; };
|
FD245C6D285066A400B966DD /* NotifyPushServerJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352A32E2557549C00338F3E /* NotifyPushServerJob.swift */; };
|
||||||
|
FD37E9C328A1C6F3003AE748 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37E9C228A1C6F3003AE748 /* ThemeManager.swift */; };
|
||||||
|
FD37E9C628A1D4EC003AE748 /* Theme+ClassicDark.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37E9C528A1D4EC003AE748 /* Theme+ClassicDark.swift */; };
|
||||||
|
FD37E9C828A1D73F003AE748 /* Theme+PrimaryColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37E9C728A1D73F003AE748 /* Theme+PrimaryColors.swift */; };
|
||||||
|
FD37E9CA28A1E4BD003AE748 /* Theme+ClassicLight.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37E9C928A1E4BD003AE748 /* Theme+ClassicLight.swift */; };
|
||||||
|
FD37E9CC28A1E578003AE748 /* AppearanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37E9CB28A1E578003AE748 /* AppearanceViewController.swift */; };
|
||||||
|
FD37E9CF28A1EB1B003AE748 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37E9CE28A1EB1B003AE748 /* Theme.swift */; };
|
||||||
|
FD37E9D128A1F2EB003AE748 /* ThemeSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37E9D028A1F2EB003AE748 /* ThemeSelectionView.swift */; };
|
||||||
|
FD37E9D328A1FCDB003AE748 /* Theme+OceanDark.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37E9D228A1FCDB003AE748 /* Theme+OceanDark.swift */; };
|
||||||
|
FD37E9D528A1FCE8003AE748 /* Theme+OceanLight.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37E9D428A1FCE8003AE748 /* Theme+OceanLight.swift */; };
|
||||||
|
FD37E9D728A20B5D003AE748 /* UIColor+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37E9D628A20B5D003AE748 /* UIColor+Utilities.swift */; };
|
||||||
|
FD37E9D928A230F2003AE748 /* TraitObservingWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37E9D828A230F2003AE748 /* TraitObservingWindow.swift */; };
|
||||||
|
FD37E9DB28A244E9003AE748 /* ThemePreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37E9DA28A244E9003AE748 /* ThemePreviewView.swift */; };
|
||||||
|
FD37E9DD28A384EB003AE748 /* PrimaryColorSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37E9DC28A384EB003AE748 /* PrimaryColorSelectionView.swift */; };
|
||||||
FD3AABE928306BBD00E5099A /* ThreadPickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3AABE828306BBD00E5099A /* ThreadPickerViewModel.swift */; };
|
FD3AABE928306BBD00E5099A /* ThreadPickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3AABE828306BBD00E5099A /* ThreadPickerViewModel.swift */; };
|
||||||
FD3C905C27E3FBEF00CD579F /* BatchRequestInfoSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C905B27E3FBEF00CD579F /* BatchRequestInfoSpec.swift */; };
|
FD3C905C27E3FBEF00CD579F /* BatchRequestInfoSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C905B27E3FBEF00CD579F /* BatchRequestInfoSpec.swift */; };
|
||||||
FD3C906027E410F700CD579F /* FileUploadResponseSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C905F27E410F700CD579F /* FileUploadResponseSpec.swift */; };
|
FD3C906027E410F700CD579F /* FileUploadResponseSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C905F27E410F700CD579F /* FileUploadResponseSpec.swift */; };
|
||||||
|
@ -1235,7 +1247,6 @@
|
||||||
B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedReminderView.swift; sourceTree = "<group>"; };
|
B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedReminderView.swift; sourceTree = "<group>"; };
|
||||||
B85357C223A1BD1200AAF6CD /* SeedVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedVC.swift; sourceTree = "<group>"; };
|
B85357C223A1BD1200AAF6CD /* SeedVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedVC.swift; sourceTree = "<group>"; };
|
||||||
B8544E3023D16CA500299F14 /* DeviceUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceUtilities.swift; sourceTree = "<group>"; };
|
B8544E3023D16CA500299F14 /* DeviceUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceUtilities.swift; sourceTree = "<group>"; };
|
||||||
B8544E3223D50E4900299F14 /* SNAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SNAppearance.swift; sourceTree = "<group>"; };
|
|
||||||
B8569AC225CB5D2900DBA3DB /* ConversationVC+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConversationVC+Interaction.swift"; sourceTree = "<group>"; };
|
B8569AC225CB5D2900DBA3DB /* ConversationVC+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConversationVC+Interaction.swift"; sourceTree = "<group>"; };
|
||||||
B8569AE225CBB19A00DBA3DB /* DocumentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentView.swift; sourceTree = "<group>"; };
|
B8569AE225CBB19A00DBA3DB /* DocumentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentView.swift; sourceTree = "<group>"; };
|
||||||
B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = "<group>"; };
|
B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1263,7 +1274,7 @@
|
||||||
B8B320B6258C30D70020074B /* HTMLMetadata.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLMetadata.swift; sourceTree = "<group>"; };
|
B8B320B6258C30D70020074B /* HTMLMetadata.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLMetadata.swift; sourceTree = "<group>"; };
|
||||||
B8B558F026C4BB0600693325 /* CameraManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraManager.swift; sourceTree = "<group>"; };
|
B8B558F026C4BB0600693325 /* CameraManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraManager.swift; sourceTree = "<group>"; };
|
||||||
B8B558FE26C4E05E00693325 /* WebRTCSession+MessageHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WebRTCSession+MessageHandling.swift"; sourceTree = "<group>"; };
|
B8B558FE26C4E05E00693325 /* WebRTCSession+MessageHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WebRTCSession+MessageHandling.swift"; sourceTree = "<group>"; };
|
||||||
B8B5BCEB2394D869003823C9 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
|
B8B5BCEB2394D869003823C9 /* OutlineButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutlineButton.swift; sourceTree = "<group>"; };
|
||||||
B8BAC75B2695645400EA1759 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
B8BAC75B2695645400EA1759 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
B8BAC75C2695648500EA1759 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
|
B8BAC75C2695648500EA1759 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
B8BAC75D2695649000EA1759 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = "<group>"; };
|
B8BAC75D2695649000EA1759 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
@ -1683,6 +1694,19 @@
|
||||||
FD245C612850664300B966DD /* Configuration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
|
FD245C612850664300B966DD /* Configuration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
|
||||||
FD28A4F327EA79F800FF65E7 /* BlockListUIUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockListUIUtils.swift; sourceTree = "<group>"; };
|
FD28A4F327EA79F800FF65E7 /* BlockListUIUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockListUIUtils.swift; sourceTree = "<group>"; };
|
||||||
FD28A4F527EAD44C00FF65E7 /* Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; };
|
FD28A4F527EAD44C00FF65E7 /* Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; };
|
||||||
|
FD37E9C228A1C6F3003AE748 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = "<group>"; };
|
||||||
|
FD37E9C528A1D4EC003AE748 /* Theme+ClassicDark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+ClassicDark.swift"; sourceTree = "<group>"; };
|
||||||
|
FD37E9C728A1D73F003AE748 /* Theme+PrimaryColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+PrimaryColors.swift"; sourceTree = "<group>"; };
|
||||||
|
FD37E9C928A1E4BD003AE748 /* Theme+ClassicLight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+ClassicLight.swift"; sourceTree = "<group>"; };
|
||||||
|
FD37E9CB28A1E578003AE748 /* AppearanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceViewController.swift; sourceTree = "<group>"; };
|
||||||
|
FD37E9CE28A1EB1B003AE748 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
|
||||||
|
FD37E9D028A1F2EB003AE748 /* ThemeSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeSelectionView.swift; sourceTree = "<group>"; };
|
||||||
|
FD37E9D228A1FCDB003AE748 /* Theme+OceanDark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+OceanDark.swift"; sourceTree = "<group>"; };
|
||||||
|
FD37E9D428A1FCE8003AE748 /* Theme+OceanLight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+OceanLight.swift"; sourceTree = "<group>"; };
|
||||||
|
FD37E9D628A20B5D003AE748 /* UIColor+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Utilities.swift"; sourceTree = "<group>"; };
|
||||||
|
FD37E9D828A230F2003AE748 /* TraitObservingWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TraitObservingWindow.swift; sourceTree = "<group>"; };
|
||||||
|
FD37E9DA28A244E9003AE748 /* ThemePreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemePreviewView.swift; sourceTree = "<group>"; };
|
||||||
|
FD37E9DC28A384EB003AE748 /* PrimaryColorSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryColorSelectionView.swift; sourceTree = "<group>"; };
|
||||||
FD3AABE828306BBD00E5099A /* ThreadPickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadPickerViewModel.swift; sourceTree = "<group>"; };
|
FD3AABE828306BBD00E5099A /* ThreadPickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadPickerViewModel.swift; sourceTree = "<group>"; };
|
||||||
FD3C905B27E3FBEF00CD579F /* BatchRequestInfoSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatchRequestInfoSpec.swift; sourceTree = "<group>"; };
|
FD3C905B27E3FBEF00CD579F /* BatchRequestInfoSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatchRequestInfoSpec.swift; sourceTree = "<group>"; };
|
||||||
FD3C905F27E410F700CD579F /* FileUploadResponseSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUploadResponseSpec.swift; sourceTree = "<group>"; };
|
FD3C905F27E410F700CD579F /* FileUploadResponseSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUploadResponseSpec.swift; sourceTree = "<group>"; };
|
||||||
|
@ -2110,7 +2134,7 @@
|
||||||
4C586924224FAB83003FD070 /* AVAudioSession+OWS.h */,
|
4C586924224FAB83003FD070 /* AVAudioSession+OWS.h */,
|
||||||
4C586925224FAB83003FD070 /* AVAudioSession+OWS.m */,
|
4C586925224FAB83003FD070 /* AVAudioSession+OWS.m */,
|
||||||
4521C3BF1F59F3BA00B4C582 /* TextFieldHelper.swift */,
|
4521C3BF1F59F3BA00B4C582 /* TextFieldHelper.swift */,
|
||||||
B8544E3223D50E4900299F14 /* SNAppearance.swift */,
|
FD37E9D828A230F2003AE748 /* TraitObservingWindow.swift */,
|
||||||
C3D0972A2510499C00F6E3E4 /* BackgroundPoller.swift */,
|
C3D0972A2510499C00F6E3E4 /* BackgroundPoller.swift */,
|
||||||
C31A6C5B247F2CF3001123EF /* CGRect+Utilities.swift */,
|
C31A6C5B247F2CF3001123EF /* CGRect+Utilities.swift */,
|
||||||
C35E8AAD2485E51D00ACB629 /* IP2Country.swift */,
|
C35E8AAD2485E51D00ACB629 /* IP2Country.swift */,
|
||||||
|
@ -2672,7 +2696,9 @@
|
||||||
C331FF5E2558FA0F00070591 /* Style Guide */ = {
|
C331FF5E2558FA0F00070591 /* Style Guide */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
FD37E9C428A1C701003AE748 /* Themes */,
|
||||||
B8C9689023FA1401005F64E0 /* AppMode.swift */,
|
B8C9689023FA1401005F64E0 /* AppMode.swift */,
|
||||||
|
FD37E9C228A1C6F3003AE748 /* ThemeManager.swift */,
|
||||||
C3310032255900A400070591 /* Notification+AppMode.swift */,
|
C3310032255900A400070591 /* Notification+AppMode.swift */,
|
||||||
B8BB829F238F322400BA5194 /* Colors.swift */,
|
B8BB829F238F322400BA5194 /* Colors.swift */,
|
||||||
C39DD28724F3318C008590FC /* Colors.xcassets */,
|
C39DD28724F3318C008590FC /* Colors.xcassets */,
|
||||||
|
@ -2687,6 +2713,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
B8544E3023D16CA500299F14 /* DeviceUtilities.swift */,
|
B8544E3023D16CA500299F14 /* DeviceUtilities.swift */,
|
||||||
|
FD37E9D628A20B5D003AE748 /* UIColor+Utilities.swift */,
|
||||||
C33100132558FFC200070591 /* UIImage+Tinting.swift */,
|
C33100132558FFC200070591 /* UIImage+Tinting.swift */,
|
||||||
B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */,
|
B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */,
|
||||||
C33100272559000A00070591 /* UIView+Rendering.swift */,
|
C33100272559000A00070591 /* UIView+Rendering.swift */,
|
||||||
|
@ -2697,7 +2724,7 @@
|
||||||
C331FFCC2558FAF300070591 /* Components */ = {
|
C331FFCC2558FAF300070591 /* Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
B8B5BCEB2394D869003823C9 /* Button.swift */,
|
B8B5BCEB2394D869003823C9 /* OutlineButton.swift */,
|
||||||
B8BB82B02390C37000BA5194 /* SearchBar.swift */,
|
B8BB82B02390C37000BA5194 /* SearchBar.swift */,
|
||||||
B8BB82B82394911B00BA5194 /* Separator.swift */,
|
B8BB82B82394911B00BA5194 /* Separator.swift */,
|
||||||
B8CCF638239721E20091D419 /* TabBar.swift */,
|
B8CCF638239721E20091D419 /* TabBar.swift */,
|
||||||
|
@ -2774,6 +2801,7 @@
|
||||||
C360969125AD1765008B62B2 /* Settings */ = {
|
C360969125AD1765008B62B2 /* Settings */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
FD37E9CD28A1E682003AE748 /* Views */,
|
||||||
340FC88B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.h */,
|
340FC88B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.h */,
|
||||||
340FC87B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.m */,
|
340FC87B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.m */,
|
||||||
340FC88A204DAC8C007AEB0F /* NotificationSettingsViewController.h */,
|
340FC88A204DAC8C007AEB0F /* NotificationSettingsViewController.h */,
|
||||||
|
@ -2787,6 +2815,7 @@
|
||||||
B886B4A62398B23E00211ABE /* QRCodeVC.swift */,
|
B886B4A62398B23E00211ABE /* QRCodeVC.swift */,
|
||||||
B86BD08523399CEF000F5AE3 /* SeedModal.swift */,
|
B86BD08523399CEF000F5AE3 /* SeedModal.swift */,
|
||||||
B8CCF6422397711F0091D419 /* SettingsVC.swift */,
|
B8CCF6422397711F0091D419 /* SettingsVC.swift */,
|
||||||
|
FD37E9CB28A1E578003AE748 /* AppearanceViewController.swift */,
|
||||||
FDE72117286C156E0093DF33 /* ChatSettingsViewController.swift */,
|
FDE72117286C156E0093DF33 /* ChatSettingsViewController.swift */,
|
||||||
7B7CB18A270591630079FF93 /* ShareLogsModal.swift */,
|
7B7CB18A270591630079FF93 /* ShareLogsModal.swift */,
|
||||||
);
|
);
|
||||||
|
@ -3567,6 +3596,29 @@
|
||||||
path = LegacyDatabase;
|
path = LegacyDatabase;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
FD37E9C428A1C701003AE748 /* Themes */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
FD37E9CE28A1EB1B003AE748 /* Theme.swift */,
|
||||||
|
FD37E9C728A1D73F003AE748 /* Theme+PrimaryColors.swift */,
|
||||||
|
FD37E9C528A1D4EC003AE748 /* Theme+ClassicDark.swift */,
|
||||||
|
FD37E9C928A1E4BD003AE748 /* Theme+ClassicLight.swift */,
|
||||||
|
FD37E9D228A1FCDB003AE748 /* Theme+OceanDark.swift */,
|
||||||
|
FD37E9D428A1FCE8003AE748 /* Theme+OceanLight.swift */,
|
||||||
|
);
|
||||||
|
path = Themes;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
FD37E9CD28A1E682003AE748 /* Views */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
FD37E9D028A1F2EB003AE748 /* ThemeSelectionView.swift */,
|
||||||
|
FD37E9DA28A244E9003AE748 /* ThemePreviewView.swift */,
|
||||||
|
FD37E9DC28A384EB003AE748 /* PrimaryColorSelectionView.swift */,
|
||||||
|
);
|
||||||
|
path = Views;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
FD3C905D27E410DB00CD579F /* Common Networking */ = {
|
FD3C905D27E410DB00CD579F /* Common Networking */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -4822,20 +4874,28 @@
|
||||||
files = (
|
files = (
|
||||||
C331FF972558FA6B00070591 /* Fonts.swift in Sources */,
|
C331FF972558FA6B00070591 /* Fonts.swift in Sources */,
|
||||||
C331FF9B2558FA6B00070591 /* Gradients.swift in Sources */,
|
C331FF9B2558FA6B00070591 /* Gradients.swift in Sources */,
|
||||||
|
FD37E9D328A1FCDB003AE748 /* Theme+OceanDark.swift in Sources */,
|
||||||
C331FFB82558FA8D00070591 /* DeviceUtilities.swift in Sources */,
|
C331FFB82558FA8D00070591 /* DeviceUtilities.swift in Sources */,
|
||||||
C331FFE72558FB0000070591 /* TextField.swift in Sources */,
|
C331FFE72558FB0000070591 /* TextField.swift in Sources */,
|
||||||
C331FFE32558FB0000070591 /* TabBar.swift in Sources */,
|
C331FFE32558FB0000070591 /* TabBar.swift in Sources */,
|
||||||
|
FD37E9D528A1FCE8003AE748 /* Theme+OceanLight.swift in Sources */,
|
||||||
|
FD37E9C828A1D73F003AE748 /* Theme+PrimaryColors.swift in Sources */,
|
||||||
|
FD37E9CF28A1EB1B003AE748 /* Theme.swift in Sources */,
|
||||||
C331FFB92558FA8D00070591 /* UIView+Constraints.swift in Sources */,
|
C331FFB92558FA8D00070591 /* UIView+Constraints.swift in Sources */,
|
||||||
C331FF962558FA6B00070591 /* Colors.swift in Sources */,
|
C331FF962558FA6B00070591 /* Colors.swift in Sources */,
|
||||||
C331FFE02558FB0000070591 /* SearchBar.swift in Sources */,
|
C331FFE02558FB0000070591 /* SearchBar.swift in Sources */,
|
||||||
C331FF982558FA6B00070591 /* AppMode.swift in Sources */,
|
C331FF982558FA6B00070591 /* AppMode.swift in Sources */,
|
||||||
C331FFE82558FB0000070591 /* TextView.swift in Sources */,
|
C331FFE82558FB0000070591 /* TextView.swift in Sources */,
|
||||||
|
FD37E9D728A20B5D003AE748 /* UIColor+Utilities.swift in Sources */,
|
||||||
|
FD37E9C328A1C6F3003AE748 /* ThemeManager.swift in Sources */,
|
||||||
C331FF9A2558FA6B00070591 /* Values.swift in Sources */,
|
C331FF9A2558FA6B00070591 /* Values.swift in Sources */,
|
||||||
C331FFE42558FB0000070591 /* Button.swift in Sources */,
|
FD37E9C628A1D4EC003AE748 /* Theme+ClassicDark.swift in Sources */,
|
||||||
|
C331FFE42558FB0000070591 /* OutlineButton.swift in Sources */,
|
||||||
C33100142558FFC200070591 /* UIImage+Tinting.swift in Sources */,
|
C33100142558FFC200070591 /* UIImage+Tinting.swift in Sources */,
|
||||||
C3310033255900A400070591 /* Notification+AppMode.swift in Sources */,
|
C3310033255900A400070591 /* Notification+AppMode.swift in Sources */,
|
||||||
C331FFE92558FB0000070591 /* Separator.swift in Sources */,
|
C331FFE92558FB0000070591 /* Separator.swift in Sources */,
|
||||||
C33100282559000A00070591 /* UIView+Rendering.swift in Sources */,
|
C33100282559000A00070591 /* UIView+Rendering.swift in Sources */,
|
||||||
|
FD37E9CA28A1E4BD003AE748 /* Theme+ClassicLight.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -5260,6 +5320,7 @@
|
||||||
C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */,
|
C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */,
|
||||||
7BAF54D027ACCEEC003D12F8 /* EmptySearchResultCell.swift in Sources */,
|
7BAF54D027ACCEEC003D12F8 /* EmptySearchResultCell.swift in Sources */,
|
||||||
B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */,
|
B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */,
|
||||||
|
FD37E9D928A230F2003AE748 /* TraitObservingWindow.swift in Sources */,
|
||||||
B893063F2383961A005EAA8E /* ScanQRCodeWrapperVC.swift in Sources */,
|
B893063F2383961A005EAA8E /* ScanQRCodeWrapperVC.swift in Sources */,
|
||||||
FD848B8F283EF2A8000E298B /* UIScrollView+Utilities.swift in Sources */,
|
FD848B8F283EF2A8000E298B /* UIScrollView+Utilities.swift in Sources */,
|
||||||
B879D449247E1BE300DB3608 /* PathVC.swift in Sources */,
|
B879D449247E1BE300DB3608 /* PathVC.swift in Sources */,
|
||||||
|
@ -5309,6 +5370,7 @@
|
||||||
B8D0A26925E4A2C200C1835E /* Onboarding.swift in Sources */,
|
B8D0A26925E4A2C200C1835E /* Onboarding.swift in Sources */,
|
||||||
34D1F0521F7E8EA30066283D /* GiphyDownloader.swift in Sources */,
|
34D1F0521F7E8EA30066283D /* GiphyDownloader.swift in Sources */,
|
||||||
4CC613362227A00400E21A3A /* ConversationSearch.swift in Sources */,
|
4CC613362227A00400E21A3A /* ConversationSearch.swift in Sources */,
|
||||||
|
FD37E9DD28A384EB003AE748 /* PrimaryColorSelectionView.swift in Sources */,
|
||||||
B82149B825D60393009C0F2A /* BlockedModal.swift in Sources */,
|
B82149B825D60393009C0F2A /* BlockedModal.swift in Sources */,
|
||||||
B82B408C239A068800A248E7 /* RegisterVC.swift in Sources */,
|
B82B408C239A068800A248E7 /* RegisterVC.swift in Sources */,
|
||||||
FDFDE126282D05380098B17F /* MediaInteractiveDismiss.swift in Sources */,
|
FDFDE126282D05380098B17F /* MediaInteractiveDismiss.swift in Sources */,
|
||||||
|
@ -5347,7 +5409,6 @@
|
||||||
34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */,
|
34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */,
|
||||||
7BAF54CF27ACCEEC003D12F8 /* GlobalSearchViewController.swift in Sources */,
|
7BAF54CF27ACCEEC003D12F8 /* GlobalSearchViewController.swift in Sources */,
|
||||||
B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */,
|
B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */,
|
||||||
B8544E3323D50E4900299F14 /* SNAppearance.swift in Sources */,
|
|
||||||
4C586926224FAB83003FD070 /* AVAudioSession+OWS.m in Sources */,
|
4C586926224FAB83003FD070 /* AVAudioSession+OWS.m in Sources */,
|
||||||
C331FFF42558FF0300070591 /* PNOptionView.swift in Sources */,
|
C331FFF42558FF0300070591 /* PNOptionView.swift in Sources */,
|
||||||
4C4AE6A1224AF35700D4AF6F /* SendMediaNavigationController.swift in Sources */,
|
4C4AE6A1224AF35700D4AF6F /* SendMediaNavigationController.swift in Sources */,
|
||||||
|
@ -5356,6 +5417,7 @@
|
||||||
7BAADFCC27B0EF23007BCF92 /* CallVideoView.swift in Sources */,
|
7BAADFCC27B0EF23007BCF92 /* CallVideoView.swift in Sources */,
|
||||||
B8CCF63F23975CFB0091D419 /* JoinOpenGroupVC.swift in Sources */,
|
B8CCF63F23975CFB0091D419 /* JoinOpenGroupVC.swift in Sources */,
|
||||||
FD848B9828422F1A000E298B /* Date+Utilities.swift in Sources */,
|
FD848B9828422F1A000E298B /* Date+Utilities.swift in Sources */,
|
||||||
|
FD37E9DB28A244E9003AE748 /* ThemePreviewView.swift in Sources */,
|
||||||
B85357C323A1BD1200AAF6CD /* SeedVC.swift in Sources */,
|
B85357C323A1BD1200AAF6CD /* SeedVC.swift in Sources */,
|
||||||
45B5360E206DD8BB00D61655 /* UIResponder+OWS.swift in Sources */,
|
45B5360E206DD8BB00D61655 /* UIResponder+OWS.swift in Sources */,
|
||||||
B8D84ECF25E3108A005A043E /* ExpandingAttachmentsButton.swift in Sources */,
|
B8D84ECF25E3108A005A043E /* ExpandingAttachmentsButton.swift in Sources */,
|
||||||
|
@ -5400,6 +5462,7 @@
|
||||||
B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */,
|
B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */,
|
||||||
C31A6C5A247F214E001123EF /* UIView+Glow.swift in Sources */,
|
C31A6C5A247F214E001123EF /* UIView+Glow.swift in Sources */,
|
||||||
4521C3C01F59F3BA00B4C582 /* TextFieldHelper.swift in Sources */,
|
4521C3C01F59F3BA00B4C582 /* TextFieldHelper.swift in Sources */,
|
||||||
|
FD37E9D128A1F2EB003AE748 /* ThemeSelectionView.swift in Sources */,
|
||||||
340FC8AC204DAC8D007AEB0F /* PrivacySettingsTableViewController.m in Sources */,
|
340FC8AC204DAC8D007AEB0F /* PrivacySettingsTableViewController.m in Sources */,
|
||||||
7B7CB18E270D066F0079FF93 /* IncomingCallBanner.swift in Sources */,
|
7B7CB18E270D066F0079FF93 /* IncomingCallBanner.swift in Sources */,
|
||||||
B8569AE325CBB19A00DBA3DB /* DocumentView.swift in Sources */,
|
B8569AE325CBB19A00DBA3DB /* DocumentView.swift in Sources */,
|
||||||
|
@ -5422,6 +5485,7 @@
|
||||||
FD1C98E4282E3C5B00B76F9E /* UINavigationBar+Utilities.swift in Sources */,
|
FD1C98E4282E3C5B00B76F9E /* UINavigationBar+Utilities.swift in Sources */,
|
||||||
C302093E25DCBF08001F572D /* MentionSelectionView.swift in Sources */,
|
C302093E25DCBF08001F572D /* MentionSelectionView.swift in Sources */,
|
||||||
C328251F25CA3A900062D0A7 /* QuoteView.swift in Sources */,
|
C328251F25CA3A900062D0A7 /* QuoteView.swift in Sources */,
|
||||||
|
FD37E9CC28A1E578003AE748 /* AppearanceViewController.swift in Sources */,
|
||||||
B8EB20F02640F7F000773E52 /* OpenGroupInvitationView.swift in Sources */,
|
B8EB20F02640F7F000773E52 /* OpenGroupInvitationView.swift in Sources */,
|
||||||
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */,
|
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */,
|
||||||
C328254025CA55880062D0A7 /* ContextMenuVC.swift in Sources */,
|
C328254025CA55880062D0A7 /* ContextMenuVC.swift in Sources */,
|
||||||
|
|
|
@ -45,8 +45,8 @@ final class EditClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegat
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var addMembersButton: Button = {
|
private lazy var addMembersButton: OutlineButton = {
|
||||||
let result: Button = Button(style: .prominentOutline, size: .large)
|
let result: OutlineButton = OutlineButton(style: .regular, size: .medium)
|
||||||
result.setTitle("Add Members", for: UIControl.State.normal)
|
result.setTitle("Add Members", for: UIControl.State.normal)
|
||||||
result.addTarget(self, action: #selector(addMembers), for: UIControl.Event.touchUpInside)
|
result.addTarget(self, action: #selector(addMembers), for: UIControl.Event.touchUpInside)
|
||||||
result.contentEdgeInsets = UIEdgeInsets(top: 0, leading: Values.mediumSpacing, bottom: 0, trailing: Values.mediumSpacing)
|
result.contentEdgeInsets = UIEdgeInsets(top: 0, leading: Values.mediumSpacing, bottom: 0, trailing: Values.mediumSpacing)
|
||||||
|
|
|
@ -67,16 +67,16 @@ final class NewClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegate
|
||||||
private func setUpViewHierarchy() {
|
private func setUpViewHierarchy() {
|
||||||
guard !contactProfiles.isEmpty else {
|
guard !contactProfiles.isEmpty else {
|
||||||
let explanationLabel: UILabel = UILabel()
|
let explanationLabel: UILabel = UILabel()
|
||||||
explanationLabel.textColor = Colors.text
|
|
||||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
explanationLabel.numberOfLines = 0
|
explanationLabel.text = "vc_create_closed_group_empty_state_message".localized()
|
||||||
explanationLabel.lineBreakMode = .byWordWrapping
|
explanationLabel.textColor = Colors.text
|
||||||
explanationLabel.textAlignment = .center
|
explanationLabel.textAlignment = .center
|
||||||
explanationLabel.text = NSLocalizedString("vc_create_closed_group_empty_state_message", comment: "")
|
explanationLabel.lineBreakMode = .byWordWrapping
|
||||||
|
explanationLabel.numberOfLines = 0
|
||||||
|
|
||||||
let createNewPrivateChatButton: Button = Button(style: .prominentOutline, size: .large)
|
let createNewPrivateChatButton: OutlineButton = OutlineButton(style: .regular, size: .medium)
|
||||||
createNewPrivateChatButton.setTitle(NSLocalizedString("vc_create_closed_group_empty_state_button_title", comment: ""), for: UIControl.State.normal)
|
createNewPrivateChatButton.setTitle("vc_create_closed_group_empty_state_button_title".localized(), for: .normal)
|
||||||
createNewPrivateChatButton.addTarget(self, action: #selector(createNewDM), for: UIControl.Event.touchUpInside)
|
createNewPrivateChatButton.addTarget(self, action: #selector(createNewDM), for: .touchUpInside)
|
||||||
createNewPrivateChatButton.set(.width, to: 196)
|
createNewPrivateChatButton.set(.width, to: 196)
|
||||||
|
|
||||||
let stackView: UIStackView = UIStackView(arrangedSubviews: [ explanationLabel, createNewPrivateChatButton ])
|
let stackView: UIStackView = UIStackView(arrangedSubviews: [ explanationLabel, createNewPrivateChatButton ])
|
||||||
|
|
|
@ -160,7 +160,7 @@ extension ConversationVC:
|
||||||
let documentPickerVC = UIDocumentPickerViewController(documentTypes: [ kUTTypeItem as String ], in: UIDocumentPickerMode.import)
|
let documentPickerVC = UIDocumentPickerViewController(documentTypes: [ kUTTypeItem as String ], in: UIDocumentPickerMode.import)
|
||||||
documentPickerVC.delegate = self
|
documentPickerVC.delegate = self
|
||||||
documentPickerVC.modalPresentationStyle = .fullScreen
|
documentPickerVC.modalPresentationStyle = .fullScreen
|
||||||
SNAppearance.switchToDocumentPickerAppearance()
|
|
||||||
present(documentPickerVC, animated: true, completion: nil)
|
present(documentPickerVC, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,12 +203,7 @@ extension ConversationVC:
|
||||||
|
|
||||||
// MARK: - UIDocumentPickerDelegate
|
// MARK: - UIDocumentPickerDelegate
|
||||||
|
|
||||||
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
|
|
||||||
SNAppearance.switchToSessionAppearance() // Switch back to the correct appearance
|
|
||||||
}
|
|
||||||
|
|
||||||
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
||||||
SNAppearance.switchToSessionAppearance()
|
|
||||||
guard let url = urls.first else { return } // TODO: Handle multiple?
|
guard let url = urls.first else { return } // TODO: Handle multiple?
|
||||||
|
|
||||||
let urlResourceValues: URLResourceValues
|
let urlResourceValues: URLResourceValues
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import SignalUtilitiesKit
|
import SignalUtilitiesKit
|
||||||
import SessionUtilitiesKit
|
import SessionUtilitiesKit
|
||||||
|
import SessionUIKit
|
||||||
|
|
||||||
final class DeletedMessageView: UIView {
|
final class DeletedMessageView: UIView {
|
||||||
private static let iconSize: CGFloat = 18
|
private static let iconSize: CGFloat = 18
|
||||||
|
@ -10,7 +11,7 @@ final class DeletedMessageView: UIView {
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
init(textColor: UIColor) {
|
init(textColor: ThemeValue) {
|
||||||
super.init(frame: CGRect.zero)
|
super.init(frame: CGRect.zero)
|
||||||
|
|
||||||
setUpViewHierarchy(textColor: textColor)
|
setUpViewHierarchy(textColor: textColor)
|
||||||
|
@ -24,7 +25,7 @@ final class DeletedMessageView: UIView {
|
||||||
preconditionFailure("Use init(textColor:) instead.")
|
preconditionFailure("Use init(textColor:) instead.")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setUpViewHierarchy(textColor: UIColor) {
|
private func setUpViewHierarchy(textColor: ThemeValue) {
|
||||||
// Image view
|
// Image view
|
||||||
let icon = UIImage(named: "ic_trash")?
|
let icon = UIImage(named: "ic_trash")?
|
||||||
.resizedImage(to: CGSize(
|
.resizedImage(to: CGSize(
|
||||||
|
@ -34,17 +35,17 @@ final class DeletedMessageView: UIView {
|
||||||
.withRenderingMode(.alwaysTemplate)
|
.withRenderingMode(.alwaysTemplate)
|
||||||
|
|
||||||
let imageView = UIImageView(image: icon)
|
let imageView = UIImageView(image: icon)
|
||||||
imageView.tintColor = textColor
|
imageView.themeTintColor = textColor
|
||||||
imageView.contentMode = .center
|
imageView.contentMode = .center
|
||||||
imageView.set(.width, to: DeletedMessageView.iconImageViewSize)
|
imageView.set(.width, to: DeletedMessageView.iconImageViewSize)
|
||||||
imageView.set(.height, to: DeletedMessageView.iconImageViewSize)
|
imageView.set(.height, to: DeletedMessageView.iconImageViewSize)
|
||||||
|
|
||||||
// Body label
|
// Body label
|
||||||
let titleLabel = UILabel()
|
let titleLabel = UILabel()
|
||||||
titleLabel.lineBreakMode = .byTruncatingTail
|
|
||||||
titleLabel.text = "message_deleted".localized()
|
|
||||||
titleLabel.textColor = textColor
|
|
||||||
titleLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
titleLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
|
titleLabel.text = "message_deleted".localized()
|
||||||
|
titleLabel.themeTextColor = textColor
|
||||||
|
titleLabel.lineBreakMode = .byTruncatingTail
|
||||||
|
|
||||||
// Stack view
|
// Stack view
|
||||||
let stackView = UIStackView(arrangedSubviews: [ imageView, titleLabel ])
|
let stackView = UIStackView(arrangedSubviews: [ imageView, titleLabel ])
|
||||||
|
|
|
@ -9,7 +9,7 @@ final class DocumentView: UIView {
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
init(attachment: Attachment, textColor: UIColor) {
|
init(attachment: Attachment, textColor: ThemeValue) {
|
||||||
super.init(frame: CGRect.zero)
|
super.init(frame: CGRect.zero)
|
||||||
|
|
||||||
setUpViewHierarchy(attachment: attachment, textColor: textColor)
|
setUpViewHierarchy(attachment: attachment, textColor: textColor)
|
||||||
|
@ -23,10 +23,10 @@ final class DocumentView: UIView {
|
||||||
preconditionFailure("Use init(viewItem:textColor:) instead.")
|
preconditionFailure("Use init(viewItem:textColor:) instead.")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setUpViewHierarchy(attachment: Attachment, textColor: UIColor) {
|
private func setUpViewHierarchy(attachment: Attachment, textColor: ThemeValue) {
|
||||||
// Image view
|
// Image view
|
||||||
let imageView = UIImageView(image: UIImage(named: "File")?.withRenderingMode(.alwaysTemplate))
|
let imageView = UIImageView(image: UIImage(named: "File")?.withRenderingMode(.alwaysTemplate))
|
||||||
imageView.tintColor = textColor
|
imageView.themeTintColor = textColor
|
||||||
imageView.contentMode = .center
|
imageView.contentMode = .center
|
||||||
|
|
||||||
let iconImageViewSize = DocumentView.iconImageViewSize
|
let iconImageViewSize = DocumentView.iconImageViewSize
|
||||||
|
@ -35,17 +35,17 @@ final class DocumentView: UIView {
|
||||||
|
|
||||||
// Body label
|
// Body label
|
||||||
let titleLabel = UILabel()
|
let titleLabel = UILabel()
|
||||||
titleLabel.lineBreakMode = .byTruncatingTail
|
|
||||||
titleLabel.text = (attachment.sourceFilename ?? "File")
|
|
||||||
titleLabel.textColor = textColor
|
|
||||||
titleLabel.font = .systemFont(ofSize: Values.smallFontSize, weight: .light)
|
titleLabel.font = .systemFont(ofSize: Values.smallFontSize, weight: .light)
|
||||||
|
titleLabel.text = (attachment.sourceFilename ?? "File")
|
||||||
|
titleLabel.themeTextColor = textColor
|
||||||
|
titleLabel.lineBreakMode = .byTruncatingTail
|
||||||
|
|
||||||
// Size label
|
// Size label
|
||||||
let sizeLabel = UILabel()
|
let sizeLabel = UILabel()
|
||||||
sizeLabel.lineBreakMode = .byTruncatingTail
|
|
||||||
sizeLabel.text = OWSFormat.formatFileSize(UInt(attachment.byteCount))
|
|
||||||
sizeLabel.textColor = textColor
|
|
||||||
sizeLabel.font = .systemFont(ofSize: Values.verySmallFontSize)
|
sizeLabel.font = .systemFont(ofSize: Values.verySmallFontSize)
|
||||||
|
sizeLabel.text = OWSFormat.formatFileSize(UInt(attachment.byteCount))
|
||||||
|
sizeLabel.themeTextColor = textColor
|
||||||
|
sizeLabel.lineBreakMode = .byTruncatingTail
|
||||||
|
|
||||||
// Label stack view
|
// Label stack view
|
||||||
let labelStackView = UIStackView(arrangedSubviews: [ titleLabel, sizeLabel ])
|
let labelStackView = UIStackView(arrangedSubviews: [ titleLabel, sizeLabel ])
|
||||||
|
|
|
@ -131,7 +131,7 @@ final class LinkPreviewView: UIView {
|
||||||
isOutgoing: Bool,
|
isOutgoing: Bool,
|
||||||
delegate: (UITextViewDelegate & BodyTextViewDelegate)? = nil,
|
delegate: (UITextViewDelegate & BodyTextViewDelegate)? = nil,
|
||||||
cellViewModel: MessageViewModel? = nil,
|
cellViewModel: MessageViewModel? = nil,
|
||||||
bodyLabelTextColor: UIColor? = nil,
|
bodyLabelTextColor: ThemeValue? = nil,
|
||||||
lastSearchText: String? = nil
|
lastSearchText: String? = nil
|
||||||
) {
|
) {
|
||||||
cancelButton.removeFromSuperview()
|
cancelButton.removeFromSuperview()
|
||||||
|
@ -139,7 +139,7 @@ final class LinkPreviewView: UIView {
|
||||||
var image: UIImage? = state.image
|
var image: UIImage? = state.image
|
||||||
let stateHasImage: Bool = (image != nil)
|
let stateHasImage: Bool = (image != nil)
|
||||||
if image == nil && (state is LinkPreview.DraftState || state is LinkPreview.SentState) {
|
if image == nil && (state is LinkPreview.DraftState || state is LinkPreview.SentState) {
|
||||||
image = UIImage(named: "Link")?.withTint(isLightMode ? .black : .white)
|
image = UIImage(named: "Link")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image view
|
// Image view
|
||||||
|
@ -156,6 +156,7 @@ final class LinkPreviewView: UIView {
|
||||||
}
|
}
|
||||||
|
|
||||||
imageView.image = image
|
imageView.image = image
|
||||||
|
imageView.themeTintColor = .textPrimary
|
||||||
imageView.contentMode = (stateHasImage ? .scaleAspectFill : .center)
|
imageView.contentMode = (stateHasImage ? .scaleAspectFill : .center)
|
||||||
|
|
||||||
// Loader
|
// Loader
|
||||||
|
@ -190,7 +191,7 @@ final class LinkPreviewView: UIView {
|
||||||
let bodyTextView = VisibleMessageCell.getBodyTextView(
|
let bodyTextView = VisibleMessageCell.getBodyTextView(
|
||||||
for: cellViewModel,
|
for: cellViewModel,
|
||||||
with: maxWidth,
|
with: maxWidth,
|
||||||
textColor: (bodyLabelTextColor ?? sentLinkPreviewTextColor),
|
textColor: (bodyLabelTextColor ?? .textPrimary),
|
||||||
searchText: lastSearchText,
|
searchText: lastSearchText,
|
||||||
delegate: delegate
|
delegate: delegate
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import SessionMessagingKit
|
import SessionMessagingKit
|
||||||
|
import SessionUIKit
|
||||||
|
|
||||||
final class MediaPlaceholderView: UIView {
|
final class MediaPlaceholderView: UIView {
|
||||||
private static let iconSize: CGFloat = 24
|
private static let iconSize: CGFloat = 24
|
||||||
|
@ -9,7 +10,7 @@ final class MediaPlaceholderView: UIView {
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
init(cellViewModel: MessageViewModel, textColor: UIColor) {
|
init(cellViewModel: MessageViewModel, textColor: ThemeValue) {
|
||||||
super.init(frame: CGRect.zero)
|
super.init(frame: CGRect.zero)
|
||||||
|
|
||||||
setUpViewHierarchy(cellViewModel: cellViewModel, textColor: textColor)
|
setUpViewHierarchy(cellViewModel: cellViewModel, textColor: textColor)
|
||||||
|
@ -25,7 +26,7 @@ final class MediaPlaceholderView: UIView {
|
||||||
|
|
||||||
private func setUpViewHierarchy(
|
private func setUpViewHierarchy(
|
||||||
cellViewModel: MessageViewModel,
|
cellViewModel: MessageViewModel,
|
||||||
textColor: UIColor
|
textColor: ThemeValue
|
||||||
) {
|
) {
|
||||||
let (iconName, attachmentDescription): (String, String) = {
|
let (iconName, attachmentDescription): (String, String) = {
|
||||||
guard
|
guard
|
||||||
|
@ -52,17 +53,17 @@ final class MediaPlaceholderView: UIView {
|
||||||
)?
|
)?
|
||||||
.withRenderingMode(.alwaysTemplate)
|
.withRenderingMode(.alwaysTemplate)
|
||||||
)
|
)
|
||||||
imageView.tintColor = textColor
|
imageView.themeTintColor = textColor
|
||||||
imageView.contentMode = .center
|
imageView.contentMode = .center
|
||||||
imageView.set(.width, to: MediaPlaceholderView.iconImageViewSize)
|
imageView.set(.width, to: MediaPlaceholderView.iconImageViewSize)
|
||||||
imageView.set(.height, to: MediaPlaceholderView.iconImageViewSize)
|
imageView.set(.height, to: MediaPlaceholderView.iconImageViewSize)
|
||||||
|
|
||||||
// Body label
|
// Body label
|
||||||
let titleLabel = UILabel()
|
let titleLabel = UILabel()
|
||||||
titleLabel.lineBreakMode = .byTruncatingTail
|
|
||||||
titleLabel.text = "Tap to download \(attachmentDescription)"
|
|
||||||
titleLabel.textColor = textColor
|
|
||||||
titleLabel.font = .systemFont(ofSize: Values.mediumFontSize)
|
titleLabel.font = .systemFont(ofSize: Values.mediumFontSize)
|
||||||
|
titleLabel.text = "Tap to download \(attachmentDescription)"
|
||||||
|
titleLabel.themeTextColor = textColor
|
||||||
|
titleLabel.lineBreakMode = .byTruncatingTail
|
||||||
|
|
||||||
// Stack view
|
// Stack view
|
||||||
let stackView = UIStackView(arrangedSubviews: [ imageView, titleLabel ])
|
let stackView = UIStackView(arrangedSubviews: [ imageView, titleLabel ])
|
||||||
|
|
|
@ -10,7 +10,7 @@ final class OpenGroupInvitationView: UIView {
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
init(name: String, url: String, textColor: UIColor, isOutgoing: Bool) {
|
init(name: String, url: String, textColor: ThemeValue, isOutgoing: Bool) {
|
||||||
super.init(frame: CGRect.zero)
|
super.init(frame: CGRect.zero)
|
||||||
|
|
||||||
setUpViewHierarchy(
|
setUpViewHierarchy(
|
||||||
|
@ -29,24 +29,24 @@ final class OpenGroupInvitationView: UIView {
|
||||||
preconditionFailure("Use init(name:url:textColor:) instead.")
|
preconditionFailure("Use init(name:url:textColor:) instead.")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setUpViewHierarchy(name: String, rawUrl: String, textColor: UIColor, isOutgoing: Bool) {
|
private func setUpViewHierarchy(name: String, rawUrl: String, textColor: ThemeValue, isOutgoing: Bool) {
|
||||||
// Title
|
// Title
|
||||||
let titleLabel = UILabel()
|
let titleLabel = UILabel()
|
||||||
titleLabel.lineBreakMode = .byTruncatingTail
|
|
||||||
titleLabel.text = name
|
|
||||||
titleLabel.textColor = textColor
|
|
||||||
titleLabel.font = .boldSystemFont(ofSize: Values.largeFontSize)
|
titleLabel.font = .boldSystemFont(ofSize: Values.largeFontSize)
|
||||||
|
titleLabel.text = name
|
||||||
|
titleLabel.themeTextColor = textColor
|
||||||
|
titleLabel.lineBreakMode = .byTruncatingTail
|
||||||
|
|
||||||
// Subtitle
|
// Subtitle
|
||||||
let subtitleLabel = UILabel()
|
let subtitleLabel = UILabel()
|
||||||
subtitleLabel.lineBreakMode = .byTruncatingTail
|
|
||||||
subtitleLabel.text = NSLocalizedString("view_open_group_invitation_description", comment: "")
|
|
||||||
subtitleLabel.textColor = textColor
|
|
||||||
subtitleLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
subtitleLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
|
subtitleLabel.text = "view_open_group_invitation_description".localized()
|
||||||
|
subtitleLabel.themeTextColor = textColor
|
||||||
|
subtitleLabel.lineBreakMode = .byTruncatingTail
|
||||||
|
|
||||||
// URL
|
// URL
|
||||||
let urlLabel = UILabel()
|
let urlLabel = UILabel()
|
||||||
urlLabel.lineBreakMode = .byCharWrapping
|
urlLabel.font = .systemFont(ofSize: Values.verySmallFontSize)
|
||||||
urlLabel.text = {
|
urlLabel.text = {
|
||||||
if let range = rawUrl.range(of: "?public_key=") {
|
if let range = rawUrl.range(of: "?public_key=") {
|
||||||
return String(rawUrl[..<range.lowerBound])
|
return String(rawUrl[..<range.lowerBound])
|
||||||
|
@ -54,9 +54,9 @@ final class OpenGroupInvitationView: UIView {
|
||||||
|
|
||||||
return rawUrl
|
return rawUrl
|
||||||
}()
|
}()
|
||||||
urlLabel.textColor = textColor
|
urlLabel.themeTextColor = textColor
|
||||||
|
urlLabel.lineBreakMode = .byCharWrapping
|
||||||
urlLabel.numberOfLines = 0
|
urlLabel.numberOfLines = 0
|
||||||
urlLabel.font = .systemFont(ofSize: Values.verySmallFontSize)
|
|
||||||
|
|
||||||
// Label stack
|
// Label stack
|
||||||
let labelStackView = UIStackView(arrangedSubviews: [ titleLabel, UIView.vSpacer(2), subtitleLabel, UIView.vSpacer(4), urlLabel ])
|
let labelStackView = UIStackView(arrangedSubviews: [ titleLabel, UIView.vSpacer(2), subtitleLabel, UIView.vSpacer(4), urlLabel ])
|
||||||
|
@ -71,11 +71,11 @@ final class OpenGroupInvitationView: UIView {
|
||||||
.resizedImage(to: CGSize(width: iconSize, height: iconSize))?
|
.resizedImage(to: CGSize(width: iconSize, height: iconSize))?
|
||||||
.withRenderingMode(.alwaysTemplate)
|
.withRenderingMode(.alwaysTemplate)
|
||||||
)
|
)
|
||||||
iconImageView.tintColor = .white
|
iconImageView.themeTintColor = .textPrimary
|
||||||
iconImageView.contentMode = .center
|
iconImageView.contentMode = .center
|
||||||
iconImageView.layer.cornerRadius = iconImageViewSize / 2
|
iconImageView.layer.cornerRadius = iconImageViewSize / 2
|
||||||
iconImageView.layer.masksToBounds = true
|
iconImageView.layer.masksToBounds = true
|
||||||
iconImageView.backgroundColor = Colors.accent
|
iconImageView.themeBackgroundColor = .primary
|
||||||
iconImageView.set(.width, to: iconImageViewSize)
|
iconImageView.set(.width, to: iconImageViewSize)
|
||||||
iconImageView.set(.height, to: iconImageViewSize)
|
iconImageView.set(.height, to: iconImageViewSize)
|
||||||
|
|
||||||
|
|
|
@ -119,15 +119,14 @@ final class QuoteView: UIView {
|
||||||
contentView.rightAnchor.constraint(lessThanOrEqualTo: self.rightAnchor).isActive = true
|
contentView.rightAnchor.constraint(lessThanOrEqualTo: self.rightAnchor).isActive = true
|
||||||
|
|
||||||
// Line view
|
// Line view
|
||||||
let lineColor: UIColor = {
|
let lineColor: ThemeValue = {
|
||||||
switch (mode, AppModeManager.shared.currentAppMode) {
|
switch mode {
|
||||||
case (.regular, .light), (.draft, .light): return .black
|
case .regular: return (direction == .outgoing ? .messageBubble_outgoingText : .primary)
|
||||||
case (.regular, .dark): return (direction == .outgoing) ? .black : Colors.accent
|
case .draft: return .primary
|
||||||
case (.draft, .dark): return Colors.accent
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
let lineView = UIView()
|
let lineView = UIView()
|
||||||
lineView.backgroundColor = lineColor
|
lineView.themeBackgroundColor = lineColor
|
||||||
lineView.set(.width, to: Values.accentLineThickness)
|
lineView.set(.width, to: Values.accentLineThickness)
|
||||||
|
|
||||||
if let attachment: Attachment = attachment {
|
if let attachment: Attachment = attachment {
|
||||||
|
@ -141,7 +140,7 @@ final class QuoteView: UIView {
|
||||||
|
|
||||||
imageView.tintColor = .white
|
imageView.tintColor = .white
|
||||||
imageView.contentMode = .center
|
imageView.contentMode = .center
|
||||||
imageView.backgroundColor = lineColor
|
imageView.themeBackgroundColor = lineColor
|
||||||
imageView.layer.cornerRadius = VisibleMessageCell.smallCornerRadius
|
imageView.layer.cornerRadius = VisibleMessageCell.smallCornerRadius
|
||||||
imageView.layer.masksToBounds = true
|
imageView.layer.masksToBounds = true
|
||||||
imageView.set(.width, to: thumbnailSize)
|
imageView.set(.width, to: thumbnailSize)
|
||||||
|
@ -180,14 +179,6 @@ final class QuoteView: UIView {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Body label
|
// Body label
|
||||||
let textColor: UIColor = {
|
|
||||||
guard mode != .draft else { return Colors.text }
|
|
||||||
|
|
||||||
switch (direction, AppModeManager.shared.currentAppMode) {
|
|
||||||
case (.outgoing, .dark), (.incoming, .light): return .black
|
|
||||||
default: return .white
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
let bodyLabel = UILabel()
|
let bodyLabel = UILabel()
|
||||||
bodyLabel.numberOfLines = 0
|
bodyLabel.numberOfLines = 0
|
||||||
bodyLabel.lineBreakMode = .byTruncatingTail
|
bodyLabel.lineBreakMode = .byTruncatingTail
|
||||||
|
@ -211,7 +202,10 @@ final class QuoteView: UIView {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.defaulting(to: NSAttributedString(string: "Document"))
|
.defaulting(to: NSAttributedString(string: "Document"))
|
||||||
bodyLabel.textColor = textColor
|
bodyLabel.themeTextColor = (direction == .outgoing ?
|
||||||
|
.messageBubble_outgoingText :
|
||||||
|
.messageBubble_incomingText
|
||||||
|
)
|
||||||
|
|
||||||
let bodyLabelSize = bodyLabel.systemLayoutSizeFitting(availableSpace)
|
let bodyLabelSize = bodyLabel.systemLayoutSizeFitting(availableSpace)
|
||||||
|
|
||||||
|
@ -226,7 +220,7 @@ final class QuoteView: UIView {
|
||||||
.asSet()
|
.asSet()
|
||||||
.contains(authorId)
|
.contains(authorId)
|
||||||
let authorLabel = UILabel()
|
let authorLabel = UILabel()
|
||||||
authorLabel.lineBreakMode = .byTruncatingTail
|
authorLabel.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
||||||
authorLabel.text = (isCurrentUser ?
|
authorLabel.text = (isCurrentUser ?
|
||||||
"MEDIA_GALLERY_SENDER_NAME_YOU".localized() :
|
"MEDIA_GALLERY_SENDER_NAME_YOU".localized() :
|
||||||
Profile.displayName(
|
Profile.displayName(
|
||||||
|
@ -234,11 +228,13 @@ final class QuoteView: UIView {
|
||||||
threadVariant: threadVariant
|
threadVariant: threadVariant
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
authorLabel.textColor = textColor
|
authorLabel.themeTextColor = .messageBubble_outgoingText
|
||||||
authorLabel.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
authorLabel.lineBreakMode = .byTruncatingTail
|
||||||
|
|
||||||
let authorLabelSize = authorLabel.systemLayoutSizeFitting(availableSpace)
|
let authorLabelSize = authorLabel.systemLayoutSizeFitting(availableSpace)
|
||||||
authorLabel.set(.height, to: authorLabelSize.height)
|
authorLabel.set(.height, to: authorLabelSize.height)
|
||||||
authorLabelHeight = authorLabelSize.height
|
authorLabelHeight = authorLabelSize.height
|
||||||
|
|
||||||
let labelStackView = UIStackView(arrangedSubviews: [ authorLabel, bodyLabel ])
|
let labelStackView = UIStackView(arrangedSubviews: [ authorLabel, bodyLabel ])
|
||||||
labelStackView.axis = .vertical
|
labelStackView.axis = .vertical
|
||||||
labelStackView.spacing = labelStackViewSpacing
|
labelStackView.spacing = labelStackViewSpacing
|
||||||
|
|
|
@ -94,11 +94,12 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
|
||||||
let size = VisibleMessageCell.replyButtonSize + 8
|
let size = VisibleMessageCell.replyButtonSize + 8
|
||||||
result.set(.width, to: size)
|
result.set(.width, to: size)
|
||||||
result.set(.height, to: size)
|
result.set(.height, to: size)
|
||||||
|
result.themeBorderColor = .textPrimary
|
||||||
result.layer.borderWidth = 1
|
result.layer.borderWidth = 1
|
||||||
result.layer.borderColor = Colors.text.cgColor
|
result.layer.cornerRadius = (size / 2)
|
||||||
result.layer.cornerRadius = size / 2
|
|
||||||
result.layer.masksToBounds = true
|
result.layer.masksToBounds = true
|
||||||
result.alpha = 0
|
result.alpha = 0
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -108,7 +109,8 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
|
||||||
result.set(.width, to: size)
|
result.set(.width, to: size)
|
||||||
result.set(.height, to: size)
|
result.set(.height, to: size)
|
||||||
result.image = UIImage(named: "ic_reply")?.withRenderingMode(.alwaysTemplate)
|
result.image = UIImage(named: "ic_reply")?.withRenderingMode(.alwaysTemplate)
|
||||||
result.tintColor = Colors.text
|
result.themeTintColor = .textPrimary
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -265,11 +267,13 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
|
||||||
cellViewModel.variant == .standardIncoming ||
|
cellViewModel.variant == .standardIncoming ||
|
||||||
cellViewModel.variant == .standardIncomingDeleted
|
cellViewModel.variant == .standardIncomingDeleted
|
||||||
)
|
)
|
||||||
bubbleView.backgroundColor = ((
|
|
||||||
|
let bubbleBackgroundColor: ThemeValue = ((
|
||||||
cellViewModel.variant == .standardIncoming ||
|
cellViewModel.variant == .standardIncoming ||
|
||||||
cellViewModel.variant == .standardIncomingDeleted
|
cellViewModel.variant == .standardIncomingDeleted
|
||||||
) ? Colors.receivedMessageBackground : Colors.sentMessageBackground)
|
) ? .messageBubble_incomingBackground : .messageBubble_outgoingBackground)
|
||||||
bubbleBackgroundView.backgroundColor = bubbleView.backgroundColor
|
bubbleView.themeBackgroundColor = bubbleBackgroundColor
|
||||||
|
bubbleBackgroundView.themeBackgroundColor = bubbleBackgroundColor
|
||||||
updateBubbleViewCorners()
|
updateBubbleViewCorners()
|
||||||
|
|
||||||
// Content view
|
// Content view
|
||||||
|
@ -286,9 +290,9 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
|
||||||
populateHeader(for: cellViewModel, shouldInsetHeader: shouldInsetHeader)
|
populateHeader(for: cellViewModel, shouldInsetHeader: shouldInsetHeader)
|
||||||
|
|
||||||
// Author label
|
// Author label
|
||||||
authorLabel.textColor = Colors.text
|
|
||||||
authorLabel.isHidden = (cellViewModel.senderName == nil)
|
authorLabel.isHidden = (cellViewModel.senderName == nil)
|
||||||
authorLabel.text = cellViewModel.senderName
|
authorLabel.text = cellViewModel.senderName
|
||||||
|
authorLabel.themeTextColor = .textPrimary
|
||||||
|
|
||||||
let authorLabelAvailableWidth: CGFloat = (VisibleMessageCell.getMaxWidth(for: cellViewModel) - 2 * VisibleMessageCell.authorLabelInset)
|
let authorLabelAvailableWidth: CGFloat = (VisibleMessageCell.getMaxWidth(for: cellViewModel) - 2 * VisibleMessageCell.authorLabelInset)
|
||||||
let authorLabelAvailableSpace = CGSize(width: authorLabelAvailableWidth, height: .greatestFiniteMagnitude)
|
let authorLabelAvailableSpace = CGSize(width: authorLabelAvailableWidth, height: .greatestFiniteMagnitude)
|
||||||
|
@ -298,8 +302,8 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
|
||||||
// Message status image view
|
// Message status image view
|
||||||
let (image, tintColor, backgroundColor) = getMessageStatusImage(for: cellViewModel)
|
let (image, tintColor, backgroundColor) = getMessageStatusImage(for: cellViewModel)
|
||||||
messageStatusImageView.image = image
|
messageStatusImageView.image = image
|
||||||
messageStatusImageView.tintColor = tintColor
|
messageStatusImageView.themeTintColor = tintColor
|
||||||
messageStatusImageView.backgroundColor = backgroundColor
|
messageStatusImageView.themeBackgroundColor = backgroundColor
|
||||||
messageStatusImageView.isHidden = (
|
messageStatusImageView.isHidden = (
|
||||||
cellViewModel.variant != .standardOutgoing ||
|
cellViewModel.variant != .standardOutgoing ||
|
||||||
cellViewModel.variant == .infoCall ||
|
cellViewModel.variant == .infoCall ||
|
||||||
|
@ -323,9 +327,9 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
|
||||||
|
|
||||||
timerView.configure(
|
timerView.configure(
|
||||||
withExpirationTimestamp: UInt64(floor(expirationTimestampMs)),
|
withExpirationTimestamp: UInt64(floor(expirationTimestampMs)),
|
||||||
initialDurationSeconds: UInt32(floor(expiresInSeconds)),
|
initialDurationSeconds: UInt32(floor(expiresInSeconds))
|
||||||
tintColor: Colors.text
|
|
||||||
)
|
)
|
||||||
|
timerView.themeTintColor = .textPrimary
|
||||||
timerView.isHidden = false
|
timerView.isHidden = false
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -352,9 +356,9 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
|
||||||
|
|
||||||
let dateBreakLabel: UILabel = UILabel()
|
let dateBreakLabel: UILabel = UILabel()
|
||||||
dateBreakLabel.font = .boldSystemFont(ofSize: Values.verySmallFontSize)
|
dateBreakLabel.font = .boldSystemFont(ofSize: Values.verySmallFontSize)
|
||||||
dateBreakLabel.textColor = Colors.text
|
|
||||||
dateBreakLabel.textAlignment = .center
|
|
||||||
dateBreakLabel.text = date.formattedForDisplay
|
dateBreakLabel.text = date.formattedForDisplay
|
||||||
|
dateBreakLabel.themeTextColor = .textPrimary
|
||||||
|
dateBreakLabel.textAlignment = .center
|
||||||
headerView.addSubview(dateBreakLabel)
|
headerView.addSubview(dateBreakLabel)
|
||||||
dateBreakLabel.pin(.top, to: .top, of: headerView, withInset: Values.smallSpacing)
|
dateBreakLabel.pin(.top, to: .top, of: headerView, withInset: Values.smallSpacing)
|
||||||
|
|
||||||
|
@ -374,19 +378,11 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
|
||||||
playbackInfo: ConversationViewModel.PlaybackInfo?,
|
playbackInfo: ConversationViewModel.PlaybackInfo?,
|
||||||
lastSearchText: String?
|
lastSearchText: String?
|
||||||
) {
|
) {
|
||||||
let bodyLabelTextColor: UIColor = {
|
let bodyLabelTextColor: ThemeValue = (cellViewModel.variant == .standardOutgoing ?
|
||||||
let direction: Direction = (cellViewModel.variant == .standardOutgoing ?
|
.messageBubble_outgoingText :
|
||||||
.outgoing :
|
.messageBubble_incomingText
|
||||||
.incoming
|
|
||||||
)
|
)
|
||||||
|
|
||||||
switch (direction, AppModeManager.shared.currentAppMode) {
|
|
||||||
case (.outgoing, .dark), (.incoming, .light): return .black
|
|
||||||
case (.outgoing, .light): return Colors.grey
|
|
||||||
default: return .white
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
snContentView.subviews.forEach { $0.removeFromSuperview() }
|
snContentView.subviews.forEach { $0.removeFromSuperview() }
|
||||||
albumView = nil
|
albumView = nil
|
||||||
bodyTextView = nil
|
bodyTextView = nil
|
||||||
|
@ -842,21 +838,21 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getMessageStatusImage(for cellViewModel: MessageViewModel) -> (image: UIImage?, tintColor: UIColor?, backgroundColor: UIColor?) {
|
private func getMessageStatusImage(for cellViewModel: MessageViewModel) -> (image: UIImage?, tintColor: ThemeValue?, backgroundColor: ThemeValue?) {
|
||||||
guard cellViewModel.variant == .standardOutgoing else { return (nil, nil, nil) }
|
guard cellViewModel.variant == .standardOutgoing else { return (nil, nil, nil) }
|
||||||
|
|
||||||
let image: UIImage
|
let image: UIImage
|
||||||
var tintColor: UIColor? = nil
|
var tintColor: ThemeValue? = nil
|
||||||
var backgroundColor: UIColor? = nil
|
var backgroundColor: ThemeValue? = nil
|
||||||
|
|
||||||
switch (cellViewModel.state, cellViewModel.hasAtLeastOneReadReceipt) {
|
switch (cellViewModel.state, cellViewModel.hasAtLeastOneReadReceipt) {
|
||||||
case (.sending, _):
|
case (.sending, _):
|
||||||
image = #imageLiteral(resourceName: "CircleDotDotDot").withRenderingMode(.alwaysTemplate)
|
image = #imageLiteral(resourceName: "CircleDotDotDot").withRenderingMode(.alwaysTemplate)
|
||||||
tintColor = Colors.text
|
tintColor = .textPrimary
|
||||||
|
|
||||||
case (.sent, false), (.skipped, _):
|
case (.sent, false), (.skipped, _):
|
||||||
image = #imageLiteral(resourceName: "CircleCheck").withRenderingMode(.alwaysTemplate)
|
image = #imageLiteral(resourceName: "CircleCheck").withRenderingMode(.alwaysTemplate)
|
||||||
tintColor = Colors.text
|
tintColor = .textPrimary
|
||||||
|
|
||||||
case (.sent, true):
|
case (.sent, true):
|
||||||
image = isLightMode ? #imageLiteral(resourceName: "FilledCircleCheckLightMode") : #imageLiteral(resourceName: "FilledCircleCheckDarkMode")
|
image = isLightMode ? #imageLiteral(resourceName: "FilledCircleCheckLightMode") : #imageLiteral(resourceName: "FilledCircleCheckDarkMode")
|
||||||
|
@ -864,7 +860,7 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
|
||||||
|
|
||||||
case (.failed, _):
|
case (.failed, _):
|
||||||
image = #imageLiteral(resourceName: "message_status_failed").withRenderingMode(.alwaysTemplate)
|
image = #imageLiteral(resourceName: "message_status_failed").withRenderingMode(.alwaysTemplate)
|
||||||
tintColor = Colors.destructive
|
tintColor = .danger
|
||||||
}
|
}
|
||||||
|
|
||||||
return (image, tintColor, backgroundColor)
|
return (image, tintColor, backgroundColor)
|
||||||
|
@ -939,7 +935,7 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
|
||||||
static func getBodyTextView(
|
static func getBodyTextView(
|
||||||
for cellViewModel: MessageViewModel,
|
for cellViewModel: MessageViewModel,
|
||||||
with availableWidth: CGFloat,
|
with availableWidth: CGFloat,
|
||||||
textColor: UIColor,
|
textColor: ThemeValue,
|
||||||
searchText: String?,
|
searchText: String?,
|
||||||
delegate: (UITextViewDelegate & BodyTextViewDelegate)?
|
delegate: (UITextViewDelegate & BodyTextViewDelegate)?
|
||||||
) -> UITextView {
|
) -> UITextView {
|
||||||
|
@ -953,6 +949,24 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
|
||||||
let isOutgoing: Bool = (cellViewModel.variant == .standardOutgoing)
|
let isOutgoing: Bool = (cellViewModel.variant == .standardOutgoing)
|
||||||
let result: BodyTextView = BodyTextView(snDelegate: delegate)
|
let result: BodyTextView = BodyTextView(snDelegate: delegate)
|
||||||
result.isEditable = false
|
result.isEditable = false
|
||||||
|
result.dataDetectorTypes = .link
|
||||||
|
result.backgroundColor = .clear
|
||||||
|
result.isOpaque = false
|
||||||
|
result.textContainerInset = UIEdgeInsets.zero
|
||||||
|
result.contentInset = UIEdgeInsets.zero
|
||||||
|
result.textContainer.lineFragmentPadding = 0
|
||||||
|
result.isScrollEnabled = false
|
||||||
|
result.isUserInteractionEnabled = true
|
||||||
|
result.delegate = delegate
|
||||||
|
|
||||||
|
ThemeManager.onThemeChange(observer: result) { [weak result] theme, _ in
|
||||||
|
guard
|
||||||
|
let actualTextColor: UIColor = theme.colors[textColor],
|
||||||
|
let backgroundPrimaryColor: UIColor = theme.colors[.backgroundPrimary],
|
||||||
|
let textPrimaryColor: UIColor = theme.colors[.textPrimary]
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
let hasPreviousSetText: Bool = ((result?.attributedText?.length ?? 0) > 0)
|
||||||
|
|
||||||
let attributedText: NSMutableAttributedString = NSMutableAttributedString(
|
let attributedText: NSMutableAttributedString = NSMutableAttributedString(
|
||||||
attributedString: MentionUtilities.highlightMentions(
|
attributedString: MentionUtilities.highlightMentions(
|
||||||
|
@ -962,7 +976,7 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
|
||||||
currentUserBlindedPublicKey: cellViewModel.currentUserBlindedPublicKey,
|
currentUserBlindedPublicKey: cellViewModel.currentUserBlindedPublicKey,
|
||||||
isOutgoingMessage: isOutgoing,
|
isOutgoingMessage: isOutgoing,
|
||||||
attributes: [
|
attributes: [
|
||||||
.foregroundColor : textColor,
|
.foregroundColor: actualTextColor,
|
||||||
.font: UIFont.systemFont(ofSize: getFontSize(for: cellViewModel))
|
.font: UIFont.systemFont(ofSize: getFontSize(for: cellViewModel))
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -991,30 +1005,23 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
|
||||||
)
|
)
|
||||||
.forEach { range in
|
.forEach { range in
|
||||||
let legacyRange: NSRange = NSRange(range, in: normalizedBody)
|
let legacyRange: NSRange = NSRange(range, in: normalizedBody)
|
||||||
attributedText.addAttribute(.backgroundColor, value: UIColor.white, range: legacyRange)
|
attributedText.addAttribute(.backgroundColor, value: backgroundPrimaryColor, range: legacyRange)
|
||||||
attributedText.addAttribute(.foregroundColor, value: UIColor.black, range: legacyRange)
|
attributedText.addAttribute(.foregroundColor, value: textPrimaryColor, range: legacyRange)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
result?.attributedText = attributedText
|
||||||
result.attributedText = attributedText
|
result?.linkTextAttributes = [
|
||||||
result.dataDetectorTypes = .link
|
.foregroundColor: actualTextColor,
|
||||||
result.backgroundColor = .clear
|
|
||||||
result.isOpaque = false
|
|
||||||
result.textContainerInset = UIEdgeInsets.zero
|
|
||||||
result.contentInset = UIEdgeInsets.zero
|
|
||||||
result.textContainer.lineFragmentPadding = 0
|
|
||||||
result.isScrollEnabled = false
|
|
||||||
result.isUserInteractionEnabled = true
|
|
||||||
result.delegate = delegate
|
|
||||||
result.linkTextAttributes = [
|
|
||||||
.foregroundColor: textColor,
|
|
||||||
.underlineStyle: NSUnderlineStyle.single.rawValue
|
.underlineStyle: NSUnderlineStyle.single.rawValue
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if let result: BodyTextView = result, !hasPreviousSetText {
|
||||||
let availableSpace = CGSize(width: availableWidth, height: .greatestFiniteMagnitude)
|
let availableSpace = CGSize(width: availableWidth, height: .greatestFiniteMagnitude)
|
||||||
let size = result.sizeThatFits(availableSpace)
|
let size = result.sizeThatFits(availableSpace)
|
||||||
result.set(.height, to: size.height)
|
result.set(.height, to: size.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
@interface OWSMessageTimerView : UIView
|
@interface OWSMessageTimerView : UIView
|
||||||
|
|
||||||
|
@property (nonatomic) UIImageView *imageView;
|
||||||
|
|
||||||
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
||||||
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
|
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
|
||||||
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
|
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
|
||||||
|
|
||||||
- (void)configureWithExpirationTimestamp:(uint64_t)expirationTimestamp
|
- (void)configureWithExpirationTimestamp:(uint64_t)expirationTimestamp
|
||||||
initialDurationSeconds:(uint32_t)initialDurationSeconds
|
initialDurationSeconds:(uint32_t)initialDurationSeconds;
|
||||||
tintColor:(UIColor *)tintColor;
|
|
||||||
|
|
||||||
- (void)prepareForReuse;
|
- (void)prepareForReuse;
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,6 @@ const CGFloat kDisappearingMessageIconSize = 12.f;
|
||||||
|
|
||||||
@property (nonatomic) uint32_t initialDurationSeconds;
|
@property (nonatomic) uint32_t initialDurationSeconds;
|
||||||
@property (nonatomic) uint64_t expirationTimestamp;
|
@property (nonatomic) uint64_t expirationTimestamp;
|
||||||
@property (nonatomic) UIColor *tintColor;
|
|
||||||
|
|
||||||
@property (nonatomic) UIImageView *imageView;
|
|
||||||
|
|
||||||
@property (nonatomic, nullable) NSTimer *animationTimer;
|
@property (nonatomic, nullable) NSTimer *animationTimer;
|
||||||
|
|
||||||
|
@ -62,11 +59,9 @@ const CGFloat kDisappearingMessageIconSize = 12.f;
|
||||||
|
|
||||||
- (void)configureWithExpirationTimestamp:(uint64_t)expirationTimestamp
|
- (void)configureWithExpirationTimestamp:(uint64_t)expirationTimestamp
|
||||||
initialDurationSeconds:(uint32_t)initialDurationSeconds
|
initialDurationSeconds:(uint32_t)initialDurationSeconds
|
||||||
tintColor:(UIColor *)tintColor;
|
|
||||||
{
|
{
|
||||||
self.expirationTimestamp = expirationTimestamp;
|
self.expirationTimestamp = expirationTimestamp;
|
||||||
self.initialDurationSeconds = initialDurationSeconds;
|
self.initialDurationSeconds = initialDurationSeconds;
|
||||||
self.tintColor = tintColor;
|
|
||||||
|
|
||||||
[self updateProgress12];
|
[self updateProgress12];
|
||||||
[self updateIcon];
|
[self updateIcon];
|
||||||
|
@ -107,7 +102,6 @@ const CGFloat kDisappearingMessageIconSize = 12.f;
|
||||||
- (void)updateIcon
|
- (void)updateIcon
|
||||||
{
|
{
|
||||||
self.imageView.image = [[self progressIcon] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
self.imageView.image = [[self progressIcon] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||||
self.imageView.tintColor = self.tintColor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIImage *)progressIcon
|
- (UIImage *)progressIcon
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import SessionUIKit
|
||||||
import SessionMessagingKit
|
import SessionMessagingKit
|
||||||
|
|
||||||
final class UserDetailsSheet: Sheet {
|
final class UserDetailsSheet: Sheet {
|
||||||
|
@ -60,8 +61,8 @@ final class UserDetailsSheet: Sheet {
|
||||||
sessionIDLabelContainer.layer.borderColor = isLightMode ? UIColor.black.cgColor : UIColor.white.cgColor
|
sessionIDLabelContainer.layer.borderColor = isLightMode ? UIColor.black.cgColor : UIColor.white.cgColor
|
||||||
|
|
||||||
// Copy button
|
// Copy button
|
||||||
let copyButton = Button(style: .prominentOutline, size: .medium)
|
let copyButton = OutlineButton(style: .regular, size: .medium)
|
||||||
copyButton.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
|
copyButton.setTitle("copy".localized(), for: .normal)
|
||||||
copyButton.addTarget(self, action: #selector(copySessionID), for: UIControl.Event.touchUpInside)
|
copyButton.addTarget(self, action: #selector(copySessionID), for: UIControl.Event.touchUpInside)
|
||||||
copyButton.set(.width, to: 160)
|
copyButton.set(.width, to: 160)
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import GRDB
|
import GRDB
|
||||||
import Curve25519Kit
|
import Curve25519Kit
|
||||||
|
import SessionUIKit
|
||||||
import SessionMessagingKit
|
import SessionMessagingKit
|
||||||
import SessionUtilitiesKit
|
import SessionUtilitiesKit
|
||||||
|
|
||||||
|
@ -14,11 +15,11 @@ final class NewDMVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControll
|
||||||
// MARK: Components
|
// MARK: Components
|
||||||
private lazy var tabBar: TabBar = {
|
private lazy var tabBar: TabBar = {
|
||||||
let tabs = [
|
let tabs = [
|
||||||
TabBar.Tab(title: NSLocalizedString("vc_create_private_chat_enter_session_id_tab_title", comment: "")) { [weak self] in
|
TabBar.Tab(title: "vc_create_private_chat_enter_session_id_tab_title".localized()) { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil)
|
self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil)
|
||||||
},
|
},
|
||||||
TabBar.Tab(title: NSLocalizedString("vc_create_private_chat_scan_qr_code_tab_title", comment: "")) { [weak self] in
|
TabBar.Tab(title: "vc_create_private_chat_scan_qr_code_tab_title".localized()) { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil)
|
self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil)
|
||||||
}
|
}
|
||||||
|
@ -201,10 +202,27 @@ private final class EnterPublicKeyVC : UIViewController {
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var copyButton: Button = {
|
private lazy var copyButton: OutlineButton = {
|
||||||
let result = Button(style: .unimportant, size: .medium)
|
let result = OutlineButton(style: .regular, size: .small)
|
||||||
result.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
|
result.setTitle("copy".lowercased(), for: UIControl.State.normal)
|
||||||
result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside)
|
result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var shareButton: OutlineButton = {
|
||||||
|
let result = OutlineButton(style: .regular, size: .small)
|
||||||
|
result.setTitle("share".lowercased(), for: UIControl.State.normal)
|
||||||
|
result.addTarget(self, action: #selector(sharePublicKey), for: UIControl.Event.touchUpInside)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var nextButton: OutlineButton = {
|
||||||
|
let result = OutlineButton(style: .regular, size: .small)
|
||||||
|
result.setTitle("next".lowercased(), for: UIControl.State.normal)
|
||||||
|
result.addTarget(self, action: #selector(startNewDMIfPossible), for: UIControl.Event.touchUpInside)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -241,8 +259,10 @@ private final class EnterPublicKeyVC : UIViewController {
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
// Remove background color
|
// Remove background color
|
||||||
view.backgroundColor = .clear
|
view.backgroundColor = .clear
|
||||||
|
|
||||||
// User session id container
|
// User session id container
|
||||||
let userPublicKeyContainer = UIView(wrapping: userPublicKeyLabel, withInsets: .zero, shouldAdaptForIPadWithWidth: Values.iPadUserSessionIdContainerWidth)
|
let userPublicKeyContainer = UIView(wrapping: userPublicKeyLabel, withInsets: .zero, shouldAdaptForIPadWithWidth: Values.iPadUserSessionIdContainerWidth)
|
||||||
|
|
||||||
// Explanation label
|
// Explanation label
|
||||||
let explanationLabel = UILabel()
|
let explanationLabel = UILabel()
|
||||||
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
|
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
|
||||||
|
@ -251,18 +271,13 @@ private final class EnterPublicKeyVC : UIViewController {
|
||||||
explanationLabel.numberOfLines = 0
|
explanationLabel.numberOfLines = 0
|
||||||
explanationLabel.textAlignment = .center
|
explanationLabel.textAlignment = .center
|
||||||
explanationLabel.lineBreakMode = .byWordWrapping
|
explanationLabel.lineBreakMode = .byWordWrapping
|
||||||
// Share button
|
|
||||||
let shareButton = Button(style: .unimportant, size: .medium)
|
|
||||||
shareButton.setTitle(NSLocalizedString("share", comment: ""), for: UIControl.State.normal)
|
|
||||||
shareButton.addTarget(self, action: #selector(sharePublicKey), for: UIControl.Event.touchUpInside)
|
|
||||||
// Button container
|
// Button container
|
||||||
buttonContainer.addArrangedSubview(copyButton)
|
buttonContainer.addArrangedSubview(copyButton)
|
||||||
buttonContainer.addArrangedSubview(shareButton)
|
buttonContainer.addArrangedSubview(shareButton)
|
||||||
// Next button
|
|
||||||
let nextButton = Button(style: .prominentOutline, size: .large)
|
|
||||||
nextButton.setTitle(NSLocalizedString("next", comment: ""), for: UIControl.State.normal)
|
|
||||||
nextButton.addTarget(self, action: #selector(startNewDMIfPossible), for: UIControl.Event.touchUpInside)
|
|
||||||
let nextButtonContainer = UIView(wrapping: nextButton, withInsets: UIEdgeInsets(top: 0, leading: 80, bottom: 0, trailing: 80), shouldAdaptForIPadWithWidth: Values.iPadButtonWidth)
|
let nextButtonContainer = UIView(wrapping: nextButton, withInsets: UIEdgeInsets(top: 0, leading: 80, bottom: 0, trailing: 80), shouldAdaptForIPadWithWidth: Values.iPadButtonWidth)
|
||||||
|
|
||||||
// Main stack view
|
// Main stack view
|
||||||
let mainStackView = UIStackView(arrangedSubviews: [ publicKeyTextView, UIView.spacer(withHeight: Values.smallSpacing), explanationLabel, spacer1, separator, spacer2, userPublicKeyContainer, spacer3, buttonContainer, UIView.vStretchingSpacer(), nextButtonContainer ])
|
let mainStackView = UIStackView(arrangedSubviews: [ publicKeyTextView, UIView.spacer(withHeight: Values.smallSpacing), explanationLabel, spacer1, separator, spacer2, userPublicKeyContainer, spacer3, buttonContainer, UIView.vStretchingSpacer(), nextButtonContainer ])
|
||||||
mainStackView.axis = .vertical
|
mainStackView.axis = .vertical
|
||||||
|
@ -274,11 +289,14 @@ private final class EnterPublicKeyVC : UIViewController {
|
||||||
mainStackView.pin(.top, to: .top, of: view)
|
mainStackView.pin(.top, to: .top, of: view)
|
||||||
view.pin(.trailing, to: .trailing, of: mainStackView)
|
view.pin(.trailing, to: .trailing, of: mainStackView)
|
||||||
bottomConstraint = view.pin(.bottom, to: .bottom, of: mainStackView, withInset: bottomMargin)
|
bottomConstraint = view.pin(.bottom, to: .bottom, of: mainStackView, withInset: bottomMargin)
|
||||||
|
|
||||||
// Width constraint
|
// Width constraint
|
||||||
view.set(.width, to: UIScreen.main.bounds.width)
|
view.set(.width, to: UIScreen.main.bounds.width)
|
||||||
|
|
||||||
// Dismiss keyboard on tap
|
// Dismiss keyboard on tap
|
||||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
|
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
|
||||||
view.addGestureRecognizer(tapGestureRecognizer)
|
view.addGestureRecognizer(tapGestureRecognizer)
|
||||||
|
|
||||||
// Listen to keyboard notifications
|
// Listen to keyboard notifications
|
||||||
let notificationCenter = NotificationCenter.default
|
let notificationCenter = NotificationCenter.default
|
||||||
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillChangeFrameNotification(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
|
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillChangeFrameNotification(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
|
||||||
|
|
|
@ -108,16 +108,18 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, NewConve
|
||||||
|
|
||||||
private lazy var emptyStateView: UIView = {
|
private lazy var emptyStateView: UIView = {
|
||||||
let explanationLabel = UILabel()
|
let explanationLabel = UILabel()
|
||||||
explanationLabel.textColor = Colors.text
|
|
||||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
explanationLabel.numberOfLines = 0
|
explanationLabel.text = "vc_home_empty_state_message".localized()
|
||||||
explanationLabel.lineBreakMode = .byWordWrapping
|
explanationLabel.textColor = Colors.text
|
||||||
explanationLabel.textAlignment = .center
|
explanationLabel.textAlignment = .center
|
||||||
explanationLabel.text = NSLocalizedString("vc_home_empty_state_message", comment: "")
|
explanationLabel.lineBreakMode = .byWordWrapping
|
||||||
let createNewPrivateChatButton = Button(style: .prominentOutline, size: .large)
|
explanationLabel.numberOfLines = 0
|
||||||
createNewPrivateChatButton.setTitle(NSLocalizedString("vc_home_empty_state_button_title", comment: ""), for: UIControl.State.normal)
|
|
||||||
createNewPrivateChatButton.addTarget(self, action: #selector(createNewDM), for: UIControl.Event.touchUpInside)
|
let createNewPrivateChatButton = OutlineButton(style: .regular, size: .large)
|
||||||
|
createNewPrivateChatButton.setTitle("vc_home_empty_state_button_title".localized(), for: .normal)
|
||||||
|
createNewPrivateChatButton.addTarget(self, action: #selector(createNewDM), for: .touchUpInside)
|
||||||
createNewPrivateChatButton.set(.width, to: Values.iPadButtonWidth)
|
createNewPrivateChatButton.set(.width, to: Values.iPadButtonWidth)
|
||||||
|
|
||||||
let result = UIStackView(arrangedSubviews: [ explanationLabel, createNewPrivateChatButton ])
|
let result = UIStackView(arrangedSubviews: [ explanationLabel, createNewPrivateChatButton ])
|
||||||
result.axis = .vertical
|
result.axis = .vertical
|
||||||
result.spacing = Values.mediumSpacing
|
result.spacing = Values.mediumSpacing
|
||||||
|
|
|
@ -78,16 +78,10 @@ class MessageRequestsViewController: BaseVC, UITableViewDelegate, UITableViewDat
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var clearAllButton: Button = {
|
private lazy var clearAllButton: OutlineButton = {
|
||||||
let result: Button = Button(style: .destructiveOutline, size: .large)
|
let result: OutlineButton = OutlineButton(style: .destructive, size: .medium)
|
||||||
result.translatesAutoresizingMaskIntoConstraints = false
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
result.setTitle(NSLocalizedString("MESSAGE_REQUESTS_CLEAR_ALL", comment: ""), for: .normal)
|
result.setTitle("MESSAGE_REQUESTS_CLEAR_ALL".localized(), for: .normal)
|
||||||
result.setBackgroundImage(
|
|
||||||
Colors.destructive
|
|
||||||
.withAlphaComponent(isDarkMode ? 0.2 : 0.06)
|
|
||||||
.toImage(isDarkMode: isDarkMode),
|
|
||||||
for: .highlighted
|
|
||||||
)
|
|
||||||
result.addTarget(self, action: #selector(clearAllTapped), for: .touchUpInside)
|
result.addTarget(self, action: #selector(clearAllTapped), for: .touchUpInside)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -23,9 +23,7 @@ class MediaGalleryNavigationController: OWSNavigationController {
|
||||||
|
|
||||||
// MARK: - View Lifecycle
|
// MARK: - View Lifecycle
|
||||||
|
|
||||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
override var preferredStatusBarStyle: UIStatusBarStyle { return ThemeManager.currentTheme.statusBarStyle }
|
||||||
return (isLightMode ? .default : .lightContent)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
|
@ -41,9 +41,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||||
// This block will be cleared in storageIsReady.
|
// This block will be cleared in storageIsReady.
|
||||||
DeviceSleepManager.sharedInstance.addBlock(blockObject: self)
|
DeviceSleepManager.sharedInstance.addBlock(blockObject: self)
|
||||||
|
|
||||||
let mainWindow: UIWindow = UIWindow(frame: UIScreen.main.bounds)
|
let mainWindow: UIWindow = TraitObservingWindow(frame: UIScreen.main.bounds)
|
||||||
self.loadingViewController = LoadingViewController()
|
self.loadingViewController = LoadingViewController()
|
||||||
|
|
||||||
|
// Store a weak reference in the ThemeManager so it can properly apply themes as needed
|
||||||
|
ThemeManager.mainWindow = mainWindow
|
||||||
|
|
||||||
AppSetup.setupEnvironment(
|
AppSetup.setupEnvironment(
|
||||||
appSpecificBlock: {
|
appSpecificBlock: {
|
||||||
// Create AppEnvironment
|
// Create AppEnvironment
|
||||||
|
@ -76,8 +79,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
SNAppearance.switchToSessionAppearance()
|
|
||||||
|
|
||||||
// No point continuing if we are running tests
|
// No point continuing if we are running tests
|
||||||
guard !CurrentAppContext().isRunningTests else { return true }
|
guard !CurrentAppContext().isRunningTests else { return true }
|
||||||
|
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -653,7 +653,7 @@
|
||||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||||
"CHATS_TITLE" = "Chats";
|
"CONVERSATIONS_TITLE" = "Conversations";
|
||||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||||
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
|
||||||
|
@ -684,3 +684,12 @@
|
||||||
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
|
||||||
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
|
||||||
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
|
||||||
|
"APPEARANCE_TITLE" = "Appearance";
|
||||||
|
"APPEARANCE_THEMES_TITLE" = "Themes";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_TITLE" = "Primary colour";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE" = "How are you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE" = "I'm good thanks, you?";
|
||||||
|
"APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE" = "I'm doing great, thanks.";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TITLE" = "Auto night-mode";
|
||||||
|
"APPEARANCE_NIGHT_MODE_TOGGLE" = "Match system settings";
|
||||||
|
"HELP_TITLE" = "Help";
|
||||||
|
|
|
@ -31,19 +31,19 @@ final class DisplayNameVC: BaseVC {
|
||||||
|
|
||||||
// Set up title label
|
// Set up title label
|
||||||
let titleLabel = UILabel()
|
let titleLabel = UILabel()
|
||||||
titleLabel.textColor = Colors.text
|
|
||||||
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
||||||
titleLabel.text = NSLocalizedString("vc_display_name_title_2", comment: "")
|
titleLabel.text = "vc_display_name_title_2".localized()
|
||||||
titleLabel.numberOfLines = 0
|
titleLabel.textColor = Colors.text
|
||||||
titleLabel.lineBreakMode = .byWordWrapping
|
titleLabel.lineBreakMode = .byWordWrapping
|
||||||
|
titleLabel.numberOfLines = 0
|
||||||
|
|
||||||
// Set up explanation label
|
// Set up explanation label
|
||||||
let explanationLabel = UILabel()
|
let explanationLabel = UILabel()
|
||||||
explanationLabel.textColor = Colors.text
|
|
||||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
explanationLabel.text = NSLocalizedString("vc_display_name_explanation", comment: "")
|
explanationLabel.text = "vc_display_name_explanation".localized()
|
||||||
explanationLabel.numberOfLines = 0
|
explanationLabel.textColor = Colors.text
|
||||||
explanationLabel.lineBreakMode = .byWordWrapping
|
explanationLabel.lineBreakMode = .byWordWrapping
|
||||||
|
explanationLabel.numberOfLines = 0
|
||||||
|
|
||||||
// Set up spacers
|
// Set up spacers
|
||||||
let topSpacer = UIView.vStretchingSpacer()
|
let topSpacer = UIView.vStretchingSpacer()
|
||||||
|
@ -56,9 +56,8 @@ final class DisplayNameVC: BaseVC {
|
||||||
registerButtonBottomOffsetConstraint = registerButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
|
registerButtonBottomOffsetConstraint = registerButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
|
||||||
|
|
||||||
// Set up register button
|
// Set up register button
|
||||||
let registerButton = Button(style: .prominentFilled, size: .large)
|
let registerButton = OutlineButton(style: .filled, size: .large)
|
||||||
registerButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
|
registerButton.setTitle("continue_2".localized(), for: UIControl.State.normal)
|
||||||
registerButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
|
||||||
registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
|
registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
|
||||||
|
|
||||||
// Set up register button container
|
// Set up register button container
|
||||||
|
|
|
@ -5,45 +5,52 @@ import SessionUIKit
|
||||||
|
|
||||||
final class LandingVC: BaseVC {
|
final class LandingVC: BaseVC {
|
||||||
|
|
||||||
// MARK: Components
|
// MARK: - Components
|
||||||
|
|
||||||
private lazy var fakeChatView: FakeChatView = {
|
private lazy var fakeChatView: FakeChatView = {
|
||||||
let result = FakeChatView()
|
let result = FakeChatView()
|
||||||
result.set(.height, to: LandingVC.fakeChatViewHeight)
|
result.set(.height, to: LandingVC.fakeChatViewHeight)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var registerButton: Button = {
|
private lazy var registerButton: OutlineButton = {
|
||||||
let result = Button(style: .prominentFilled, size: .large)
|
let result = OutlineButton(style: .filled, size: .large)
|
||||||
result.setTitle(NSLocalizedString("vc_landing_register_button_title", comment: ""), for: UIControl.State.normal)
|
result.setTitle("vc_landing_register_button_title".localized(), for: .normal)
|
||||||
result.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
result.addTarget(self, action: #selector(register), for: .touchUpInside)
|
||||||
result.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var restoreButton: Button = {
|
private lazy var restoreButton: OutlineButton = {
|
||||||
let result = Button(style: .prominentOutline, size: .large)
|
let result = OutlineButton(style: .regular, size: .large)
|
||||||
result.setTitle(NSLocalizedString("vc_landing_restore_button_title", comment: ""), for: UIControl.State.normal)
|
result.setTitle("vc_landing_restore_button_title".localized(), for: .normal)
|
||||||
result.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
result.addTarget(self, action: #selector(restore), for: .touchUpInside)
|
||||||
result.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside)
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// MARK: Settings
|
// MARK: - Settings
|
||||||
|
|
||||||
private static let fakeChatViewHeight = isIPhone5OrSmaller ? CGFloat(234) : CGFloat(260)
|
private static let fakeChatViewHeight = isIPhone5OrSmaller ? CGFloat(234) : CGFloat(260)
|
||||||
|
|
||||||
// MARK: Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
setUpGradientBackground()
|
setUpGradientBackground()
|
||||||
setUpNavBarStyle()
|
setUpNavBarStyle()
|
||||||
setUpNavBarSessionIcon()
|
setUpNavBarSessionIcon()
|
||||||
|
|
||||||
// Title label
|
// Title label
|
||||||
let titleLabel = UILabel()
|
let titleLabel = UILabel()
|
||||||
titleLabel.textColor = Colors.text
|
|
||||||
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
||||||
titleLabel.text = NSLocalizedString("vc_landing_title_2", comment: "")
|
titleLabel.text = "vc_landing_title_2".localized()
|
||||||
titleLabel.numberOfLines = 0
|
titleLabel.textColor = Colors.text
|
||||||
titleLabel.lineBreakMode = .byWordWrapping
|
titleLabel.lineBreakMode = .byWordWrapping
|
||||||
|
titleLabel.numberOfLines = 0
|
||||||
|
|
||||||
// Title label container
|
// Title label container
|
||||||
let titleLabelContainer = UIView()
|
let titleLabelContainer = UIView()
|
||||||
titleLabelContainer.addSubview(titleLabel)
|
titleLabelContainer.addSubview(titleLabel)
|
||||||
|
@ -51,22 +58,27 @@ final class LandingVC: BaseVC {
|
||||||
titleLabel.pin(.top, to: .top, of: titleLabelContainer)
|
titleLabel.pin(.top, to: .top, of: titleLabelContainer)
|
||||||
titleLabelContainer.pin(.trailing, to: .trailing, of: titleLabel, withInset: Values.veryLargeSpacing)
|
titleLabelContainer.pin(.trailing, to: .trailing, of: titleLabel, withInset: Values.veryLargeSpacing)
|
||||||
titleLabelContainer.pin(.bottom, to: .bottom, of: titleLabel)
|
titleLabelContainer.pin(.bottom, to: .bottom, of: titleLabel)
|
||||||
|
|
||||||
// Spacers
|
// Spacers
|
||||||
let topSpacer = UIView.vStretchingSpacer()
|
let topSpacer = UIView.vStretchingSpacer()
|
||||||
let bottomSpacer = UIView.vStretchingSpacer()
|
let bottomSpacer = UIView.vStretchingSpacer()
|
||||||
|
|
||||||
// Link button
|
// Link button
|
||||||
let linkButton = UIButton()
|
let linkButton = UIButton()
|
||||||
linkButton.setTitle(NSLocalizedString("vc_landing_link_button_title", comment: ""), for: UIControl.State.normal)
|
linkButton.titleLabel?.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
||||||
|
linkButton.setTitle("vc_landing_link_button_title".localized(), for: .normal)
|
||||||
linkButton.setTitleColor(Colors.text, for: UIControl.State.normal)
|
linkButton.setTitleColor(Colors.text, for: UIControl.State.normal)
|
||||||
linkButton.titleLabel!.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
linkButton.addTarget(self, action: #selector(link), for: .touchUpInside)
|
||||||
linkButton.addTarget(self, action: #selector(link), for: UIControl.Event.touchUpInside)
|
|
||||||
// Link button container
|
// Link button container
|
||||||
let linkButtonContainer = UIView()
|
let linkButtonContainer = UIView()
|
||||||
linkButtonContainer.set(.height, to: Values.onboardingButtonBottomOffset)
|
linkButtonContainer.set(.height, to: Values.onboardingButtonBottomOffset)
|
||||||
linkButtonContainer.addSubview(linkButton)
|
linkButtonContainer.addSubview(linkButton)
|
||||||
linkButton.center(.horizontal, in: linkButtonContainer)
|
linkButton.center(.horizontal, in: linkButtonContainer)
|
||||||
|
|
||||||
let isIPhoneX = (UIApplication.shared.keyWindow!.safeAreaInsets.bottom > 0)
|
let isIPhoneX = (UIApplication.shared.keyWindow!.safeAreaInsets.bottom > 0)
|
||||||
linkButton.centerYAnchor.constraint(equalTo: linkButtonContainer.centerYAnchor, constant: isIPhoneX ? -4 : 0).isActive = true
|
linkButton.centerYAnchor.constraint(equalTo: linkButtonContainer.centerYAnchor, constant: isIPhoneX ? -4 : 0).isActive = true
|
||||||
|
|
||||||
// Button stack view
|
// Button stack view
|
||||||
let buttonStackView = UIStackView(arrangedSubviews: [ registerButton, restoreButton ])
|
let buttonStackView = UIStackView(arrangedSubviews: [ registerButton, restoreButton ])
|
||||||
buttonStackView.axis = .vertical
|
buttonStackView.axis = .vertical
|
||||||
|
@ -77,6 +89,7 @@ final class LandingVC: BaseVC {
|
||||||
restoreButton.set(.width, to: Values.iPadButtonWidth)
|
restoreButton.set(.width, to: Values.iPadButtonWidth)
|
||||||
buttonStackView.alignment = .center
|
buttonStackView.alignment = .center
|
||||||
}
|
}
|
||||||
|
|
||||||
// Button stack view container
|
// Button stack view container
|
||||||
let buttonStackViewContainer = UIView()
|
let buttonStackViewContainer = UIView()
|
||||||
buttonStackViewContainer.addSubview(buttonStackView)
|
buttonStackViewContainer.addSubview(buttonStackView)
|
||||||
|
@ -84,6 +97,7 @@ final class LandingVC: BaseVC {
|
||||||
buttonStackView.pin(.top, to: .top, of: buttonStackViewContainer)
|
buttonStackView.pin(.top, to: .top, of: buttonStackViewContainer)
|
||||||
buttonStackViewContainer.pin(.trailing, to: .trailing, of: buttonStackView, withInset: isIPhone5OrSmaller ? CGFloat(52) : Values.massiveSpacing)
|
buttonStackViewContainer.pin(.trailing, to: .trailing, of: buttonStackView, withInset: isIPhone5OrSmaller ? CGFloat(52) : Values.massiveSpacing)
|
||||||
buttonStackViewContainer.pin(.bottom, to: .bottom, of: buttonStackView)
|
buttonStackViewContainer.pin(.bottom, to: .bottom, of: buttonStackView)
|
||||||
|
|
||||||
// Main stack view
|
// Main stack view
|
||||||
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, titleLabelContainer, UIView.spacer(withHeight: isIPhone5OrSmaller ? Values.smallSpacing : Values.mediumSpacing), fakeChatView, bottomSpacer, buttonStackViewContainer, linkButtonContainer ])
|
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, titleLabelContainer, UIView.spacer(withHeight: isIPhone5OrSmaller ? Values.smallSpacing : Values.mediumSpacing), fakeChatView, bottomSpacer, buttonStackViewContainer, linkButtonContainer ])
|
||||||
mainStackView.axis = .vertical
|
mainStackView.axis = .vertical
|
||||||
|
@ -93,7 +107,8 @@ final class LandingVC: BaseVC {
|
||||||
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
|
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Interaction
|
// MARK: - Interaction
|
||||||
|
|
||||||
@objc private func register() {
|
@objc private func register() {
|
||||||
let registerVC = RegisterVC()
|
let registerVC = RegisterVC()
|
||||||
navigationController!.pushViewController(registerVC, animated: true)
|
navigationController!.pushViewController(registerVC, animated: true)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import PromiseKit
|
import PromiseKit
|
||||||
|
import SessionUIKit
|
||||||
import SessionUtilitiesKit
|
import SessionUtilitiesKit
|
||||||
import SessionSnodeKit
|
import SessionSnodeKit
|
||||||
|
|
||||||
|
@ -12,14 +13,15 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon
|
||||||
private var tabBarTopConstraint: NSLayoutConstraint!
|
private var tabBarTopConstraint: NSLayoutConstraint!
|
||||||
private var activityIndicatorModal: ModalActivityIndicatorViewController?
|
private var activityIndicatorModal: ModalActivityIndicatorViewController?
|
||||||
|
|
||||||
// MARK: Components
|
// MARK: - Components
|
||||||
|
|
||||||
private lazy var tabBar: TabBar = {
|
private lazy var tabBar: TabBar = {
|
||||||
let tabs = [
|
let tabs = [
|
||||||
TabBar.Tab(title: NSLocalizedString("vc_link_device_recovery_phrase_tab_title", comment: "")) { [weak self] in
|
TabBar.Tab(title: "vc_link_device_recovery_phrase_tab_title".localized()) { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil)
|
self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil)
|
||||||
},
|
},
|
||||||
TabBar.Tab(title: NSLocalizedString("vc_link_device_scan_qr_code_tab_title", comment: "")) { [weak self] in
|
TabBar.Tab(title: "vc_link_device_scan_qr_code_tab_title".localized()) { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil)
|
self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil)
|
||||||
}
|
}
|
||||||
|
@ -180,20 +182,23 @@ private final class RecoveryPhraseVC : UIViewController {
|
||||||
// MARK: Lifecycle
|
// MARK: Lifecycle
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
view.backgroundColor = .clear
|
view.backgroundColor = .clear
|
||||||
|
|
||||||
// Title label
|
// Title label
|
||||||
let titleLabel = UILabel()
|
let titleLabel = UILabel()
|
||||||
titleLabel.textColor = Colors.text
|
|
||||||
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
||||||
titleLabel.text = NSLocalizedString("vc_enter_recovery_phrase_title", comment: "")
|
titleLabel.text = "vc_enter_recovery_phrase_title".localized()
|
||||||
titleLabel.numberOfLines = 0
|
titleLabel.textColor = Colors.text
|
||||||
titleLabel.lineBreakMode = .byWordWrapping
|
titleLabel.lineBreakMode = .byWordWrapping
|
||||||
|
titleLabel.numberOfLines = 0
|
||||||
|
|
||||||
// Explanation label
|
// Explanation label
|
||||||
let explanationLabel = UILabel()
|
let explanationLabel = UILabel()
|
||||||
explanationLabel.textColor = Colors.text
|
|
||||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
explanationLabel.text = NSLocalizedString("vc_enter_recovery_phrase_explanation", comment: "")
|
explanationLabel.text = "vc_enter_recovery_phrase_explanation".localized()
|
||||||
explanationLabel.numberOfLines = 0
|
explanationLabel.textColor = Colors.text
|
||||||
explanationLabel.lineBreakMode = .byWordWrapping
|
explanationLabel.lineBreakMode = .byWordWrapping
|
||||||
|
explanationLabel.numberOfLines = 0
|
||||||
|
|
||||||
// Spacers
|
// Spacers
|
||||||
let topSpacer = UIView.vStretchingSpacer()
|
let topSpacer = UIView.vStretchingSpacer()
|
||||||
let spacer1 = UIView()
|
let spacer1 = UIView()
|
||||||
|
@ -203,17 +208,20 @@ private final class RecoveryPhraseVC : UIViewController {
|
||||||
let bottomSpacer = UIView.vStretchingSpacer()
|
let bottomSpacer = UIView.vStretchingSpacer()
|
||||||
let restoreButtonBottomOffsetSpacer = UIView()
|
let restoreButtonBottomOffsetSpacer = UIView()
|
||||||
restoreButtonBottomOffsetConstraint = restoreButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
|
restoreButtonBottomOffsetConstraint = restoreButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
|
||||||
|
|
||||||
// Continue button
|
// Continue button
|
||||||
let continueButton = Button(style: .prominentFilled, size: .large)
|
let continueButton = OutlineButton(style: .filled, size: .large)
|
||||||
continueButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
|
continueButton.setTitle("continue_2".localized(), for: UIControl.State.normal)
|
||||||
continueButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
|
||||||
continueButton.addTarget(self, action: #selector(handleContinueButtonTapped), for: UIControl.Event.touchUpInside)
|
continueButton.addTarget(self, action: #selector(handleContinueButtonTapped), for: UIControl.Event.touchUpInside)
|
||||||
|
|
||||||
// Continue button container
|
// Continue button container
|
||||||
let continueButtonContainer = UIView(wrapping: continueButton, withInsets: UIEdgeInsets(top: 0, leading: Values.massiveSpacing, bottom: 0, trailing: Values.massiveSpacing), shouldAdaptForIPadWithWidth: Values.iPadButtonWidth)
|
let continueButtonContainer = UIView(wrapping: continueButton, withInsets: UIEdgeInsets(top: 0, leading: Values.massiveSpacing, bottom: 0, trailing: Values.massiveSpacing), shouldAdaptForIPadWithWidth: Values.iPadButtonWidth)
|
||||||
|
|
||||||
// Top stack view
|
// Top stack view
|
||||||
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, spacer1, explanationLabel, spacer2, mnemonicTextView ])
|
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, spacer1, explanationLabel, spacer2, mnemonicTextView ])
|
||||||
topStackView.axis = .vertical
|
topStackView.axis = .vertical
|
||||||
topStackView.alignment = .fill
|
topStackView.alignment = .fill
|
||||||
|
|
||||||
// Top stack view container
|
// Top stack view container
|
||||||
let topStackViewContainer = UIView()
|
let topStackViewContainer = UIView()
|
||||||
topStackViewContainer.addSubview(topStackView)
|
topStackViewContainer.addSubview(topStackView)
|
||||||
|
@ -221,6 +229,7 @@ private final class RecoveryPhraseVC : UIViewController {
|
||||||
topStackView.pin(.top, to: .top, of: topStackViewContainer)
|
topStackView.pin(.top, to: .top, of: topStackViewContainer)
|
||||||
topStackViewContainer.pin(.trailing, to: .trailing, of: topStackView, withInset: Values.veryLargeSpacing)
|
topStackViewContainer.pin(.trailing, to: .trailing, of: topStackView, withInset: Values.veryLargeSpacing)
|
||||||
topStackViewContainer.pin(.bottom, to: .bottom, of: topStackView)
|
topStackViewContainer.pin(.bottom, to: .bottom, of: topStackView)
|
||||||
|
|
||||||
// Main stack view
|
// Main stack view
|
||||||
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, continueButtonContainer, restoreButtonBottomOffsetSpacer ])
|
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, continueButtonContainer, restoreButtonBottomOffsetSpacer ])
|
||||||
mainStackView.axis = .vertical
|
mainStackView.axis = .vertical
|
||||||
|
@ -231,13 +240,16 @@ private final class RecoveryPhraseVC : UIViewController {
|
||||||
mainStackView.pin(.trailing, to: .trailing, of: view)
|
mainStackView.pin(.trailing, to: .trailing, of: view)
|
||||||
bottomConstraint = mainStackView.pin(.bottom, to: .bottom, of: view)
|
bottomConstraint = mainStackView.pin(.bottom, to: .bottom, of: view)
|
||||||
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
|
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
|
||||||
|
|
||||||
// Dismiss keyboard on tap
|
// Dismiss keyboard on tap
|
||||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
|
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
|
||||||
view.addGestureRecognizer(tapGestureRecognizer)
|
view.addGestureRecognizer(tapGestureRecognizer)
|
||||||
|
|
||||||
// Listen to keyboard notifications
|
// Listen to keyboard notifications
|
||||||
let notificationCenter = NotificationCenter.default
|
let notificationCenter = NotificationCenter.default
|
||||||
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillChangeFrameNotification(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
|
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillChangeFrameNotification(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
|
||||||
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillHideNotification(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
|
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillHideNotification(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
|
||||||
|
|
||||||
// Set up width constraint
|
// Set up width constraint
|
||||||
view.set(.width, to: UIScreen.main.bounds.width)
|
view.set(.width, to: UIScreen.main.bounds.width)
|
||||||
}
|
}
|
||||||
|
@ -246,7 +258,8 @@ private final class RecoveryPhraseVC : UIViewController {
|
||||||
NotificationCenter.default.removeObserver(self)
|
NotificationCenter.default.removeObserver(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: General
|
// MARK: - General
|
||||||
|
|
||||||
func constrainHeight(to height: CGFloat) {
|
func constrainHeight(to height: CGFloat) {
|
||||||
view.set(.height, to: height)
|
view.set(.height, to: height)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import PromiseKit
|
import PromiseKit
|
||||||
|
import SessionUIKit
|
||||||
import SessionMessagingKit
|
import SessionMessagingKit
|
||||||
import SessionSnodeKit
|
import SessionSnodeKit
|
||||||
|
|
||||||
|
@ -33,42 +34,51 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
|
||||||
// MARK: Lifecycle
|
// MARK: Lifecycle
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
setUpGradientBackground()
|
setUpGradientBackground()
|
||||||
setUpNavBarStyle()
|
setUpNavBarStyle()
|
||||||
setUpNavBarSessionIcon()
|
setUpNavBarSessionIcon()
|
||||||
|
|
||||||
let learnMoreButton = UIBarButtonItem(image: #imageLiteral(resourceName: "ic_info"), style: .plain, target: self, action: #selector(learnMore))
|
let learnMoreButton = UIBarButtonItem(image: #imageLiteral(resourceName: "ic_info"), style: .plain, target: self, action: #selector(learnMore))
|
||||||
learnMoreButton.tintColor = Colors.text
|
learnMoreButton.tintColor = Colors.text
|
||||||
navigationItem.rightBarButtonItem = learnMoreButton
|
navigationItem.rightBarButtonItem = learnMoreButton
|
||||||
|
|
||||||
// Set up title label
|
// Set up title label
|
||||||
let titleLabel = UILabel()
|
let titleLabel = UILabel()
|
||||||
titleLabel.textColor = Colors.text
|
|
||||||
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
||||||
titleLabel.text = NSLocalizedString("vc_pn_mode_title", comment: "")
|
titleLabel.text = "vc_pn_mode_title".localized()
|
||||||
titleLabel.numberOfLines = 0
|
titleLabel.textColor = Colors.text
|
||||||
titleLabel.lineBreakMode = .byWordWrapping
|
titleLabel.lineBreakMode = .byWordWrapping
|
||||||
|
titleLabel.numberOfLines = 0
|
||||||
|
|
||||||
// Set up spacers
|
// Set up spacers
|
||||||
let topSpacer = UIView.vStretchingSpacer()
|
let topSpacer = UIView.vStretchingSpacer()
|
||||||
let bottomSpacer = UIView.vStretchingSpacer()
|
let bottomSpacer = UIView.vStretchingSpacer()
|
||||||
let registerButtonBottomOffsetSpacer = UIView()
|
let registerButtonBottomOffsetSpacer = UIView()
|
||||||
registerButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
|
registerButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
|
||||||
|
|
||||||
// Set up register button
|
// Set up register button
|
||||||
let registerButton = Button(style: .prominentFilled, size: .large)
|
let registerButton = OutlineButton(style: .filled, size: .large)
|
||||||
registerButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
|
registerButton.setTitle("continue_2".localized(), for: .normal)
|
||||||
registerButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
|
||||||
registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
|
registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
|
||||||
|
|
||||||
// Set up register button container
|
// Set up register button container
|
||||||
let registerButtonContainer = UIView(wrapping: registerButton, withInsets: UIEdgeInsets(top: 0, leading: Values.massiveSpacing, bottom: 0, trailing: Values.massiveSpacing), shouldAdaptForIPadWithWidth: Values.iPadButtonWidth)
|
let registerButtonContainer = UIView(wrapping: registerButton, withInsets: UIEdgeInsets(top: 0, leading: Values.massiveSpacing, bottom: 0, trailing: Values.massiveSpacing), shouldAdaptForIPadWithWidth: Values.iPadButtonWidth)
|
||||||
|
|
||||||
// Set up options stack view
|
// Set up options stack view
|
||||||
let optionsStackView = UIStackView(arrangedSubviews: optionViews)
|
let optionsStackView = UIStackView(arrangedSubviews: optionViews)
|
||||||
optionsStackView.axis = .vertical
|
optionsStackView.axis = .vertical
|
||||||
optionsStackView.spacing = Values.smallSpacing
|
optionsStackView.spacing = Values.smallSpacing
|
||||||
optionsStackView.alignment = .fill
|
optionsStackView.alignment = .fill
|
||||||
|
|
||||||
// Set up top stack view
|
// Set up top stack view
|
||||||
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, UIView.spacer(withHeight: isIPhone6OrSmaller ? Values.mediumSpacing : Values.veryLargeSpacing), optionsStackView ])
|
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, UIView.spacer(withHeight: isIPhone6OrSmaller ? Values.mediumSpacing : Values.veryLargeSpacing), optionsStackView ])
|
||||||
topStackView.axis = .vertical
|
topStackView.axis = .vertical
|
||||||
topStackView.alignment = .fill
|
topStackView.alignment = .fill
|
||||||
|
|
||||||
// Set up top stack view container
|
// Set up top stack view container
|
||||||
let topStackViewContainer = UIView(wrapping: topStackView, withInsets: UIEdgeInsets(top: 0, leading: Values.veryLargeSpacing, bottom: 0, trailing: Values.veryLargeSpacing))
|
let topStackViewContainer = UIView(wrapping: topStackView, withInsets: UIEdgeInsets(top: 0, leading: Values.veryLargeSpacing, bottom: 0, trailing: Values.veryLargeSpacing))
|
||||||
|
|
||||||
// Set up main stack view
|
// Set up main stack view
|
||||||
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, registerButtonContainer, registerButtonBottomOffsetSpacer ])
|
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, registerButtonContainer, registerButtonBottomOffsetSpacer ])
|
||||||
mainStackView.axis = .vertical
|
mainStackView.axis = .vertical
|
||||||
|
@ -76,14 +86,16 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
|
||||||
view.addSubview(mainStackView)
|
view.addSubview(mainStackView)
|
||||||
mainStackView.pin(to: view)
|
mainStackView.pin(to: view)
|
||||||
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
|
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
|
||||||
|
|
||||||
// Preselect APNs mode
|
// Preselect APNs mode
|
||||||
optionViews[0].isSelected = true
|
optionViews[0].isSelected = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Interaction
|
// MARK: - Interaction
|
||||||
|
|
||||||
@objc private func learnMore() {
|
@objc private func learnMore() {
|
||||||
let urlAsString = "https://getsession.org/faq/#privacy"
|
guard let url: URL = URL(string: "https://getsession.org/faq/#privacy") else { return }
|
||||||
let url = URL(string: urlAsString)!
|
|
||||||
UIApplication.shared.open(url)
|
UIApplication.shared.open(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,9 +105,9 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
|
||||||
|
|
||||||
@objc private func register() {
|
@objc private func register() {
|
||||||
guard selectedOptionView != nil else {
|
guard selectedOptionView != nil else {
|
||||||
let title = NSLocalizedString("vc_pn_mode_no_option_picked_modal_title", comment: "")
|
let title = "vc_pn_mode_no_option_picked_modal_title".localized()
|
||||||
let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
|
let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
|
||||||
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil))
|
alert.addAction(UIAlertAction(title: "BUTTON_OK".localized(), style: .default, handler: nil))
|
||||||
return presentAlert(alert)
|
return presentAlert(alert)
|
||||||
}
|
}
|
||||||
UserDefaults.standard[.isUsingFullAPNs] = (selectedOptionView == apnsOptionView)
|
UserDefaults.standard[.isUsingFullAPNs] = (selectedOptionView == apnsOptionView)
|
||||||
|
|
|
@ -3,13 +3,15 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import Sodium
|
import Sodium
|
||||||
import Curve25519Kit
|
import Curve25519Kit
|
||||||
|
import SessionUIKit
|
||||||
|
|
||||||
final class RegisterVC : BaseVC {
|
final class RegisterVC : BaseVC {
|
||||||
private var seed: Data! { didSet { updateKeyPair() } }
|
private var seed: Data! { didSet { updateKeyPair() } }
|
||||||
private var ed25519KeyPair: Sign.KeyPair!
|
private var ed25519KeyPair: Sign.KeyPair!
|
||||||
private var x25519KeyPair: ECKeyPair! { didSet { updatePublicKeyLabel() } }
|
private var x25519KeyPair: ECKeyPair! { didSet { updatePublicKeyLabel() } }
|
||||||
|
|
||||||
// MARK: Components
|
// MARK: - Components
|
||||||
|
|
||||||
private lazy var publicKeyLabel: UILabel = {
|
private lazy var publicKeyLabel: UILabel = {
|
||||||
let result = UILabel()
|
let result = UILabel()
|
||||||
result.textColor = Colors.text
|
result.textColor = Colors.text
|
||||||
|
@ -17,14 +19,15 @@ final class RegisterVC : BaseVC {
|
||||||
result.numberOfLines = 0
|
result.numberOfLines = 0
|
||||||
result.lineBreakMode = .byCharWrapping
|
result.lineBreakMode = .byCharWrapping
|
||||||
result.accessibilityLabel = "Session ID label"
|
result.accessibilityLabel = "Session ID label"
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var copyPublicKeyButton: Button = {
|
private lazy var copyPublicKeyButton: OutlineButton = {
|
||||||
let result = Button(style: .prominentOutline, size: .large)
|
let result = OutlineButton(style: .regular, size: .large)
|
||||||
result.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
|
result.setTitle("copy".localized(), for: .normal)
|
||||||
result.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
result.addTarget(self, action: #selector(copyPublicKey), for: .touchUpInside)
|
||||||
result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside)
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -50,20 +53,23 @@ final class RegisterVC : BaseVC {
|
||||||
setUpGradientBackground()
|
setUpGradientBackground()
|
||||||
setUpNavBarStyle()
|
setUpNavBarStyle()
|
||||||
setUpNavBarSessionIcon()
|
setUpNavBarSessionIcon()
|
||||||
|
|
||||||
// Set up title label
|
// Set up title label
|
||||||
let titleLabel = UILabel()
|
let titleLabel = UILabel()
|
||||||
titleLabel.textColor = Colors.text
|
|
||||||
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
||||||
titleLabel.text = NSLocalizedString("vc_register_title", comment: "")
|
titleLabel.text = "vc_register_title".localized()
|
||||||
titleLabel.numberOfLines = 0
|
titleLabel.textColor = Colors.text
|
||||||
titleLabel.lineBreakMode = .byWordWrapping
|
titleLabel.lineBreakMode = .byWordWrapping
|
||||||
|
titleLabel.numberOfLines = 0
|
||||||
|
|
||||||
// Set up explanation label
|
// Set up explanation label
|
||||||
let explanationLabel = UILabel()
|
let explanationLabel = UILabel()
|
||||||
explanationLabel.textColor = Colors.text
|
|
||||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
explanationLabel.text = NSLocalizedString("vc_register_explanation", comment: "")
|
explanationLabel.text = "vc_register_explanation".localized()
|
||||||
explanationLabel.numberOfLines = 0
|
explanationLabel.textColor = Colors.text
|
||||||
explanationLabel.lineBreakMode = .byWordWrapping
|
explanationLabel.lineBreakMode = .byWordWrapping
|
||||||
|
explanationLabel.numberOfLines = 0
|
||||||
|
|
||||||
// Set up public key label container
|
// Set up public key label container
|
||||||
let publicKeyLabelContainer = UIView()
|
let publicKeyLabelContainer = UIView()
|
||||||
publicKeyLabelContainer.addSubview(publicKeyLabel)
|
publicKeyLabelContainer.addSubview(publicKeyLabel)
|
||||||
|
@ -71,14 +77,16 @@ final class RegisterVC : BaseVC {
|
||||||
publicKeyLabelContainer.layer.cornerRadius = TextField.cornerRadius
|
publicKeyLabelContainer.layer.cornerRadius = TextField.cornerRadius
|
||||||
publicKeyLabelContainer.layer.borderWidth = 1
|
publicKeyLabelContainer.layer.borderWidth = 1
|
||||||
publicKeyLabelContainer.layer.borderColor = Colors.text.cgColor
|
publicKeyLabelContainer.layer.borderColor = Colors.text.cgColor
|
||||||
|
|
||||||
// Set up spacers
|
// Set up spacers
|
||||||
let topSpacer = UIView.vStretchingSpacer()
|
let topSpacer = UIView.vStretchingSpacer()
|
||||||
let bottomSpacer = UIView.vStretchingSpacer()
|
let bottomSpacer = UIView.vStretchingSpacer()
|
||||||
|
|
||||||
// Set up register button
|
// Set up register button
|
||||||
let registerButton = Button(style: .prominentFilled, size: .large)
|
let registerButton = OutlineButton(style: .filled, size: .large)
|
||||||
registerButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
|
registerButton.setTitle("continue_2".localized(), for: .normal)
|
||||||
registerButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
|
||||||
registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
|
registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
|
||||||
|
|
||||||
// Set up button stack view
|
// Set up button stack view
|
||||||
let buttonStackView = UIStackView(arrangedSubviews: [ registerButton, copyPublicKeyButton ])
|
let buttonStackView = UIStackView(arrangedSubviews: [ registerButton, copyPublicKeyButton ])
|
||||||
buttonStackView.axis = .vertical
|
buttonStackView.axis = .vertical
|
||||||
|
@ -89,6 +97,7 @@ final class RegisterVC : BaseVC {
|
||||||
copyPublicKeyButton.set(.width, to: Values.iPadButtonWidth)
|
copyPublicKeyButton.set(.width, to: Values.iPadButtonWidth)
|
||||||
buttonStackView.alignment = .center
|
buttonStackView.alignment = .center
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up button stack view container
|
// Set up button stack view container
|
||||||
let buttonStackViewContainer = UIView()
|
let buttonStackViewContainer = UIView()
|
||||||
buttonStackViewContainer.addSubview(buttonStackView)
|
buttonStackViewContainer.addSubview(buttonStackView)
|
||||||
|
@ -96,10 +105,12 @@ final class RegisterVC : BaseVC {
|
||||||
buttonStackView.pin(.top, to: .top, of: buttonStackViewContainer)
|
buttonStackView.pin(.top, to: .top, of: buttonStackViewContainer)
|
||||||
buttonStackViewContainer.pin(.trailing, to: .trailing, of: buttonStackView, withInset: Values.massiveSpacing)
|
buttonStackViewContainer.pin(.trailing, to: .trailing, of: buttonStackView, withInset: Values.massiveSpacing)
|
||||||
buttonStackViewContainer.pin(.bottom, to: .bottom, of: buttonStackView)
|
buttonStackViewContainer.pin(.bottom, to: .bottom, of: buttonStackView)
|
||||||
|
|
||||||
// Set up legal label
|
// Set up legal label
|
||||||
legalLabel.isUserInteractionEnabled = true
|
legalLabel.isUserInteractionEnabled = true
|
||||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleLegalLabelTapped))
|
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleLegalLabelTapped))
|
||||||
legalLabel.addGestureRecognizer(tapGestureRecognizer)
|
legalLabel.addGestureRecognizer(tapGestureRecognizer)
|
||||||
|
|
||||||
// Set up legal label container
|
// Set up legal label container
|
||||||
let legalLabelContainer = UIView()
|
let legalLabelContainer = UIView()
|
||||||
legalLabelContainer.set(.height, to: Values.onboardingButtonBottomOffset)
|
legalLabelContainer.set(.height, to: Values.onboardingButtonBottomOffset)
|
||||||
|
@ -108,11 +119,13 @@ final class RegisterVC : BaseVC {
|
||||||
legalLabel.pin(.top, to: .top, of: legalLabelContainer)
|
legalLabel.pin(.top, to: .top, of: legalLabelContainer)
|
||||||
legalLabelContainer.pin(.trailing, to: .trailing, of: legalLabel, withInset: Values.massiveSpacing)
|
legalLabelContainer.pin(.trailing, to: .trailing, of: legalLabel, withInset: Values.massiveSpacing)
|
||||||
legalLabelContainer.pin(.bottom, to: .bottom, of: legalLabel, withInset: isIPhone5OrSmaller ? 6 : 10)
|
legalLabelContainer.pin(.bottom, to: .bottom, of: legalLabel, withInset: isIPhone5OrSmaller ? 6 : 10)
|
||||||
|
|
||||||
// Set up top stack view
|
// Set up top stack view
|
||||||
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, explanationLabel, publicKeyLabelContainer ])
|
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, explanationLabel, publicKeyLabelContainer ])
|
||||||
topStackView.axis = .vertical
|
topStackView.axis = .vertical
|
||||||
topStackView.spacing = isIPhone5OrSmaller ? Values.smallSpacing : Values.veryLargeSpacing
|
topStackView.spacing = isIPhone5OrSmaller ? Values.smallSpacing : Values.veryLargeSpacing
|
||||||
topStackView.alignment = .fill
|
topStackView.alignment = .fill
|
||||||
|
|
||||||
// Set up top stack view container
|
// Set up top stack view container
|
||||||
let topStackViewContainer = UIView()
|
let topStackViewContainer = UIView()
|
||||||
topStackViewContainer.addSubview(topStackView)
|
topStackViewContainer.addSubview(topStackView)
|
||||||
|
@ -120,6 +133,7 @@ final class RegisterVC : BaseVC {
|
||||||
topStackView.pin(.top, to: .top, of: topStackViewContainer)
|
topStackView.pin(.top, to: .top, of: topStackViewContainer)
|
||||||
topStackViewContainer.pin(.trailing, to: .trailing, of: topStackView, withInset: Values.veryLargeSpacing)
|
topStackViewContainer.pin(.trailing, to: .trailing, of: topStackView, withInset: Values.veryLargeSpacing)
|
||||||
topStackViewContainer.pin(.bottom, to: .bottom, of: topStackView)
|
topStackViewContainer.pin(.bottom, to: .bottom, of: topStackView)
|
||||||
|
|
||||||
// Set up main stack view
|
// Set up main stack view
|
||||||
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, buttonStackViewContainer, legalLabelContainer ])
|
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, buttonStackViewContainer, legalLabelContainer ])
|
||||||
mainStackView.axis = .vertical
|
mainStackView.axis = .vertical
|
||||||
|
@ -127,6 +141,7 @@ final class RegisterVC : BaseVC {
|
||||||
view.addSubview(mainStackView)
|
view.addSubview(mainStackView)
|
||||||
mainStackView.pin(to: view)
|
mainStackView.pin(to: view)
|
||||||
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
|
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
|
||||||
|
|
||||||
// Peform initial seed update
|
// Peform initial seed update
|
||||||
updateSeed()
|
updateSeed()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import SessionUIKit
|
||||||
import SessionUtilitiesKit
|
import SessionUtilitiesKit
|
||||||
|
|
||||||
final class RestoreVC: BaseVC {
|
final class RestoreVC: BaseVC {
|
||||||
|
@ -12,49 +13,60 @@ final class RestoreVC: BaseVC {
|
||||||
|
|
||||||
// MARK: Components
|
// MARK: Components
|
||||||
private lazy var mnemonicTextView: TextView = {
|
private lazy var mnemonicTextView: TextView = {
|
||||||
let result = TextView(placeholder: NSLocalizedString("vc_restore_seed_text_field_hint", comment: ""))
|
let result = TextView(placeholder: "vc_restore_seed_text_field_hint".localized())
|
||||||
result.autocapitalizationType = .none
|
result.autocapitalizationType = .none
|
||||||
result.layer.borderColor = Colors.text.cgColor
|
result.layer.borderColor = Colors.text.cgColor
|
||||||
result.accessibilityLabel = "Recovery phrase text view"
|
result.accessibilityLabel = "Recovery phrase text view"
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var legalLabel: UILabel = {
|
private lazy var legalLabel: UILabel = {
|
||||||
let result = UILabel()
|
let result = UILabel()
|
||||||
result.textColor = Colors.text
|
|
||||||
result.font = .systemFont(ofSize: Values.verySmallFontSize)
|
result.font = .systemFont(ofSize: Values.verySmallFontSize)
|
||||||
let text = "By using this service, you agree to our Terms of Service, End User License Agreement (EULA) and Privacy Policy"
|
let text = "By using this service, you agree to our Terms of Service, End User License Agreement (EULA) and Privacy Policy"
|
||||||
let attributedText = NSMutableAttributedString(string: text, attributes: [ .font : UIFont.systemFont(ofSize: Values.verySmallFontSize) ])
|
let attributedText = NSMutableAttributedString(
|
||||||
|
string: text,
|
||||||
|
attributes: [
|
||||||
|
.font: UIFont.systemFont(ofSize: Values.verySmallFontSize)
|
||||||
|
]
|
||||||
|
)
|
||||||
attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.verySmallFontSize), range: (text as NSString).range(of: "Terms of Service"))
|
attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.verySmallFontSize), range: (text as NSString).range(of: "Terms of Service"))
|
||||||
attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.verySmallFontSize), range: (text as NSString).range(of: "End User License Agreement (EULA)"))
|
attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.verySmallFontSize), range: (text as NSString).range(of: "End User License Agreement (EULA)"))
|
||||||
attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.verySmallFontSize), range: (text as NSString).range(of: "Privacy Policy"))
|
attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.verySmallFontSize), range: (text as NSString).range(of: "Privacy Policy"))
|
||||||
|
result.textColor = Colors.text
|
||||||
result.attributedText = attributedText
|
result.attributedText = attributedText
|
||||||
result.numberOfLines = 0
|
|
||||||
result.textAlignment = .center
|
result.textAlignment = .center
|
||||||
result.lineBreakMode = .byWordWrapping
|
result.lineBreakMode = .byWordWrapping
|
||||||
|
result.numberOfLines = 0
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// MARK: Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
setUpGradientBackground()
|
setUpGradientBackground()
|
||||||
setUpNavBarStyle()
|
setUpNavBarStyle()
|
||||||
setUpNavBarSessionIcon()
|
setUpNavBarSessionIcon()
|
||||||
|
|
||||||
// Set up title label
|
// Set up title label
|
||||||
let titleLabel = UILabel()
|
let titleLabel = UILabel()
|
||||||
titleLabel.textColor = Colors.text
|
|
||||||
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
||||||
titleLabel.text = NSLocalizedString("vc_restore_title", comment: "")
|
titleLabel.text = "vc_restore_title".localized()
|
||||||
titleLabel.numberOfLines = 0
|
titleLabel.textColor = Colors.text
|
||||||
titleLabel.lineBreakMode = .byWordWrapping
|
titleLabel.lineBreakMode = .byWordWrapping
|
||||||
|
titleLabel.numberOfLines = 0
|
||||||
|
|
||||||
// Set up explanation label
|
// Set up explanation label
|
||||||
let explanationLabel = UILabel()
|
let explanationLabel = UILabel()
|
||||||
explanationLabel.textColor = Colors.text
|
|
||||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
explanationLabel.text = NSLocalizedString("vc_restore_explanation", comment: "")
|
explanationLabel.text = "vc_restore_explanation".localized()
|
||||||
explanationLabel.numberOfLines = 0
|
explanationLabel.textColor = Colors.text
|
||||||
explanationLabel.lineBreakMode = .byWordWrapping
|
explanationLabel.lineBreakMode = .byWordWrapping
|
||||||
|
explanationLabel.numberOfLines = 0
|
||||||
|
|
||||||
// Set up legal label
|
// Set up legal label
|
||||||
legalLabel.isUserInteractionEnabled = true
|
legalLabel.isUserInteractionEnabled = true
|
||||||
let legalLabelTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleLegalLabelTapped))
|
let legalLabelTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleLegalLabelTapped))
|
||||||
|
@ -70,17 +82,20 @@ final class RestoreVC: BaseVC {
|
||||||
let bottomSpacer = UIView.vStretchingSpacer()
|
let bottomSpacer = UIView.vStretchingSpacer()
|
||||||
let restoreButtonBottomOffsetSpacer = UIView()
|
let restoreButtonBottomOffsetSpacer = UIView()
|
||||||
restoreButtonBottomOffsetConstraint = restoreButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
|
restoreButtonBottomOffsetConstraint = restoreButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
|
||||||
|
|
||||||
// Set up restore button
|
// Set up restore button
|
||||||
let restoreButton = Button(style: .prominentFilled, size: .large)
|
let restoreButton = OutlineButton(style: .filled, size: .large)
|
||||||
restoreButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
|
restoreButton.setTitle("continue_2".localized(), for: UIControl.State.normal)
|
||||||
restoreButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
|
||||||
restoreButton.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside)
|
restoreButton.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside)
|
||||||
|
|
||||||
// Set up restore button container
|
// Set up restore button container
|
||||||
let restoreButtonContainer = UIView(wrapping: restoreButton, withInsets: UIEdgeInsets(top: 0, leading: Values.massiveSpacing, bottom: 0, trailing: Values.massiveSpacing), shouldAdaptForIPadWithWidth: Values.iPadButtonWidth)
|
let restoreButtonContainer = UIView(wrapping: restoreButton, withInsets: UIEdgeInsets(top: 0, leading: Values.massiveSpacing, bottom: 0, trailing: Values.massiveSpacing), shouldAdaptForIPadWithWidth: Values.iPadButtonWidth)
|
||||||
|
|
||||||
// Set up top stack view
|
// Set up top stack view
|
||||||
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, spacer1, explanationLabel, spacer2, mnemonicTextView, spacer3, legalLabel ])
|
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, spacer1, explanationLabel, spacer2, mnemonicTextView, spacer3, legalLabel ])
|
||||||
topStackView.axis = .vertical
|
topStackView.axis = .vertical
|
||||||
topStackView.alignment = .fill
|
topStackView.alignment = .fill
|
||||||
|
|
||||||
// Set up top stack view container
|
// Set up top stack view container
|
||||||
let topStackViewContainer = UIView()
|
let topStackViewContainer = UIView()
|
||||||
topStackViewContainer.addSubview(topStackView)
|
topStackViewContainer.addSubview(topStackView)
|
||||||
|
@ -88,19 +103,23 @@ final class RestoreVC: BaseVC {
|
||||||
topStackView.pin(.top, to: .top, of: topStackViewContainer)
|
topStackView.pin(.top, to: .top, of: topStackViewContainer)
|
||||||
topStackViewContainer.pin(.trailing, to: .trailing, of: topStackView, withInset: Values.veryLargeSpacing)
|
topStackViewContainer.pin(.trailing, to: .trailing, of: topStackView, withInset: Values.veryLargeSpacing)
|
||||||
topStackViewContainer.pin(.bottom, to: .bottom, of: topStackView)
|
topStackViewContainer.pin(.bottom, to: .bottom, of: topStackView)
|
||||||
|
|
||||||
// Set up main stack view
|
// Set up main stack view
|
||||||
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, restoreButtonContainer, restoreButtonBottomOffsetSpacer ])
|
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, restoreButtonContainer, restoreButtonBottomOffsetSpacer ])
|
||||||
mainStackView.axis = .vertical
|
mainStackView.axis = .vertical
|
||||||
mainStackView.alignment = .fill
|
mainStackView.alignment = .fill
|
||||||
|
|
||||||
view.addSubview(mainStackView)
|
view.addSubview(mainStackView)
|
||||||
mainStackView.pin(.leading, to: .leading, of: view)
|
mainStackView.pin(.leading, to: .leading, of: view)
|
||||||
mainStackView.pin(.top, to: .top, of: view)
|
mainStackView.pin(.top, to: .top, of: view)
|
||||||
mainStackView.pin(.trailing, to: .trailing, of: view)
|
mainStackView.pin(.trailing, to: .trailing, of: view)
|
||||||
bottomConstraint = mainStackView.pin(.bottom, to: .bottom, of: view)
|
bottomConstraint = mainStackView.pin(.bottom, to: .bottom, of: view)
|
||||||
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
|
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
|
||||||
|
|
||||||
// Dismiss keyboard on tap
|
// Dismiss keyboard on tap
|
||||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
|
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
|
||||||
view.addGestureRecognizer(tapGestureRecognizer)
|
view.addGestureRecognizer(tapGestureRecognizer)
|
||||||
|
|
||||||
// Listen to keyboard notifications
|
// Listen to keyboard notifications
|
||||||
let notificationCenter = NotificationCenter.default
|
let notificationCenter = NotificationCenter.default
|
||||||
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillChangeFrameNotification(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
|
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillChangeFrameNotification(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
|
||||||
|
|
|
@ -2,28 +2,35 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import SessionUIKit
|
import SessionUIKit
|
||||||
|
import SessionUtilitiesKit
|
||||||
|
|
||||||
final class SeedReminderView: UIView {
|
final class SeedReminderView: UIView {
|
||||||
|
private static let progressBarThickness: CGFloat = 2
|
||||||
|
|
||||||
private let hasContinueButton: Bool
|
private let hasContinueButton: Bool
|
||||||
|
|
||||||
var title = NSAttributedString(string: "") { didSet { titleLabel.attributedText = title } }
|
var title = NSAttributedString(string: "") { didSet { titleLabel.attributedText = title } }
|
||||||
var subtitle = "" { didSet { subtitleLabel.text = subtitle } }
|
var subtitle = "" { didSet { subtitleLabel.text = subtitle } }
|
||||||
var delegate: SeedReminderViewDelegate?
|
var delegate: SeedReminderViewDelegate?
|
||||||
|
|
||||||
// MARK: Components
|
// MARK: - Components
|
||||||
|
|
||||||
private lazy var progressIndicatorView: UIProgressView = {
|
private lazy var progressIndicatorView: UIProgressView = {
|
||||||
let result = UIProgressView()
|
let result = UIProgressView()
|
||||||
result.progressViewStyle = .bar
|
result.progressViewStyle = .bar
|
||||||
result.progressTintColor = Colors.accent
|
result.progressTintColor = Colors.accent
|
||||||
result.backgroundColor = isLightMode ? UIColor(hex: 0x000000).withAlphaComponent(0.1) : UIColor(hex: 0xFFFFFF).withAlphaComponent(0.1)
|
result.backgroundColor = isLightMode ? UIColor(hex: 0x000000).withAlphaComponent(0.1) : UIColor(hex: 0xFFFFFF).withAlphaComponent(0.1)
|
||||||
result.set(.height, to: SeedReminderView.progressBarThickness)
|
result.set(.height, to: SeedReminderView.progressBarThickness)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
lazy var titleLabel: UILabel = {
|
lazy var titleLabel: UILabel = {
|
||||||
let result = UILabel()
|
let result = UILabel()
|
||||||
result.textColor = Colors.text
|
|
||||||
result.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
result.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
||||||
|
result.textColor = Colors.text
|
||||||
result.lineBreakMode = .byTruncatingTail
|
result.lineBreakMode = .byTruncatingTail
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -36,13 +43,13 @@ final class SeedReminderView: UIView {
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// MARK: Settings
|
// MARK: - Lifecycle
|
||||||
private static let progressBarThickness: CGFloat = 2
|
|
||||||
|
|
||||||
// MARK: Lifecycle
|
|
||||||
init(hasContinueButton: Bool) {
|
init(hasContinueButton: Bool) {
|
||||||
self.hasContinueButton = hasContinueButton
|
self.hasContinueButton = hasContinueButton
|
||||||
|
|
||||||
super.init(frame: CGRect.zero)
|
super.init(frame: CGRect.zero)
|
||||||
|
|
||||||
setUpViewHierarchy()
|
setUpViewHierarchy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,16 +64,18 @@ final class SeedReminderView: UIView {
|
||||||
private func setUpViewHierarchy() {
|
private func setUpViewHierarchy() {
|
||||||
// Set background color
|
// Set background color
|
||||||
backgroundColor = Colors.cellBackground
|
backgroundColor = Colors.cellBackground
|
||||||
|
|
||||||
// Set up label stack view
|
// Set up label stack view
|
||||||
let labelStackView = UIStackView(arrangedSubviews: [ titleLabel, subtitleLabel ])
|
let labelStackView = UIStackView(arrangedSubviews: [ titleLabel, subtitleLabel ])
|
||||||
labelStackView.axis = .vertical
|
labelStackView.axis = .vertical
|
||||||
labelStackView.spacing = 4
|
labelStackView.spacing = 4
|
||||||
|
|
||||||
// Set up button
|
// Set up button
|
||||||
let button = Button(style: .prominentOutline, size: .small)
|
let button = OutlineButton(style: .regular, size: .small)
|
||||||
button.titleLabel!.font = .boldSystemFont(ofSize: CGFloat(13))
|
button.setTitle("continue_2".localized(), for: UIControl.State.normal)
|
||||||
button.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
|
|
||||||
button.set(.width, to: 96)
|
button.set(.width, to: 96)
|
||||||
button.addTarget(self, action: #selector(handleContinueButtonTapped), for: UIControl.Event.touchUpInside)
|
button.addTarget(self, action: #selector(handleContinueButtonTapped), for: UIControl.Event.touchUpInside)
|
||||||
|
|
||||||
// Set up content stack view
|
// Set up content stack view
|
||||||
let contentStackView = UIStackView(arrangedSubviews: [ labelStackView ])
|
let contentStackView = UIStackView(arrangedSubviews: [ labelStackView ])
|
||||||
if hasContinueButton {
|
if hasContinueButton {
|
||||||
|
@ -79,10 +88,12 @@ final class SeedReminderView: UIView {
|
||||||
let horizontalSpacing = isIPhone5OrSmaller ? Values.smallSpacing : Values.mediumSpacing
|
let horizontalSpacing = isIPhone5OrSmaller ? Values.smallSpacing : Values.mediumSpacing
|
||||||
contentStackView.layoutMargins = UIEdgeInsets(top: 0, leading: horizontalSpacing + Values.accentLineThickness, bottom: 0, trailing: horizontalSpacing)
|
contentStackView.layoutMargins = UIEdgeInsets(top: 0, leading: horizontalSpacing + Values.accentLineThickness, bottom: 0, trailing: horizontalSpacing)
|
||||||
contentStackView.isLayoutMarginsRelativeArrangement = true
|
contentStackView.isLayoutMarginsRelativeArrangement = true
|
||||||
|
|
||||||
// Set up separator
|
// Set up separator
|
||||||
let separator = UIView()
|
let separator = UIView()
|
||||||
separator.set(.height, to: Values.separatorThickness)
|
separator.set(.height, to: Values.separatorThickness)
|
||||||
separator.backgroundColor = Colors.separator
|
separator.backgroundColor = Colors.separator
|
||||||
|
|
||||||
// Set up stack view
|
// Set up stack view
|
||||||
let stackView = UIStackView(arrangedSubviews: [ progressIndicatorView, contentStackView, separator ])
|
let stackView = UIStackView(arrangedSubviews: [ progressIndicatorView, contentStackView, separator ])
|
||||||
stackView.axis = .vertical
|
stackView.axis = .vertical
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import SessionUIKit
|
||||||
import SessionUtilitiesKit
|
import SessionUtilitiesKit
|
||||||
|
|
||||||
final class SeedVC: BaseVC {
|
final class SeedVC: BaseVC {
|
||||||
|
@ -16,70 +17,81 @@ final class SeedVC: BaseVC {
|
||||||
private lazy var redactedMnemonic: String = {
|
private lazy var redactedMnemonic: String = {
|
||||||
if isIPhone5OrSmaller {
|
if isIPhone5OrSmaller {
|
||||||
return "▆▆▆▆ ▆▆▆▆▆▆ ▆▆▆ ▆▆▆▆▆▆▆ ▆▆ ▆▆▆▆ ▆▆▆ ▆▆▆▆▆ ▆▆▆ ▆ ▆▆▆▆ ▆▆ ▆▆▆▆▆▆▆ ▆▆▆▆▆"
|
return "▆▆▆▆ ▆▆▆▆▆▆ ▆▆▆ ▆▆▆▆▆▆▆ ▆▆ ▆▆▆▆ ▆▆▆ ▆▆▆▆▆ ▆▆▆ ▆ ▆▆▆▆ ▆▆ ▆▆▆▆▆▆▆ ▆▆▆▆▆"
|
||||||
} else {
|
|
||||||
return "▆▆▆▆ ▆▆▆▆▆▆ ▆▆▆ ▆▆▆▆▆▆▆ ▆▆ ▆▆▆▆ ▆▆▆ ▆▆▆▆▆ ▆▆▆ ▆ ▆▆▆▆ ▆▆ ▆▆▆▆▆▆▆ ▆▆▆▆▆ ▆▆▆▆▆▆▆▆ ▆▆ ▆▆▆ ▆▆▆▆▆▆▆"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return "▆▆▆▆ ▆▆▆▆▆▆ ▆▆▆ ▆▆▆▆▆▆▆ ▆▆ ▆▆▆▆ ▆▆▆ ▆▆▆▆▆ ▆▆▆ ▆ ▆▆▆▆ ▆▆ ▆▆▆▆▆▆▆ ▆▆▆▆▆ ▆▆▆▆▆▆▆▆ ▆▆ ▆▆▆ ▆▆▆▆▆▆▆"
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// MARK: Components
|
// MARK: - Components
|
||||||
|
|
||||||
private lazy var seedReminderView: SeedReminderView = {
|
private lazy var seedReminderView: SeedReminderView = {
|
||||||
let result = SeedReminderView(hasContinueButton: false)
|
let result = SeedReminderView(hasContinueButton: false)
|
||||||
let title = "You're almost finished! 90%"
|
let title = "You're almost finished! 90%"
|
||||||
let attributedTitle = NSMutableAttributedString(string: title)
|
let attributedTitle = NSMutableAttributedString(string: title)
|
||||||
attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "90%"))
|
attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "90%"))
|
||||||
result.title = attributedTitle
|
result.title = attributedTitle
|
||||||
result.subtitle = NSLocalizedString("view_seed_reminder_subtitle_2", comment: "")
|
result.subtitle = "view_seed_reminder_subtitle_2".localized()
|
||||||
result.setProgress(0.9, animated: false)
|
result.setProgress(0.9, animated: false)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var mnemonicLabel: UILabel = {
|
private lazy var mnemonicLabel: UILabel = {
|
||||||
let result = UILabel()
|
let result = UILabel()
|
||||||
result.textColor = Colors.accent
|
|
||||||
result.font = Fonts.spaceMono(ofSize: Values.mediumFontSize)
|
result.font = Fonts.spaceMono(ofSize: Values.mediumFontSize)
|
||||||
result.numberOfLines = 0
|
result.textColor = Colors.accent
|
||||||
result.textAlignment = .center
|
result.textAlignment = .center
|
||||||
result.lineBreakMode = .byWordWrapping
|
result.lineBreakMode = .byWordWrapping
|
||||||
|
result.numberOfLines = 0
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var copyButton: Button = {
|
private lazy var copyButton: OutlineButton = {
|
||||||
let result = Button(style: .prominentOutline, size: .large)
|
let result = OutlineButton(style: .regular, size: .large)
|
||||||
result.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
|
result.setTitle("copy".localized(), for: UIControl.State.normal)
|
||||||
result.addTarget(self, action: #selector(copyMnemonic), for: UIControl.Event.touchUpInside)
|
result.addTarget(self, action: #selector(copyMnemonic), for: UIControl.Event.touchUpInside)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// MARK: Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
setUpGradientBackground()
|
setUpGradientBackground()
|
||||||
setUpNavBarStyle()
|
setUpNavBarStyle()
|
||||||
setNavBarTitle(NSLocalizedString("vc_seed_title", comment: ""))
|
setNavBarTitle("vc_seed_title".localized())
|
||||||
|
|
||||||
// Set up navigation bar buttons
|
// Set up navigation bar buttons
|
||||||
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
|
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
|
||||||
closeButton.tintColor = Colors.text
|
closeButton.tintColor = Colors.text
|
||||||
navigationItem.leftBarButtonItem = closeButton
|
navigationItem.leftBarButtonItem = closeButton
|
||||||
|
|
||||||
// Set up title label
|
// Set up title label
|
||||||
let titleLabel = UILabel()
|
let titleLabel = UILabel()
|
||||||
titleLabel.textColor = Colors.text
|
|
||||||
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
|
||||||
titleLabel.text = NSLocalizedString("vc_seed_title_2", comment: "")
|
titleLabel.text = "vc_seed_title_2".localized()
|
||||||
titleLabel.numberOfLines = 0
|
titleLabel.textColor = Colors.text
|
||||||
titleLabel.lineBreakMode = .byWordWrapping
|
titleLabel.lineBreakMode = .byWordWrapping
|
||||||
|
titleLabel.numberOfLines = 0
|
||||||
|
|
||||||
// Set up explanation label
|
// Set up explanation label
|
||||||
let explanationLabel = UILabel()
|
let explanationLabel = UILabel()
|
||||||
explanationLabel.textColor = Colors.text
|
|
||||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
explanationLabel.text = NSLocalizedString("vc_seed_explanation", comment: "")
|
explanationLabel.text = "vc_seed_explanation".localized()
|
||||||
explanationLabel.numberOfLines = 0
|
explanationLabel.textColor = Colors.text
|
||||||
explanationLabel.lineBreakMode = .byWordWrapping
|
explanationLabel.lineBreakMode = .byWordWrapping
|
||||||
|
explanationLabel.numberOfLines = 0
|
||||||
|
|
||||||
// Set up mnemonic label
|
// Set up mnemonic label
|
||||||
mnemonicLabel.text = redactedMnemonic
|
mnemonicLabel.text = redactedMnemonic
|
||||||
let mnemonicLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonic))
|
let mnemonicLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonic))
|
||||||
mnemonicLabel.addGestureRecognizer(mnemonicLabelGestureRecognizer)
|
mnemonicLabel.addGestureRecognizer(mnemonicLabelGestureRecognizer)
|
||||||
mnemonicLabel.isUserInteractionEnabled = true
|
mnemonicLabel.isUserInteractionEnabled = true
|
||||||
mnemonicLabel.isEnabled = true
|
mnemonicLabel.isEnabled = true
|
||||||
|
|
||||||
// Set up mnemonic label container
|
// Set up mnemonic label container
|
||||||
let mnemonicLabelContainer = UIView()
|
let mnemonicLabelContainer = UIView()
|
||||||
mnemonicLabelContainer.addSubview(mnemonicLabel)
|
mnemonicLabelContainer.addSubview(mnemonicLabel)
|
||||||
|
@ -87,19 +99,23 @@ final class SeedVC: BaseVC {
|
||||||
mnemonicLabelContainer.layer.cornerRadius = TextField.cornerRadius
|
mnemonicLabelContainer.layer.cornerRadius = TextField.cornerRadius
|
||||||
mnemonicLabelContainer.layer.borderWidth = 1
|
mnemonicLabelContainer.layer.borderWidth = 1
|
||||||
mnemonicLabelContainer.layer.borderColor = Colors.text.cgColor
|
mnemonicLabelContainer.layer.borderColor = Colors.text.cgColor
|
||||||
|
|
||||||
// Set up call to action label
|
// Set up call to action label
|
||||||
let callToActionLabel = UILabel()
|
let callToActionLabel = UILabel()
|
||||||
callToActionLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
|
|
||||||
callToActionLabel.font = .systemFont(ofSize: isIPhone5OrSmaller ? Values.smallFontSize : Values.mediumFontSize)
|
callToActionLabel.font = .systemFont(ofSize: isIPhone5OrSmaller ? Values.smallFontSize : Values.mediumFontSize)
|
||||||
callToActionLabel.text = NSLocalizedString("vc_seed_reveal_button_title", comment: "")
|
callToActionLabel.text = "vc_seed_reveal_button_title".localized()
|
||||||
|
callToActionLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
|
||||||
callToActionLabel.textAlignment = .center
|
callToActionLabel.textAlignment = .center
|
||||||
|
|
||||||
let callToActionLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonic))
|
let callToActionLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonic))
|
||||||
callToActionLabel.addGestureRecognizer(callToActionLabelGestureRecognizer)
|
callToActionLabel.addGestureRecognizer(callToActionLabelGestureRecognizer)
|
||||||
callToActionLabel.isUserInteractionEnabled = true
|
callToActionLabel.isUserInteractionEnabled = true
|
||||||
callToActionLabel.isEnabled = true
|
callToActionLabel.isEnabled = true
|
||||||
|
|
||||||
// Set up spacers
|
// Set up spacers
|
||||||
let topSpacer = UIView.vStretchingSpacer()
|
let topSpacer = UIView.vStretchingSpacer()
|
||||||
let bottomSpacer = UIView.vStretchingSpacer()
|
let bottomSpacer = UIView.vStretchingSpacer()
|
||||||
|
|
||||||
// Set up copy button container
|
// Set up copy button container
|
||||||
let copyButtonContainer = UIView()
|
let copyButtonContainer = UIView()
|
||||||
copyButtonContainer.addSubview(copyButton)
|
copyButtonContainer.addSubview(copyButton)
|
||||||
|
@ -107,6 +123,7 @@ final class SeedVC: BaseVC {
|
||||||
copyButton.pin(.top, to: .top, of: copyButtonContainer)
|
copyButton.pin(.top, to: .top, of: copyButtonContainer)
|
||||||
copyButtonContainer.pin(.trailing, to: .trailing, of: copyButton, withInset: Values.massiveSpacing)
|
copyButtonContainer.pin(.trailing, to: .trailing, of: copyButton, withInset: Values.massiveSpacing)
|
||||||
copyButtonContainer.pin(.bottom, to: .bottom, of: copyButton)
|
copyButtonContainer.pin(.bottom, to: .bottom, of: copyButton)
|
||||||
|
|
||||||
// Set up top stack view
|
// Set up top stack view
|
||||||
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, UIView.spacer(withHeight: isIPhone6OrSmaller ? Values.smallSpacing : Values.largeSpacing), explanationLabel,
|
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, UIView.spacer(withHeight: isIPhone6OrSmaller ? Values.smallSpacing : Values.largeSpacing), explanationLabel,
|
||||||
UIView.spacer(withHeight: isIPhone6OrSmaller ? Values.smallSpacing + 2 : Values.largeSpacing), mnemonicLabelContainer ])
|
UIView.spacer(withHeight: isIPhone6OrSmaller ? Values.smallSpacing + 2 : Values.largeSpacing), mnemonicLabelContainer ])
|
||||||
|
@ -116,6 +133,7 @@ final class SeedVC: BaseVC {
|
||||||
}
|
}
|
||||||
topStackView.axis = .vertical
|
topStackView.axis = .vertical
|
||||||
topStackView.alignment = .fill
|
topStackView.alignment = .fill
|
||||||
|
|
||||||
// Set up top stack view container
|
// Set up top stack view container
|
||||||
let topStackViewContainer = UIView()
|
let topStackViewContainer = UIView()
|
||||||
topStackViewContainer.addSubview(topStackView)
|
topStackViewContainer.addSubview(topStackView)
|
||||||
|
@ -123,11 +141,13 @@ final class SeedVC: BaseVC {
|
||||||
topStackView.pin(.top, to: .top, of: topStackViewContainer)
|
topStackView.pin(.top, to: .top, of: topStackViewContainer)
|
||||||
topStackViewContainer.pin(.trailing, to: .trailing, of: topStackView, withInset: Values.veryLargeSpacing)
|
topStackViewContainer.pin(.trailing, to: .trailing, of: topStackView, withInset: Values.veryLargeSpacing)
|
||||||
topStackViewContainer.pin(.bottom, to: .bottom, of: topStackView)
|
topStackViewContainer.pin(.bottom, to: .bottom, of: topStackView)
|
||||||
|
|
||||||
// Set up seed reminder view
|
// Set up seed reminder view
|
||||||
view.addSubview(seedReminderView)
|
view.addSubview(seedReminderView)
|
||||||
seedReminderView.pin(.leading, to: .leading, of: view)
|
seedReminderView.pin(.leading, to: .leading, of: view)
|
||||||
seedReminderView.pin(.top, to: .top, of: view)
|
seedReminderView.pin(.top, to: .top, of: view)
|
||||||
seedReminderView.pin(.trailing, to: .trailing, of: view)
|
seedReminderView.pin(.trailing, to: .trailing, of: view)
|
||||||
|
|
||||||
// Set up main stack view
|
// Set up main stack view
|
||||||
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, copyButtonContainer ])
|
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, copyButtonContainer ])
|
||||||
mainStackView.axis = .vertical
|
mainStackView.axis = .vertical
|
||||||
|
@ -135,6 +155,7 @@ final class SeedVC: BaseVC {
|
||||||
mainStackView.layoutMargins = UIEdgeInsets(top: 0, leading: 0, bottom: Values.mediumSpacing, trailing: 0)
|
mainStackView.layoutMargins = UIEdgeInsets(top: 0, leading: 0, bottom: Values.mediumSpacing, trailing: 0)
|
||||||
mainStackView.isLayoutMarginsRelativeArrangement = true
|
mainStackView.isLayoutMarginsRelativeArrangement = true
|
||||||
view.addSubview(mainStackView)
|
view.addSubview(mainStackView)
|
||||||
|
|
||||||
mainStackView.pin(.leading, to: .leading, of: view)
|
mainStackView.pin(.leading, to: .leading, of: view)
|
||||||
mainStackView.pin(.top, to: .bottom, of: seedReminderView)
|
mainStackView.pin(.top, to: .bottom, of: seedReminderView)
|
||||||
mainStackView.pin(.trailing, to: .trailing, of: view)
|
mainStackView.pin(.trailing, to: .trailing, of: view)
|
||||||
|
@ -142,15 +163,17 @@ final class SeedVC: BaseVC {
|
||||||
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
|
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: General
|
// MARK: - General
|
||||||
|
|
||||||
@objc private func enableCopyButton() {
|
@objc private func enableCopyButton() {
|
||||||
copyButton.isUserInteractionEnabled = true
|
copyButton.isUserInteractionEnabled = true
|
||||||
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
|
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
|
||||||
self.copyButton.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
|
self.copyButton.setTitle("copy".localized(), for: UIControl.State.normal)
|
||||||
}, completion: nil)
|
}, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Interaction
|
// MARK: - Interaction
|
||||||
|
|
||||||
@objc private func close() {
|
@objc private func close() {
|
||||||
dismiss(animated: true, completion: nil)
|
dismiss(animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
@ -160,14 +183,16 @@ final class SeedVC: BaseVC {
|
||||||
self.mnemonicLabel.text = self.mnemonic
|
self.mnemonicLabel.text = self.mnemonic
|
||||||
self.mnemonicLabel.textColor = Colors.text
|
self.mnemonicLabel.textColor = Colors.text
|
||||||
}, completion: nil)
|
}, completion: nil)
|
||||||
|
|
||||||
UIView.transition(with: seedReminderView.titleLabel, duration: 0.25, options: .transitionCrossDissolve, animations: {
|
UIView.transition(with: seedReminderView.titleLabel, duration: 0.25, options: .transitionCrossDissolve, animations: {
|
||||||
let title = "Account Secured! 100%"
|
let title = "Account Secured! 100%"
|
||||||
let attributedTitle = NSMutableAttributedString(string: title)
|
let attributedTitle = NSMutableAttributedString(string: title)
|
||||||
attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "100%"))
|
attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "100%"))
|
||||||
self.seedReminderView.title = attributedTitle
|
self.seedReminderView.title = attributedTitle
|
||||||
}, completion: nil)
|
}, completion: nil)
|
||||||
|
|
||||||
UIView.transition(with: seedReminderView.subtitleLabel, duration: 1, options: .transitionCrossDissolve, animations: {
|
UIView.transition(with: seedReminderView.subtitleLabel, duration: 1, options: .transitionCrossDissolve, animations: {
|
||||||
self.seedReminderView.subtitle = NSLocalizedString("view_seed_reminder_subtitle_3", comment: "")
|
self.seedReminderView.subtitle = "view_seed_reminder_subtitle_3".localized()
|
||||||
}, completion: nil)
|
}, completion: nil)
|
||||||
seedReminderView.setProgress(1, animated: true)
|
seedReminderView.setProgress(1, animated: true)
|
||||||
|
|
||||||
|
@ -176,11 +201,15 @@ final class SeedVC: BaseVC {
|
||||||
|
|
||||||
@objc private func copyMnemonic() {
|
@objc private func copyMnemonic() {
|
||||||
revealMnemonic()
|
revealMnemonic()
|
||||||
|
|
||||||
UIPasteboard.general.string = mnemonic
|
UIPasteboard.general.string = mnemonic
|
||||||
|
|
||||||
copyButton.isUserInteractionEnabled = false
|
copyButton.isUserInteractionEnabled = false
|
||||||
|
|
||||||
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
|
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
|
||||||
self.copyButton.setTitle(NSLocalizedString("copied", comment: ""), for: UIControl.State.normal)
|
self.copyButton.setTitle("copied".localized(), for: UIControl.State.normal)
|
||||||
}, completion: nil)
|
}, completion: nil)
|
||||||
|
|
||||||
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false)
|
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,12 +219,12 @@ private final class EnterURLVC: UIViewController, UIGestureRecognizerDelegate, O
|
||||||
|
|
||||||
private lazy var suggestionGridTitleLabel: UILabel = {
|
private lazy var suggestionGridTitleLabel: UILabel = {
|
||||||
let result: UILabel = UILabel()
|
let result: UILabel = UILabel()
|
||||||
result.textColor = Colors.text
|
result.setContentHuggingPriority(.required, for: .vertical)
|
||||||
result.font = .boldSystemFont(ofSize: Values.largeFontSize)
|
result.font = .boldSystemFont(ofSize: Values.largeFontSize)
|
||||||
result.text = "vc_join_open_group_suggestions_title".localized()
|
result.text = "vc_join_open_group_suggestions_title".localized()
|
||||||
result.numberOfLines = 0
|
result.textColor = Colors.text
|
||||||
result.lineBreakMode = .byWordWrapping
|
result.lineBreakMode = .byWordWrapping
|
||||||
result.setContentHuggingPriority(.required, for: .vertical)
|
result.numberOfLines = 0
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
@ -244,9 +244,9 @@ private final class EnterURLVC: UIViewController, UIGestureRecognizerDelegate, O
|
||||||
view.backgroundColor = .clear
|
view.backgroundColor = .clear
|
||||||
|
|
||||||
// Next button
|
// Next button
|
||||||
let nextButton = Button(style: .prominentOutline, size: .large)
|
let nextButton = OutlineButton(style: .regular, size: .large)
|
||||||
nextButton.setTitle(NSLocalizedString("next", comment: ""), for: UIControl.State.normal)
|
nextButton.setTitle("next".localized(), for: .normal)
|
||||||
nextButton.addTarget(self, action: #selector(joinOpenGroup), for: UIControl.Event.touchUpInside)
|
nextButton.addTarget(self, action: #selector(joinOpenGroup), for: .touchUpInside)
|
||||||
|
|
||||||
let nextButtonContainer = UIView(
|
let nextButtonContainer = UIView(
|
||||||
wrapping: nextButton,
|
wrapping: nextButton,
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
import NVActivityIndicatorView
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import NVActivityIndicatorView
|
||||||
import SessionMessagingKit
|
import SessionMessagingKit
|
||||||
|
import SessionUIKit
|
||||||
|
|
||||||
final class PathVC: BaseVC {
|
final class PathVC: BaseVC {
|
||||||
|
public static let dotSize: CGFloat = 8
|
||||||
|
public static let expandedDotSize: CGFloat = 16
|
||||||
|
private static let rowHeight: CGFloat = (isIPhone5OrSmaller ? 52 : 75)
|
||||||
|
|
||||||
|
// MARK: - Components
|
||||||
|
|
||||||
// MARK: Components
|
|
||||||
private lazy var pathStackView: UIStackView = {
|
private lazy var pathStackView: UIStackView = {
|
||||||
let result = UIStackView()
|
let result = UIStackView()
|
||||||
result.axis = .vertical
|
result.axis = .vertical
|
||||||
|
@ -18,21 +25,18 @@ final class PathVC : BaseVC {
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var learnMoreButton: Button = {
|
private lazy var learnMoreButton: OutlineButton = {
|
||||||
let result = Button(style: .prominentOutline, size: .large)
|
let result = OutlineButton(style: .regular, size: .large)
|
||||||
result.setTitle(NSLocalizedString("vc_path_learn_more_button_title", comment: ""), for: UIControl.State.normal)
|
result.setTitle("vc_path_learn_more_button_title".localized(), for: UIControl.State.normal)
|
||||||
result.addTarget(self, action: #selector(learnMore), for: UIControl.Event.touchUpInside)
|
result.addTarget(self, action: #selector(learnMore), for: UIControl.Event.touchUpInside)
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// MARK: Settings
|
// MARK: - Lifecycle
|
||||||
static let dotSize = CGFloat(8)
|
|
||||||
static let expandedDotSize = CGFloat(16)
|
|
||||||
static let rowHeight = isIPhone5OrSmaller ? CGFloat(52) : CGFloat(75)
|
|
||||||
|
|
||||||
// MARK: Lifecycle
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
setUpGradientBackground()
|
setUpGradientBackground()
|
||||||
setUpNavBar()
|
setUpNavBar()
|
||||||
setUpViewHierarchy()
|
setUpViewHierarchy()
|
||||||
|
@ -41,15 +45,15 @@ final class PathVC : BaseVC {
|
||||||
|
|
||||||
private func setUpNavBar() {
|
private func setUpNavBar() {
|
||||||
setUpNavBarStyle()
|
setUpNavBarStyle()
|
||||||
setNavBarTitle(NSLocalizedString("vc_path_title", comment: ""))
|
setNavBarTitle("vc_path_title".localized())
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setUpViewHierarchy() {
|
private func setUpViewHierarchy() {
|
||||||
// Set up explanation label
|
// Set up explanation label
|
||||||
let explanationLabel = UILabel()
|
let explanationLabel = UILabel()
|
||||||
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
|
|
||||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
explanationLabel.text = NSLocalizedString("vc_path_explanation", comment: "")
|
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
|
||||||
|
explanationLabel.text = "vc_path_explanation".localized()
|
||||||
explanationLabel.numberOfLines = 0
|
explanationLabel.numberOfLines = 0
|
||||||
explanationLabel.textAlignment = .center
|
explanationLabel.textAlignment = .center
|
||||||
explanationLabel.lineBreakMode = .byWordWrapping
|
explanationLabel.lineBreakMode = .byWordWrapping
|
||||||
|
|
|
@ -0,0 +1,273 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SessionUIKit
|
||||||
|
import SignalUtilitiesKit
|
||||||
|
|
||||||
|
final class AppearanceViewController: BaseVC {
|
||||||
|
// MARK: - Components
|
||||||
|
|
||||||
|
private let scrollView: UIScrollView = {
|
||||||
|
let result: UIScrollView = UIScrollView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.showsVerticalScrollIndicator = false
|
||||||
|
result.showsHorizontalScrollIndicator = false
|
||||||
|
result.contentInset = UIEdgeInsets(
|
||||||
|
top: 0,
|
||||||
|
leading: 0,
|
||||||
|
bottom: Values.largeSpacing,
|
||||||
|
trailing: 0
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let themesTitleLabel: UILabel = {
|
||||||
|
let result: UILabel = UILabel()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.font = UIFont.systemFont(ofSize: Values.mediumFontSize, weight: .regular)
|
||||||
|
result.themeTextColor = .textSecondary
|
||||||
|
result.text = "APPEARANCE_THEMES_TITLE".localized()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let themesStackView: UIStackView = {
|
||||||
|
let result: UIStackView = UIStackView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = true
|
||||||
|
result.axis = .vertical
|
||||||
|
result.distribution = .equalCentering
|
||||||
|
result.alignment = .fill
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var themeSelectionViews: [ThemeSelectionView] = Theme.allCases
|
||||||
|
.map { theme in
|
||||||
|
let result: ThemeSelectionView = ThemeSelectionView(theme: theme) { [weak self] theme in
|
||||||
|
ThemeManager.currentTheme = theme
|
||||||
|
}
|
||||||
|
result.update(isSelected: (ThemeManager.currentTheme == theme))
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private let primaryColorTitleLabel: UILabel = {
|
||||||
|
let result: UILabel = UILabel()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.font = UIFont.systemFont(ofSize: Values.mediumFontSize, weight: .regular)
|
||||||
|
result.themeTextColor = .textSecondary
|
||||||
|
result.text = "APPEARANCE_PRIMARY_COLOR_TITLE".localized()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let primaryColorPreviewStackView: UIStackView = {
|
||||||
|
let result: UIStackView = UIStackView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.axis = .vertical
|
||||||
|
result.distribution = .equalCentering
|
||||||
|
result.alignment = .fill
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let primaryColorPreviewView: ThemePreviewView = {
|
||||||
|
let result: ThemePreviewView = ThemePreviewView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let primaryColorScrollView: UIScrollView = {
|
||||||
|
let result: UIScrollView = UIScrollView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.showsVerticalScrollIndicator = false
|
||||||
|
result.showsHorizontalScrollIndicator = false
|
||||||
|
result.contentInset = UIEdgeInsets(
|
||||||
|
top: 0,
|
||||||
|
leading: Values.largeSpacing,
|
||||||
|
bottom: 0,
|
||||||
|
trailing: Values.largeSpacing
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let primaryColorSelectionStackView: UIStackView = {
|
||||||
|
let result: UIStackView = UIStackView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.axis = .horizontal
|
||||||
|
result.distribution = .equalCentering
|
||||||
|
result.alignment = .fill
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var primaryColorSelectionViews: [PrimaryColorSelectionView] = Theme.PrimaryColor.allCases
|
||||||
|
.map { color in
|
||||||
|
let result: PrimaryColorSelectionView = PrimaryColorSelectionView(color: color) { [weak self] color in
|
||||||
|
ThemeManager.primaryColor = color
|
||||||
|
}
|
||||||
|
result.update(isSelected: (ThemeManager.primaryColor == color))
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private let nightModeTitleLabel: UILabel = {
|
||||||
|
let result: UILabel = UILabel()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.font = UIFont.systemFont(ofSize: Values.mediumFontSize, weight: .regular)
|
||||||
|
result.themeTextColor = .textSecondary
|
||||||
|
result.text = "APPEARANCE_NIGHT_MODE_TITLE".localized()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let nightModeStackView: UIStackView = {
|
||||||
|
let result: UIStackView = UIStackView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.axis = .vertical
|
||||||
|
result.distribution = .equalCentering
|
||||||
|
result.alignment = .fill
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let nightModeToggleView: UIView = {
|
||||||
|
let result: UIView = UIView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.themeBackgroundColor = .appearance_sectionBackground
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let nightModeToggleLabel: UILabel = {
|
||||||
|
let result: UILabel = UILabel()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.font = UIFont.systemFont(ofSize: Values.mediumFontSize, weight: .regular)
|
||||||
|
result.themeTextColor = .textPrimary
|
||||||
|
result.text = "APPEARANCE_NIGHT_MODE_TOGGLE".localized()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var nightModeToggleSwitch: UISwitch = {
|
||||||
|
let result: UISwitch = UISwitch()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.themeOnTintColor = .primary
|
||||||
|
result.isOn = ThemeManager.matchSystemNightModeSetting
|
||||||
|
result.addTarget(self, action: #selector(nightModeToggleChanged(sender:)), for: .valueChanged)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
ViewControllerUtilities.setUpDefaultSessionStyle(
|
||||||
|
for: self,
|
||||||
|
title: "APPEARANCE_TITLE".localized(),
|
||||||
|
hasCustomBackButton: false
|
||||||
|
)
|
||||||
|
|
||||||
|
view.themeBackgroundColor = .backgroundPrimary
|
||||||
|
view.addSubview(scrollView)
|
||||||
|
|
||||||
|
scrollView.addSubview(themesTitleLabel)
|
||||||
|
scrollView.addSubview(themesStackView)
|
||||||
|
scrollView.addSubview(primaryColorTitleLabel)
|
||||||
|
scrollView.addSubview(primaryColorPreviewStackView)
|
||||||
|
scrollView.addSubview(primaryColorScrollView)
|
||||||
|
scrollView.addSubview(nightModeTitleLabel)
|
||||||
|
scrollView.addSubview(nightModeStackView)
|
||||||
|
|
||||||
|
themesStackView.addArrangedSubview(UIView.separator())
|
||||||
|
themeSelectionViews.forEach { view in
|
||||||
|
themesStackView.addArrangedSubview(view)
|
||||||
|
themesStackView.addArrangedSubview(UIView.separator())
|
||||||
|
}
|
||||||
|
|
||||||
|
primaryColorPreviewStackView.addArrangedSubview(UIView.separator())
|
||||||
|
primaryColorPreviewStackView.addArrangedSubview(primaryColorPreviewView)
|
||||||
|
primaryColorPreviewStackView.addArrangedSubview(UIView.separator())
|
||||||
|
|
||||||
|
primaryColorScrollView.addSubview(primaryColorSelectionStackView)
|
||||||
|
|
||||||
|
primaryColorSelectionViews.forEach { view in
|
||||||
|
primaryColorSelectionStackView.addArrangedSubview(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
nightModeStackView.addArrangedSubview(UIView.separator())
|
||||||
|
nightModeStackView.addArrangedSubview(nightModeToggleView)
|
||||||
|
nightModeStackView.addArrangedSubview(UIView.separator())
|
||||||
|
|
||||||
|
nightModeToggleView.addSubview(nightModeToggleLabel)
|
||||||
|
nightModeToggleView.addSubview(nightModeToggleSwitch)
|
||||||
|
|
||||||
|
// Register an observer so when the theme changes the selected theme and primary colour
|
||||||
|
// are both updated to match
|
||||||
|
ThemeManager.onThemeChange(observer: self) { [weak self] theme, primaryColor in
|
||||||
|
self?.themeSelectionViews.forEach { view in
|
||||||
|
view.update(isSelected: (theme == view.theme))
|
||||||
|
}
|
||||||
|
|
||||||
|
self?.primaryColorSelectionViews.forEach { view in
|
||||||
|
view.update(isSelected: (primaryColor == view.color))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupLayout() {
|
||||||
|
scrollView.pin(to: view)
|
||||||
|
|
||||||
|
themesTitleLabel.pin(.top, to: .top, of: scrollView)
|
||||||
|
themesTitleLabel.pin(.left, to: .left, of: scrollView, withInset: Values.largeSpacing)
|
||||||
|
|
||||||
|
themesStackView.pin(.top, to: .bottom, of: themesTitleLabel, withInset: Values.mediumSpacing)
|
||||||
|
themesStackView.pin(.left, to: .left, of: scrollView)
|
||||||
|
themesStackView.set(.width, to: .width, of: scrollView)
|
||||||
|
|
||||||
|
primaryColorTitleLabel.pin(.top, to: .bottom, of: themesStackView, withInset: Values.mediumSpacing)
|
||||||
|
primaryColorTitleLabel.pin(.left, to: .left, of: scrollView, withInset: Values.largeSpacing)
|
||||||
|
|
||||||
|
primaryColorPreviewStackView.pin(.top, to: .bottom, of: primaryColorTitleLabel, withInset: Values.smallSpacing)
|
||||||
|
primaryColorPreviewStackView.pin(.left, to: .left, of: scrollView)
|
||||||
|
primaryColorPreviewStackView.set(.width, to: .width, of: scrollView)
|
||||||
|
|
||||||
|
primaryColorScrollView.pin(.top, to: .bottom, of: primaryColorPreviewStackView, withInset: Values.mediumSpacing)
|
||||||
|
primaryColorScrollView.pin(.left, to: .left, of: scrollView)
|
||||||
|
primaryColorScrollView.set(.width, to: .width, of: scrollView)
|
||||||
|
|
||||||
|
primaryColorSelectionStackView.pin(to: primaryColorScrollView)
|
||||||
|
primaryColorSelectionStackView.set(.height, to: .height, of: primaryColorScrollView)
|
||||||
|
|
||||||
|
nightModeTitleLabel.pin(.top, to: .bottom, of: primaryColorScrollView, withInset: Values.largeSpacing)
|
||||||
|
nightModeTitleLabel.pin(.left, to: .left, of: scrollView, withInset: Values.largeSpacing)
|
||||||
|
nightModeTitleLabel.set(.width, to: .width, of: scrollView)
|
||||||
|
|
||||||
|
nightModeStackView.pin(.top, to: .bottom, of: nightModeTitleLabel, withInset: Values.smallSpacing)
|
||||||
|
nightModeStackView.pin(.bottom, to: .bottom, of: scrollView)
|
||||||
|
nightModeStackView.pin(.left, to: .left, of: scrollView)
|
||||||
|
nightModeStackView.set(.width, to: .width, of: scrollView)
|
||||||
|
|
||||||
|
nightModeToggleLabel.setContentHuggingVerticalHigh()
|
||||||
|
nightModeToggleLabel.setCompressionResistanceVerticalHigh()
|
||||||
|
nightModeToggleLabel.center(.vertical, in: nightModeToggleView)
|
||||||
|
nightModeToggleLabel.pin(.left, to: .left, of: nightModeToggleView, withInset: Values.largeSpacing)
|
||||||
|
|
||||||
|
nightModeToggleSwitch.pin(.top, to: .top, of: nightModeToggleView, withInset: Values.smallSpacing)
|
||||||
|
nightModeToggleSwitch.pin(.bottom, to: .bottom, of: nightModeToggleView, withInset: -Values.smallSpacing)
|
||||||
|
nightModeToggleSwitch.pin(.right, to: .right, of: nightModeToggleView, withInset: -Values.largeSpacing)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Actions
|
||||||
|
|
||||||
|
@objc private func nightModeToggleChanged(sender: UISwitch) {
|
||||||
|
ThemeManager.matchSystemNightModeSetting = sender.isOn
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ class ChatSettingsViewController: OWSTableViewController {
|
||||||
|
|
||||||
self.updateTableContents()
|
self.updateTableContents()
|
||||||
|
|
||||||
ViewControllerUtilities.setUpDefaultSessionStyle(for: self, title: "CHATS_TITLE".localized(), hasCustomBackButton: false)
|
ViewControllerUtilities.setUpDefaultSessionStyle(for: self, title: "CONVERSATIONS_TITLE".localized(), hasCustomBackButton: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import Curve25519Kit
|
import Curve25519Kit
|
||||||
|
import SessionUIKit
|
||||||
import SessionUtilitiesKit
|
import SessionUtilitiesKit
|
||||||
|
|
||||||
final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, OWSQRScannerDelegate {
|
final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, OWSQRScannerDelegate {
|
||||||
|
@ -10,14 +11,15 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
|
||||||
private var targetVCIndex: Int?
|
private var targetVCIndex: Int?
|
||||||
private var tabBarTopConstraint: NSLayoutConstraint!
|
private var tabBarTopConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
// MARK: Components
|
// MARK: - Components
|
||||||
|
|
||||||
private lazy var tabBar: TabBar = {
|
private lazy var tabBar: TabBar = {
|
||||||
let tabs = [
|
let tabs = [
|
||||||
TabBar.Tab(title: NSLocalizedString("vc_qr_code_view_my_qr_code_tab_title", comment: "")) { [weak self] in
|
TabBar.Tab(title: "vc_qr_code_view_my_qr_code_tab_title".localized()) { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil)
|
self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil)
|
||||||
},
|
},
|
||||||
TabBar.Tab(title: NSLocalizedString("vc_qr_code_view_scan_qr_code_tab_title", comment: "")) { [weak self] in
|
TabBar.Tab(title: "vc_qr_code_view_scan_qr_code_tab_title".localized()) { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil)
|
self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil)
|
||||||
}
|
}
|
||||||
|
@ -28,50 +30,60 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
|
||||||
private lazy var viewMyQRCodeVC: ViewMyQRCodeVC = {
|
private lazy var viewMyQRCodeVC: ViewMyQRCodeVC = {
|
||||||
let result = ViewMyQRCodeVC()
|
let result = ViewMyQRCodeVC()
|
||||||
result.qrCodeVC = self
|
result.qrCodeVC = self
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var scanQRCodePlaceholderVC: ScanQRCodePlaceholderVC = {
|
private lazy var scanQRCodePlaceholderVC: ScanQRCodePlaceholderVC = {
|
||||||
let result = ScanQRCodePlaceholderVC()
|
let result = ScanQRCodePlaceholderVC()
|
||||||
result.qrCodeVC = self
|
result.qrCodeVC = self
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = {
|
private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = {
|
||||||
let message = NSLocalizedString("vc_qr_code_view_scan_qr_code_explanation", comment: "")
|
let message = "vc_qr_code_view_scan_qr_code_explanation".localized()
|
||||||
let result = ScanQRCodeWrapperVC(message: message)
|
let result = ScanQRCodeWrapperVC(message: message)
|
||||||
result.delegate = self
|
result.delegate = self
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// MARK: Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
setUpGradientBackground()
|
setUpGradientBackground()
|
||||||
setUpNavBarStyle()
|
setUpNavBarStyle()
|
||||||
setNavBarTitle(NSLocalizedString("vc_qr_code_title", comment: ""))
|
setNavBarTitle("vc_qr_code_title".localized())
|
||||||
let navigationBar = navigationController!.navigationBar
|
|
||||||
// Set up page VC
|
// Set up page VC
|
||||||
let hasCameraAccess = (AVCaptureDevice.authorizationStatus(for: .video) == .authorized)
|
let hasCameraAccess = (AVCaptureDevice.authorizationStatus(for: .video) == .authorized)
|
||||||
pages = [ viewMyQRCodeVC, (hasCameraAccess ? scanQRCodeWrapperVC : scanQRCodePlaceholderVC) ]
|
pages = [ viewMyQRCodeVC, (hasCameraAccess ? scanQRCodeWrapperVC : scanQRCodePlaceholderVC) ]
|
||||||
pageVC.dataSource = self
|
pageVC.dataSource = self
|
||||||
pageVC.delegate = self
|
pageVC.delegate = self
|
||||||
pageVC.setViewControllers([ viewMyQRCodeVC ], direction: .forward, animated: false, completion: nil)
|
pageVC.setViewControllers([ viewMyQRCodeVC ], direction: .forward, animated: false, completion: nil)
|
||||||
|
|
||||||
// Set up tab bar
|
// Set up tab bar
|
||||||
view.addSubview(tabBar)
|
view.addSubview(tabBar)
|
||||||
tabBar.pin(.leading, to: .leading, of: view)
|
tabBar.pin(.leading, to: .leading, of: view)
|
||||||
tabBarTopConstraint = tabBar.autoPinEdge(toSuperviewSafeArea: .top)
|
tabBarTopConstraint = tabBar.autoPinEdge(toSuperviewSafeArea: .top)
|
||||||
view.pin(.trailing, to: .trailing, of: tabBar)
|
view.pin(.trailing, to: .trailing, of: tabBar)
|
||||||
|
|
||||||
// Set up page VC constraints
|
// Set up page VC constraints
|
||||||
let pageVCView = pageVC.view!
|
let pageVCView = pageVC.view!
|
||||||
view.addSubview(pageVCView)
|
view.addSubview(pageVCView)
|
||||||
|
|
||||||
pageVCView.pin(.leading, to: .leading, of: view)
|
pageVCView.pin(.leading, to: .leading, of: view)
|
||||||
pageVCView.pin(.top, to: .bottom, of: tabBar)
|
pageVCView.pin(.top, to: .bottom, of: tabBar)
|
||||||
view.pin(.trailing, to: .trailing, of: pageVCView)
|
view.pin(.trailing, to: .trailing, of: pageVCView)
|
||||||
view.pin(.bottom, to: .bottom, of: pageVCView)
|
view.pin(.bottom, to: .bottom, of: pageVCView)
|
||||||
|
|
||||||
let screen = UIScreen.main.bounds
|
let screen = UIScreen.main.bounds
|
||||||
pageVCView.set(.width, to: screen.width)
|
pageVCView.set(.width, to: screen.width)
|
||||||
let height: CGFloat = (navigationController!.view.bounds.height - navigationBar.height() - TabBar.snHeight)
|
|
||||||
|
let height: CGFloat = ((navigationController?.view.bounds.height ?? 0) - (navigationController?.navigationBar.height() ?? 0) - TabBar.snHeight)
|
||||||
pageVCView.set(.height, to: height)
|
pageVCView.set(.height, to: height)
|
||||||
viewMyQRCodeVC.constrainHeight(to: height)
|
viewMyQRCodeVC.constrainHeight(to: height)
|
||||||
scanQRCodePlaceholderVC.constrainHeight(to: height)
|
scanQRCodePlaceholderVC.constrainHeight(to: height)
|
||||||
|
@ -82,7 +94,8 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
|
||||||
tabBarTopConstraint.constant = navigationController!.navigationBar.height()
|
tabBarTopConstraint.constant = navigationController!.navigationBar.height()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: General
|
// MARK: - General
|
||||||
|
|
||||||
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
|
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
|
||||||
guard let index = pages.firstIndex(of: viewController), index != 0 else { return nil }
|
guard let index = pages.firstIndex(of: viewController), index != 0 else { return nil }
|
||||||
return pages[index - 1]
|
return pages[index - 1]
|
||||||
|
@ -98,7 +111,8 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
|
||||||
pageVC.setViewControllers([ scanQRCodeWrapperVC ], direction: .forward, animated: false, completion: nil)
|
pageVC.setViewControllers([ scanQRCodeWrapperVC ], direction: .forward, animated: false, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Updating
|
// MARK: - Updating
|
||||||
|
|
||||||
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
|
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
|
||||||
guard let targetVC = pendingViewControllers.first, let index = pages.firstIndex(of: targetVC) else { return }
|
guard let targetVC = pendingViewControllers.first, let index = pages.firstIndex(of: targetVC) else { return }
|
||||||
targetVCIndex = index
|
targetVCIndex = index
|
||||||
|
@ -109,7 +123,8 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
|
||||||
tabBar.selectTab(at: index)
|
tabBar.selectTab(at: index)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Interaction
|
// MARK: - Interaction
|
||||||
|
|
||||||
@objc private func close() {
|
@objc private func close() {
|
||||||
dismiss(animated: true, completion: nil)
|
dismiss(animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
@ -124,7 +139,7 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
|
||||||
let alert = UIAlertController(
|
let alert = UIAlertController(
|
||||||
title: "invalid_session_id".localized(),
|
title: "invalid_session_id".localized(),
|
||||||
message: "INVALID_SESSION_ID_MESSAGE".localized(), preferredStyle: .alert)
|
message: "INVALID_SESSION_ID_MESSAGE".localized(), preferredStyle: .alert)
|
||||||
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil))
|
alert.addAction(UIAlertAction(title: "BUTTON_OK".localized(), style: .default, handler: nil))
|
||||||
presentAlert(alert)
|
presentAlert(alert)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -145,7 +160,8 @@ private final class ViewMyQRCodeVC : UIViewController {
|
||||||
weak var qrCodeVC: QRCodeVC!
|
weak var qrCodeVC: QRCodeVC!
|
||||||
private var bottomConstraint: NSLayoutConstraint!
|
private var bottomConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
// MARK: Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
// Remove background color
|
// Remove background color
|
||||||
view.backgroundColor = .clear
|
view.backgroundColor = .clear
|
||||||
|
@ -181,10 +197,12 @@ private final class ViewMyQRCodeVC : UIViewController {
|
||||||
explanationLabel.numberOfLines = 0
|
explanationLabel.numberOfLines = 0
|
||||||
explanationLabel.textAlignment = .center
|
explanationLabel.textAlignment = .center
|
||||||
explanationLabel.lineBreakMode = .byWordWrapping
|
explanationLabel.lineBreakMode = .byWordWrapping
|
||||||
|
|
||||||
// Set up share button
|
// Set up share button
|
||||||
let shareButton = Button(style: .regular, size: .large)
|
let shareButton = OutlineButton(style: .regular, size: .large)
|
||||||
shareButton.setTitle(NSLocalizedString("share", comment: ""), for: UIControl.State.normal)
|
shareButton.setTitle("share".localized(), for: .normal)
|
||||||
shareButton.addTarget(self, action: #selector(shareQRCode), for: UIControl.Event.touchUpInside)
|
shareButton.addTarget(self, action: #selector(shareQRCode), for: .touchUpInside)
|
||||||
|
|
||||||
// Set up share button container
|
// Set up share button container
|
||||||
let shareButtonContainer = UIView()
|
let shareButtonContainer = UIView()
|
||||||
shareButtonContainer.addSubview(shareButton)
|
shareButtonContainer.addSubview(shareButton)
|
||||||
|
@ -193,6 +211,7 @@ private final class ViewMyQRCodeVC : UIViewController {
|
||||||
shareButtonContainer.pin(.trailing, to: .trailing, of: shareButton, withInset: 80)
|
shareButtonContainer.pin(.trailing, to: .trailing, of: shareButton, withInset: 80)
|
||||||
shareButtonContainer.pin(.bottom, to: .bottom, of: shareButton, withInset: isIPhone6OrSmaller ? Values.largeSpacing : Values.onboardingButtonBottomOffset)
|
shareButtonContainer.pin(.bottom, to: .bottom, of: shareButton, withInset: isIPhone6OrSmaller ? Values.largeSpacing : Values.onboardingButtonBottomOffset)
|
||||||
let spacing = isIPhone5OrSmaller ? Values.mediumSpacing : Values.largeSpacing
|
let spacing = isIPhone5OrSmaller ? Values.mediumSpacing : Values.largeSpacing
|
||||||
|
|
||||||
// Set up stack view
|
// Set up stack view
|
||||||
let stackView = UIStackView(arrangedSubviews: [ titleLabel, UIView.spacer(withHeight: spacing), qrCodeImageViewContainer, UIView.spacer(withHeight: spacing),
|
let stackView = UIStackView(arrangedSubviews: [ titleLabel, UIView.spacer(withHeight: spacing), qrCodeImageViewContainer, UIView.spacer(withHeight: spacing),
|
||||||
explanationLabel, UIView.vStretchingSpacer(), shareButtonContainer ])
|
explanationLabel, UIView.vStretchingSpacer(), shareButtonContainer ])
|
||||||
|
|
|
@ -33,8 +33,8 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
|
|
||||||
private lazy var displayNameLabel: UILabel = {
|
private lazy var displayNameLabel: UILabel = {
|
||||||
let result = UILabel()
|
let result = UILabel()
|
||||||
result.textColor = Colors.text
|
|
||||||
result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
||||||
|
result.themeTextColor = .textPrimary
|
||||||
result.lineBreakMode = .byTruncatingTail
|
result.lineBreakMode = .byTruncatingTail
|
||||||
result.textAlignment = .center
|
result.textAlignment = .center
|
||||||
|
|
||||||
|
@ -42,7 +42,10 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var displayNameTextField: TextField = {
|
private lazy var displayNameTextField: TextField = {
|
||||||
let result = TextField(placeholder: NSLocalizedString("vc_settings_display_name_text_field_hint", comment: ""), usesDefaultHeight: false)
|
let result = TextField(
|
||||||
|
placeholder: "vc_settings_display_name_text_field_hint".localized(),
|
||||||
|
usesDefaultHeight: false
|
||||||
|
)
|
||||||
result.textAlignment = .center
|
result.textAlignment = .center
|
||||||
result.accessibilityLabel = "Edit display name text field"
|
result.accessibilityLabel = "Edit display name text field"
|
||||||
|
|
||||||
|
@ -51,8 +54,8 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
|
|
||||||
private lazy var publicKeyLabel: UILabel = {
|
private lazy var publicKeyLabel: UILabel = {
|
||||||
let result = UILabel()
|
let result = UILabel()
|
||||||
result.textColor = Colors.text
|
|
||||||
result.font = Fonts.spaceMono(ofSize: isIPhone5OrSmaller ? Values.mediumFontSize : Values.largeFontSize)
|
result.font = Fonts.spaceMono(ofSize: isIPhone5OrSmaller ? Values.mediumFontSize : Values.largeFontSize)
|
||||||
|
result.themeTextColor = .textPrimary
|
||||||
result.numberOfLines = 0
|
result.numberOfLines = 0
|
||||||
result.textAlignment = .center
|
result.textAlignment = .center
|
||||||
result.lineBreakMode = .byCharWrapping
|
result.lineBreakMode = .byCharWrapping
|
||||||
|
@ -61,14 +64,22 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var copyButton: Button = {
|
private lazy var copyButton: OutlineButton = {
|
||||||
let result = Button(style: .prominentOutline, size: .medium)
|
let result = OutlineButton(style: .regular, size: .medium)
|
||||||
result.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
|
result.setTitle("copy".localized(), for: UIControl.State.normal)
|
||||||
result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside)
|
result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private lazy var shareButton: OutlineButton = {
|
||||||
|
let result = OutlineButton(style: .regular, size: .medium)
|
||||||
|
result.setTitle("share".localized(), for: UIControl.State.normal)
|
||||||
|
result.addTarget(self, action: #selector(sharePublicKey), for: UIControl.Event.touchUpInside)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
private lazy var settingButtonsStackView: UIStackView = {
|
private lazy var settingButtonsStackView: UIStackView = {
|
||||||
let result = UIStackView()
|
let result = UIStackView()
|
||||||
result.axis = .vertical
|
result.axis = .vertical
|
||||||
|
@ -77,74 +88,32 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var inviteButton: UIButton = {
|
|
||||||
let result = UIButton()
|
|
||||||
result.setTitle(NSLocalizedString("vc_settings_invite_a_friend_button_title", comment: ""), for: UIControl.State.normal)
|
|
||||||
result.setTitleColor(Colors.text, for: UIControl.State.normal)
|
|
||||||
result.titleLabel!.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
|
||||||
result.addTarget(self, action: #selector(sendInvitation), for: UIControl.Event.touchUpInside)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var faqButton: UIButton = {
|
|
||||||
let result = UIButton()
|
|
||||||
result.setTitle(NSLocalizedString("vc_settings_faq_button_title", comment: ""), for: UIControl.State.normal)
|
|
||||||
result.setTitleColor(Colors.text, for: UIControl.State.normal)
|
|
||||||
result.titleLabel!.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
|
||||||
result.addTarget(self, action: #selector(openFAQ), for: UIControl.Event.touchUpInside)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var surveyButton: UIButton = {
|
|
||||||
let result = UIButton()
|
|
||||||
result.setTitle(NSLocalizedString("vc_settings_survey_button_title", comment: ""), for: UIControl.State.normal)
|
|
||||||
result.setTitleColor(Colors.text, for: UIControl.State.normal)
|
|
||||||
result.titleLabel?.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
|
||||||
result.addTarget(self, action: #selector(openSurvey), for: UIControl.Event.touchUpInside)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var supportButton: UIButton = {
|
|
||||||
let result = UIButton()
|
|
||||||
result.setTitle(NSLocalizedString("vc_settings_support_button_title", comment: ""), for: UIControl.State.normal)
|
|
||||||
result.setTitleColor(Colors.text, for: UIControl.State.normal)
|
|
||||||
result.titleLabel!.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
|
||||||
result.addTarget(self, action: #selector(shareLogs), for: UIControl.Event.touchUpInside)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var helpTranslateButton: UIButton = {
|
|
||||||
let result = UIButton()
|
|
||||||
result.setTitle(NSLocalizedString("vc_settings_help_us_translate_button_title", comment: ""), for: UIControl.State.normal)
|
|
||||||
result.setTitleColor(Colors.text, for: UIControl.State.normal)
|
|
||||||
result.titleLabel!.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
|
||||||
result.addTarget(self, action: #selector(helpTranslate), for: UIControl.Event.touchUpInside)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var logoImageView: UIImageView = {
|
private lazy var logoImageView: UIImageView = {
|
||||||
let result = UIImageView()
|
let result = UIImageView(
|
||||||
result.set(.height, to: 24)
|
image: UIImage(named: "OxenLightMode")?
|
||||||
|
.withRenderingMode(.alwaysTemplate)
|
||||||
|
)
|
||||||
|
result.themeTintColor = .textPrimary
|
||||||
result.contentMode = .scaleAspectFit
|
result.contentMode = .scaleAspectFit
|
||||||
|
result.set(.height, to: 24)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var versionLabel: UILabel = {
|
private lazy var versionLabel: UILabel = {
|
||||||
|
let version: String = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String)
|
||||||
|
.defaulting(to: "0.0.0")
|
||||||
|
let buildNumber: String = (Bundle.main.infoDictionary?["CFBundleVersion"] as? String)
|
||||||
|
.defaulting(to: "0")
|
||||||
|
|
||||||
let result = UILabel()
|
let result = UILabel()
|
||||||
result.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
|
|
||||||
result.font = .systemFont(ofSize: Values.verySmallFontSize)
|
result.font = .systemFont(ofSize: Values.verySmallFontSize)
|
||||||
|
result.text = "Version \(version) (\(buildNumber))"
|
||||||
|
result.themeTextColor = .textPrimary
|
||||||
result.numberOfLines = 0
|
result.numberOfLines = 0
|
||||||
result.textAlignment = .center
|
result.textAlignment = .center
|
||||||
result.lineBreakMode = .byCharWrapping
|
result.lineBreakMode = .byCharWrapping
|
||||||
let version = Bundle.main.infoDictionary!["CFBundleShortVersionString"]!
|
result.alpha = Values.mediumOpacity
|
||||||
let buildNumber = Bundle.main.infoDictionary!["CFBundleVersion"]!
|
|
||||||
result.text = "Version \(version) (\(buildNumber))"
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
@ -160,7 +129,7 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
|
|
||||||
setUpGradientBackground()
|
setUpGradientBackground()
|
||||||
setUpNavBarStyle()
|
setUpNavBarStyle()
|
||||||
setNavBarTitle(NSLocalizedString("vc_settings_title", comment: ""))
|
setNavBarTitle("vc_settings_title".localized())
|
||||||
|
|
||||||
// Navigation bar buttons
|
// Navigation bar buttons
|
||||||
updateNavigationBarButtons()
|
updateNavigationBarButtons()
|
||||||
|
@ -198,12 +167,7 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
headerStackView.alignment = .center
|
headerStackView.alignment = .center
|
||||||
|
|
||||||
// Separator
|
// Separator
|
||||||
let separator = Separator(title: NSLocalizedString("your_session_id", comment: ""))
|
let separator = Separator(title: "your_session_id".localized())
|
||||||
|
|
||||||
// Share button
|
|
||||||
let shareButton = Button(style: .regular, size: .medium)
|
|
||||||
shareButton.setTitle(NSLocalizedString("share", comment: ""), for: UIControl.State.normal)
|
|
||||||
shareButton.addTarget(self, action: #selector(sharePublicKey), for: UIControl.Event.touchUpInside)
|
|
||||||
|
|
||||||
// Button container
|
// Button container
|
||||||
let buttonContainer = UIStackView(arrangedSubviews: [ copyButton, shareButton ])
|
let buttonContainer = UIStackView(arrangedSubviews: [ copyButton, shareButton ])
|
||||||
|
@ -232,7 +196,6 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oxen logo
|
// Oxen logo
|
||||||
updateLogo()
|
|
||||||
let logoContainer = UIView()
|
let logoContainer = UIView()
|
||||||
logoContainer.addSubview(logoImageView)
|
logoContainer.addSubview(logoImageView)
|
||||||
logoImageView.pin(.top, to: .top, of: logoContainer)
|
logoImageView.pin(.top, to: .top, of: logoContainer)
|
||||||
|
@ -240,7 +203,7 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
logoImageView.centerXAnchor.constraint(equalTo: logoContainer.centerXAnchor, constant: -2).isActive = true
|
logoImageView.centerXAnchor.constraint(equalTo: logoContainer.centerXAnchor, constant: -2).isActive = true
|
||||||
|
|
||||||
// Main stack view
|
// Main stack view
|
||||||
let stackView = UIStackView(arrangedSubviews: [ topStackView, settingButtonsStackView, inviteButton, faqButton, surveyButton, supportButton, helpTranslateButton, logoContainer, versionLabel ])
|
let stackView = UIStackView(arrangedSubviews: [ topStackView, settingButtonsStackView, logoContainer, versionLabel ])
|
||||||
stackView.axis = .vertical
|
stackView.axis = .vertical
|
||||||
stackView.spacing = Values.largeSpacing
|
stackView.spacing = Values.largeSpacing
|
||||||
stackView.alignment = .fill
|
stackView.alignment = .fill
|
||||||
|
@ -258,47 +221,25 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getSettingButtons() -> [UIView] {
|
private func getSettingButtons() -> [UIView] {
|
||||||
func getSeparator() -> UIView {
|
func getSettingButton(
|
||||||
let result = UIView()
|
title: String,
|
||||||
result.backgroundColor = Colors.separator
|
color: ThemeValue = .textPrimary,
|
||||||
result.set(.height, to: Values.separatorThickness)
|
action selector: Selector
|
||||||
|
) -> UIButton {
|
||||||
|
let result: UIButton = UIButton()
|
||||||
|
result.setTitle(title, for: UIControl.State.normal)
|
||||||
|
result.setThemeTitleColor(color, for: UIControl.State.normal)
|
||||||
|
result.titleLabel?.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
||||||
|
result.titleLabel?.textAlignment = .center
|
||||||
|
result.setThemeBackgroundColor(.settings_tabBackground, for: .normal)
|
||||||
|
result.setThemeBackgroundColor(.settings_tabHighlight, for: .highlighted)
|
||||||
|
result.addTarget(self, action: selector, for: UIControl.Event.touchUpInside)
|
||||||
|
result.set(.height, to: SettingsVC.buttonHeight)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSettingButton(withTitle title: String, color: UIColor, action selector: Selector) -> UIButton {
|
let pathButton = getSettingButton(title: "vc_path_title".localized(), action: #selector(showPath))
|
||||||
let button = UIButton()
|
|
||||||
button.setTitle(title, for: UIControl.State.normal)
|
|
||||||
button.setTitleColor(color, for: UIControl.State.normal)
|
|
||||||
button.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
|
||||||
button.titleLabel!.textAlignment = .center
|
|
||||||
|
|
||||||
func getImage(withColor color: UIColor) -> UIImage {
|
|
||||||
let rect = CGRect(origin: CGPoint.zero, size: CGSize(width: 1, height: 1))
|
|
||||||
UIGraphicsBeginImageContext(rect.size)
|
|
||||||
|
|
||||||
let context = UIGraphicsGetCurrentContext()!
|
|
||||||
context.setFillColor(color.cgColor)
|
|
||||||
context.fill(rect)
|
|
||||||
|
|
||||||
let image = UIGraphicsGetImageFromCurrentImageContext()
|
|
||||||
UIGraphicsEndImageContext()
|
|
||||||
|
|
||||||
return image!
|
|
||||||
}
|
|
||||||
|
|
||||||
let backgroundColor = isLightMode ? UIColor(hex: 0xFCFCFC) : UIColor(hex: 0x1B1B1B)
|
|
||||||
button.setBackgroundImage(getImage(withColor: backgroundColor), for: UIControl.State.normal)
|
|
||||||
|
|
||||||
let selectedColor = isLightMode ? UIColor(hex: 0xDFDFDF) : UIColor(hex: 0x0C0C0C)
|
|
||||||
button.setBackgroundImage(getImage(withColor: selectedColor), for: UIControl.State.highlighted)
|
|
||||||
button.addTarget(self, action: selector, for: UIControl.Event.touchUpInside)
|
|
||||||
button.set(.height, to: SettingsVC.buttonHeight)
|
|
||||||
|
|
||||||
return button
|
|
||||||
}
|
|
||||||
|
|
||||||
let pathButton = getSettingButton(withTitle: NSLocalizedString("vc_path_title", comment: ""), color: Colors.text, action: #selector(showPath))
|
|
||||||
let pathStatusView = PathStatusView()
|
let pathStatusView = PathStatusView()
|
||||||
pathStatusView.set(.width, to: PathStatusView.size)
|
pathStatusView.set(.width, to: PathStatusView.size)
|
||||||
pathStatusView.set(.height, to: PathStatusView.size)
|
pathStatusView.set(.height, to: PathStatusView.size)
|
||||||
|
@ -308,21 +249,27 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
pathStatusView.autoVCenterInSuperview()
|
pathStatusView.autoVCenterInSuperview()
|
||||||
|
|
||||||
return [
|
return [
|
||||||
getSeparator(),
|
UIView.separator(),
|
||||||
pathButton,
|
pathButton,
|
||||||
getSeparator(),
|
UIView.separator(),
|
||||||
getSettingButton(withTitle: NSLocalizedString("vc_settings_privacy_button_title", comment: ""), color: Colors.text, action: #selector(showPrivacySettings)),
|
getSettingButton(title: "vc_settings_privacy_button_title".localized(), action: #selector(showPrivacySettings)),
|
||||||
getSeparator(),
|
UIView.separator(),
|
||||||
getSettingButton(withTitle: NSLocalizedString("vc_settings_notifications_button_title", comment: ""), color: Colors.text, action: #selector(showNotificationSettings)),
|
getSettingButton(title: "vc_settings_notifications_button_title".localized(), action: #selector(showNotificationSettings)),
|
||||||
getSeparator(),
|
UIView.separator(),
|
||||||
getSettingButton(withTitle: NSLocalizedString("MESSAGE_REQUESTS_TITLE", comment: ""), color: Colors.text, action: #selector(showMessageRequests)),
|
getSettingButton(title: "CONVERSATIONS_TITLE".localized(), action: #selector(showChatSettings)),
|
||||||
getSeparator(),
|
UIView.separator(),
|
||||||
getSettingButton(withTitle: NSLocalizedString("CHATS_TITLE", comment: ""), color: Colors.text, action: #selector(showChatSettings)),
|
getSettingButton(title: "MESSAGE_REQUESTS_TITLE".localized(), action: #selector(showMessageRequests)),
|
||||||
getSeparator(),
|
UIView.separator(),
|
||||||
getSettingButton(withTitle: NSLocalizedString("vc_settings_recovery_phrase_button_title", comment: ""), color: Colors.text, action: #selector(showSeed)),
|
getSettingButton(title: "APPEARANCE_TITLE".localized(), action: #selector(showAppearanceSettings)),
|
||||||
getSeparator(),
|
UIView.separator(),
|
||||||
getSettingButton(withTitle: NSLocalizedString("vc_settings_clear_all_data_button_title", comment: ""), color: Colors.destructive, action: #selector(clearAllData)),
|
getSettingButton(title: "vc_settings_invite_a_friend_button_title".localized(), action: #selector(sendInvitation)),
|
||||||
getSeparator()
|
UIView.separator(),
|
||||||
|
getSettingButton(title: "vc_settings_recovery_phrase_button_title".localized(), action: #selector(showSeed)),
|
||||||
|
UIView.separator(),
|
||||||
|
getSettingButton(title: "HELP_TITLE".localized(), action: #selector(showHelp)),
|
||||||
|
UIView.separator(),
|
||||||
|
getSettingButton(title: "vc_settings_clear_all_data_button_title".localized(), color: .danger, action: #selector(clearAllData)),
|
||||||
|
UIView.separator()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,46 +315,35 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
private func updateNavigationBarButtons() {
|
private func updateNavigationBarButtons() {
|
||||||
if isEditingDisplayName {
|
if isEditingDisplayName {
|
||||||
let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(handleCancelDisplayNameEditingButtonTapped))
|
let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(handleCancelDisplayNameEditingButtonTapped))
|
||||||
cancelButton.tintColor = Colors.text
|
cancelButton.themeTintColor = .textPrimary
|
||||||
cancelButton.accessibilityLabel = "Cancel button"
|
cancelButton.accessibilityLabel = "Cancel button"
|
||||||
cancelButton.isAccessibilityElement = true
|
cancelButton.isAccessibilityElement = true
|
||||||
navigationItem.leftBarButtonItem = cancelButton
|
navigationItem.leftBarButtonItem = cancelButton
|
||||||
|
|
||||||
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleSaveDisplayNameButtonTapped))
|
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleSaveDisplayNameButtonTapped))
|
||||||
doneButton.tintColor = Colors.text
|
doneButton.themeTintColor = .textPrimary
|
||||||
doneButton.accessibilityLabel = "Done button"
|
doneButton.accessibilityLabel = "Done button"
|
||||||
doneButton.isAccessibilityElement = true
|
doneButton.isAccessibilityElement = true
|
||||||
navigationItem.rightBarButtonItem = doneButton
|
navigationItem.rightBarButtonItem = doneButton
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
|
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
|
||||||
closeButton.tintColor = Colors.text
|
closeButton.themeTintColor = .textPrimary
|
||||||
closeButton.accessibilityLabel = "Close button"
|
closeButton.accessibilityLabel = "Close button"
|
||||||
closeButton.isAccessibilityElement = true
|
closeButton.isAccessibilityElement = true
|
||||||
navigationItem.leftBarButtonItem = closeButton
|
navigationItem.leftBarButtonItem = closeButton
|
||||||
|
|
||||||
let appModeIcon: UIImage
|
|
||||||
if isSystemDefault {
|
|
||||||
appModeIcon = isDarkMode ? #imageLiteral(resourceName: "ic_theme_auto").withTintColor(.white) : #imageLiteral(resourceName: "ic_theme_auto").withTintColor(.black)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
appModeIcon = isDarkMode ? #imageLiteral(resourceName: "ic_dark_theme_on").withTintColor(.white) : #imageLiteral(resourceName: "ic_dark_theme_off").withTintColor(.black)
|
|
||||||
}
|
|
||||||
|
|
||||||
let appModeButton = UIButton()
|
|
||||||
appModeButton.setImage(appModeIcon, for: UIControl.State.normal)
|
|
||||||
appModeButton.tintColor = Colors.text
|
|
||||||
appModeButton.addTarget(self, action: #selector(switchAppMode), for: UIControl.Event.touchUpInside)
|
|
||||||
appModeButton.accessibilityLabel = "Switch app mode button"
|
|
||||||
|
|
||||||
let qrCodeIcon = isDarkMode ? #imageLiteral(resourceName: "QRCode").withTintColor(.white) : #imageLiteral(resourceName: "QRCode").withTintColor(.black)
|
|
||||||
let qrCodeButton = UIButton()
|
let qrCodeButton = UIButton()
|
||||||
qrCodeButton.setImage(qrCodeIcon, for: UIControl.State.normal)
|
qrCodeButton.setImage(
|
||||||
qrCodeButton.tintColor = Colors.text
|
UIImage(named: "QRCode")?
|
||||||
|
.withRenderingMode(.alwaysTemplate),
|
||||||
|
for: .normal
|
||||||
|
)
|
||||||
|
qrCodeButton.themeTintColor = .textPrimary
|
||||||
qrCodeButton.addTarget(self, action: #selector(showQRCode), for: UIControl.Event.touchUpInside)
|
qrCodeButton.addTarget(self, action: #selector(showQRCode), for: UIControl.Event.touchUpInside)
|
||||||
qrCodeButton.accessibilityLabel = "Show QR code button"
|
qrCodeButton.accessibilityLabel = "Show QR code button"
|
||||||
|
|
||||||
let stackView = UIStackView(arrangedSubviews: [ appModeButton, qrCodeButton ])
|
let stackView = UIStackView(arrangedSubviews: [ qrCodeButton ])
|
||||||
stackView.axis = .horizontal
|
stackView.axis = .horizontal
|
||||||
stackView.spacing = Values.mediumSpacing
|
stackView.spacing = Values.mediumSpacing
|
||||||
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: stackView)
|
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: stackView)
|
||||||
|
@ -601,17 +537,17 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
|
|
||||||
@objc private func showPath() {
|
@objc private func showPath() {
|
||||||
let pathVC = PathVC()
|
let pathVC = PathVC()
|
||||||
navigationController!.pushViewController(pathVC, animated: true)
|
self.navigationController?.pushViewController(pathVC, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func showPrivacySettings() {
|
@objc private func showPrivacySettings() {
|
||||||
let privacySettingsVC = PrivacySettingsTableViewController()
|
let privacySettingsVC = PrivacySettingsTableViewController()
|
||||||
navigationController!.pushViewController(privacySettingsVC, animated: true)
|
self.navigationController?.pushViewController(privacySettingsVC, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func showNotificationSettings() {
|
@objc private func showNotificationSettings() {
|
||||||
let notificationSettingsVC = NotificationSettingsViewController()
|
let notificationSettingsVC = NotificationSettingsViewController()
|
||||||
navigationController!.pushViewController(notificationSettingsVC, animated: true)
|
self.navigationController?.pushViewController(notificationSettingsVC, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func showMessageRequests() {
|
@objc private func showMessageRequests() {
|
||||||
|
@ -621,7 +557,12 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
|
|
||||||
@objc private func showChatSettings() {
|
@objc private func showChatSettings() {
|
||||||
let chatSettingsVC = ChatSettingsViewController()
|
let chatSettingsVC = ChatSettingsViewController()
|
||||||
navigationController!.pushViewController(chatSettingsVC, animated: true)
|
self.navigationController?.pushViewController(chatSettingsVC, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func showAppearanceSettings() {
|
||||||
|
let appearanceViewController: AppearanceViewController = AppearanceViewController()
|
||||||
|
self.navigationController?.pushViewController(appearanceViewController, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func showSeed() {
|
@objc private func showSeed() {
|
||||||
|
@ -631,6 +572,10 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||||
present(seedModal, animated: true, completion: nil)
|
present(seedModal, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func showHelp() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func clearAllData() {
|
@objc private func clearAllData() {
|
||||||
let nukeDataModal = NukeDataModal()
|
let nukeDataModal = NukeDataModal()
|
||||||
nukeDataModal.modalPresentationStyle = .overFullScreen
|
nukeDataModal.modalPresentationStyle = .overFullScreen
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SessionUIKit
|
||||||
|
|
||||||
|
class PrimaryColorSelectionView: UIView {
|
||||||
|
private static let selectionBorderSize: CGFloat = 36
|
||||||
|
private static let selectionSize: CGFloat = 30
|
||||||
|
|
||||||
|
public let color: Theme.PrimaryColor
|
||||||
|
private let onSelected: (Theme.PrimaryColor) -> ()
|
||||||
|
|
||||||
|
// MARK: - Components
|
||||||
|
|
||||||
|
private lazy var backgroundButton: UIButton = {
|
||||||
|
let result: UIButton = UIButton()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.addTarget(self, action: #selector(itemSelected), for: .touchUpInside)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let selectionBorderView: UIView = {
|
||||||
|
let result: UIView = UIView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.isUserInteractionEnabled = false
|
||||||
|
result.themeBorderColor = .radioButton_selectedBorder
|
||||||
|
result.layer.borderWidth = 1
|
||||||
|
result.layer.cornerRadius = (PrimaryColorSelectionView.selectionBorderSize / 2)
|
||||||
|
result.isHidden = true
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let selectionView: UIView = {
|
||||||
|
let result: UIView = UIView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.isUserInteractionEnabled = false
|
||||||
|
result.layer.cornerRadius = (PrimaryColorSelectionView.selectionSize / 2)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
// MARK: - Initializtion
|
||||||
|
|
||||||
|
init(color: Theme.PrimaryColor, onSelected: @escaping (Theme.PrimaryColor) -> ()) {
|
||||||
|
self.color = color
|
||||||
|
self.onSelected = onSelected
|
||||||
|
|
||||||
|
super.init(frame: .zero)
|
||||||
|
|
||||||
|
setupUI(color: color)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("Use init(color:) instead")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Layout
|
||||||
|
|
||||||
|
private func setupUI(color: Theme.PrimaryColor) {
|
||||||
|
// Set the appropriate colours
|
||||||
|
selectionView.backgroundColor = color.color
|
||||||
|
|
||||||
|
// Add the UI
|
||||||
|
addSubview(backgroundButton)
|
||||||
|
addSubview(selectionBorderView)
|
||||||
|
addSubview(selectionView)
|
||||||
|
|
||||||
|
setupLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupLayout() {
|
||||||
|
backgroundButton.pin(to: self)
|
||||||
|
|
||||||
|
selectionBorderView.pin(to: self)
|
||||||
|
selectionBorderView.set(.width, to: PrimaryColorSelectionView.selectionBorderSize)
|
||||||
|
selectionBorderView.set(.height, to: PrimaryColorSelectionView.selectionBorderSize)
|
||||||
|
|
||||||
|
selectionView.center(in: selectionBorderView)
|
||||||
|
selectionView.set(.width, to: PrimaryColorSelectionView.selectionSize)
|
||||||
|
selectionView.set(.height, to: PrimaryColorSelectionView.selectionSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Content
|
||||||
|
|
||||||
|
func update(isSelected: Bool) {
|
||||||
|
selectionBorderView.isHidden = !isSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func itemSelected() {
|
||||||
|
onSelected(color)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SessionUIKit
|
||||||
|
import SessionMessagingKit
|
||||||
|
|
||||||
|
public class ThemePreviewView: UIView {
|
||||||
|
// MARK: - Components
|
||||||
|
|
||||||
|
private lazy var incomingMessagePreview: UIView = {
|
||||||
|
let result: VisibleMessageCell = VisibleMessageCell()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = true
|
||||||
|
result.update(
|
||||||
|
with: MessageViewModel(
|
||||||
|
variant: .standardIncoming,
|
||||||
|
body: "APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_MESSAGE".localized(),
|
||||||
|
quote: Quote(
|
||||||
|
interactionId: -1,
|
||||||
|
authorId: "",
|
||||||
|
timestampMs: 0,
|
||||||
|
body: "APPEARANCE_PRIMARY_COLOR_PREVIEW_INC_QUOTE".localized(),
|
||||||
|
attachmentId: nil
|
||||||
|
),
|
||||||
|
cellType: .textOnlyMessage
|
||||||
|
),
|
||||||
|
mediaCache: NSCache(),
|
||||||
|
playbackInfo: nil,
|
||||||
|
lastSearchText: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var outgoingMessagePreview: UIView = {
|
||||||
|
let result: VisibleMessageCell = VisibleMessageCell()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = true
|
||||||
|
result.update(
|
||||||
|
with: MessageViewModel(
|
||||||
|
variant: .standardOutgoing,
|
||||||
|
body: "APPEARANCE_PRIMARY_COLOR_PREVIEW_OUT_MESSAGE".localized(),
|
||||||
|
cellType: .textOnlyMessage,
|
||||||
|
isLast: false // To hide the status indicator
|
||||||
|
),
|
||||||
|
mediaCache: NSCache(),
|
||||||
|
playbackInfo: nil,
|
||||||
|
lastSearchText: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
// MARK: - Initializtion
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init(frame: .zero)
|
||||||
|
|
||||||
|
setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Layout
|
||||||
|
|
||||||
|
private func setupUI() {
|
||||||
|
self.themeBackgroundColor = .appearance_sectionBackground
|
||||||
|
|
||||||
|
addSubview(incomingMessagePreview)
|
||||||
|
addSubview(outgoingMessagePreview)
|
||||||
|
|
||||||
|
setupLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupLayout() {
|
||||||
|
incomingMessagePreview.pin(.top, to: .top, of: self)
|
||||||
|
incomingMessagePreview.pin(.left, to: .left, of: self, withInset: Values.veryLargeSpacing)
|
||||||
|
|
||||||
|
outgoingMessagePreview.pin(.top, to: .bottom, of: incomingMessagePreview)
|
||||||
|
outgoingMessagePreview.pin(.bottom, to: .bottom, of: self, withInset: -Values.mediumSpacing)
|
||||||
|
outgoingMessagePreview.pin(.right, to: .right, of: self, withInset: -Values.veryLargeSpacing)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SessionUIKit
|
||||||
|
|
||||||
|
class ThemeSelectionView: UIView {
|
||||||
|
private static let selectionBorderSize: CGFloat = 26
|
||||||
|
private static let selectionSize: CGFloat = 20
|
||||||
|
|
||||||
|
public let theme: Theme
|
||||||
|
private let onSelected: (Theme) -> ()
|
||||||
|
|
||||||
|
// MARK: - Components
|
||||||
|
|
||||||
|
private lazy var backgroundButton: UIButton = {
|
||||||
|
let result: UIButton = UIButton()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.setThemeBackgroundColor(.appearance_buttonBackground, for: .normal)
|
||||||
|
result.setThemeBackgroundColor(.appearance_buttonHighlight, for: .highlighted)
|
||||||
|
result.addTarget(self, action: #selector(itemSelected), for: .touchUpInside)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let previewView: UIView = {
|
||||||
|
let result: UIView = UIView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.isUserInteractionEnabled = false
|
||||||
|
result.layer.cornerRadius = 6
|
||||||
|
result.layer.borderWidth = 1
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let previewIncomingMessageView: UIView = {
|
||||||
|
let result: UIView = UIView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.isUserInteractionEnabled = false
|
||||||
|
result.layer.cornerRadius = 6
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let previewOutgoingMessageView: UIView = {
|
||||||
|
let result: UIView = UIView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.isUserInteractionEnabled = false
|
||||||
|
result.layer.cornerRadius = 6
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let titleLabel: UILabel = {
|
||||||
|
let result: UILabel = UILabel()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.isUserInteractionEnabled = false
|
||||||
|
result.font = UIFont.systemFont(ofSize: Values.mediumFontSize, weight: .bold)
|
||||||
|
result.themeTextColor = .textPrimary
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let selectionBorderView: UIView = {
|
||||||
|
let result: UIView = UIView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.isUserInteractionEnabled = false
|
||||||
|
result.layer.borderWidth = 1
|
||||||
|
result.layer.cornerRadius = (ThemeSelectionView.selectionBorderSize / 2)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let selectionView: UIView = {
|
||||||
|
let result: UIView = UIView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.isUserInteractionEnabled = false
|
||||||
|
result.layer.cornerRadius = (ThemeSelectionView.selectionSize / 2)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
// MARK: - Initializtion
|
||||||
|
|
||||||
|
init(theme: Theme, onSelected: @escaping (Theme) -> ()) {
|
||||||
|
self.theme = theme
|
||||||
|
self.onSelected = onSelected
|
||||||
|
|
||||||
|
super.init(frame: .zero)
|
||||||
|
|
||||||
|
setupUI(theme: theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("Use init(theme:) instead")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Layout
|
||||||
|
|
||||||
|
private func setupUI(theme: Theme) {
|
||||||
|
self.themeBackgroundColor = .appearance_sectionBackground
|
||||||
|
|
||||||
|
// Set the appropriate colours
|
||||||
|
previewView.backgroundColor = theme.colors[.backgroundPrimary]
|
||||||
|
previewView.layer.borderColor = theme.colors[.borderSeparator]?.cgColor
|
||||||
|
previewIncomingMessageView.backgroundColor = theme.colors[.messageBubble_incomingBackground]
|
||||||
|
previewOutgoingMessageView.backgroundColor = theme.colors[.defaultPrimary]
|
||||||
|
titleLabel.text = theme.title
|
||||||
|
|
||||||
|
// Add the UI
|
||||||
|
addSubview(backgroundButton)
|
||||||
|
addSubview(previewView)
|
||||||
|
addSubview(titleLabel)
|
||||||
|
addSubview(selectionBorderView)
|
||||||
|
addSubview(selectionView)
|
||||||
|
|
||||||
|
previewView.addSubview(previewIncomingMessageView)
|
||||||
|
previewView.addSubview(previewOutgoingMessageView)
|
||||||
|
|
||||||
|
setupLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupLayout() {
|
||||||
|
backgroundButton.pin(to: self)
|
||||||
|
|
||||||
|
previewView.pin(.top, to: .top, of: self, withInset: Values.smallSpacing)
|
||||||
|
previewView.pin(.left, to: .left, of: self, withInset: Values.largeSpacing)
|
||||||
|
previewView.pin(.bottom, to: .bottom, of: self, withInset: -Values.smallSpacing)
|
||||||
|
previewView.set(.width, to: 76)
|
||||||
|
previewView.set(.height, to: 70)
|
||||||
|
|
||||||
|
previewIncomingMessageView.bottomAnchor
|
||||||
|
.constraint(equalTo: previewView.centerYAnchor, constant: -1)
|
||||||
|
.isActive = true
|
||||||
|
previewIncomingMessageView.pin(.left, to: .left, of: previewView, withInset: Values.smallSpacing)
|
||||||
|
previewIncomingMessageView.set(.width, to: 40)
|
||||||
|
previewIncomingMessageView.set(.height, to: 12)
|
||||||
|
|
||||||
|
previewOutgoingMessageView.topAnchor
|
||||||
|
.constraint(equalTo: previewView.centerYAnchor, constant: 1)
|
||||||
|
.isActive = true
|
||||||
|
previewOutgoingMessageView.pin(.right, to: .right, of: previewView, withInset: -Values.smallSpacing)
|
||||||
|
previewOutgoingMessageView.set(.width, to: 40)
|
||||||
|
previewOutgoingMessageView.set(.height, to: 12)
|
||||||
|
|
||||||
|
titleLabel.center(.vertical, in: self)
|
||||||
|
titleLabel.pin(.left, to: .right, of: previewView, withInset: Values.mediumSpacing)
|
||||||
|
|
||||||
|
selectionBorderView.center(.vertical, in: self)
|
||||||
|
selectionBorderView.pin(.right, to: .right, of: self, withInset: -Values.veryLargeSpacing)
|
||||||
|
selectionBorderView.set(.width, to: ThemeSelectionView.selectionBorderSize)
|
||||||
|
selectionBorderView.set(.height, to: ThemeSelectionView.selectionBorderSize)
|
||||||
|
|
||||||
|
selectionView.center(in: selectionBorderView)
|
||||||
|
selectionView.set(.width, to: ThemeSelectionView.selectionSize)
|
||||||
|
selectionView.set(.height, to: ThemeSelectionView.selectionSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Content
|
||||||
|
|
||||||
|
func update(isSelected: Bool) {
|
||||||
|
selectionBorderView.themeBorderColor = (isSelected ?
|
||||||
|
.radioButton_selectedBorder :
|
||||||
|
.radioButton_unselectedBorder
|
||||||
|
)
|
||||||
|
selectionView.themeBackgroundColor = (isSelected ?
|
||||||
|
.radioButton_selectedBackground :
|
||||||
|
.radioButton_unselectedBackground
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func itemSelected() {
|
||||||
|
onSelected(theme)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,28 +1,37 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SessionUIKit
|
||||||
|
|
||||||
class BaseVC: UIViewController {
|
class BaseVC: UIViewController {
|
||||||
private var hasGradient = false
|
override var preferredStatusBarStyle: UIStatusBarStyle { return ThemeManager.currentTheme.statusBarStyle }
|
||||||
|
|
||||||
override var preferredStatusBarStyle: UIStatusBarStyle { return isLightMode ? .default : .lightContent }
|
|
||||||
|
|
||||||
lazy var navBarTitleLabel: UILabel = {
|
lazy var navBarTitleLabel: UILabel = {
|
||||||
let result = UILabel()
|
let result = UILabel()
|
||||||
result.textColor = Colors.text
|
|
||||||
result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
||||||
result.alpha = 1
|
result.themeTextColor = .textPrimary
|
||||||
result.textAlignment = .center
|
result.textAlignment = .center
|
||||||
|
result.alpha = 1
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
lazy var crossfadeLabel: UILabel = {
|
lazy var crossfadeLabel: UILabel = {
|
||||||
let result = UILabel()
|
let result = UILabel()
|
||||||
result.textColor = Colors.text
|
|
||||||
result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
||||||
result.alpha = 0
|
result.themeTextColor = .textPrimary
|
||||||
result.textAlignment = .center
|
result.textAlignment = .center
|
||||||
|
result.alpha = 0
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
navigationItem.backButtonTitle = ""
|
||||||
|
view.themeBackgroundColor = .backgroundPrimary
|
||||||
|
|
||||||
setNeedsStatusBarAppearanceUpdate()
|
setNeedsStatusBarAppearanceUpdate()
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(handleAppModeChangedNotification(_:)), name: .appModeChanged, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(handleAppModeChangedNotification(_:)), name: .appModeChanged, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive(_:)), name: .OWSApplicationDidBecomeActive, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive(_:)), name: .OWSApplicationDidBecomeActive, object: nil)
|
||||||
|
@ -69,24 +78,31 @@ class BaseVC : UIViewController {
|
||||||
let container = UIView()
|
let container = UIView()
|
||||||
navBarTitleLabel.text = title
|
navBarTitleLabel.text = title
|
||||||
crossfadeLabel.text = title
|
crossfadeLabel.text = title
|
||||||
|
|
||||||
if let customFontSize = customFontSize {
|
if let customFontSize = customFontSize {
|
||||||
navBarTitleLabel.font = .boldSystemFont(ofSize: customFontSize)
|
navBarTitleLabel.font = .boldSystemFont(ofSize: customFontSize)
|
||||||
crossfadeLabel.font = .boldSystemFont(ofSize: customFontSize)
|
crossfadeLabel.font = .boldSystemFont(ofSize: customFontSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
container.addSubview(navBarTitleLabel)
|
container.addSubview(navBarTitleLabel)
|
||||||
navBarTitleLabel.pin(to: container)
|
|
||||||
container.addSubview(crossfadeLabel)
|
container.addSubview(crossfadeLabel)
|
||||||
|
|
||||||
|
navBarTitleLabel.pin(to: container)
|
||||||
crossfadeLabel.pin(to: container)
|
crossfadeLabel.pin(to: container)
|
||||||
|
|
||||||
navigationItem.titleView = container
|
navigationItem.titleView = container
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func setUpNavBarSessionHeading() {
|
internal func setUpNavBarSessionHeading() {
|
||||||
let headingImageView = UIImageView()
|
let headingImageView = UIImageView(
|
||||||
headingImageView.tintColor = Colors.text
|
image: UIImage(named: "SessionHeading")?
|
||||||
headingImageView.image = UIImage(named: "SessionHeading")?.withRenderingMode(.alwaysTemplate)
|
.withRenderingMode(.alwaysTemplate)
|
||||||
|
)
|
||||||
|
headingImageView.themeTintColor = .textPrimary
|
||||||
headingImageView.contentMode = .scaleAspectFit
|
headingImageView.contentMode = .scaleAspectFit
|
||||||
headingImageView.set(.width, to: 150)
|
headingImageView.set(.width, to: 150)
|
||||||
headingImageView.set(.height, to: Values.mediumFontSize)
|
headingImageView.set(.height, to: Values.mediumFontSize)
|
||||||
|
|
||||||
navigationItem.titleView = headingImageView
|
navigationItem.titleView = headingImageView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +112,7 @@ class BaseVC : UIViewController {
|
||||||
logoImageView.contentMode = .scaleAspectFit
|
logoImageView.contentMode = .scaleAspectFit
|
||||||
logoImageView.set(.width, to: 32)
|
logoImageView.set(.width, to: 32)
|
||||||
logoImageView.set(.height, to: 32)
|
logoImageView.set(.height, to: 32)
|
||||||
|
|
||||||
navigationItem.titleView = logoImageView
|
navigationItem.titleView = logoImageView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,36 +54,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
[self.delegate.fromViewController presentAlert:actionSheet];
|
[self.delegate.fromViewController presentAlert:actionSheet];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)takePicture
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
OWSAssertDebug(self.delegate);
|
|
||||||
|
|
||||||
[SNAppearance switchToImagePickerAppearance];
|
|
||||||
|
|
||||||
[self.delegate.fromViewController ows_askForCameraPermissions:^(BOOL granted) {
|
|
||||||
if (!granted) {
|
|
||||||
OWSLogWarn(@"Camera permission denied.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UIImagePickerController *picker = [OWSImagePickerController new];
|
|
||||||
picker.delegate = self;
|
|
||||||
picker.allowsEditing = NO;
|
|
||||||
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
|
|
||||||
picker.mediaTypes = @[ (__bridge NSString *)kUTTypeImage ];
|
|
||||||
|
|
||||||
[self.delegate.fromViewController presentViewController:picker animated:YES completion:nil];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)chooseFromLibrary
|
- (void)chooseFromLibrary
|
||||||
{
|
{
|
||||||
OWSAssertIsOnMainThread();
|
OWSAssertIsOnMainThread();
|
||||||
OWSAssertDebug(self.delegate);
|
OWSAssertDebug(self.delegate);
|
||||||
|
|
||||||
[SNAppearance switchToImagePickerAppearance];
|
|
||||||
|
|
||||||
[self.delegate.fromViewController ows_askForMediaLibraryPermissions:^(BOOL granted) {
|
[self.delegate.fromViewController ows_askForMediaLibraryPermissions:^(BOOL granted) {
|
||||||
if (!granted) {
|
if (!granted) {
|
||||||
OWSLogWarn(@"Media Library permission denied.");
|
OWSLogWarn(@"Media Library permission denied.");
|
||||||
|
@ -108,8 +83,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
OWSAssertIsOnMainThread();
|
OWSAssertIsOnMainThread();
|
||||||
OWSAssertDebug(self.delegate);
|
OWSAssertDebug(self.delegate);
|
||||||
|
|
||||||
[SNAppearance switchToSessionAppearance];
|
|
||||||
|
|
||||||
[self.delegate.fromViewController dismissViewControllerAnimated:YES completion:nil];
|
[self.delegate.fromViewController dismissViewControllerAnimated:YES completion:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,9 +94,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
OWSAssertIsOnMainThread();
|
OWSAssertIsOnMainThread();
|
||||||
OWSAssertDebug(self.delegate);
|
OWSAssertDebug(self.delegate);
|
||||||
|
|
||||||
[SNAppearance switchToSessionAppearance];
|
|
||||||
|
|
||||||
|
|
||||||
NSURL* imageURL = [info objectForKey:UIImagePickerControllerImageURL];
|
NSURL* imageURL = [info objectForKey:UIImagePickerControllerImageURL];
|
||||||
UIImage *rawAvatar = [info objectForKey:UIImagePickerControllerOriginalImage];
|
UIImage *rawAvatar = [info objectForKey:UIImagePickerControllerOriginalImage];
|
||||||
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
|
|
||||||
@objc final class SNAppearance : NSObject {
|
|
||||||
|
|
||||||
@objc static func switchToSessionAppearance() {
|
|
||||||
UINavigationBar.appearance().barTintColor = Colors.navigationBarBackground
|
|
||||||
UINavigationBar.appearance().isTranslucent = false
|
|
||||||
UINavigationBar.appearance().tintColor = Colors.text
|
|
||||||
UIToolbar.appearance().barTintColor = Colors.navigationBarBackground
|
|
||||||
UIToolbar.appearance().isTranslucent = false
|
|
||||||
UIToolbar.appearance().tintColor = Colors.text
|
|
||||||
UISwitch.appearance().onTintColor = Colors.accent
|
|
||||||
UINavigationBar.appearance().titleTextAttributes = [ NSAttributedString.Key.foregroundColor : Colors.text ]
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc static func switchToImagePickerAppearance() {
|
|
||||||
UINavigationBar.appearance().barTintColor = .white
|
|
||||||
UINavigationBar.appearance().isTranslucent = false
|
|
||||||
UINavigationBar.appearance().tintColor = .black
|
|
||||||
UINavigationBar.appearance().titleTextAttributes = [ NSAttributedString.Key.foregroundColor : UIColor.black ]
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc static func switchToDocumentPickerAppearance() {
|
|
||||||
let textColor: UIColor = isDarkMode ? .white : .black
|
|
||||||
UINavigationBar.appearance().tintColor = textColor
|
|
||||||
UINavigationBar.appearance().titleTextAttributes = [ NSAttributedString.Key.foregroundColor : textColor ]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SessionUIKit
|
||||||
|
|
||||||
|
public class TraitObservingWindow: UIWindow {
|
||||||
|
public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||||
|
super.traitCollectionDidChange(previousTraitCollection)
|
||||||
|
|
||||||
|
ThemeManager.traitCollectionDidChange(previousTraitCollection)
|
||||||
|
}
|
||||||
|
}
|
|
@ -453,7 +453,14 @@ public extension MessageViewModel {
|
||||||
static let typingIndicatorId: Int64 = -2
|
static let typingIndicatorId: Int64 = -2
|
||||||
|
|
||||||
// Note: This init method is only used system-created cells or empty states
|
// Note: This init method is only used system-created cells or empty states
|
||||||
init(isTypingIndicator: Bool? = nil) {
|
init(
|
||||||
|
variant: Interaction.Variant = .standardOutgoing,
|
||||||
|
body: String? = nil,
|
||||||
|
quote: Quote? = nil,
|
||||||
|
cellType: CellType = .typingIndicator,
|
||||||
|
isTypingIndicator: Bool? = nil,
|
||||||
|
isLast: Bool = true
|
||||||
|
) {
|
||||||
self.threadId = "INVALID_THREAD_ID"
|
self.threadId = "INVALID_THREAD_ID"
|
||||||
self.threadVariant = .contact
|
self.threadVariant = .contact
|
||||||
self.threadIsTrusted = false
|
self.threadIsTrusted = false
|
||||||
|
@ -470,11 +477,11 @@ public extension MessageViewModel {
|
||||||
)
|
)
|
||||||
self.rowId = targetId
|
self.rowId = targetId
|
||||||
self.id = targetId
|
self.id = targetId
|
||||||
self.variant = .standardOutgoing
|
self.variant = variant
|
||||||
self.timestampMs = Int64.max
|
self.timestampMs = Int64.max
|
||||||
self.authorId = ""
|
self.authorId = ""
|
||||||
self.authorNameInternal = nil
|
self.authorNameInternal = nil
|
||||||
self.body = nil
|
self.body = body
|
||||||
self.rawBody = nil
|
self.rawBody = nil
|
||||||
self.expiresStartedAtMs = nil
|
self.expiresStartedAtMs = nil
|
||||||
self.expiresInSeconds = nil
|
self.expiresInSeconds = nil
|
||||||
|
@ -485,7 +492,7 @@ public extension MessageViewModel {
|
||||||
self.isSenderOpenGroupModerator = false
|
self.isSenderOpenGroupModerator = false
|
||||||
self.isTypingIndicator = isTypingIndicator
|
self.isTypingIndicator = isTypingIndicator
|
||||||
self.profile = nil
|
self.profile = nil
|
||||||
self.quote = nil
|
self.quote = quote
|
||||||
self.quoteAttachment = nil
|
self.quoteAttachment = nil
|
||||||
self.linkPreview = nil
|
self.linkPreview = nil
|
||||||
self.linkPreviewAttachment = nil
|
self.linkPreviewAttachment = nil
|
||||||
|
@ -494,7 +501,7 @@ public extension MessageViewModel {
|
||||||
// Post-Query Processing Data
|
// Post-Query Processing Data
|
||||||
|
|
||||||
self.attachments = nil
|
self.attachments = nil
|
||||||
self.cellType = .typingIndicator
|
self.cellType = cellType
|
||||||
self.authorName = ""
|
self.authorName = ""
|
||||||
self.senderName = nil
|
self.senderName = nil
|
||||||
self.shouldShowProfile = false
|
self.shouldShowProfile = false
|
||||||
|
@ -504,7 +511,7 @@ public extension MessageViewModel {
|
||||||
self.previousVariant = nil
|
self.previousVariant = nil
|
||||||
self.positionInCluster = .middle
|
self.positionInCluster = .middle
|
||||||
self.isOnlyMessageInCluster = true
|
self.isOnlyMessageInCluster = true
|
||||||
self.isLast = true
|
self.isLast = isLast
|
||||||
self.currentUserBlindedPublicKey = nil
|
self.currentUserBlindedPublicKey = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ public extension Setting.DoubleKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Preferences {
|
public enum Preferences {
|
||||||
public enum NotificationPreviewType: Int, CaseIterable, EnumSetting {
|
public enum NotificationPreviewType: Int, CaseIterable, EnumIntSetting {
|
||||||
/// Notifications should include both the sender name and a preview of the message content
|
/// Notifications should include both the sender name and a preview of the message content
|
||||||
case nameAndPreview
|
case nameAndPreview
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ public enum Preferences {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Sound: Int, Codable, DatabaseValueConvertible, EnumSetting {
|
public enum Sound: Int, Codable, DatabaseValueConvertible, EnumIntSetting {
|
||||||
public static var defaultiOSIncomingRingtone: Sound = .opening
|
public static var defaultiOSIncomingRingtone: Sound = .opening
|
||||||
public static var defaultNotificationSound: Sound = .note
|
public static var defaultNotificationSound: Sound = .note
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
import CoreServices
|
import CoreServices
|
||||||
import PromiseKit
|
import PromiseKit
|
||||||
import SignalUtilitiesKit
|
import SignalUtilitiesKit
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import SessionUIKit
|
import SessionUIKit
|
||||||
import SessionMessagingKit
|
import SessionMessagingKit
|
||||||
|
import SignalUtilitiesKit
|
||||||
|
|
||||||
final class SimplifiedConversationCell: UITableViewCell {
|
final class SimplifiedConversationCell: UITableViewCell {
|
||||||
// MARK: - Initialization
|
// MARK: - Initialization
|
||||||
|
@ -32,7 +33,7 @@ final class SimplifiedConversationCell: UITableViewCell {
|
||||||
private lazy var accentLineView: UIView = {
|
private lazy var accentLineView: UIView = {
|
||||||
let result = UIView()
|
let result = UIView()
|
||||||
result.translatesAutoresizingMaskIntoConstraints = false
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
result.backgroundColor = Colors.destructive
|
result.themeBackgroundColor = .danger
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
@ -47,7 +48,7 @@ final class SimplifiedConversationCell: UITableViewCell {
|
||||||
private lazy var displayNameLabel: UILabel = {
|
private lazy var displayNameLabel: UILabel = {
|
||||||
let result = UILabel()
|
let result = UILabel()
|
||||||
result.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
result.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
||||||
result.textColor = Colors.text
|
result.themeTextColor = .textPrimary
|
||||||
result.lineBreakMode = .byTruncatingTail
|
result.lineBreakMode = .byTruncatingTail
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -56,10 +57,10 @@ final class SimplifiedConversationCell: UITableViewCell {
|
||||||
// MARK: - Initialization
|
// MARK: - Initialization
|
||||||
|
|
||||||
private func setUpViewHierarchy() {
|
private func setUpViewHierarchy() {
|
||||||
backgroundColor = Colors.cellBackground
|
themeBackgroundColor = .conversationButton_background
|
||||||
|
|
||||||
let selectedBackgroundView = UIView()
|
let selectedBackgroundView = UIView()
|
||||||
selectedBackgroundView.backgroundColor = Colors.cellSelected
|
selectedBackgroundView.themeBackgroundColor = .conversationButton_highlight
|
||||||
self.selectedBackgroundView = selectedBackgroundView
|
self.selectedBackgroundView = selectedBackgroundView
|
||||||
|
|
||||||
addSubview(stackView)
|
addSubview(stackView)
|
||||||
|
|
|
@ -26,16 +26,16 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView
|
||||||
|
|
||||||
private lazy var titleLabel: UILabel = {
|
private lazy var titleLabel: UILabel = {
|
||||||
let titleLabel: UILabel = UILabel()
|
let titleLabel: UILabel = UILabel()
|
||||||
titleLabel.text = "vc_share_title".localized()
|
|
||||||
titleLabel.textColor = Colors.text
|
|
||||||
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
||||||
|
titleLabel.text = "vc_share_title".localized()
|
||||||
|
titleLabel.themeTextColor = .textPrimary
|
||||||
|
|
||||||
return titleLabel
|
return titleLabel
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var tableView: UITableView = {
|
private lazy var tableView: UITableView = {
|
||||||
let tableView: UITableView = UITableView()
|
let tableView: UITableView = UITableView()
|
||||||
tableView.backgroundColor = .clear
|
tableView.themeBackgroundColor = .backgroundPrimary
|
||||||
tableView.separatorStyle = .none
|
tableView.separatorStyle = .none
|
||||||
tableView.register(view: SimplifiedConversationCell.self)
|
tableView.register(view: SimplifiedConversationCell.self)
|
||||||
tableView.showsVerticalScrollIndicator = false
|
tableView.showsVerticalScrollIndicator = false
|
||||||
|
@ -62,8 +62,7 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView
|
||||||
setupNavBar()
|
setupNavBar()
|
||||||
|
|
||||||
// Gradient
|
// Gradient
|
||||||
view.backgroundColor = .clear
|
view.themeBackgroundColor = .backgroundPrimary
|
||||||
view.setGradient(Gradients.defaultBackground)
|
|
||||||
|
|
||||||
// Title
|
// Title
|
||||||
navigationItem.titleView = titleLabel
|
navigationItem.titleView = titleLabel
|
||||||
|
@ -130,13 +129,7 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView
|
||||||
// MARK: Layout
|
// MARK: Layout
|
||||||
|
|
||||||
private func setupLayout() {
|
private func setupLayout() {
|
||||||
let topInset = 0.15 * view.height()
|
|
||||||
|
|
||||||
tableView.pin(to: view)
|
tableView.pin(to: view)
|
||||||
fadeView.pin(.leading, to: .leading, of: view)
|
|
||||||
fadeView.pin(.top, to: .top, of: view, withInset: topInset)
|
|
||||||
fadeView.pin(.trailing, to: .trailing, of: view)
|
|
||||||
fadeView.pin(.bottom, to: .bottom, of: view)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Updating
|
// MARK: - Updating
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
import UIKit
|
|
||||||
|
|
||||||
public final class Button : UIButton {
|
|
||||||
private let style: Style
|
|
||||||
private let size: Size
|
|
||||||
private var heightConstraint: NSLayoutConstraint!
|
|
||||||
|
|
||||||
public enum Style {
|
|
||||||
case unimportant, regular, prominentOutline, prominentFilled, regularBorderless, destructiveOutline
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Size {
|
|
||||||
case medium, large, small
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(style: Style, size: Size) {
|
|
||||||
self.style = style
|
|
||||||
self.size = size
|
|
||||||
super.init(frame: .zero)
|
|
||||||
setUpStyle()
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(handleAppModeChangedNotification(_:)), name: .appModeChanged, object: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
preconditionFailure("Use init(style:) instead.")
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
preconditionFailure("Use init(style:) instead.")
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
NotificationCenter.default.removeObserver(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setUpStyle() {
|
|
||||||
let fillColor: UIColor
|
|
||||||
switch style {
|
|
||||||
case .unimportant: fillColor = isLightMode ? UIColor.clear : Colors.unimportantButtonBackground
|
|
||||||
case .regular: fillColor = UIColor.clear
|
|
||||||
case .prominentOutline: fillColor = UIColor.clear
|
|
||||||
case .prominentFilled: fillColor = isLightMode ? Colors.text : Colors.accent
|
|
||||||
case .regularBorderless: fillColor = UIColor.clear
|
|
||||||
case .destructiveOutline: fillColor = UIColor.clear
|
|
||||||
}
|
|
||||||
let borderColor: UIColor
|
|
||||||
switch style {
|
|
||||||
case .unimportant: borderColor = isLightMode ? Colors.text : Colors.unimportantButtonBackground
|
|
||||||
case .regular: borderColor = Colors.text
|
|
||||||
case .prominentOutline: borderColor = isLightMode ? Colors.text : Colors.accent
|
|
||||||
case .prominentFilled: borderColor = isLightMode ? Colors.text : Colors.accent
|
|
||||||
case .regularBorderless: borderColor = UIColor.clear
|
|
||||||
case .destructiveOutline: borderColor = Colors.destructive
|
|
||||||
}
|
|
||||||
let textColor: UIColor
|
|
||||||
switch style {
|
|
||||||
case .unimportant: textColor = Colors.text
|
|
||||||
case .regular: textColor = Colors.text
|
|
||||||
case .prominentOutline: textColor = isLightMode ? Colors.text : Colors.accent
|
|
||||||
case .prominentFilled: textColor = isLightMode ? UIColor.white : Colors.text
|
|
||||||
case .regularBorderless: textColor = Colors.text
|
|
||||||
case .destructiveOutline: textColor = Colors.destructive
|
|
||||||
}
|
|
||||||
let height: CGFloat
|
|
||||||
switch size {
|
|
||||||
case .small: height = Values.smallButtonHeight
|
|
||||||
case .medium: height = Values.mediumButtonHeight
|
|
||||||
case .large: height = Values.largeButtonHeight
|
|
||||||
}
|
|
||||||
if heightConstraint == nil { heightConstraint = set(.height, to: height) }
|
|
||||||
layer.cornerRadius = height / 2
|
|
||||||
backgroundColor = fillColor
|
|
||||||
if #available(iOS 13.0, *) {
|
|
||||||
layer.borderColor = borderColor
|
|
||||||
.resolvedColor(
|
|
||||||
// Note: This is needed for '.cgColor' to support dark mode
|
|
||||||
with: UITraitCollection(userInterfaceStyle: isDarkMode ? .dark : .light)
|
|
||||||
).cgColor
|
|
||||||
} else {
|
|
||||||
layer.borderColor = borderColor.cgColor
|
|
||||||
}
|
|
||||||
layer.borderWidth = 1
|
|
||||||
let fontSize = (size == .small) ? Values.smallFontSize : Values.mediumFontSize
|
|
||||||
titleLabel!.font = .boldSystemFont(ofSize: fontSize)
|
|
||||||
setTitleColor(textColor, for: UIControl.State.normal)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func handleAppModeChangedNotification(_ notification: Notification) {
|
|
||||||
setUpStyle()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
public final class OutlineButton: UIButton {
|
||||||
|
public enum Style {
|
||||||
|
case regular
|
||||||
|
case borderless
|
||||||
|
case destructive
|
||||||
|
case filled
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Size {
|
||||||
|
case small
|
||||||
|
case medium
|
||||||
|
case large
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(style: Style, size: Size) {
|
||||||
|
super.init(frame: .zero)
|
||||||
|
|
||||||
|
setUpStyle(style: style, size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
preconditionFailure("Use init(style:) instead.")
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
preconditionFailure("Use init(style:) instead.")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setUpStyle(style: Style, size: Size) {
|
||||||
|
clipsToBounds = true
|
||||||
|
titleLabel?.font = .boldSystemFont(ofSize: (size == .small ?
|
||||||
|
Values.smallFontSize :
|
||||||
|
Values.mediumFontSize
|
||||||
|
))
|
||||||
|
setThemeTitleColor(
|
||||||
|
{
|
||||||
|
switch style {
|
||||||
|
case .regular, .borderless: return .outlineButton_text
|
||||||
|
case .destructive: return .outlineButton_destructiveText
|
||||||
|
case .filled: return .outlineButton_filledText
|
||||||
|
}
|
||||||
|
}(),
|
||||||
|
for: .normal
|
||||||
|
)
|
||||||
|
|
||||||
|
setThemeBackgroundColor(
|
||||||
|
{
|
||||||
|
switch style {
|
||||||
|
case .regular, .borderless: return .outlineButton_background
|
||||||
|
case .destructive: return .outlineButton_destructiveBackground
|
||||||
|
case .filled: return .outlineButton_filledBackground
|
||||||
|
}
|
||||||
|
}(),
|
||||||
|
for: .normal
|
||||||
|
)
|
||||||
|
setThemeBackgroundColor(
|
||||||
|
{
|
||||||
|
switch style {
|
||||||
|
case .regular, .borderless: return .outlineButton_highlight
|
||||||
|
case .destructive: return .outlineButton_destructiveHighlight
|
||||||
|
case .filled: return .outlineButton_filledHighlight
|
||||||
|
}
|
||||||
|
}(),
|
||||||
|
for: .highlighted
|
||||||
|
)
|
||||||
|
|
||||||
|
layer.borderWidth = 1
|
||||||
|
themeBorderColor = {
|
||||||
|
switch style {
|
||||||
|
case .regular: return .outlineButton_border
|
||||||
|
case .destructive: return .outlineButton_destructiveBorder
|
||||||
|
case .filled, .borderless: return nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
let height: CGFloat = {
|
||||||
|
switch size {
|
||||||
|
case .small: return Values.smallButtonHeight
|
||||||
|
case .medium: return Values.mediumButtonHeight
|
||||||
|
case .large: return Values.largeButtonHeight
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
set(.height, to: height)
|
||||||
|
layer.cornerRadius = height / 2
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,33 +1,37 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
public final class Separator: UIView {
|
public final class Separator: UIView {
|
||||||
private let title: String
|
private static let height: CGFloat = 24
|
||||||
|
|
||||||
|
// MARK: - Components
|
||||||
|
|
||||||
// MARK: Components
|
|
||||||
private lazy var titleLabel: UILabel = {
|
private lazy var titleLabel: UILabel = {
|
||||||
let result = UILabel()
|
let result = UILabel()
|
||||||
result.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
|
|
||||||
result.font = .systemFont(ofSize: Values.smallFontSize)
|
result.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
|
result.themeTextColor = .textPrimary
|
||||||
result.textAlignment = .center
|
result.textAlignment = .center
|
||||||
|
result.alpha = Values.mediumOpacity
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var lineLayer: CAShapeLayer = {
|
private lazy var lineLayer: CAShapeLayer = {
|
||||||
let result = CAShapeLayer()
|
let result = CAShapeLayer()
|
||||||
result.lineWidth = Values.separatorThickness
|
result.lineWidth = Values.separatorThickness
|
||||||
result.strokeColor = Colors.separator.cgColor
|
result.themeStrokeColor = .borderSeparator
|
||||||
result.fillColor = UIColor.clear.cgColor
|
result.themeFillColor = .clear
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// MARK: Settings
|
// MARK: - Initialization
|
||||||
private static let height: CGFloat = 24
|
|
||||||
|
|
||||||
// MARK: Initialization
|
|
||||||
public init(title: String) {
|
public init(title: String) {
|
||||||
self.title = title
|
|
||||||
super.init(frame: CGRect.zero)
|
super.init(frame: CGRect.zero)
|
||||||
setUpViewHierarchy()
|
|
||||||
|
setUpViewHierarchy(title: title)
|
||||||
}
|
}
|
||||||
|
|
||||||
public override init(frame: CGRect) {
|
public override init(frame: CGRect) {
|
||||||
|
@ -38,18 +42,22 @@ public final class Separator : UIView {
|
||||||
preconditionFailure("Use init(title:) instead.")
|
preconditionFailure("Use init(title:) instead.")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setUpViewHierarchy() {
|
private func setUpViewHierarchy(title: String) {
|
||||||
titleLabel.text = title
|
titleLabel.text = title
|
||||||
addSubview(titleLabel)
|
addSubview(titleLabel)
|
||||||
|
|
||||||
titleLabel.center(.horizontal, in: self)
|
titleLabel.center(.horizontal, in: self)
|
||||||
titleLabel.center(.vertical, in: self)
|
titleLabel.center(.vertical, in: self)
|
||||||
layer.insertSublayer(lineLayer, at: 0)
|
layer.insertSublayer(lineLayer, at: 0)
|
||||||
|
|
||||||
set(.height, to: Separator.height)
|
set(.height, to: Separator.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Updating
|
// MARK: - Updating
|
||||||
|
|
||||||
public override func layoutSubviews() {
|
public override func layoutSubviews() {
|
||||||
super.layoutSubviews()
|
super.layoutSubviews()
|
||||||
|
|
||||||
updateLineLayer()
|
updateLineLayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,13 +66,16 @@ public final class Separator : UIView {
|
||||||
let h = bounds.height
|
let h = bounds.height
|
||||||
let path = UIBezierPath()
|
let path = UIBezierPath()
|
||||||
path.move(to: CGPoint(x: 0, y: h / 2))
|
path.move(to: CGPoint(x: 0, y: h / 2))
|
||||||
|
|
||||||
let titleLabelFrame = titleLabel.frame.insetBy(dx: -10, dy: -6)
|
let titleLabelFrame = titleLabel.frame.insetBy(dx: -10, dy: -6)
|
||||||
path.addLine(to: CGPoint(x: titleLabelFrame.origin.x, y: h / 2))
|
path.addLine(to: CGPoint(x: titleLabelFrame.origin.x, y: h / 2))
|
||||||
|
|
||||||
let oval = UIBezierPath(roundedRect: titleLabelFrame, cornerRadius: Separator.height / 2)
|
let oval = UIBezierPath(roundedRect: titleLabelFrame, cornerRadius: Separator.height / 2)
|
||||||
path.append(oval)
|
path.append(oval)
|
||||||
path.move(to: CGPoint(x: titleLabelFrame.origin.x + titleLabelFrame.width, y: h / 2))
|
path.move(to: CGPoint(x: titleLabelFrame.origin.x + titleLabelFrame.width, y: h / 2))
|
||||||
path.addLine(to: CGPoint(x: w, y: h / 2))
|
path.addLine(to: CGPoint(x: w, y: h / 2))
|
||||||
path.close()
|
path.close()
|
||||||
|
|
||||||
lineLayer.path = path.cgPath
|
lineLayer.path = path.cgPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SessionUtilitiesKit
|
||||||
|
|
||||||
|
public enum SUIKit {
|
||||||
|
public static func migrations() -> TargetMigrations {
|
||||||
|
return TargetMigrations(
|
||||||
|
identifier: .uiKit,
|
||||||
|
migrations: [
|
||||||
|
// Want to ensure the initial DB stuff has been completed before doing any
|
||||||
|
// SUIKit migrations
|
||||||
|
[], // Initial DB Creation
|
||||||
|
[], // YDB to GRDB Migration
|
||||||
|
[], // YDB Removal
|
||||||
|
[
|
||||||
|
_001_ThemePreferences.self
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func configure() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import GRDB
|
||||||
|
import SessionUtilitiesKit
|
||||||
|
|
||||||
|
/// This migration extracts an old theme preference from UserDefaults and saves it to the database as well as set the default for the other
|
||||||
|
/// theme preferences
|
||||||
|
enum _001_ThemePreferences: Migration {
|
||||||
|
static let target: TargetMigrations.Identifier = .uiKit
|
||||||
|
static let identifier: String = "ThemePreferences"
|
||||||
|
static let needsConfigSync: Bool = false
|
||||||
|
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||||
|
|
||||||
|
static func migrate(_ db: Database) throws {
|
||||||
|
// Start by adding the jobs that don't have collections (in the jobs like these
|
||||||
|
// will be added via migrations)
|
||||||
|
let isMatchingSystemSetting: Bool = UserDefaults.standard.dictionaryRepresentation()
|
||||||
|
.keys
|
||||||
|
.contains("appMode")
|
||||||
|
// TODO: Test the migration works
|
||||||
|
db[.themeMatchSystemDayNightCycle] = isMatchingSystemSetting
|
||||||
|
db[.theme] = (isMatchingSystemSetting ?
|
||||||
|
Theme.classicDark : (
|
||||||
|
UserDefaults.standard.integer(forKey: "appMode") == 0 ?
|
||||||
|
Theme.classicLight :
|
||||||
|
Theme.classicDark
|
||||||
|
)
|
||||||
|
)
|
||||||
|
db[.themePrimaryColor] = Theme.PrimaryColor.green
|
||||||
|
|
||||||
|
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,480 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import GRDB
|
||||||
|
import SessionUtilitiesKit
|
||||||
|
|
||||||
|
// MARK: - Preferences
|
||||||
|
|
||||||
|
public extension Setting.EnumKey {
|
||||||
|
/// Controls what theme should be used
|
||||||
|
static let theme: Setting.EnumKey = "selectedTheme"
|
||||||
|
|
||||||
|
/// Controls what primary color should be used for the theme
|
||||||
|
static let themePrimaryColor: Setting.EnumKey = "selectedThemePrimaryColor"
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension Setting.BoolKey {
|
||||||
|
/// A flag indicating whether the app should match system day/night settings
|
||||||
|
static let themeMatchSystemDayNightCycle: Setting.BoolKey = "themeMatchSystemDayNightCycle"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - ThemeManager
|
||||||
|
|
||||||
|
public enum ThemeManager {
|
||||||
|
fileprivate class ThemeApplier {
|
||||||
|
enum InfoKey: String {
|
||||||
|
case keyPath
|
||||||
|
case controlState
|
||||||
|
}
|
||||||
|
|
||||||
|
private let applyTheme: (Theme) -> ()
|
||||||
|
private let info: [AnyHashable]
|
||||||
|
private var otherAppliers: [ThemeManager.ThemeApplier]?
|
||||||
|
|
||||||
|
init(
|
||||||
|
existingApplier: ThemeManager.ThemeApplier?,
|
||||||
|
info: [AnyHashable],
|
||||||
|
applyTheme: @escaping (Theme) -> ()
|
||||||
|
) {
|
||||||
|
self.applyTheme = applyTheme
|
||||||
|
self.info = info
|
||||||
|
|
||||||
|
// Store any existing "appliers" (removing their 'otherApplier' references to prevent
|
||||||
|
// loops and excluding any which match the current "info" as they should be replaced
|
||||||
|
// by this applier)
|
||||||
|
self.otherAppliers = [existingApplier]
|
||||||
|
.appending(contentsOf: existingApplier?.otherAppliers)
|
||||||
|
.compactMap { $0?.clearingOtherAppliers() }
|
||||||
|
.filter { $0.info != info }
|
||||||
|
|
||||||
|
// Automatically apply the theme immediately
|
||||||
|
self.apply(theme: ThemeManager.currentTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Functions
|
||||||
|
|
||||||
|
private func clearingOtherAppliers() -> ThemeManager.ThemeApplier {
|
||||||
|
self.otherAppliers = nil
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func apply(theme: Theme) {
|
||||||
|
self.applyTheme(theme)
|
||||||
|
|
||||||
|
// If there are otherAppliers stored against this one then trigger those as well
|
||||||
|
self.otherAppliers?.forEach { applier in
|
||||||
|
applier.applyTheme(theme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// **Note:** Using `weakToStrongObjects` means that the value types will continue to be maintained until the map table resizes
|
||||||
|
/// itself (ie. until a new UI element is registered to the table)
|
||||||
|
///
|
||||||
|
/// Unfortunately if we don't do this the `ThemeApplier` is immediately deallocated and we can't use it to update the theme
|
||||||
|
private static var uiRegistry: NSMapTable<AnyObject, ThemeApplier> = NSMapTable.weakToStrongObjects()
|
||||||
|
|
||||||
|
public static var currentTheme: Theme = {
|
||||||
|
Storage.shared[.theme].defaulting(to: Theme.classicDark)
|
||||||
|
}() {
|
||||||
|
didSet {
|
||||||
|
// Only update if it was changed
|
||||||
|
guard oldValue != currentTheme else { return }
|
||||||
|
|
||||||
|
Storage.shared.writeAsync { db in
|
||||||
|
db[.theme] = currentTheme
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only trigger the UI update if the primary colour wasn't changed (otherwise we'd be doing
|
||||||
|
// an extra UI update
|
||||||
|
if let defaultPrimaryColor: Theme.PrimaryColor = Theme.PrimaryColor(color: currentTheme.colors[.defaultPrimary]) {
|
||||||
|
guard primaryColor == defaultPrimaryColor else {
|
||||||
|
ThemeManager.primaryColor = defaultPrimaryColor
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAllUI()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static var primaryColor: Theme.PrimaryColor = {
|
||||||
|
Storage.shared[.themePrimaryColor].defaulting(to: Theme.PrimaryColor.green)
|
||||||
|
}() {
|
||||||
|
didSet {
|
||||||
|
// Only update if it was changed
|
||||||
|
guard oldValue != primaryColor else { return }
|
||||||
|
|
||||||
|
Storage.shared.writeAsync { db in
|
||||||
|
db[.themePrimaryColor] = primaryColor
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAllUI()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static var matchSystemNightModeSetting: Bool = {
|
||||||
|
Storage.shared[.themeMatchSystemDayNightCycle]
|
||||||
|
}() {
|
||||||
|
didSet {
|
||||||
|
// Only update if it was changed
|
||||||
|
guard oldValue != matchSystemNightModeSetting else { return }
|
||||||
|
|
||||||
|
Storage.shared.writeAsync { db in
|
||||||
|
db[.themeMatchSystemDayNightCycle] = matchSystemNightModeSetting
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user enabled the "match system" setting then update the UI if needed
|
||||||
|
guard
|
||||||
|
matchSystemNightModeSetting &&
|
||||||
|
UITraitCollection.current.userInterfaceStyle != ThemeManager.currentTheme.interfaceStyle
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
traitCollectionDidChange(UITraitCollection.current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When this gets set we need to update the UI to ensure the global appearance stuff is set
|
||||||
|
// correctly on launch
|
||||||
|
public static weak var mainWindow: UIWindow? {
|
||||||
|
didSet { updateAllUI() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Functions
|
||||||
|
|
||||||
|
public static func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||||
|
let currentUserInterfaceStyle: UIUserInterfaceStyle = UITraitCollection.current.userInterfaceStyle
|
||||||
|
|
||||||
|
// Only trigger updates if the style changed and the device is set to match the system style
|
||||||
|
guard
|
||||||
|
currentUserInterfaceStyle != ThemeManager.currentTheme.interfaceStyle,
|
||||||
|
ThemeManager.matchSystemNightModeSetting
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
// Swap to the appropriate light/dark mode
|
||||||
|
switch (currentUserInterfaceStyle, ThemeManager.currentTheme) {
|
||||||
|
case (.light, .classicDark): ThemeManager.currentTheme = .classicLight
|
||||||
|
case (.light, .oceanDark): ThemeManager.currentTheme = .oceanLight
|
||||||
|
case (.dark, .classicLight): ThemeManager.currentTheme = .classicDark
|
||||||
|
case (.dark, .oceanLight): ThemeManager.currentTheme = .oceanDark
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func applyNavigationStyling() {
|
||||||
|
let textPrimary: UIColor = (ThemeManager.currentTheme.colors[.textPrimary] ?? .white)
|
||||||
|
|
||||||
|
// Set the `mainWindow.tintColor` for system screens to use the right colour for text
|
||||||
|
ThemeManager.mainWindow?.tintColor = textPrimary
|
||||||
|
ThemeManager.mainWindow?.rootViewController?.setNeedsStatusBarAppearanceUpdate()
|
||||||
|
|
||||||
|
// Update the nav bars to use the right colours
|
||||||
|
UINavigationBar.appearance().barTintColor = ThemeManager.currentTheme.colors[.backgroundPrimary]
|
||||||
|
UINavigationBar.appearance().isTranslucent = false
|
||||||
|
UINavigationBar.appearance().tintColor = textPrimary
|
||||||
|
UINavigationBar.appearance().shadowImage = ThemeManager.currentTheme.colors[.backgroundPrimary]?.toImage()
|
||||||
|
UINavigationBar.appearance().titleTextAttributes = [
|
||||||
|
NSAttributedString.Key.foregroundColor: textPrimary
|
||||||
|
]
|
||||||
|
UINavigationBar.appearance().largeTitleTextAttributes = [
|
||||||
|
NSAttributedString.Key.foregroundColor: textPrimary
|
||||||
|
]
|
||||||
|
|
||||||
|
// Update the bar button item appearance
|
||||||
|
UIBarButtonItem.appearance().tintColor = textPrimary
|
||||||
|
|
||||||
|
// Update toolbars to use the right colours
|
||||||
|
UIToolbar.appearance().barTintColor = ThemeManager.currentTheme.colors[.backgroundPrimary]
|
||||||
|
UIToolbar.appearance().isTranslucent = false
|
||||||
|
UIToolbar.appearance().tintColor = textPrimary
|
||||||
|
|
||||||
|
// Note: Looks like there were changes to the appearance behaviour in iOS 15, unfortunately
|
||||||
|
// this breaks parts of the old 'UINavigationBar.appearance()' logic so we need to do everything
|
||||||
|
// again using the new API...
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
let appearance = UINavigationBarAppearance()
|
||||||
|
appearance.configureWithOpaqueBackground()
|
||||||
|
appearance.backgroundColor = ThemeManager.currentTheme.colors[.backgroundPrimary]
|
||||||
|
appearance.shadowImage = ThemeManager.currentTheme.colors[.backgroundPrimary]?.toImage()
|
||||||
|
appearance.titleTextAttributes = [
|
||||||
|
NSAttributedString.Key.foregroundColor: textPrimary
|
||||||
|
]
|
||||||
|
appearance.largeTitleTextAttributes = [
|
||||||
|
NSAttributedString.Key.foregroundColor: textPrimary
|
||||||
|
]
|
||||||
|
|
||||||
|
// Apply the button item appearance as well
|
||||||
|
let barButtonItemAppearance = UIBarButtonItemAppearance(style: .plain)
|
||||||
|
barButtonItemAppearance.normal.titleTextAttributes = [ .foregroundColor: textPrimary ]
|
||||||
|
barButtonItemAppearance.disabled.titleTextAttributes = [ .foregroundColor: textPrimary ]
|
||||||
|
barButtonItemAppearance.highlighted.titleTextAttributes = [ .foregroundColor: textPrimary ]
|
||||||
|
barButtonItemAppearance.focused.titleTextAttributes = [ .foregroundColor: textPrimary ]
|
||||||
|
appearance.buttonAppearance = barButtonItemAppearance
|
||||||
|
appearance.backButtonAppearance = barButtonItemAppearance
|
||||||
|
appearance.doneButtonAppearance = barButtonItemAppearance
|
||||||
|
|
||||||
|
UINavigationBar.appearance().standardAppearance = appearance
|
||||||
|
UINavigationBar.appearance().scrollEdgeAppearance = appearance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: 'UINavigationBar.appearance' only affects newly created nav bars so we need
|
||||||
|
// to force-update the current navigation bar (unfortunately the only way to do this
|
||||||
|
// is to remove the nav controller from the view hierarchy and then re-add it)
|
||||||
|
let currentNavController: UINavigationController? = {
|
||||||
|
var targetViewController: UIViewController? = ThemeManager.mainWindow?.rootViewController
|
||||||
|
|
||||||
|
while targetViewController?.presentedViewController != nil {
|
||||||
|
targetViewController = targetViewController?.presentedViewController
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
(targetViewController as? UINavigationController) ??
|
||||||
|
targetViewController?.navigationController
|
||||||
|
)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if
|
||||||
|
let navController: UINavigationController = currentNavController,
|
||||||
|
let superview: UIView = navController.view.superview,
|
||||||
|
!navController.isNavigationBarHidden
|
||||||
|
{
|
||||||
|
navController.view.removeFromSuperview()
|
||||||
|
superview.addSubview(navController.view)
|
||||||
|
navController.topViewController?.setNeedsStatusBarAppearanceUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func applyWindowStyling() {
|
||||||
|
mainWindow?.overrideUserInterfaceStyle = {
|
||||||
|
guard !Storage.shared[.themeMatchSystemDayNightCycle] else {
|
||||||
|
return .unspecified
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ThemeManager.currentTheme.interfaceStyle {
|
||||||
|
case .light: return .light
|
||||||
|
case .dark, .unspecified: return .dark
|
||||||
|
@unknown default: return .dark
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
mainWindow?.backgroundColor = ThemeManager.currentTheme.colors[.backgroundPrimary]
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func onThemeChange(observer: AnyObject, callback: @escaping (Theme, Theme.PrimaryColor) -> ()) {
|
||||||
|
ThemeManager.uiRegistry.setObject(
|
||||||
|
ThemeManager.ThemeApplier(
|
||||||
|
existingApplier: nil,
|
||||||
|
info: []
|
||||||
|
) { theme in callback(theme, ThemeManager.primaryColor) },
|
||||||
|
forKey: observer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func updateAllUI() {
|
||||||
|
ThemeManager.uiRegistry.objectEnumerator()?.forEach { applier in
|
||||||
|
(applier as? ThemeApplier)?.apply(theme: currentTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
applyNavigationStyling()
|
||||||
|
applyWindowStyling()
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate static func set<T: AnyObject>(
|
||||||
|
_ view: T,
|
||||||
|
keyPath: ReferenceWritableKeyPath<T, UIColor?>,
|
||||||
|
to value: ThemeValue?,
|
||||||
|
for state: UIControl.State = .normal
|
||||||
|
) {
|
||||||
|
ThemeManager.uiRegistry.setObject(
|
||||||
|
ThemeManager.ThemeApplier(
|
||||||
|
existingApplier: ThemeManager.get(for: view),
|
||||||
|
info: [ keyPath ]
|
||||||
|
) { [weak view] theme in
|
||||||
|
guard let value: ThemeValue = value else {
|
||||||
|
view?[keyPath: keyPath] = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
view?[keyPath: keyPath] = ThemeManager.resolvedColor(theme.colors[value])
|
||||||
|
},
|
||||||
|
forKey: view
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate static func set<T: AnyObject>(
|
||||||
|
_ view: T,
|
||||||
|
keyPath: ReferenceWritableKeyPath<T, CGColor?>,
|
||||||
|
to value: ThemeValue?,
|
||||||
|
for state: UIControl.State = .normal
|
||||||
|
) {
|
||||||
|
ThemeManager.uiRegistry.setObject(
|
||||||
|
ThemeManager.ThemeApplier(
|
||||||
|
existingApplier: ThemeManager.get(for: view),
|
||||||
|
info: [ keyPath ]
|
||||||
|
) { [weak view] theme in
|
||||||
|
guard let value: ThemeValue = value else {
|
||||||
|
view?[keyPath: keyPath] = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
view?[keyPath: keyPath] = ThemeManager.resolvedColor(theme.colors[value])?.cgColor
|
||||||
|
},
|
||||||
|
forKey: view
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate static func set<T: AnyObject>(
|
||||||
|
_ view: T,
|
||||||
|
to applier: ThemeManager.ThemeApplier,
|
||||||
|
for state: UIControl.State = .normal
|
||||||
|
) {
|
||||||
|
ThemeManager.uiRegistry.setObject(applier, forKey: view)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Using a `UIColor(dynamicProvider:)` unfortunately doesn't seem to work properly for some controls (eg. UISwitch) so
|
||||||
|
/// since we are already explicitly updating all UI when changing colours & states we just force-resolve the primary colour to avoid
|
||||||
|
/// running into these glitches
|
||||||
|
fileprivate static func resolvedColor(_ color: UIColor?) -> UIColor? {
|
||||||
|
return color?.resolvedColor(with: UITraitCollection())
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate static func get(for view: AnyObject) -> ThemeApplier? {
|
||||||
|
return ThemeManager.uiRegistry.object(forKey: view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - View Extensions
|
||||||
|
|
||||||
|
public extension UIView {
|
||||||
|
var themeBackgroundColor: ThemeValue? {
|
||||||
|
set { ThemeManager.set(self, keyPath: \.backgroundColor, to: newValue) }
|
||||||
|
get { return nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
var themeTintColor: ThemeValue? {
|
||||||
|
set { ThemeManager.set(self, keyPath: \.tintColor, to: newValue) }
|
||||||
|
get { return nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
var themeBorderColor: ThemeValue? {
|
||||||
|
set { ThemeManager.set(self, keyPath: \.layer.borderColor, to: newValue) }
|
||||||
|
get { return nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
var themeShadowColor: ThemeValue? {
|
||||||
|
set { ThemeManager.set(self, keyPath: \.layer.shadowColor, to: newValue) }
|
||||||
|
get { return nil }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension UILabel {
|
||||||
|
var themeTextColor: ThemeValue? {
|
||||||
|
set { ThemeManager.set(self, keyPath: \.textColor, to: newValue) }
|
||||||
|
get { return nil }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension UITextView {
|
||||||
|
var themeTextColor: ThemeValue? {
|
||||||
|
set { ThemeManager.set(self, keyPath: \.textColor, to: newValue) }
|
||||||
|
get { return nil }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension UITextField {
|
||||||
|
var themeTextColor: ThemeValue? {
|
||||||
|
set { ThemeManager.set(self, keyPath: \.textColor, to: newValue) }
|
||||||
|
get { return nil }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension UIButton {
|
||||||
|
func setThemeBackgroundColor(_ value: ThemeValue?, for state: UIControl.State) {
|
||||||
|
let keyPath: KeyPath<UIButton, UIImage?> = \.imageView?.image
|
||||||
|
|
||||||
|
ThemeManager.set(
|
||||||
|
self,
|
||||||
|
to: ThemeManager.ThemeApplier(
|
||||||
|
existingApplier: ThemeManager.get(for: self),
|
||||||
|
info: [
|
||||||
|
keyPath,
|
||||||
|
state.rawValue
|
||||||
|
]
|
||||||
|
) { [weak self] theme in
|
||||||
|
guard
|
||||||
|
let value: ThemeValue = value,
|
||||||
|
let color: UIColor = ThemeManager.resolvedColor(theme.colors[value])
|
||||||
|
else {
|
||||||
|
self?.setBackgroundImage(nil, for: state)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self?.setBackgroundImage(color.toImage(), for: state)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setThemeTitleColor(_ value: ThemeValue?, for state: UIControl.State) {
|
||||||
|
let keyPath: KeyPath<UIButton, UIColor?> = \.titleLabel?.textColor
|
||||||
|
|
||||||
|
ThemeManager.set(
|
||||||
|
self,
|
||||||
|
to: ThemeManager.ThemeApplier(
|
||||||
|
existingApplier: ThemeManager.get(for: self),
|
||||||
|
info: [
|
||||||
|
keyPath,
|
||||||
|
state.rawValue
|
||||||
|
]
|
||||||
|
) { [weak self] theme in
|
||||||
|
guard let value: ThemeValue = value else {
|
||||||
|
self?.setTitleColor(nil, for: state)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self?.setTitleColor(
|
||||||
|
ThemeManager.resolvedColor(theme.colors[value]),
|
||||||
|
for: state
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension UISwitch {
|
||||||
|
var themeOnTintColor: ThemeValue? {
|
||||||
|
set { ThemeManager.set(self, keyPath: \.onTintColor, to: newValue) }
|
||||||
|
get { return nil }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension UIBarButtonItem {
|
||||||
|
var themeTintColor: ThemeValue? {
|
||||||
|
set { ThemeManager.set(self, keyPath: \.tintColor, to: newValue) }
|
||||||
|
get { return nil }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension CAShapeLayer {
|
||||||
|
var themeStrokeColor: ThemeValue? {
|
||||||
|
set { ThemeManager.set(self, keyPath: \.strokeColor, to: newValue) }
|
||||||
|
get { return nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
var themeFillColor: ThemeValue? {
|
||||||
|
set { ThemeManager.set(self, keyPath: \.fillColor, to: newValue) }
|
||||||
|
get { return nil }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Convenience Extensions
|
||||||
|
|
||||||
|
extension Array {
|
||||||
|
fileprivate func appending(contentsOf other: [Element]?) -> [Element] {
|
||||||
|
guard let other: [Element] = other else { return self }
|
||||||
|
|
||||||
|
var updatedArray: [Element] = self
|
||||||
|
updatedArray.append(contentsOf: other)
|
||||||
|
return updatedArray
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit.UIColor
|
||||||
|
|
||||||
|
internal enum Theme_ClassicDark: ThemeColors {
|
||||||
|
static let theme: [ThemeValue: UIColor] = [
|
||||||
|
// General
|
||||||
|
.primary: .primary,
|
||||||
|
.defaultPrimary: Theme.PrimaryColor.green.color,
|
||||||
|
.danger: #colorLiteral(red: 1, green: 0.2274509804, blue: 0.2274509804, alpha: 1),
|
||||||
|
.clear: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0),
|
||||||
|
.backgroundPrimary: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1),
|
||||||
|
.backgroundSecondary: #colorLiteral(red: 0.1058823529, green: 0.1058823529, blue: 0.1058823529, alpha: 1),
|
||||||
|
.textPrimary: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.textSecondary: #colorLiteral(red: 0.631372549, green: 0.6352941176, blue: 0.631372549, alpha: 1),
|
||||||
|
.borderSeparator: #colorLiteral(red: 0.2549019608, green: 0.2549019608, blue: 0.2549019608, alpha: 1),
|
||||||
|
|
||||||
|
// TextBox
|
||||||
|
.textBox_background: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1),
|
||||||
|
.textBox_border: #colorLiteral(red: 0.2549019608, green: 0.2549019608, blue: 0.2549019608, alpha: 1),
|
||||||
|
|
||||||
|
// MessageBubble
|
||||||
|
.messageBubble_outgoingBackground: .primary,
|
||||||
|
.messageBubble_incomingBackground: #colorLiteral(red: 0.1764705882, green: 0.1764705882, blue: 0.1764705882, alpha: 1),
|
||||||
|
.messageBubble_outgoingText: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1),
|
||||||
|
.messageBubble_incomingText: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
|
||||||
|
// MenuButton
|
||||||
|
.menuButton_background: .primary,
|
||||||
|
.menuButton_icon: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.menuButton_shadow: .primary,
|
||||||
|
|
||||||
|
// RadioButton
|
||||||
|
.radioButton_selectedBackground: .primary,
|
||||||
|
.radioButton_unselectedBackground: .clear,
|
||||||
|
.radioButton_selectedBorder: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.radioButton_unselectedBorder: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
|
||||||
|
// OutlineButton
|
||||||
|
.outlineButton_text: .primary,
|
||||||
|
.outlineButton_background: .clear,
|
||||||
|
.outlineButton_highlight: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 0.3),
|
||||||
|
.outlineButton_border: .primary,
|
||||||
|
.outlineButton_filledText: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.outlineButton_filledBackground: #colorLiteral(red: 0.1058823529, green: 0.1058823529, blue: 0.1058823529, alpha: 1),
|
||||||
|
.outlineButton_filledHighlight: #colorLiteral(red: 0.2549019608, green: 0.2549019608, blue: 0.2549019608, alpha: 1),
|
||||||
|
.outlineButton_destructiveText: #colorLiteral(red: 1, green: 0.2274509804, blue: 0.2274509804, alpha: 1),
|
||||||
|
.outlineButton_destructiveBackground: .clear,
|
||||||
|
.outlineButton_destructiveHighlight: #colorLiteral(red: 1, green: 0.2274509804, blue: 0.2274509804, alpha: 0.3),
|
||||||
|
.outlineButton_destructiveBorder: #colorLiteral(red: 1, green: 0.2274509804, blue: 0.2274509804, alpha: 1),
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
.settings_tabBackground: #colorLiteral(red: 0.1058823529, green: 0.1058823529, blue: 0.1058823529, alpha: 1),
|
||||||
|
.settings_tabHighlight: #colorLiteral(red: 0.2549019608, green: 0.2549019608, blue: 0.2549019608, alpha: 1),
|
||||||
|
|
||||||
|
// Appearance
|
||||||
|
.appearance_sectionBackground: #colorLiteral(red: 0.1058823529, green: 0.1058823529, blue: 0.1058823529, alpha: 1),
|
||||||
|
.appearance_buttonBackground: #colorLiteral(red: 0.1058823529, green: 0.1058823529, blue: 0.1058823529, alpha: 1),
|
||||||
|
.appearance_buttonHighlight: #colorLiteral(red: 0.2549019608, green: 0.2549019608, blue: 0.2549019608, alpha: 1),
|
||||||
|
|
||||||
|
// ConversationButton
|
||||||
|
.conversationButton_background: #colorLiteral(red: 0.1058823529, green: 0.1058823529, blue: 0.1058823529, alpha: 1),
|
||||||
|
.conversationButton_highlight: #colorLiteral(red: 0.2549019608, green: 0.2549019608, blue: 0.2549019608, alpha: 1)
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit.UIColor
|
||||||
|
|
||||||
|
internal enum Theme_ClassicLight: ThemeColors {
|
||||||
|
static let theme: [ThemeValue: UIColor] = [
|
||||||
|
// General
|
||||||
|
.primary: .primary,
|
||||||
|
.defaultPrimary: Theme.PrimaryColor.green.color,
|
||||||
|
.danger: #colorLiteral(red: 0.8823529412, green: 0.1764705882, blue: 0.09803921569, alpha: 1),
|
||||||
|
.clear: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0),
|
||||||
|
.backgroundPrimary: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.backgroundSecondary: #colorLiteral(red: 0.9764705882, green: 0.9764705882, blue: 0.9764705882, alpha: 1),
|
||||||
|
.textPrimary: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1),
|
||||||
|
.textSecondary: #colorLiteral(red: 0.4274509804, green: 0.4274509804, blue: 0.4274509804, alpha: 1),
|
||||||
|
.borderSeparator: #colorLiteral(red: 0.631372549, green: 0.6352941176, blue: 0.631372549, alpha: 1),
|
||||||
|
|
||||||
|
// TextBox
|
||||||
|
.textBox_background: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.textBox_border: #colorLiteral(red: 0.631372549, green: 0.6352941176, blue: 0.631372549, alpha: 1),
|
||||||
|
|
||||||
|
// MessageBubble
|
||||||
|
.messageBubble_outgoingBackground: .primary,
|
||||||
|
.messageBubble_incomingBackground: #colorLiteral(red: 0.9411764706, green: 0.9411764706, blue: 0.9411764706, alpha: 1),
|
||||||
|
.messageBubble_outgoingText: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1),
|
||||||
|
.messageBubble_incomingText: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1),
|
||||||
|
|
||||||
|
// MenuButton
|
||||||
|
.menuButton_background: .primary,
|
||||||
|
.menuButton_icon: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.menuButton_shadow: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1),
|
||||||
|
|
||||||
|
// RadioButton
|
||||||
|
.radioButton_selectedBackground: .primary,
|
||||||
|
.radioButton_unselectedBackground: .clear,
|
||||||
|
.radioButton_selectedBorder: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1),
|
||||||
|
.radioButton_unselectedBorder: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1),
|
||||||
|
|
||||||
|
// OutlineButton
|
||||||
|
.outlineButton_text: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1),
|
||||||
|
.outlineButton_background: .clear,
|
||||||
|
.outlineButton_highlight: #colorLiteral(red: 0.06666666667, green: 0.06666666667, blue: 0.06666666667, alpha: 0.1),
|
||||||
|
.outlineButton_border: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1),
|
||||||
|
.outlineButton_filledText: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.outlineButton_filledBackground: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1),
|
||||||
|
.outlineButton_filledHighlight: #colorLiteral(red: 0.4274509804, green: 0.4274509804, blue: 0.4274509804, alpha: 1),
|
||||||
|
.outlineButton_destructiveText: #colorLiteral(red: 0.8823529412, green: 0.1764705882, blue: 0.09803921569, alpha: 1),
|
||||||
|
.outlineButton_destructiveBackground: .clear,
|
||||||
|
.outlineButton_destructiveHighlight: #colorLiteral(red: 0.8823529412, green: 0.1764705882, blue: 0.09803921569, alpha: 0.3),
|
||||||
|
.outlineButton_destructiveBorder: #colorLiteral(red: 0.8823529412, green: 0.1764705882, blue: 0.09803921569, alpha: 1),
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
.settings_tabBackground: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.settings_tabHighlight: #colorLiteral(red: 0.8745098039, green: 0.8745098039, blue: 0.8745098039, alpha: 1),
|
||||||
|
|
||||||
|
// AppearanceButton
|
||||||
|
.appearance_sectionBackground: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.appearance_buttonBackground: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.appearance_buttonHighlight: #colorLiteral(red: 0.9411764706, green: 0.9411764706, blue: 0.9411764706, alpha: 1),
|
||||||
|
|
||||||
|
// ConversationButton
|
||||||
|
.conversationButton_background: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.conversationButton_highlight: #colorLiteral(red: 0.9411764706, green: 0.9411764706, blue: 0.9411764706, alpha: 1)
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit.UIColor
|
||||||
|
|
||||||
|
internal enum Theme_OceanDark: ThemeColors {
|
||||||
|
static let theme: [ThemeValue: UIColor] = [
|
||||||
|
// General
|
||||||
|
.primary: .primary,
|
||||||
|
.defaultPrimary: Theme.PrimaryColor.blue.color,
|
||||||
|
.danger: #colorLiteral(red: 1, green: 0.2274509804, blue: 0.2274509804, alpha: 1),
|
||||||
|
.clear: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0),
|
||||||
|
.backgroundPrimary: #colorLiteral(red: 0.1450980392, green: 0.1529411765, blue: 0.2078431373, alpha: 1),
|
||||||
|
.backgroundSecondary: #colorLiteral(red: 0.1019607843, green: 0.1098039216, blue: 0.1568627451, alpha: 1),
|
||||||
|
.textPrimary: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.textSecondary: #colorLiteral(red: 0.6509803922, green: 0.662745098, blue: 0.8078431373, alpha: 1),
|
||||||
|
.borderSeparator: #colorLiteral(red: 0.2392156863, green: 0.2901960784, blue: 0.3647058824, alpha: 1),
|
||||||
|
|
||||||
|
// TextBox
|
||||||
|
.textBox_background: #colorLiteral(red: 0.1019607843, green: 0.1098039216, blue: 0.1568627451, alpha: 1),
|
||||||
|
.textBox_border: #colorLiteral(red: 0.2392156863, green: 0.2901960784, blue: 0.3647058824, alpha: 1),
|
||||||
|
|
||||||
|
// MessageBubble
|
||||||
|
.messageBubble_outgoingBackground: .primary,
|
||||||
|
.messageBubble_incomingBackground: #colorLiteral(red: 0.2392156863, green: 0.2901960784, blue: 0.3647058824, alpha: 1),
|
||||||
|
.messageBubble_outgoingText: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1),
|
||||||
|
.messageBubble_incomingText: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
|
||||||
|
// MenuButton
|
||||||
|
.menuButton_background: .primary,
|
||||||
|
.menuButton_icon: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.menuButton_shadow: .primary,
|
||||||
|
|
||||||
|
// RadioButton
|
||||||
|
.radioButton_selectedBackground: .primary,
|
||||||
|
.radioButton_unselectedBackground: .clear,
|
||||||
|
.radioButton_selectedBorder: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.radioButton_unselectedBorder: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
|
||||||
|
// OutlineButton
|
||||||
|
.outlineButton_text: .primary,
|
||||||
|
.outlineButton_background: .clear,
|
||||||
|
.outlineButton_highlight: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 0.3),
|
||||||
|
.outlineButton_border: .primary,
|
||||||
|
.outlineButton_filledText: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.outlineButton_filledBackground: #colorLiteral(red: 0.1019607843, green: 0.1098039216, blue: 0.1568627451, alpha: 1),
|
||||||
|
.outlineButton_filledHighlight: #colorLiteral(red: 0.168627451, green: 0.1764705882, blue: 0.2509803922, alpha: 1),
|
||||||
|
.outlineButton_destructiveText: #colorLiteral(red: 1, green: 0.2274509804, blue: 0.2274509804, alpha: 1),
|
||||||
|
.outlineButton_destructiveBackground: .clear,
|
||||||
|
.outlineButton_destructiveHighlight: #colorLiteral(red: 1, green: 0.2274509804, blue: 0.2274509804, alpha: 0.3),
|
||||||
|
.outlineButton_destructiveBorder: #colorLiteral(red: 1, green: 0.2274509804, blue: 0.2274509804, alpha: 1),
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
.settings_tabBackground: #colorLiteral(red: 0.1019607843, green: 0.1098039216, blue: 0.1568627451, alpha: 1),
|
||||||
|
.settings_tabHighlight: #colorLiteral(red: 0.168627451, green: 0.1764705882, blue: 0.2509803922, alpha: 1),
|
||||||
|
|
||||||
|
// Appearance
|
||||||
|
.appearance_sectionBackground: #colorLiteral(red: 0.1019607843, green: 0.1098039216, blue: 0.1568627451, alpha: 1),
|
||||||
|
.appearance_buttonBackground: #colorLiteral(red: 0.1019607843, green: 0.1098039216, blue: 0.1568627451, alpha: 1),
|
||||||
|
.appearance_buttonHighlight: #colorLiteral(red: 0.168627451, green: 0.1764705882, blue: 0.2509803922, alpha: 1),
|
||||||
|
|
||||||
|
// ConversationButton
|
||||||
|
.conversationButton_background: #colorLiteral(red: 0.168627451, green: 0.168627451, blue: 0.2509803922, alpha: 1),
|
||||||
|
.conversationButton_highlight: #colorLiteral(red: 0.168627451, green: 0.1764705882, blue: 0.2509803922, alpha: 1)
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit.UIColor
|
||||||
|
|
||||||
|
internal enum Theme_OceanLight: ThemeColors {
|
||||||
|
static let theme: [ThemeValue: UIColor] = [
|
||||||
|
// General
|
||||||
|
.primary: .primary,
|
||||||
|
.defaultPrimary: Theme.PrimaryColor.blue.color,
|
||||||
|
.danger: #colorLiteral(red: 0.8823529412, green: 0.1764705882, blue: 0.09803921569, alpha: 1),
|
||||||
|
.clear: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0),
|
||||||
|
.backgroundPrimary: #colorLiteral(red: 0.9882352941, green: 1, blue: 1, alpha: 1),
|
||||||
|
.backgroundSecondary: #colorLiteral(red: 0.9254901961, green: 0.9803921569, blue: 0.9843137255, alpha: 1),
|
||||||
|
.textPrimary: #colorLiteral(red: 0.09803921569, green: 0.2039215686, blue: 0.3647058824, alpha: 1),
|
||||||
|
.textSecondary: #colorLiteral(red: 0.4156862745, green: 0.431372549, blue: 0.5647058824, alpha: 1),
|
||||||
|
.borderSeparator: #colorLiteral(red: 0.3607843137, green: 0.6666666667, blue: 0.8, alpha: 1),
|
||||||
|
|
||||||
|
// TextBox
|
||||||
|
.textBox_background: #colorLiteral(red: 0.9882352941, green: 1, blue: 1, alpha: 1),
|
||||||
|
.textBox_border: #colorLiteral(red: 0.3607843137, green: 0.6666666667, blue: 0.8, alpha: 1),
|
||||||
|
|
||||||
|
// MessageBubble
|
||||||
|
.messageBubble_outgoingBackground: .primary,
|
||||||
|
.messageBubble_incomingBackground: #colorLiteral(red: 0.7019607843, green: 0.9294117647, blue: 0.9490196078, alpha: 1),
|
||||||
|
.messageBubble_outgoingText: #colorLiteral(red: 0.09803921569, green: 0.2039215686, blue: 0.3647058824, alpha: 1),
|
||||||
|
.messageBubble_incomingText: #colorLiteral(red: 0.09803921569, green: 0.2039215686, blue: 0.3647058824, alpha: 1),
|
||||||
|
|
||||||
|
// MenuButton
|
||||||
|
.menuButton_background: .primary,
|
||||||
|
.menuButton_icon: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
|
||||||
|
.menuButton_shadow: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1),
|
||||||
|
|
||||||
|
// RadioButton
|
||||||
|
.radioButton_selectedBackground: .primary,
|
||||||
|
.radioButton_unselectedBackground: .clear,
|
||||||
|
.radioButton_selectedBorder: #colorLiteral(red: 0.09803921569, green: 0.2039215686, blue: 0.3647058824, alpha: 1),
|
||||||
|
.radioButton_unselectedBorder: #colorLiteral(red: 0.3607843137, green: 0.6666666667, blue: 0.8, alpha: 1),
|
||||||
|
|
||||||
|
// OutlineButton
|
||||||
|
.outlineButton_text: #colorLiteral(red: 0.09803921569, green: 0.2039215686, blue: 0.3647058824, alpha: 1),
|
||||||
|
.outlineButton_background: .clear,
|
||||||
|
.outlineButton_highlight: #colorLiteral(red: 0.09803921569, green: 0.2039215686, blue: 0.3647058824, alpha: 0.1),
|
||||||
|
.outlineButton_border: #colorLiteral(red: 0.09803921569, green: 0.2039215686, blue: 0.3647058824, alpha: 1),
|
||||||
|
.outlineButton_filledText: #colorLiteral(red: 0.9882352941, green: 1, blue: 1, alpha: 1),
|
||||||
|
.outlineButton_filledBackground: #colorLiteral(red: 0.09803921569, green: 0.2039215686, blue: 0.3647058824, alpha: 1),
|
||||||
|
.outlineButton_filledHighlight: #colorLiteral(red: 0.4156862745, green: 0.431372549, blue: 0.5647058824, alpha: 1),
|
||||||
|
.outlineButton_destructiveText: #colorLiteral(red: 0.8823529412, green: 0.1764705882, blue: 0.09803921569, alpha: 1),
|
||||||
|
.outlineButton_destructiveBackground: .clear,
|
||||||
|
.outlineButton_destructiveHighlight: #colorLiteral(red: 0.8823529412, green: 0.1764705882, blue: 0.09803921569, alpha: 0.3),
|
||||||
|
.outlineButton_destructiveBorder: #colorLiteral(red: 0.8823529412, green: 0.1764705882, blue: 0.09803921569, alpha: 1),
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
.settings_tabBackground: #colorLiteral(red: 0.9882352941, green: 1, blue: 1, alpha: 1),
|
||||||
|
.settings_tabHighlight: #colorLiteral(red: 0.9058823529, green: 0.9529411765, blue: 0.9568627451, alpha: 1),
|
||||||
|
|
||||||
|
// Appearance
|
||||||
|
.appearance_sectionBackground: #colorLiteral(red: 0.9882352941, green: 1, blue: 1, alpha: 1),
|
||||||
|
.appearance_buttonBackground: #colorLiteral(red: 0.9882352941, green: 1, blue: 1, alpha: 1),
|
||||||
|
.appearance_buttonHighlight: #colorLiteral(red: 0.9058823529, green: 0.9529411765, blue: 0.9568627451, alpha: 1),
|
||||||
|
|
||||||
|
// ConversationButton
|
||||||
|
.conversationButton_background: #colorLiteral(red: 0.9882352941, green: 1, blue: 1, alpha: 1),
|
||||||
|
.conversationButton_highlight: #colorLiteral(red: 0.9058823529, green: 0.9529411765, blue: 0.9568627451, alpha: 1)
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit.UIColor
|
||||||
|
import SessionUtilitiesKit
|
||||||
|
|
||||||
|
public extension Theme {
|
||||||
|
public enum PrimaryColor: String, Codable, CaseIterable, EnumStringSetting {
|
||||||
|
case green
|
||||||
|
case blue
|
||||||
|
case purple
|
||||||
|
case pink
|
||||||
|
case red
|
||||||
|
case orange
|
||||||
|
case yellow
|
||||||
|
|
||||||
|
internal init?(color: UIColor?) {
|
||||||
|
guard
|
||||||
|
let color: UIColor = color,
|
||||||
|
let primaryColor: PrimaryColor = PrimaryColor.allCases.first(where: { $0.color == color })
|
||||||
|
else { return nil }
|
||||||
|
|
||||||
|
self = primaryColor
|
||||||
|
}
|
||||||
|
|
||||||
|
public var color: UIColor {
|
||||||
|
switch self {
|
||||||
|
case .green: return #colorLiteral(red: 0.1882352941, green: 0.9411764706, blue: 0.6549019608, alpha: 1)
|
||||||
|
case .blue: return #colorLiteral(red: 0.3411764706, green: 0.7882352941, blue: 0.9803921569, alpha: 1)
|
||||||
|
case .purple: return #colorLiteral(red: 0.7882352941, green: 0.5764705882, blue: 1, alpha: 1)
|
||||||
|
case .pink: return #colorLiteral(red: 1, green: 0.5843137255, blue: 0.937254902, alpha: 1)
|
||||||
|
case .red: return #colorLiteral(red: 1, green: 0.6117647059, blue: 0.5568627451, alpha: 1)
|
||||||
|
case .orange: return #colorLiteral(red: 0.9882352941, green: 0.6941176471, blue: 0.3490196078, alpha: 1)
|
||||||
|
case .yellow: return #colorLiteral(red: 0.9803921569, green: 0.8392156863, blue: 0.3411764706, alpha: 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension UIColor {
|
||||||
|
static let primary: UIColor = UIColor(dynamicProvider: { _ in
|
||||||
|
return ThemeManager.primaryColor.color
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit.UIColor
|
||||||
|
import SessionUtilitiesKit
|
||||||
|
|
||||||
|
// MARK: - Theme
|
||||||
|
|
||||||
|
public enum Theme: String, CaseIterable, Codable, EnumStringSetting {
|
||||||
|
case classicDark = "classic_dark"
|
||||||
|
case classicLight = "classic_light"
|
||||||
|
case oceanDark = "ocean_dark"
|
||||||
|
case oceanLight = "ocean_light"
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
public var title: String {
|
||||||
|
switch self {
|
||||||
|
case .classicDark: return "Classic Dark"
|
||||||
|
case .classicLight: return "Classic Light"
|
||||||
|
case .oceanDark: return "Ocean Dark"
|
||||||
|
case .oceanLight: return "Ocean Light"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var colors: [ThemeValue: UIColor] {
|
||||||
|
switch self {
|
||||||
|
case .classicDark: return Theme_ClassicDark.theme
|
||||||
|
case .classicLight: return Theme_ClassicLight.theme
|
||||||
|
case .oceanDark: return Theme_OceanDark.theme
|
||||||
|
case .oceanLight: return Theme_OceanLight.theme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var interfaceStyle: UIUserInterfaceStyle {
|
||||||
|
switch self {
|
||||||
|
case .classicDark, .oceanDark: return .dark
|
||||||
|
case .classicLight, .oceanLight: return .light
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var statusBarStyle: UIStatusBarStyle {
|
||||||
|
switch self {
|
||||||
|
case .classicDark, .oceanDark: return .lightContent
|
||||||
|
case .classicLight, .oceanLight: return .darkContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - ThemeColors
|
||||||
|
|
||||||
|
public protocol ThemeColors {
|
||||||
|
static var theme: [ThemeValue: UIColor] { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - ThemeValue
|
||||||
|
|
||||||
|
public enum ThemeValue {
|
||||||
|
// General
|
||||||
|
case primary
|
||||||
|
case defaultPrimary
|
||||||
|
case danger
|
||||||
|
case clear
|
||||||
|
case backgroundPrimary
|
||||||
|
case backgroundSecondary
|
||||||
|
case textPrimary
|
||||||
|
case textSecondary
|
||||||
|
case borderSeparator
|
||||||
|
|
||||||
|
// TextBox
|
||||||
|
case textBox_background
|
||||||
|
case textBox_border
|
||||||
|
|
||||||
|
// MessageBubble
|
||||||
|
case messageBubble_outgoingBackground
|
||||||
|
case messageBubble_incomingBackground
|
||||||
|
case messageBubble_outgoingText
|
||||||
|
case messageBubble_incomingText
|
||||||
|
|
||||||
|
// MenuButton
|
||||||
|
case menuButton_background
|
||||||
|
case menuButton_icon
|
||||||
|
case menuButton_shadow
|
||||||
|
|
||||||
|
// RadioButton
|
||||||
|
case radioButton_selectedBackground
|
||||||
|
case radioButton_unselectedBackground
|
||||||
|
case radioButton_selectedBorder
|
||||||
|
case radioButton_unselectedBorder
|
||||||
|
|
||||||
|
// OutlineButton
|
||||||
|
case outlineButton_text
|
||||||
|
case outlineButton_background
|
||||||
|
case outlineButton_highlight
|
||||||
|
case outlineButton_border
|
||||||
|
case outlineButton_filledText
|
||||||
|
case outlineButton_filledBackground
|
||||||
|
case outlineButton_filledHighlight
|
||||||
|
case outlineButton_destructiveText
|
||||||
|
case outlineButton_destructiveBackground
|
||||||
|
case outlineButton_destructiveHighlight
|
||||||
|
case outlineButton_destructiveBorder
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
case settings_tabBackground
|
||||||
|
case settings_tabHighlight
|
||||||
|
|
||||||
|
// Appearance
|
||||||
|
case appearance_sectionBackground
|
||||||
|
case appearance_buttonBackground
|
||||||
|
case appearance_buttonHighlight
|
||||||
|
|
||||||
|
// ConversationButton
|
||||||
|
case conversationButton_background
|
||||||
|
case conversationButton_highlight
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit.UIColor
|
||||||
|
|
||||||
|
internal extension UIColor {
|
||||||
|
func toImage() -> UIImage {
|
||||||
|
let bounds: CGRect = CGRect(x: 0, y: 0, width: 1, height: 1)
|
||||||
|
let renderer: UIGraphicsImageRenderer = UIGraphicsImageRenderer(bounds: bounds)
|
||||||
|
|
||||||
|
return renderer.image { rendererContext in
|
||||||
|
rendererContext.cgContext.setFillColor(self.cgColor)
|
||||||
|
rendererContext.cgContext.fill(bounds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,17 +2,6 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
@objc
|
|
||||||
public final class SNUtilitiesKitConfiguration : NSObject {
|
|
||||||
public let maxFileSize: UInt
|
|
||||||
|
|
||||||
@objc public static var shared: SNUtilitiesKitConfiguration!
|
|
||||||
|
|
||||||
fileprivate init(maxFileSize: UInt) {
|
|
||||||
self.maxFileSize = maxFileSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum SNUtilitiesKit { // Just to make the external API nice
|
public enum SNUtilitiesKit { // Just to make the external API nice
|
||||||
public static func migrations() -> TargetMigrations {
|
public static func migrations() -> TargetMigrations {
|
||||||
return TargetMigrations(
|
return TargetMigrations(
|
||||||
|
@ -25,12 +14,19 @@ public enum SNUtilitiesKit { // Just to make the external API nice
|
||||||
_001_InitialSetupMigration.self,
|
_001_InitialSetupMigration.self,
|
||||||
_002_SetupStandardJobs.self,
|
_002_SetupStandardJobs.self,
|
||||||
_003_YDBToGRDBMigration.self
|
_003_YDBToGRDBMigration.self
|
||||||
]
|
],
|
||||||
|
[], // Other DB migrations
|
||||||
|
[], // Legacy DB removal
|
||||||
|
[]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func configure(maxFileSize: UInt) {
|
public static func configure(maxFileSize: UInt) {
|
||||||
SNUtilitiesKitConfiguration.shared = SNUtilitiesKitConfiguration(maxFileSize: maxFileSize)
|
SNUtilitiesKitConfiguration.maxFileSize = maxFileSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc public final class SNUtilitiesKitConfiguration: NSObject {
|
||||||
|
@objc public static var maxFileSize: UInt = 0
|
||||||
|
}
|
||||||
|
|
|
@ -144,7 +144,8 @@ public extension Setting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol EnumSetting: RawRepresentable where RawValue == Int {}
|
public protocol EnumIntSetting: RawRepresentable where RawValue == Int {}
|
||||||
|
public protocol EnumStringSetting: RawRepresentable where RawValue == String {}
|
||||||
|
|
||||||
// MARK: - GRDB Interactions
|
// MARK: - GRDB Interactions
|
||||||
|
|
||||||
|
@ -159,7 +160,8 @@ public extension Storage {
|
||||||
subscript(key: Setting.StringKey) -> String? { return read { db in db[key] } }
|
subscript(key: Setting.StringKey) -> String? { return read { db in db[key] } }
|
||||||
subscript(key: Setting.DateKey) -> Date? { return read { db in db[key] } }
|
subscript(key: Setting.DateKey) -> Date? { return read { db in db[key] } }
|
||||||
|
|
||||||
subscript<T: EnumSetting>(key: Setting.EnumKey) -> T? { return read { db in db[key] } }
|
subscript<T: EnumIntSetting>(key: Setting.EnumKey) -> T? { return read { db in db[key] } }
|
||||||
|
subscript<T: EnumStringSetting>(key: Setting.EnumKey) -> T? { return read { db in db[key] } }
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension Database {
|
public extension Database {
|
||||||
|
@ -198,7 +200,7 @@ public extension Database {
|
||||||
set { self[key.rawValue] = Setting(key: key.rawValue, value: newValue) }
|
set { self[key.rawValue] = Setting(key: key.rawValue, value: newValue) }
|
||||||
}
|
}
|
||||||
|
|
||||||
subscript<T: EnumSetting>(key: Setting.EnumKey) -> T? {
|
subscript<T: EnumIntSetting>(key: Setting.EnumKey) -> T? {
|
||||||
get {
|
get {
|
||||||
guard let rawValue: Int = self[key.rawValue]?.value(as: Int.self) else {
|
guard let rawValue: Int = self[key.rawValue]?.value(as: Int.self) else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -209,6 +211,17 @@ public extension Database {
|
||||||
set { self[key.rawValue] = Setting(key: key.rawValue, value: newValue?.rawValue) }
|
set { self[key.rawValue] = Setting(key: key.rawValue, value: newValue?.rawValue) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subscript<T: EnumStringSetting>(key: Setting.EnumKey) -> T? {
|
||||||
|
get {
|
||||||
|
guard let rawValue: String = self[key.rawValue]?.value(as: String.self) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return T(rawValue: rawValue)
|
||||||
|
}
|
||||||
|
set { self[key.rawValue] = Setting(key: key.rawValue, value: newValue?.rawValue) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Value will be stored as a timestamp in seconds since 1970
|
/// Value will be stored as a timestamp in seconds since 1970
|
||||||
subscript(key: Setting.DateKey) -> Date? {
|
subscript(key: Setting.DateKey) -> Date? {
|
||||||
get {
|
get {
|
||||||
|
|
|
@ -19,6 +19,7 @@ public struct TargetMigrations: Comparable {
|
||||||
case utilitiesKit
|
case utilitiesKit
|
||||||
case snodeKit
|
case snodeKit
|
||||||
case messagingKit
|
case messagingKit
|
||||||
|
case uiKit
|
||||||
|
|
||||||
public static func < (lhs: Self, rhs: Self) -> Bool {
|
public static func < (lhs: Self, rhs: Self) -> Bool {
|
||||||
let lhsIndex: Int = (Identifier.allCases.firstIndex(of: lhs) ?? Identifier.allCases.count)
|
let lhsIndex: Int = (Identifier.allCases.firstIndex(of: lhs) ?? Identifier.allCases.count)
|
||||||
|
|
|
@ -113,15 +113,15 @@ public enum OWSMediaError: Error {
|
||||||
* https://github.com/signalapp/Signal-Android/blob/master/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java
|
* https://github.com/signalapp/Signal-Android/blob/master/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java
|
||||||
*/
|
*/
|
||||||
@objc
|
@objc
|
||||||
public static var kMaxFileSizeAnimatedImage: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
|
public static var kMaxFileSizeAnimatedImage: UInt { SNUtilitiesKitConfiguration.maxFileSize }
|
||||||
@objc
|
@objc
|
||||||
public static var kMaxFileSizeImage: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
|
public static var kMaxFileSizeImage: UInt { SNUtilitiesKitConfiguration.maxFileSize }
|
||||||
@objc
|
@objc
|
||||||
public static var kMaxFileSizeVideo: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
|
public static var kMaxFileSizeVideo: UInt { SNUtilitiesKitConfiguration.maxFileSize }
|
||||||
@objc
|
@objc
|
||||||
public static var kMaxFileSizeAudio: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
|
public static var kMaxFileSizeAudio: UInt { SNUtilitiesKitConfiguration.maxFileSize }
|
||||||
@objc
|
@objc
|
||||||
public static var kMaxFileSizeGeneric: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
|
public static var kMaxFileSizeGeneric: UInt { SNUtilitiesKitConfiguration.maxFileSize }
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
public static let kMaxVideoDimensions: CGFloat = 3 * 1024
|
public static let kMaxVideoDimensions: CGFloat = 3 * 1024
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import SessionUIKit
|
||||||
import SessionSnodeKit
|
import SessionSnodeKit
|
||||||
import SessionMessagingKit
|
import SessionMessagingKit
|
||||||
|
|
||||||
|
@ -13,5 +14,6 @@ public enum Configuration {
|
||||||
|
|
||||||
SNMessagingKit.configure()
|
SNMessagingKit.configure()
|
||||||
SNSnodeKit.configure()
|
SNSnodeKit.configure()
|
||||||
|
SUIKit.configure()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,15 +126,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
if ([topViewController conformsToProtocol:@protocol(OWSNavigationView)]) {
|
if ([topViewController conformsToProtocol:@protocol(OWSNavigationView)]) {
|
||||||
id<OWSNavigationView> navigationView = (id<OWSNavigationView>)topViewController;
|
id<OWSNavigationView> navigationView = (id<OWSNavigationView>)topViewController;
|
||||||
return ![navigationView shouldCancelNavigationBack];
|
return ![navigationView shouldCancelNavigationBack];
|
||||||
} else {
|
}
|
||||||
|
|
||||||
UIViewController *rootViewController = self.viewControllers.firstObject;
|
UIViewController *rootViewController = self.viewControllers.firstObject;
|
||||||
if (topViewController == rootViewController) {
|
if (topViewController == rootViewController) {
|
||||||
return NO;
|
return NO;
|
||||||
} else {
|
}
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - NavBarLayoutDelegate
|
#pragma mark - NavBarLayoutDelegate
|
||||||
|
|
||||||
|
@ -146,14 +146,18 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||||
{
|
{
|
||||||
if (!CurrentAppContext().isMainApp) {
|
if (OWSWindowManager.sharedManager.hasCall) {
|
||||||
return super.preferredStatusBarStyle;
|
|
||||||
} else if (OWSWindowManager.sharedManager.hasCall) {
|
|
||||||
// Status bar is overlaying the green "call banner"
|
// Status bar is overlaying the green "call banner"
|
||||||
return UIStatusBarStyleLightContent;
|
return UIStatusBarStyleLightContent;
|
||||||
} else {
|
|
||||||
return LKAppModeUtilities.isLightMode ? UIStatusBarStyleDefault : UIStatusBarStyleLightContent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: The view controllers contain the logic needed to get the correct statusBarStyle
|
||||||
|
// for the current theme
|
||||||
|
if (self.topViewController != nil) {
|
||||||
|
return [self.topViewController preferredStatusBarStyle];
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.preferredStatusBarStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateLayoutForNavbar:(OWSNavigationBar *)navbar
|
- (void)updateLayoutForNavbar:(OWSNavigationBar *)navbar
|
||||||
|
@ -164,9 +168,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
if (!CurrentAppContext().isMainApp) {
|
if (!CurrentAppContext().isMainApp) {
|
||||||
self.additionalSafeAreaInsets = UIEdgeInsetsZero;
|
self.additionalSafeAreaInsets = UIEdgeInsetsZero;
|
||||||
} else if (OWSWindowManager.sharedManager.hasCall) {
|
}
|
||||||
|
else if (OWSWindowManager.sharedManager.hasCall) {
|
||||||
self.additionalSafeAreaInsets = UIEdgeInsetsMake(20, 0, 0, 0);
|
self.additionalSafeAreaInsets = UIEdgeInsetsMake(20, 0, 0, 0);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
self.additionalSafeAreaInsets = UIEdgeInsetsZero;
|
self.additionalSafeAreaInsets = UIEdgeInsetsZero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import Foundation
|
||||||
import SessionMessagingKit
|
import SessionMessagingKit
|
||||||
import SessionUtilitiesKit
|
import SessionUtilitiesKit
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import SessionUIKit
|
||||||
|
|
||||||
public enum AppSetup {
|
public enum AppSetup {
|
||||||
private static var hasRun: Bool = false
|
private static var hasRun: Bool = false
|
||||||
|
@ -68,7 +69,8 @@ public enum AppSetup {
|
||||||
migrations: [
|
migrations: [
|
||||||
SNUtilitiesKit.migrations(),
|
SNUtilitiesKit.migrations(),
|
||||||
SNSnodeKit.migrations(),
|
SNSnodeKit.migrations(),
|
||||||
SNMessagingKit.migrations()
|
SNMessagingKit.migrations(),
|
||||||
|
SUIKit.migrations()
|
||||||
],
|
],
|
||||||
onProgressUpdate: migrationProgressChanged,
|
onProgressUpdate: migrationProgressChanged,
|
||||||
onComplete: { error, needsConfigSync in
|
onComplete: { error, needsConfigSync in
|
||||||
|
|
|
@ -24,7 +24,7 @@ public extension UIView {
|
||||||
static func separator() -> UIView {
|
static func separator() -> UIView {
|
||||||
let result = UIView()
|
let result = UIView()
|
||||||
result.set(.height, to: Values.separatorThickness)
|
result.set(.height, to: Values.separatorThickness)
|
||||||
result.backgroundColor = Colors.separator
|
result.themeBackgroundColor = .borderSeparator
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,29 +7,19 @@ public final class ViewControllerUtilities : NSObject {
|
||||||
|
|
||||||
@objc(setUpDefaultSessionStyleForVC:withTitle:customBackButton:)
|
@objc(setUpDefaultSessionStyleForVC:withTitle:customBackButton:)
|
||||||
public static func setUpDefaultSessionStyle(for vc: UIViewController, title: String?, hasCustomBackButton: Bool) {
|
public static func setUpDefaultSessionStyle(for vc: UIViewController, title: String?, hasCustomBackButton: Bool) {
|
||||||
// Set gradient background
|
|
||||||
vc.view.backgroundColor = .clear
|
|
||||||
let gradient = Gradients.defaultBackground
|
|
||||||
vc.view.setGradient(gradient)
|
|
||||||
// Set navigation bar background color
|
|
||||||
if let navigationBar = vc.navigationController?.navigationBar {
|
|
||||||
navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
|
|
||||||
navigationBar.shadowImage = UIImage()
|
|
||||||
navigationBar.isTranslucent = false
|
|
||||||
navigationBar.barTintColor = Colors.navigationBarBackground
|
|
||||||
}
|
|
||||||
// Customize title
|
// Customize title
|
||||||
if let title = title {
|
if let title = title {
|
||||||
let titleLabel = UILabel()
|
let titleLabel = UILabel()
|
||||||
titleLabel.text = title
|
|
||||||
titleLabel.textColor = Colors.text
|
|
||||||
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
||||||
|
titleLabel.text = title
|
||||||
|
titleLabel.themeTextColor = .textPrimary
|
||||||
vc.navigationItem.titleView = titleLabel
|
vc.navigationItem.titleView = titleLabel
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up back button
|
// Set up back button
|
||||||
if hasCustomBackButton {
|
if hasCustomBackButton {
|
||||||
let backButton = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
|
let backButton = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
|
||||||
backButton.tintColor = Colors.text
|
backButton.themeTintColor = .textPrimary
|
||||||
vc.navigationItem.backBarButtonItem = backButton
|
vc.navigationItem.backBarButtonItem = backButton
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue