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:
Morgan Pretty 2022-08-12 13:35:17 +10:00
parent b40752dc78
commit d56cee8234
91 changed files with 2729 additions and 783 deletions

View File

@ -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|

View File

@ -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

View File

@ -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 */,

View File

@ -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)

View File

@ -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 ])

View File

@ -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

View File

@ -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 ])

View File

@ -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 ])

View File

@ -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
) )

View File

@ -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 ])

View File

@ -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)

View File

@ -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

View File

@ -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
} }

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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 }

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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

View File

@ -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)

View File

@ -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)
} }

View File

@ -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)

View File

@ -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()
} }

View File

@ -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)

View File

@ -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

View File

@ -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)
} }
} }

View File

@ -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,

View File

@ -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

View File

@ -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
}
}

View File

@ -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) {

View File

@ -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 ])

View File

@ -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

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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
} }

View File

@ -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];

View File

@ -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 ]
}
}

View File

@ -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)
}
}

View File

@ -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
} }
} }

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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()
}
}

View File

@ -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
}
}

View File

@ -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
} }
} }

View File

@ -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() {
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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)
]
}

View File

@ -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)
]
}

View File

@ -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)
]
}

View File

@ -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)
]
}

View File

@ -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
})
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

@ -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()
} }
} }

View File

@ -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;
} }

View File

@ -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

View File

@ -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
} }
} }

View File

@ -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
} }
} }