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,11 +82,11 @@ abstract_target 'GlobalDependencies' do
end
end
end
# No extra dependencies for this
target 'SessionUIKit'
end
# No dependencies for this
target 'SessionUIKit'
# Actions to perform post-install
post_install do |installer|
enable_whole_module_optimization_for_crypto_swift(installer)

View File

@ -242,6 +242,6 @@ SPEC CHECKSUMS:
YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
PODFILE CHECKSUM: f0857369c4831b2e5c1946345e76e493f3286805
PODFILE CHECKSUM: 3423ad813952f2c56952aa7118df68e7878c3c65
COCOAPODS: 1.11.3

View File

@ -202,7 +202,6 @@
B84A89BC25DE328A0040017D /* ProfilePictureVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84A89BB25DE328A0040017D /* ProfilePictureVC.swift */; };
B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85357BE23A1AE0800AAF6CD /* SeedReminderView.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 */; };
B8569AE325CBB19A00DBA3DB /* DocumentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AE225CBB19A00DBA3DB /* DocumentView.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 */; };
C331FFE02558FB0000070591 /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B02390C37000BA5194 /* SearchBar.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 */; };
C331FFE82558FB0000070591 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C3CF8824D8EED300E1CCE7 /* TextView.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 */; };
FD245C6C2850669200B966DD /* MessageReceiveJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352A31225574F5200338F3E /* MessageReceiveJob.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 */; };
FD3C905C27E3FBEF00CD579F /* BatchRequestInfoSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C905B27E3FBEF00CD579F /* BatchRequestInfoSpec.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>"; };
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>"; };
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>"; };
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>"; };
@ -1263,7 +1274,7 @@
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>"; };
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>"; };
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>"; };
@ -1683,6 +1694,19 @@
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>"; };
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>"; };
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>"; };
@ -2110,7 +2134,7 @@
4C586924224FAB83003FD070 /* AVAudioSession+OWS.h */,
4C586925224FAB83003FD070 /* AVAudioSession+OWS.m */,
4521C3BF1F59F3BA00B4C582 /* TextFieldHelper.swift */,
B8544E3223D50E4900299F14 /* SNAppearance.swift */,
FD37E9D828A230F2003AE748 /* TraitObservingWindow.swift */,
C3D0972A2510499C00F6E3E4 /* BackgroundPoller.swift */,
C31A6C5B247F2CF3001123EF /* CGRect+Utilities.swift */,
C35E8AAD2485E51D00ACB629 /* IP2Country.swift */,
@ -2672,7 +2696,9 @@
C331FF5E2558FA0F00070591 /* Style Guide */ = {
isa = PBXGroup;
children = (
FD37E9C428A1C701003AE748 /* Themes */,
B8C9689023FA1401005F64E0 /* AppMode.swift */,
FD37E9C228A1C6F3003AE748 /* ThemeManager.swift */,
C3310032255900A400070591 /* Notification+AppMode.swift */,
B8BB829F238F322400BA5194 /* Colors.swift */,
C39DD28724F3318C008590FC /* Colors.xcassets */,
@ -2687,6 +2713,7 @@
isa = PBXGroup;
children = (
B8544E3023D16CA500299F14 /* DeviceUtilities.swift */,
FD37E9D628A20B5D003AE748 /* UIColor+Utilities.swift */,
C33100132558FFC200070591 /* UIImage+Tinting.swift */,
B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */,
C33100272559000A00070591 /* UIView+Rendering.swift */,
@ -2697,7 +2724,7 @@
C331FFCC2558FAF300070591 /* Components */ = {
isa = PBXGroup;
children = (
B8B5BCEB2394D869003823C9 /* Button.swift */,
B8B5BCEB2394D869003823C9 /* OutlineButton.swift */,
B8BB82B02390C37000BA5194 /* SearchBar.swift */,
B8BB82B82394911B00BA5194 /* Separator.swift */,
B8CCF638239721E20091D419 /* TabBar.swift */,
@ -2774,6 +2801,7 @@
C360969125AD1765008B62B2 /* Settings */ = {
isa = PBXGroup;
children = (
FD37E9CD28A1E682003AE748 /* Views */,
340FC88B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.h */,
340FC87B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.m */,
340FC88A204DAC8C007AEB0F /* NotificationSettingsViewController.h */,
@ -2787,6 +2815,7 @@
B886B4A62398B23E00211ABE /* QRCodeVC.swift */,
B86BD08523399CEF000F5AE3 /* SeedModal.swift */,
B8CCF6422397711F0091D419 /* SettingsVC.swift */,
FD37E9CB28A1E578003AE748 /* AppearanceViewController.swift */,
FDE72117286C156E0093DF33 /* ChatSettingsViewController.swift */,
7B7CB18A270591630079FF93 /* ShareLogsModal.swift */,
);
@ -3567,6 +3596,29 @@
path = LegacyDatabase;
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 */ = {
isa = PBXGroup;
children = (
@ -4822,20 +4874,28 @@
files = (
C331FF972558FA6B00070591 /* Fonts.swift in Sources */,
C331FF9B2558FA6B00070591 /* Gradients.swift in Sources */,
FD37E9D328A1FCDB003AE748 /* Theme+OceanDark.swift in Sources */,
C331FFB82558FA8D00070591 /* DeviceUtilities.swift in Sources */,
C331FFE72558FB0000070591 /* TextField.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 */,
C331FF962558FA6B00070591 /* Colors.swift in Sources */,
C331FFE02558FB0000070591 /* SearchBar.swift in Sources */,
C331FF982558FA6B00070591 /* AppMode.swift in Sources */,
C331FFE82558FB0000070591 /* TextView.swift in Sources */,
FD37E9D728A20B5D003AE748 /* UIColor+Utilities.swift in Sources */,
FD37E9C328A1C6F3003AE748 /* ThemeManager.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 */,
C3310033255900A400070591 /* Notification+AppMode.swift in Sources */,
C331FFE92558FB0000070591 /* Separator.swift in Sources */,
C33100282559000A00070591 /* UIView+Rendering.swift in Sources */,
FD37E9CA28A1E4BD003AE748 /* Theme+ClassicLight.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -5260,6 +5320,7 @@
C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */,
7BAF54D027ACCEEC003D12F8 /* EmptySearchResultCell.swift in Sources */,
B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */,
FD37E9D928A230F2003AE748 /* TraitObservingWindow.swift in Sources */,
B893063F2383961A005EAA8E /* ScanQRCodeWrapperVC.swift in Sources */,
FD848B8F283EF2A8000E298B /* UIScrollView+Utilities.swift in Sources */,
B879D449247E1BE300DB3608 /* PathVC.swift in Sources */,
@ -5309,6 +5370,7 @@
B8D0A26925E4A2C200C1835E /* Onboarding.swift in Sources */,
34D1F0521F7E8EA30066283D /* GiphyDownloader.swift in Sources */,
4CC613362227A00400E21A3A /* ConversationSearch.swift in Sources */,
FD37E9DD28A384EB003AE748 /* PrimaryColorSelectionView.swift in Sources */,
B82149B825D60393009C0F2A /* BlockedModal.swift in Sources */,
B82B408C239A068800A248E7 /* RegisterVC.swift in Sources */,
FDFDE126282D05380098B17F /* MediaInteractiveDismiss.swift in Sources */,
@ -5347,7 +5409,6 @@
34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */,
7BAF54CF27ACCEEC003D12F8 /* GlobalSearchViewController.swift in Sources */,
B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */,
B8544E3323D50E4900299F14 /* SNAppearance.swift in Sources */,
4C586926224FAB83003FD070 /* AVAudioSession+OWS.m in Sources */,
C331FFF42558FF0300070591 /* PNOptionView.swift in Sources */,
4C4AE6A1224AF35700D4AF6F /* SendMediaNavigationController.swift in Sources */,
@ -5356,6 +5417,7 @@
7BAADFCC27B0EF23007BCF92 /* CallVideoView.swift in Sources */,
B8CCF63F23975CFB0091D419 /* JoinOpenGroupVC.swift in Sources */,
FD848B9828422F1A000E298B /* Date+Utilities.swift in Sources */,
FD37E9DB28A244E9003AE748 /* ThemePreviewView.swift in Sources */,
B85357C323A1BD1200AAF6CD /* SeedVC.swift in Sources */,
45B5360E206DD8BB00D61655 /* UIResponder+OWS.swift in Sources */,
B8D84ECF25E3108A005A043E /* ExpandingAttachmentsButton.swift in Sources */,
@ -5400,6 +5462,7 @@
B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */,
C31A6C5A247F214E001123EF /* UIView+Glow.swift in Sources */,
4521C3C01F59F3BA00B4C582 /* TextFieldHelper.swift in Sources */,
FD37E9D128A1F2EB003AE748 /* ThemeSelectionView.swift in Sources */,
340FC8AC204DAC8D007AEB0F /* PrivacySettingsTableViewController.m in Sources */,
7B7CB18E270D066F0079FF93 /* IncomingCallBanner.swift in Sources */,
B8569AE325CBB19A00DBA3DB /* DocumentView.swift in Sources */,
@ -5422,6 +5485,7 @@
FD1C98E4282E3C5B00B76F9E /* UINavigationBar+Utilities.swift in Sources */,
C302093E25DCBF08001F572D /* MentionSelectionView.swift in Sources */,
C328251F25CA3A900062D0A7 /* QuoteView.swift in Sources */,
FD37E9CC28A1E578003AE748 /* AppearanceViewController.swift in Sources */,
B8EB20F02640F7F000773E52 /* OpenGroupInvitationView.swift in Sources */,
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */,
C328254025CA55880062D0A7 /* ContextMenuVC.swift in Sources */,

View File

@ -45,8 +45,8 @@ final class EditClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegat
return result
}()
private lazy var addMembersButton: Button = {
let result: Button = Button(style: .prominentOutline, size: .large)
private lazy var addMembersButton: OutlineButton = {
let result: OutlineButton = OutlineButton(style: .regular, size: .medium)
result.setTitle("Add Members", for: UIControl.State.normal)
result.addTarget(self, action: #selector(addMembers), for: UIControl.Event.touchUpInside)
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() {
guard !contactProfiles.isEmpty else {
let explanationLabel: UILabel = UILabel()
explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping
explanationLabel.text = "vc_create_closed_group_empty_state_message".localized()
explanationLabel.textColor = Colors.text
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)
createNewPrivateChatButton.setTitle(NSLocalizedString("vc_create_closed_group_empty_state_button_title", comment: ""), for: UIControl.State.normal)
createNewPrivateChatButton.addTarget(self, action: #selector(createNewDM), for: UIControl.Event.touchUpInside)
let createNewPrivateChatButton: OutlineButton = OutlineButton(style: .regular, size: .medium)
createNewPrivateChatButton.setTitle("vc_create_closed_group_empty_state_button_title".localized(), for: .normal)
createNewPrivateChatButton.addTarget(self, action: #selector(createNewDM), for: .touchUpInside)
createNewPrivateChatButton.set(.width, to: 196)
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)
documentPickerVC.delegate = self
documentPickerVC.modalPresentationStyle = .fullScreen
SNAppearance.switchToDocumentPickerAppearance()
present(documentPickerVC, animated: true, completion: nil)
}
@ -202,13 +202,8 @@ extension ConversationVC:
}
// MARK: - UIDocumentPickerDelegate
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
SNAppearance.switchToSessionAppearance() // Switch back to the correct appearance
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
SNAppearance.switchToSessionAppearance()
guard let url = urls.first else { return } // TODO: Handle multiple?
let urlResourceValues: URLResourceValues

View File

@ -3,6 +3,7 @@
import UIKit
import SignalUtilitiesKit
import SessionUtilitiesKit
import SessionUIKit
final class DeletedMessageView: UIView {
private static let iconSize: CGFloat = 18
@ -10,7 +11,7 @@ final class DeletedMessageView: UIView {
// MARK: - Lifecycle
init(textColor: UIColor) {
init(textColor: ThemeValue) {
super.init(frame: CGRect.zero)
setUpViewHierarchy(textColor: textColor)
@ -24,7 +25,7 @@ final class DeletedMessageView: UIView {
preconditionFailure("Use init(textColor:) instead.")
}
private func setUpViewHierarchy(textColor: UIColor) {
private func setUpViewHierarchy(textColor: ThemeValue) {
// Image view
let icon = UIImage(named: "ic_trash")?
.resizedImage(to: CGSize(
@ -34,17 +35,17 @@ final class DeletedMessageView: UIView {
.withRenderingMode(.alwaysTemplate)
let imageView = UIImageView(image: icon)
imageView.tintColor = textColor
imageView.themeTintColor = textColor
imageView.contentMode = .center
imageView.set(.width, to: DeletedMessageView.iconImageViewSize)
imageView.set(.height, to: DeletedMessageView.iconImageViewSize)
// Body label
let titleLabel = UILabel()
titleLabel.lineBreakMode = .byTruncatingTail
titleLabel.text = "message_deleted".localized()
titleLabel.textColor = textColor
titleLabel.font = .systemFont(ofSize: Values.smallFontSize)
titleLabel.text = "message_deleted".localized()
titleLabel.themeTextColor = textColor
titleLabel.lineBreakMode = .byTruncatingTail
// Stack view
let stackView = UIStackView(arrangedSubviews: [ imageView, titleLabel ])

View File

@ -9,7 +9,7 @@ final class DocumentView: UIView {
// MARK: - Lifecycle
init(attachment: Attachment, textColor: UIColor) {
init(attachment: Attachment, textColor: ThemeValue) {
super.init(frame: CGRect.zero)
setUpViewHierarchy(attachment: attachment, textColor: textColor)
@ -23,10 +23,10 @@ final class DocumentView: UIView {
preconditionFailure("Use init(viewItem:textColor:) instead.")
}
private func setUpViewHierarchy(attachment: Attachment, textColor: UIColor) {
private func setUpViewHierarchy(attachment: Attachment, textColor: ThemeValue) {
// Image view
let imageView = UIImageView(image: UIImage(named: "File")?.withRenderingMode(.alwaysTemplate))
imageView.tintColor = textColor
imageView.themeTintColor = textColor
imageView.contentMode = .center
let iconImageViewSize = DocumentView.iconImageViewSize
@ -35,17 +35,17 @@ final class DocumentView: UIView {
// Body label
let titleLabel = UILabel()
titleLabel.lineBreakMode = .byTruncatingTail
titleLabel.text = (attachment.sourceFilename ?? "File")
titleLabel.textColor = textColor
titleLabel.font = .systemFont(ofSize: Values.smallFontSize, weight: .light)
titleLabel.text = (attachment.sourceFilename ?? "File")
titleLabel.themeTextColor = textColor
titleLabel.lineBreakMode = .byTruncatingTail
// Size label
let sizeLabel = UILabel()
sizeLabel.lineBreakMode = .byTruncatingTail
sizeLabel.text = OWSFormat.formatFileSize(UInt(attachment.byteCount))
sizeLabel.textColor = textColor
sizeLabel.font = .systemFont(ofSize: Values.verySmallFontSize)
sizeLabel.text = OWSFormat.formatFileSize(UInt(attachment.byteCount))
sizeLabel.themeTextColor = textColor
sizeLabel.lineBreakMode = .byTruncatingTail
// Label stack view
let labelStackView = UIStackView(arrangedSubviews: [ titleLabel, sizeLabel ])

View File

@ -131,7 +131,7 @@ final class LinkPreviewView: UIView {
isOutgoing: Bool,
delegate: (UITextViewDelegate & BodyTextViewDelegate)? = nil,
cellViewModel: MessageViewModel? = nil,
bodyLabelTextColor: UIColor? = nil,
bodyLabelTextColor: ThemeValue? = nil,
lastSearchText: String? = nil
) {
cancelButton.removeFromSuperview()
@ -139,7 +139,7 @@ final class LinkPreviewView: UIView {
var image: UIImage? = state.image
let stateHasImage: Bool = (image != nil)
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
@ -156,6 +156,7 @@ final class LinkPreviewView: UIView {
}
imageView.image = image
imageView.themeTintColor = .textPrimary
imageView.contentMode = (stateHasImage ? .scaleAspectFill : .center)
// Loader
@ -190,7 +191,7 @@ final class LinkPreviewView: UIView {
let bodyTextView = VisibleMessageCell.getBodyTextView(
for: cellViewModel,
with: maxWidth,
textColor: (bodyLabelTextColor ?? sentLinkPreviewTextColor),
textColor: (bodyLabelTextColor ?? .textPrimary),
searchText: lastSearchText,
delegate: delegate
)

View File

@ -2,6 +2,7 @@
import UIKit
import SessionMessagingKit
import SessionUIKit
final class MediaPlaceholderView: UIView {
private static let iconSize: CGFloat = 24
@ -9,7 +10,7 @@ final class MediaPlaceholderView: UIView {
// MARK: - Lifecycle
init(cellViewModel: MessageViewModel, textColor: UIColor) {
init(cellViewModel: MessageViewModel, textColor: ThemeValue) {
super.init(frame: CGRect.zero)
setUpViewHierarchy(cellViewModel: cellViewModel, textColor: textColor)
@ -25,7 +26,7 @@ final class MediaPlaceholderView: UIView {
private func setUpViewHierarchy(
cellViewModel: MessageViewModel,
textColor: UIColor
textColor: ThemeValue
) {
let (iconName, attachmentDescription): (String, String) = {
guard
@ -52,17 +53,17 @@ final class MediaPlaceholderView: UIView {
)?
.withRenderingMode(.alwaysTemplate)
)
imageView.tintColor = textColor
imageView.themeTintColor = textColor
imageView.contentMode = .center
imageView.set(.width, to: MediaPlaceholderView.iconImageViewSize)
imageView.set(.height, to: MediaPlaceholderView.iconImageViewSize)
// Body label
let titleLabel = UILabel()
titleLabel.lineBreakMode = .byTruncatingTail
titleLabel.text = "Tap to download \(attachmentDescription)"
titleLabel.textColor = textColor
titleLabel.font = .systemFont(ofSize: Values.mediumFontSize)
titleLabel.text = "Tap to download \(attachmentDescription)"
titleLabel.themeTextColor = textColor
titleLabel.lineBreakMode = .byTruncatingTail
// Stack view
let stackView = UIStackView(arrangedSubviews: [ imageView, titleLabel ])

View File

@ -10,7 +10,7 @@ final class OpenGroupInvitationView: UIView {
// 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)
setUpViewHierarchy(
@ -29,24 +29,24 @@ final class OpenGroupInvitationView: UIView {
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
let titleLabel = UILabel()
titleLabel.lineBreakMode = .byTruncatingTail
titleLabel.text = name
titleLabel.textColor = textColor
titleLabel.font = .boldSystemFont(ofSize: Values.largeFontSize)
titleLabel.text = name
titleLabel.themeTextColor = textColor
titleLabel.lineBreakMode = .byTruncatingTail
// Subtitle
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.text = "view_open_group_invitation_description".localized()
subtitleLabel.themeTextColor = textColor
subtitleLabel.lineBreakMode = .byTruncatingTail
// URL
let urlLabel = UILabel()
urlLabel.lineBreakMode = .byCharWrapping
urlLabel.font = .systemFont(ofSize: Values.verySmallFontSize)
urlLabel.text = {
if let range = rawUrl.range(of: "?public_key=") {
return String(rawUrl[..<range.lowerBound])
@ -54,9 +54,9 @@ final class OpenGroupInvitationView: UIView {
return rawUrl
}()
urlLabel.textColor = textColor
urlLabel.themeTextColor = textColor
urlLabel.lineBreakMode = .byCharWrapping
urlLabel.numberOfLines = 0
urlLabel.font = .systemFont(ofSize: Values.verySmallFontSize)
// Label stack
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))?
.withRenderingMode(.alwaysTemplate)
)
iconImageView.tintColor = .white
iconImageView.themeTintColor = .textPrimary
iconImageView.contentMode = .center
iconImageView.layer.cornerRadius = iconImageViewSize / 2
iconImageView.layer.masksToBounds = true
iconImageView.backgroundColor = Colors.accent
iconImageView.themeBackgroundColor = .primary
iconImageView.set(.width, 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
// Line view
let lineColor: UIColor = {
switch (mode, AppModeManager.shared.currentAppMode) {
case (.regular, .light), (.draft, .light): return .black
case (.regular, .dark): return (direction == .outgoing) ? .black : Colors.accent
case (.draft, .dark): return Colors.accent
let lineColor: ThemeValue = {
switch mode {
case .regular: return (direction == .outgoing ? .messageBubble_outgoingText : .primary)
case .draft: return .primary
}
}()
let lineView = UIView()
lineView.backgroundColor = lineColor
lineView.themeBackgroundColor = lineColor
lineView.set(.width, to: Values.accentLineThickness)
if let attachment: Attachment = attachment {
@ -141,7 +140,7 @@ final class QuoteView: UIView {
imageView.tintColor = .white
imageView.contentMode = .center
imageView.backgroundColor = lineColor
imageView.themeBackgroundColor = lineColor
imageView.layer.cornerRadius = VisibleMessageCell.smallCornerRadius
imageView.layer.masksToBounds = true
imageView.set(.width, to: thumbnailSize)
@ -180,14 +179,6 @@ final class QuoteView: UIView {
}
// 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()
bodyLabel.numberOfLines = 0
bodyLabel.lineBreakMode = .byTruncatingTail
@ -211,7 +202,10 @@ final class QuoteView: UIView {
}
)
.defaulting(to: NSAttributedString(string: "Document"))
bodyLabel.textColor = textColor
bodyLabel.themeTextColor = (direction == .outgoing ?
.messageBubble_outgoingText :
.messageBubble_incomingText
)
let bodyLabelSize = bodyLabel.systemLayoutSizeFitting(availableSpace)
@ -226,7 +220,7 @@ final class QuoteView: UIView {
.asSet()
.contains(authorId)
let authorLabel = UILabel()
authorLabel.lineBreakMode = .byTruncatingTail
authorLabel.font = .boldSystemFont(ofSize: Values.smallFontSize)
authorLabel.text = (isCurrentUser ?
"MEDIA_GALLERY_SENDER_NAME_YOU".localized() :
Profile.displayName(
@ -234,11 +228,13 @@ final class QuoteView: UIView {
threadVariant: threadVariant
)
)
authorLabel.textColor = textColor
authorLabel.font = .boldSystemFont(ofSize: Values.smallFontSize)
authorLabel.themeTextColor = .messageBubble_outgoingText
authorLabel.lineBreakMode = .byTruncatingTail
let authorLabelSize = authorLabel.systemLayoutSizeFitting(availableSpace)
authorLabel.set(.height, to: authorLabelSize.height)
authorLabelHeight = authorLabelSize.height
let labelStackView = UIStackView(arrangedSubviews: [ authorLabel, bodyLabel ])
labelStackView.axis = .vertical
labelStackView.spacing = labelStackViewSpacing

View File

@ -94,11 +94,12 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
let size = VisibleMessageCell.replyButtonSize + 8
result.set(.width, to: size)
result.set(.height, to: size)
result.themeBorderColor = .textPrimary
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.alpha = 0
return result
}()
@ -108,7 +109,8 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
result.set(.width, to: size)
result.set(.height, to: size)
result.image = UIImage(named: "ic_reply")?.withRenderingMode(.alwaysTemplate)
result.tintColor = Colors.text
result.themeTintColor = .textPrimary
return result
}()
@ -265,11 +267,13 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
cellViewModel.variant == .standardIncoming ||
cellViewModel.variant == .standardIncomingDeleted
)
bubbleView.backgroundColor = ((
let bubbleBackgroundColor: ThemeValue = ((
cellViewModel.variant == .standardIncoming ||
cellViewModel.variant == .standardIncomingDeleted
) ? Colors.receivedMessageBackground : Colors.sentMessageBackground)
bubbleBackgroundView.backgroundColor = bubbleView.backgroundColor
) ? .messageBubble_incomingBackground : .messageBubble_outgoingBackground)
bubbleView.themeBackgroundColor = bubbleBackgroundColor
bubbleBackgroundView.themeBackgroundColor = bubbleBackgroundColor
updateBubbleViewCorners()
// Content view
@ -286,9 +290,9 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
populateHeader(for: cellViewModel, shouldInsetHeader: shouldInsetHeader)
// Author label
authorLabel.textColor = Colors.text
authorLabel.isHidden = (cellViewModel.senderName == nil)
authorLabel.text = cellViewModel.senderName
authorLabel.themeTextColor = .textPrimary
let authorLabelAvailableWidth: CGFloat = (VisibleMessageCell.getMaxWidth(for: cellViewModel) - 2 * VisibleMessageCell.authorLabelInset)
let authorLabelAvailableSpace = CGSize(width: authorLabelAvailableWidth, height: .greatestFiniteMagnitude)
@ -298,8 +302,8 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
// Message status image view
let (image, tintColor, backgroundColor) = getMessageStatusImage(for: cellViewModel)
messageStatusImageView.image = image
messageStatusImageView.tintColor = tintColor
messageStatusImageView.backgroundColor = backgroundColor
messageStatusImageView.themeTintColor = tintColor
messageStatusImageView.themeBackgroundColor = backgroundColor
messageStatusImageView.isHidden = (
cellViewModel.variant != .standardOutgoing ||
cellViewModel.variant == .infoCall ||
@ -323,9 +327,9 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
timerView.configure(
withExpirationTimestamp: UInt64(floor(expirationTimestampMs)),
initialDurationSeconds: UInt32(floor(expiresInSeconds)),
tintColor: Colors.text
initialDurationSeconds: UInt32(floor(expiresInSeconds))
)
timerView.themeTintColor = .textPrimary
timerView.isHidden = false
}
else {
@ -352,9 +356,9 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
let dateBreakLabel: UILabel = UILabel()
dateBreakLabel.font = .boldSystemFont(ofSize: Values.verySmallFontSize)
dateBreakLabel.textColor = Colors.text
dateBreakLabel.textAlignment = .center
dateBreakLabel.text = date.formattedForDisplay
dateBreakLabel.themeTextColor = .textPrimary
dateBreakLabel.textAlignment = .center
headerView.addSubview(dateBreakLabel)
dateBreakLabel.pin(.top, to: .top, of: headerView, withInset: Values.smallSpacing)
@ -374,18 +378,10 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
playbackInfo: ConversationViewModel.PlaybackInfo?,
lastSearchText: String?
) {
let bodyLabelTextColor: UIColor = {
let direction: Direction = (cellViewModel.variant == .standardOutgoing ?
.outgoing :
.incoming
)
switch (direction, AppModeManager.shared.currentAppMode) {
case (.outgoing, .dark), (.incoming, .light): return .black
case (.outgoing, .light): return Colors.grey
default: return .white
}
}()
let bodyLabelTextColor: ThemeValue = (cellViewModel.variant == .standardOutgoing ?
.messageBubble_outgoingText :
.messageBubble_incomingText
)
snContentView.subviews.forEach { $0.removeFromSuperview() }
albumView = 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) }
let image: UIImage
var tintColor: UIColor? = nil
var backgroundColor: UIColor? = nil
var tintColor: ThemeValue? = nil
var backgroundColor: ThemeValue? = nil
switch (cellViewModel.state, cellViewModel.hasAtLeastOneReadReceipt) {
case (.sending, _):
image = #imageLiteral(resourceName: "CircleDotDotDot").withRenderingMode(.alwaysTemplate)
tintColor = Colors.text
tintColor = .textPrimary
case (.sent, false), (.skipped, _):
image = #imageLiteral(resourceName: "CircleCheck").withRenderingMode(.alwaysTemplate)
tintColor = Colors.text
tintColor = .textPrimary
case (.sent, true):
image = isLightMode ? #imageLiteral(resourceName: "FilledCircleCheckLightMode") : #imageLiteral(resourceName: "FilledCircleCheckDarkMode")
@ -864,7 +860,7 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
case (.failed, _):
image = #imageLiteral(resourceName: "message_status_failed").withRenderingMode(.alwaysTemplate)
tintColor = Colors.destructive
tintColor = .danger
}
return (image, tintColor, backgroundColor)
@ -939,7 +935,7 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
static func getBodyTextView(
for cellViewModel: MessageViewModel,
with availableWidth: CGFloat,
textColor: UIColor,
textColor: ThemeValue,
searchText: String?,
delegate: (UITextViewDelegate & BodyTextViewDelegate)?
) -> UITextView {
@ -953,51 +949,6 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
let isOutgoing: Bool = (cellViewModel.variant == .standardOutgoing)
let result: BodyTextView = BodyTextView(snDelegate: delegate)
result.isEditable = false
let attributedText: NSMutableAttributedString = NSMutableAttributedString(
attributedString: MentionUtilities.highlightMentions(
in: (cellViewModel.body ?? ""),
threadVariant: cellViewModel.threadVariant,
currentUserPublicKey: cellViewModel.currentUserPublicKey,
currentUserBlindedPublicKey: cellViewModel.currentUserBlindedPublicKey,
isOutgoingMessage: isOutgoing,
attributes: [
.foregroundColor : textColor,
.font : UIFont.systemFont(ofSize: getFontSize(for: cellViewModel))
]
)
)
// If there is a valid search term then highlight each part that matched
if let searchText = searchText, searchText.count >= ConversationSearchController.minimumSearchTextLength {
let normalizedBody: String = attributedText.string.lowercased()
SessionThreadViewModel.searchTermParts(searchText)
.map { part -> String in
guard part.hasPrefix("\"") && part.hasSuffix("\"") else { return part }
return String(part[part.index(after: part.startIndex)..<part.endIndex])
}
.forEach { part in
// Highlight all ranges of the text (Note: The search logic only finds results that start
// with the term so we use the regex below to ensure we only highlight those cases)
normalizedBody
.ranges(
of: (CurrentAppContext().isRTL ?
"\(part.lowercased())(^|[ ])" :
"(^|[ ])\(part.lowercased())"
),
options: [.regularExpression]
)
.forEach { range in
let legacyRange: NSRange = NSRange(range, in: normalizedBody)
attributedText.addAttribute(.backgroundColor, value: UIColor.white, range: legacyRange)
attributedText.addAttribute(.foregroundColor, value: UIColor.black, range: legacyRange)
}
}
}
result.attributedText = attributedText
result.dataDetectorTypes = .link
result.backgroundColor = .clear
result.isOpaque = false
@ -1007,14 +958,70 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
result.isScrollEnabled = false
result.isUserInteractionEnabled = true
result.delegate = delegate
result.linkTextAttributes = [
.foregroundColor: textColor,
.underlineStyle: NSUnderlineStyle.single.rawValue
]
let availableSpace = CGSize(width: availableWidth, height: .greatestFiniteMagnitude)
let size = result.sizeThatFits(availableSpace)
result.set(.height, to: size.height)
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(
attributedString: MentionUtilities.highlightMentions(
in: (cellViewModel.body ?? ""),
threadVariant: cellViewModel.threadVariant,
currentUserPublicKey: cellViewModel.currentUserPublicKey,
currentUserBlindedPublicKey: cellViewModel.currentUserBlindedPublicKey,
isOutgoingMessage: isOutgoing,
attributes: [
.foregroundColor: actualTextColor,
.font: UIFont.systemFont(ofSize: getFontSize(for: cellViewModel))
]
)
)
// If there is a valid search term then highlight each part that matched
if let searchText = searchText, searchText.count >= ConversationSearchController.minimumSearchTextLength {
let normalizedBody: String = attributedText.string.lowercased()
SessionThreadViewModel.searchTermParts(searchText)
.map { part -> String in
guard part.hasPrefix("\"") && part.hasSuffix("\"") else { return part }
return String(part[part.index(after: part.startIndex)..<part.endIndex])
}
.forEach { part in
// Highlight all ranges of the text (Note: The search logic only finds results that start
// with the term so we use the regex below to ensure we only highlight those cases)
normalizedBody
.ranges(
of: (CurrentAppContext().isRTL ?
"\(part.lowercased())(^|[ ])" :
"(^|[ ])\(part.lowercased())"
),
options: [.regularExpression]
)
.forEach { range in
let legacyRange: NSRange = NSRange(range, in: normalizedBody)
attributedText.addAttribute(.backgroundColor, value: backgroundPrimaryColor, range: legacyRange)
attributedText.addAttribute(.foregroundColor, value: textPrimaryColor, range: legacyRange)
}
}
}
result?.attributedText = attributedText
result?.linkTextAttributes = [
.foregroundColor: actualTextColor,
.underlineStyle: NSUnderlineStyle.single.rawValue
]
if let result: BodyTextView = result, !hasPreviousSetText {
let availableSpace = CGSize(width: availableWidth, height: .greatestFiniteMagnitude)
let size = result.sizeThatFits(availableSpace)
result.set(.height, to: size.height)
}
}
return result
}

View File

@ -8,13 +8,14 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSMessageTimerView : UIView
@property (nonatomic) UIImageView *imageView;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
- (void)configureWithExpirationTimestamp:(uint64_t)expirationTimestamp
initialDurationSeconds:(uint32_t)initialDurationSeconds
tintColor:(UIColor *)tintColor;
initialDurationSeconds:(uint32_t)initialDurationSeconds;
- (void)prepareForReuse;

View File

@ -18,9 +18,6 @@ const CGFloat kDisappearingMessageIconSize = 12.f;
@property (nonatomic) uint32_t initialDurationSeconds;
@property (nonatomic) uint64_t expirationTimestamp;
@property (nonatomic) UIColor *tintColor;
@property (nonatomic) UIImageView *imageView;
@property (nonatomic, nullable) NSTimer *animationTimer;
@ -62,11 +59,9 @@ const CGFloat kDisappearingMessageIconSize = 12.f;
- (void)configureWithExpirationTimestamp:(uint64_t)expirationTimestamp
initialDurationSeconds:(uint32_t)initialDurationSeconds
tintColor:(UIColor *)tintColor;
{
self.expirationTimestamp = expirationTimestamp;
self.initialDurationSeconds = initialDurationSeconds;
self.tintColor = tintColor;
[self updateProgress12];
[self updateIcon];
@ -107,7 +102,6 @@ const CGFloat kDisappearingMessageIconSize = 12.f;
- (void)updateIcon
{
self.imageView.image = [[self progressIcon] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
self.imageView.tintColor = self.tintColor;
}
- (UIImage *)progressIcon

View File

@ -1,6 +1,7 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import UIKit
import SessionUIKit
import SessionMessagingKit
final class UserDetailsSheet: Sheet {
@ -60,8 +61,8 @@ final class UserDetailsSheet: Sheet {
sessionIDLabelContainer.layer.borderColor = isLightMode ? UIColor.black.cgColor : UIColor.white.cgColor
// Copy button
let copyButton = Button(style: .prominentOutline, size: .medium)
copyButton.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
let copyButton = OutlineButton(style: .regular, size: .medium)
copyButton.setTitle("copy".localized(), for: .normal)
copyButton.addTarget(self, action: #selector(copySessionID), for: UIControl.Event.touchUpInside)
copyButton.set(.width, to: 160)

View File

@ -3,6 +3,7 @@
import UIKit
import GRDB
import Curve25519Kit
import SessionUIKit
import SessionMessagingKit
import SessionUtilitiesKit
@ -14,11 +15,11 @@ final class NewDMVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControll
// MARK: Components
private lazy var tabBar: TabBar = {
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 }
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 }
self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil)
}
@ -201,10 +202,27 @@ private final class EnterPublicKeyVC : UIViewController {
return result
}()
private lazy var copyButton: Button = {
let result = Button(style: .unimportant, size: .medium)
result.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
private lazy var copyButton: OutlineButton = {
let result = OutlineButton(style: .regular, size: .small)
result.setTitle("copy".lowercased(), for: UIControl.State.normal)
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
}()
@ -241,8 +259,10 @@ private final class EnterPublicKeyVC : UIViewController {
override func viewDidLoad() {
// Remove background color
view.backgroundColor = .clear
// User session id container
let userPublicKeyContainer = UIView(wrapping: userPublicKeyLabel, withInsets: .zero, shouldAdaptForIPadWithWidth: Values.iPadUserSessionIdContainerWidth)
// Explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
@ -251,18 +271,13 @@ private final class EnterPublicKeyVC : UIViewController {
explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center
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
buttonContainer.addArrangedSubview(copyButton)
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)
// Main stack view
let mainStackView = UIStackView(arrangedSubviews: [ publicKeyTextView, UIView.spacer(withHeight: Values.smallSpacing), explanationLabel, spacer1, separator, spacer2, userPublicKeyContainer, spacer3, buttonContainer, UIView.vStretchingSpacer(), nextButtonContainer ])
mainStackView.axis = .vertical
@ -274,11 +289,14 @@ private final class EnterPublicKeyVC : UIViewController {
mainStackView.pin(.top, to: .top, of: view)
view.pin(.trailing, to: .trailing, of: mainStackView)
bottomConstraint = view.pin(.bottom, to: .bottom, of: mainStackView, withInset: bottomMargin)
// Width constraint
view.set(.width, to: UIScreen.main.bounds.width)
// Dismiss keyboard on tap
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tapGestureRecognizer)
// Listen to keyboard notifications
let notificationCenter = NotificationCenter.default
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 = {
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping
explanationLabel.text = "vc_home_empty_state_message".localized()
explanationLabel.textColor = Colors.text
explanationLabel.textAlignment = .center
explanationLabel.text = NSLocalizedString("vc_home_empty_state_message", comment: "")
let createNewPrivateChatButton = Button(style: .prominentOutline, size: .large)
createNewPrivateChatButton.setTitle(NSLocalizedString("vc_home_empty_state_button_title", comment: ""), for: UIControl.State.normal)
createNewPrivateChatButton.addTarget(self, action: #selector(createNewDM), for: UIControl.Event.touchUpInside)
explanationLabel.lineBreakMode = .byWordWrapping
explanationLabel.numberOfLines = 0
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)
let result = UIStackView(arrangedSubviews: [ explanationLabel, createNewPrivateChatButton ])
result.axis = .vertical
result.spacing = Values.mediumSpacing

View File

@ -78,16 +78,10 @@ class MessageRequestsViewController: BaseVC, UITableViewDelegate, UITableViewDat
return result
}()
private lazy var clearAllButton: Button = {
let result: Button = Button(style: .destructiveOutline, size: .large)
private lazy var clearAllButton: OutlineButton = {
let result: OutlineButton = OutlineButton(style: .destructive, size: .medium)
result.translatesAutoresizingMaskIntoConstraints = false
result.setTitle(NSLocalizedString("MESSAGE_REQUESTS_CLEAR_ALL", comment: ""), for: .normal)
result.setBackgroundImage(
Colors.destructive
.withAlphaComponent(isDarkMode ? 0.2 : 0.06)
.toImage(isDarkMode: isDarkMode),
for: .highlighted
)
result.setTitle("MESSAGE_REQUESTS_CLEAR_ALL".localized(), for: .normal)
result.addTarget(self, action: #selector(clearAllTapped), for: .touchUpInside)
return result

View File

@ -23,9 +23,7 @@ class MediaGalleryNavigationController: OWSNavigationController {
// MARK: - View Lifecycle
override var preferredStatusBarStyle: UIStatusBarStyle {
return (isLightMode ? .default : .lightContent)
}
override var preferredStatusBarStyle: UIStatusBarStyle { return ThemeManager.currentTheme.statusBarStyle }
override func viewDidLoad() {
super.viewDidLoad()

View File

@ -41,9 +41,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// This block will be cleared in storageIsReady.
DeviceSleepManager.sharedInstance.addBlock(blockObject: self)
let mainWindow: UIWindow = UIWindow(frame: UIScreen.main.bounds)
let mainWindow: UIWindow = TraitObservingWindow(frame: UIScreen.main.bounds)
self.loadingViewController = LoadingViewController()
// Store a weak reference in the ThemeManager so it can properly apply themes as needed
ThemeManager.mainWindow = mainWindow
AppSetup.setupEnvironment(
appSpecificBlock: {
// Create AppEnvironment
@ -76,8 +79,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
)
SNAppearance.switchToSessionAppearance()
// No point continuing if we are running tests
guard !CurrentAppContext().isRunningTests else { return true }

View File

@ -653,7 +653,7 @@
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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";
"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";
"CHATS_TITLE" = "Chats";
"CONVERSATIONS_TITLE" = "Conversations";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"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";
@ -684,3 +684,12 @@
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID 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
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("vc_display_name_title_2", comment: "")
titleLabel.numberOfLines = 0
titleLabel.text = "vc_display_name_title_2".localized()
titleLabel.textColor = Colors.text
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.numberOfLines = 0
// Set up explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("vc_display_name_explanation", comment: "")
explanationLabel.numberOfLines = 0
explanationLabel.text = "vc_display_name_explanation".localized()
explanationLabel.textColor = Colors.text
explanationLabel.lineBreakMode = .byWordWrapping
explanationLabel.numberOfLines = 0
// Set up spacers
let topSpacer = UIView.vStretchingSpacer()
@ -56,9 +56,8 @@ final class DisplayNameVC: BaseVC {
registerButtonBottomOffsetConstraint = registerButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
// Set up register button
let registerButton = Button(style: .prominentFilled, size: .large)
registerButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
registerButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
let registerButton = OutlineButton(style: .filled, size: .large)
registerButton.setTitle("continue_2".localized(), for: UIControl.State.normal)
registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
// Set up register button container

View File

@ -5,45 +5,52 @@ import SessionUIKit
final class LandingVC: BaseVC {
// MARK: Components
// MARK: - Components
private lazy var fakeChatView: FakeChatView = {
let result = FakeChatView()
result.set(.height, to: LandingVC.fakeChatViewHeight)
return result
}()
private lazy var registerButton: Button = {
let result = Button(style: .prominentFilled, size: .large)
result.setTitle(NSLocalizedString("vc_landing_register_button_title", comment: ""), for: UIControl.State.normal)
result.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
result.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
private lazy var registerButton: OutlineButton = {
let result = OutlineButton(style: .filled, size: .large)
result.setTitle("vc_landing_register_button_title".localized(), for: .normal)
result.addTarget(self, action: #selector(register), for: .touchUpInside)
return result
}()
private lazy var restoreButton: Button = {
let result = Button(style: .prominentOutline, size: .large)
result.setTitle(NSLocalizedString("vc_landing_restore_button_title", comment: ""), for: UIControl.State.normal)
result.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
result.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside)
private lazy var restoreButton: OutlineButton = {
let result = OutlineButton(style: .regular, size: .large)
result.setTitle("vc_landing_restore_button_title".localized(), for: .normal)
result.addTarget(self, action: #selector(restore), for: .touchUpInside)
return result
}()
// MARK: Settings
// MARK: - Settings
private static let fakeChatViewHeight = isIPhone5OrSmaller ? CGFloat(234) : CGFloat(260)
// MARK: Lifecycle
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
setUpNavBarSessionIcon()
// Title label
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("vc_landing_title_2", comment: "")
titleLabel.numberOfLines = 0
titleLabel.text = "vc_landing_title_2".localized()
titleLabel.textColor = Colors.text
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.numberOfLines = 0
// Title label container
let titleLabelContainer = UIView()
titleLabelContainer.addSubview(titleLabel)
@ -51,22 +58,27 @@ final class LandingVC: BaseVC {
titleLabel.pin(.top, to: .top, of: titleLabelContainer)
titleLabelContainer.pin(.trailing, to: .trailing, of: titleLabel, withInset: Values.veryLargeSpacing)
titleLabelContainer.pin(.bottom, to: .bottom, of: titleLabel)
// Spacers
let topSpacer = UIView.vStretchingSpacer()
let bottomSpacer = UIView.vStretchingSpacer()
// Link button
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.titleLabel!.font = .boldSystemFont(ofSize: Values.smallFontSize)
linkButton.addTarget(self, action: #selector(link), for: UIControl.Event.touchUpInside)
linkButton.addTarget(self, action: #selector(link), for: .touchUpInside)
// Link button container
let linkButtonContainer = UIView()
linkButtonContainer.set(.height, to: Values.onboardingButtonBottomOffset)
linkButtonContainer.addSubview(linkButton)
linkButton.center(.horizontal, in: linkButtonContainer)
let isIPhoneX = (UIApplication.shared.keyWindow!.safeAreaInsets.bottom > 0)
linkButton.centerYAnchor.constraint(equalTo: linkButtonContainer.centerYAnchor, constant: isIPhoneX ? -4 : 0).isActive = true
// Button stack view
let buttonStackView = UIStackView(arrangedSubviews: [ registerButton, restoreButton ])
buttonStackView.axis = .vertical
@ -77,6 +89,7 @@ final class LandingVC: BaseVC {
restoreButton.set(.width, to: Values.iPadButtonWidth)
buttonStackView.alignment = .center
}
// Button stack view container
let buttonStackViewContainer = UIView()
buttonStackViewContainer.addSubview(buttonStackView)
@ -84,6 +97,7 @@ final class LandingVC: BaseVC {
buttonStackView.pin(.top, to: .top, of: buttonStackViewContainer)
buttonStackViewContainer.pin(.trailing, to: .trailing, of: buttonStackView, withInset: isIPhone5OrSmaller ? CGFloat(52) : Values.massiveSpacing)
buttonStackViewContainer.pin(.bottom, to: .bottom, of: buttonStackView)
// Main stack view
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, titleLabelContainer, UIView.spacer(withHeight: isIPhone5OrSmaller ? Values.smallSpacing : Values.mediumSpacing), fakeChatView, bottomSpacer, buttonStackViewContainer, linkButtonContainer ])
mainStackView.axis = .vertical
@ -93,7 +107,8 @@ final class LandingVC: BaseVC {
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
}
// MARK: Interaction
// MARK: - Interaction
@objc private func register() {
let registerVC = RegisterVC()
navigationController!.pushViewController(registerVC, animated: true)

View File

@ -2,24 +2,26 @@
import UIKit
import PromiseKit
import SessionUIKit
import SessionUtilitiesKit
import SessionSnodeKit
final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, OWSQRScannerDelegate {
final class LinkDeviceVC: BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, OWSQRScannerDelegate {
private let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
private var pages: [UIViewController] = []
private var targetVCIndex: Int?
private var tabBarTopConstraint: NSLayoutConstraint!
private var activityIndicatorModal: ModalActivityIndicatorViewController?
// MARK: Components
// MARK: - Components
private lazy var tabBar: TabBar = {
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 }
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 }
self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil)
}
@ -180,20 +182,23 @@ private final class RecoveryPhraseVC : UIViewController {
// MARK: Lifecycle
override func viewDidLoad() {
view.backgroundColor = .clear
// Title label
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("vc_enter_recovery_phrase_title", comment: "")
titleLabel.numberOfLines = 0
titleLabel.text = "vc_enter_recovery_phrase_title".localized()
titleLabel.textColor = Colors.text
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.numberOfLines = 0
// Explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("vc_enter_recovery_phrase_explanation", comment: "")
explanationLabel.numberOfLines = 0
explanationLabel.text = "vc_enter_recovery_phrase_explanation".localized()
explanationLabel.textColor = Colors.text
explanationLabel.lineBreakMode = .byWordWrapping
explanationLabel.numberOfLines = 0
// Spacers
let topSpacer = UIView.vStretchingSpacer()
let spacer1 = UIView()
@ -203,17 +208,20 @@ private final class RecoveryPhraseVC : UIViewController {
let bottomSpacer = UIView.vStretchingSpacer()
let restoreButtonBottomOffsetSpacer = UIView()
restoreButtonBottomOffsetConstraint = restoreButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
// Continue button
let continueButton = Button(style: .prominentFilled, size: .large)
continueButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
continueButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
let continueButton = OutlineButton(style: .filled, size: .large)
continueButton.setTitle("continue_2".localized(), for: UIControl.State.normal)
continueButton.addTarget(self, action: #selector(handleContinueButtonTapped), for: UIControl.Event.touchUpInside)
// Continue button container
let continueButtonContainer = UIView(wrapping: continueButton, withInsets: UIEdgeInsets(top: 0, leading: Values.massiveSpacing, bottom: 0, trailing: Values.massiveSpacing), shouldAdaptForIPadWithWidth: Values.iPadButtonWidth)
// Top stack view
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, spacer1, explanationLabel, spacer2, mnemonicTextView ])
topStackView.axis = .vertical
topStackView.alignment = .fill
// Top stack view container
let topStackViewContainer = UIView()
topStackViewContainer.addSubview(topStackView)
@ -221,6 +229,7 @@ private final class RecoveryPhraseVC : UIViewController {
topStackView.pin(.top, to: .top, of: topStackViewContainer)
topStackViewContainer.pin(.trailing, to: .trailing, of: topStackView, withInset: Values.veryLargeSpacing)
topStackViewContainer.pin(.bottom, to: .bottom, of: topStackView)
// Main stack view
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, continueButtonContainer, restoreButtonBottomOffsetSpacer ])
mainStackView.axis = .vertical
@ -231,13 +240,16 @@ private final class RecoveryPhraseVC : UIViewController {
mainStackView.pin(.trailing, to: .trailing, of: view)
bottomConstraint = mainStackView.pin(.bottom, to: .bottom, of: view)
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
// Dismiss keyboard on tap
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tapGestureRecognizer)
// Listen to keyboard notifications
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillChangeFrameNotification(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillHideNotification(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
// Set up width constraint
view.set(.width, to: UIScreen.main.bounds.width)
}
@ -246,7 +258,8 @@ private final class RecoveryPhraseVC : UIViewController {
NotificationCenter.default.removeObserver(self)
}
// MARK: General
// MARK: - General
func constrainHeight(to height: CGFloat) {
view.set(.height, to: height)
}

View File

@ -2,6 +2,7 @@
import UIKit
import PromiseKit
import SessionUIKit
import SessionMessagingKit
import SessionSnodeKit
@ -33,42 +34,51 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
// MARK: Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
setUpNavBarSessionIcon()
let learnMoreButton = UIBarButtonItem(image: #imageLiteral(resourceName: "ic_info"), style: .plain, target: self, action: #selector(learnMore))
learnMoreButton.tintColor = Colors.text
navigationItem.rightBarButtonItem = learnMoreButton
// Set up title label
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("vc_pn_mode_title", comment: "")
titleLabel.numberOfLines = 0
titleLabel.text = "vc_pn_mode_title".localized()
titleLabel.textColor = Colors.text
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.numberOfLines = 0
// Set up spacers
let topSpacer = UIView.vStretchingSpacer()
let bottomSpacer = UIView.vStretchingSpacer()
let registerButtonBottomOffsetSpacer = UIView()
registerButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
// Set up register button
let registerButton = Button(style: .prominentFilled, size: .large)
registerButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
registerButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
let registerButton = OutlineButton(style: .filled, size: .large)
registerButton.setTitle("continue_2".localized(), for: .normal)
registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
// 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)
// Set up options stack view
let optionsStackView = UIStackView(arrangedSubviews: optionViews)
optionsStackView.axis = .vertical
optionsStackView.spacing = Values.smallSpacing
optionsStackView.alignment = .fill
// Set up top stack view
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, UIView.spacer(withHeight: isIPhone6OrSmaller ? Values.mediumSpacing : Values.veryLargeSpacing), optionsStackView ])
topStackView.axis = .vertical
topStackView.alignment = .fill
// Set up top stack view container
let topStackViewContainer = UIView(wrapping: topStackView, withInsets: UIEdgeInsets(top: 0, leading: Values.veryLargeSpacing, bottom: 0, trailing: Values.veryLargeSpacing))
// Set up main stack view
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, registerButtonContainer, registerButtonBottomOffsetSpacer ])
mainStackView.axis = .vertical
@ -76,14 +86,16 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
view.addSubview(mainStackView)
mainStackView.pin(to: view)
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
// Preselect APNs mode
optionViews[0].isSelected = true
}
// MARK: Interaction
// MARK: - Interaction
@objc private func learnMore() {
let urlAsString = "https://getsession.org/faq/#privacy"
let url = URL(string: urlAsString)!
guard let url: URL = URL(string: "https://getsession.org/faq/#privacy") else { return }
UIApplication.shared.open(url)
}
@ -93,9 +105,9 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
@objc private func register() {
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)
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)
}
UserDefaults.standard[.isUsingFullAPNs] = (selectedOptionView == apnsOptionView)

View File

@ -3,13 +3,15 @@
import UIKit
import Sodium
import Curve25519Kit
import SessionUIKit
final class RegisterVC : BaseVC {
private var seed: Data! { didSet { updateKeyPair() } }
private var ed25519KeyPair: Sign.KeyPair!
private var x25519KeyPair: ECKeyPair! { didSet { updatePublicKeyLabel() } }
// MARK: Components
// MARK: - Components
private lazy var publicKeyLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
@ -17,14 +19,15 @@ final class RegisterVC : BaseVC {
result.numberOfLines = 0
result.lineBreakMode = .byCharWrapping
result.accessibilityLabel = "Session ID label"
return result
}()
private lazy var copyPublicKeyButton: Button = {
let result = Button(style: .prominentOutline, size: .large)
result.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
result.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside)
private lazy var copyPublicKeyButton: OutlineButton = {
let result = OutlineButton(style: .regular, size: .large)
result.setTitle("copy".localized(), for: .normal)
result.addTarget(self, action: #selector(copyPublicKey), for: .touchUpInside)
return result
}()
@ -50,20 +53,23 @@ final class RegisterVC : BaseVC {
setUpGradientBackground()
setUpNavBarStyle()
setUpNavBarSessionIcon()
// Set up title label
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("vc_register_title", comment: "")
titleLabel.numberOfLines = 0
titleLabel.text = "vc_register_title".localized()
titleLabel.textColor = Colors.text
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.numberOfLines = 0
// Set up explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("vc_register_explanation", comment: "")
explanationLabel.numberOfLines = 0
explanationLabel.text = "vc_register_explanation".localized()
explanationLabel.textColor = Colors.text
explanationLabel.lineBreakMode = .byWordWrapping
explanationLabel.numberOfLines = 0
// Set up public key label container
let publicKeyLabelContainer = UIView()
publicKeyLabelContainer.addSubview(publicKeyLabel)
@ -71,14 +77,16 @@ final class RegisterVC : BaseVC {
publicKeyLabelContainer.layer.cornerRadius = TextField.cornerRadius
publicKeyLabelContainer.layer.borderWidth = 1
publicKeyLabelContainer.layer.borderColor = Colors.text.cgColor
// Set up spacers
let topSpacer = UIView.vStretchingSpacer()
let bottomSpacer = UIView.vStretchingSpacer()
// Set up register button
let registerButton = Button(style: .prominentFilled, size: .large)
registerButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
registerButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
let registerButton = OutlineButton(style: .filled, size: .large)
registerButton.setTitle("continue_2".localized(), for: .normal)
registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
// Set up button stack view
let buttonStackView = UIStackView(arrangedSubviews: [ registerButton, copyPublicKeyButton ])
buttonStackView.axis = .vertical
@ -89,6 +97,7 @@ final class RegisterVC : BaseVC {
copyPublicKeyButton.set(.width, to: Values.iPadButtonWidth)
buttonStackView.alignment = .center
}
// Set up button stack view container
let buttonStackViewContainer = UIView()
buttonStackViewContainer.addSubview(buttonStackView)
@ -96,10 +105,12 @@ final class RegisterVC : BaseVC {
buttonStackView.pin(.top, to: .top, of: buttonStackViewContainer)
buttonStackViewContainer.pin(.trailing, to: .trailing, of: buttonStackView, withInset: Values.massiveSpacing)
buttonStackViewContainer.pin(.bottom, to: .bottom, of: buttonStackView)
// Set up legal label
legalLabel.isUserInteractionEnabled = true
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleLegalLabelTapped))
legalLabel.addGestureRecognizer(tapGestureRecognizer)
// Set up legal label container
let legalLabelContainer = UIView()
legalLabelContainer.set(.height, to: Values.onboardingButtonBottomOffset)
@ -108,11 +119,13 @@ final class RegisterVC : BaseVC {
legalLabel.pin(.top, to: .top, of: legalLabelContainer)
legalLabelContainer.pin(.trailing, to: .trailing, of: legalLabel, withInset: Values.massiveSpacing)
legalLabelContainer.pin(.bottom, to: .bottom, of: legalLabel, withInset: isIPhone5OrSmaller ? 6 : 10)
// Set up top stack view
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, explanationLabel, publicKeyLabelContainer ])
topStackView.axis = .vertical
topStackView.spacing = isIPhone5OrSmaller ? Values.smallSpacing : Values.veryLargeSpacing
topStackView.alignment = .fill
// Set up top stack view container
let topStackViewContainer = UIView()
topStackViewContainer.addSubview(topStackView)
@ -120,6 +133,7 @@ final class RegisterVC : BaseVC {
topStackView.pin(.top, to: .top, of: topStackViewContainer)
topStackViewContainer.pin(.trailing, to: .trailing, of: topStackView, withInset: Values.veryLargeSpacing)
topStackViewContainer.pin(.bottom, to: .bottom, of: topStackView)
// Set up main stack view
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, buttonStackViewContainer, legalLabelContainer ])
mainStackView.axis = .vertical
@ -127,6 +141,7 @@ final class RegisterVC : BaseVC {
view.addSubview(mainStackView)
mainStackView.pin(to: view)
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
// Peform initial seed update
updateSeed()
}

View File

@ -1,6 +1,7 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import UIKit
import SessionUIKit
import SessionUtilitiesKit
final class RestoreVC: BaseVC {
@ -12,49 +13,60 @@ final class RestoreVC: BaseVC {
// MARK: Components
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.layer.borderColor = Colors.text.cgColor
result.accessibilityLabel = "Recovery phrase text view"
return result
}()
private lazy var legalLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
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 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: "End User License Agreement (EULA)"))
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.numberOfLines = 0
result.textAlignment = .center
result.lineBreakMode = .byWordWrapping
result.numberOfLines = 0
return result
}()
// MARK: Lifecycle
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
setUpNavBarSessionIcon()
// Set up title label
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("vc_restore_title", comment: "")
titleLabel.numberOfLines = 0
titleLabel.text = "vc_restore_title".localized()
titleLabel.textColor = Colors.text
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.numberOfLines = 0
// Set up explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("vc_restore_explanation", comment: "")
explanationLabel.numberOfLines = 0
explanationLabel.text = "vc_restore_explanation".localized()
explanationLabel.textColor = Colors.text
explanationLabel.lineBreakMode = .byWordWrapping
explanationLabel.numberOfLines = 0
// Set up legal label
legalLabel.isUserInteractionEnabled = true
let legalLabelTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleLegalLabelTapped))
@ -70,17 +82,20 @@ final class RestoreVC: BaseVC {
let bottomSpacer = UIView.vStretchingSpacer()
let restoreButtonBottomOffsetSpacer = UIView()
restoreButtonBottomOffsetConstraint = restoreButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
// Set up restore button
let restoreButton = Button(style: .prominentFilled, size: .large)
restoreButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
restoreButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
let restoreButton = OutlineButton(style: .filled, size: .large)
restoreButton.setTitle("continue_2".localized(), for: UIControl.State.normal)
restoreButton.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside)
// 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)
// Set up top stack view
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, spacer1, explanationLabel, spacer2, mnemonicTextView, spacer3, legalLabel ])
topStackView.axis = .vertical
topStackView.alignment = .fill
// Set up top stack view container
let topStackViewContainer = UIView()
topStackViewContainer.addSubview(topStackView)
@ -88,19 +103,23 @@ final class RestoreVC: BaseVC {
topStackView.pin(.top, to: .top, of: topStackViewContainer)
topStackViewContainer.pin(.trailing, to: .trailing, of: topStackView, withInset: Values.veryLargeSpacing)
topStackViewContainer.pin(.bottom, to: .bottom, of: topStackView)
// Set up main stack view
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, restoreButtonContainer, restoreButtonBottomOffsetSpacer ])
mainStackView.axis = .vertical
mainStackView.alignment = .fill
view.addSubview(mainStackView)
mainStackView.pin(.leading, to: .leading, of: view)
mainStackView.pin(.top, to: .top, of: view)
mainStackView.pin(.trailing, to: .trailing, of: view)
bottomConstraint = mainStackView.pin(.bottom, to: .bottom, of: view)
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
// Dismiss keyboard on tap
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tapGestureRecognizer)
// Listen to keyboard notifications
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillChangeFrameNotification(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)

View File

@ -2,28 +2,35 @@
import UIKit
import SessionUIKit
import SessionUtilitiesKit
final class SeedReminderView: UIView {
private static let progressBarThickness: CGFloat = 2
private let hasContinueButton: Bool
var title = NSAttributedString(string: "") { didSet { titleLabel.attributedText = title } }
var subtitle = "" { didSet { subtitleLabel.text = subtitle } }
var delegate: SeedReminderViewDelegate?
// MARK: Components
// MARK: - Components
private lazy var progressIndicatorView: UIProgressView = {
let result = UIProgressView()
result.progressViewStyle = .bar
result.progressTintColor = Colors.accent
result.backgroundColor = isLightMode ? UIColor(hex: 0x000000).withAlphaComponent(0.1) : UIColor(hex: 0xFFFFFF).withAlphaComponent(0.1)
result.set(.height, to: SeedReminderView.progressBarThickness)
return result
}()
lazy var titleLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.font = .boldSystemFont(ofSize: Values.smallFontSize)
result.textColor = Colors.text
result.lineBreakMode = .byTruncatingTail
return result
}()
@ -36,13 +43,13 @@ final class SeedReminderView: UIView {
return result
}()
// MARK: Settings
private static let progressBarThickness: CGFloat = 2
// MARK: - Lifecycle
// MARK: Lifecycle
init(hasContinueButton: Bool) {
self.hasContinueButton = hasContinueButton
super.init(frame: CGRect.zero)
setUpViewHierarchy()
}
@ -57,16 +64,18 @@ final class SeedReminderView: UIView {
private func setUpViewHierarchy() {
// Set background color
backgroundColor = Colors.cellBackground
// Set up label stack view
let labelStackView = UIStackView(arrangedSubviews: [ titleLabel, subtitleLabel ])
labelStackView.axis = .vertical
labelStackView.spacing = 4
// Set up button
let button = Button(style: .prominentOutline, size: .small)
button.titleLabel!.font = .boldSystemFont(ofSize: CGFloat(13))
button.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
let button = OutlineButton(style: .regular, size: .small)
button.setTitle("continue_2".localized(), for: UIControl.State.normal)
button.set(.width, to: 96)
button.addTarget(self, action: #selector(handleContinueButtonTapped), for: UIControl.Event.touchUpInside)
// Set up content stack view
let contentStackView = UIStackView(arrangedSubviews: [ labelStackView ])
if hasContinueButton {
@ -79,10 +88,12 @@ final class SeedReminderView: UIView {
let horizontalSpacing = isIPhone5OrSmaller ? Values.smallSpacing : Values.mediumSpacing
contentStackView.layoutMargins = UIEdgeInsets(top: 0, leading: horizontalSpacing + Values.accentLineThickness, bottom: 0, trailing: horizontalSpacing)
contentStackView.isLayoutMarginsRelativeArrangement = true
// Set up separator
let separator = UIView()
separator.set(.height, to: Values.separatorThickness)
separator.backgroundColor = Colors.separator
// Set up stack view
let stackView = UIStackView(arrangedSubviews: [ progressIndicatorView, contentStackView, separator ])
stackView.axis = .vertical

View File

@ -1,6 +1,7 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import UIKit
import SessionUIKit
import SessionUtilitiesKit
final class SeedVC: BaseVC {
@ -16,70 +17,81 @@ final class SeedVC: BaseVC {
private lazy var redactedMnemonic: String = {
if isIPhone5OrSmaller {
return "▆▆▆▆ ▆▆▆▆▆▆ ▆▆▆ ▆▆▆▆▆▆▆ ▆▆ ▆▆▆▆ ▆▆▆ ▆▆▆▆▆ ▆▆▆ ▆ ▆▆▆▆ ▆▆ ▆▆▆▆▆▆▆ ▆▆▆▆▆"
} else {
return "▆▆▆▆ ▆▆▆▆▆▆ ▆▆▆ ▆▆▆▆▆▆▆ ▆▆ ▆▆▆▆ ▆▆▆ ▆▆▆▆▆ ▆▆▆ ▆ ▆▆▆▆ ▆▆ ▆▆▆▆▆▆▆ ▆▆▆▆▆ ▆▆▆▆▆▆▆▆ ▆▆ ▆▆▆ ▆▆▆▆▆▆▆"
}
return "▆▆▆▆ ▆▆▆▆▆▆ ▆▆▆ ▆▆▆▆▆▆▆ ▆▆ ▆▆▆▆ ▆▆▆ ▆▆▆▆▆ ▆▆▆ ▆ ▆▆▆▆ ▆▆ ▆▆▆▆▆▆▆ ▆▆▆▆▆ ▆▆▆▆▆▆▆▆ ▆▆ ▆▆▆ ▆▆▆▆▆▆▆"
}()
// MARK: Components
// MARK: - Components
private lazy var seedReminderView: SeedReminderView = {
let result = SeedReminderView(hasContinueButton: false)
let title = "You're almost finished! 90%"
let attributedTitle = NSMutableAttributedString(string: title)
attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "90%"))
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)
return result
}()
private lazy var mnemonicLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.accent
result.font = Fonts.spaceMono(ofSize: Values.mediumFontSize)
result.numberOfLines = 0
result.textColor = Colors.accent
result.textAlignment = .center
result.lineBreakMode = .byWordWrapping
result.numberOfLines = 0
return result
}()
private lazy var copyButton: Button = {
let result = Button(style: .prominentOutline, size: .large)
result.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
private lazy var copyButton: OutlineButton = {
let result = OutlineButton(style: .regular, size: .large)
result.setTitle("copy".localized(), for: UIControl.State.normal)
result.addTarget(self, action: #selector(copyMnemonic), for: UIControl.Event.touchUpInside)
return result
}()
// MARK: Lifecycle
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("vc_seed_title", comment: ""))
setNavBarTitle("vc_seed_title".localized())
// Set up navigation bar buttons
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
closeButton.tintColor = Colors.text
navigationItem.leftBarButtonItem = closeButton
// Set up title label
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("vc_seed_title_2", comment: "")
titleLabel.numberOfLines = 0
titleLabel.text = "vc_seed_title_2".localized()
titleLabel.textColor = Colors.text
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.numberOfLines = 0
// Set up explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("vc_seed_explanation", comment: "")
explanationLabel.numberOfLines = 0
explanationLabel.text = "vc_seed_explanation".localized()
explanationLabel.textColor = Colors.text
explanationLabel.lineBreakMode = .byWordWrapping
explanationLabel.numberOfLines = 0
// Set up mnemonic label
mnemonicLabel.text = redactedMnemonic
let mnemonicLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonic))
mnemonicLabel.addGestureRecognizer(mnemonicLabelGestureRecognizer)
mnemonicLabel.isUserInteractionEnabled = true
mnemonicLabel.isEnabled = true
// Set up mnemonic label container
let mnemonicLabelContainer = UIView()
mnemonicLabelContainer.addSubview(mnemonicLabel)
@ -87,19 +99,23 @@ final class SeedVC: BaseVC {
mnemonicLabelContainer.layer.cornerRadius = TextField.cornerRadius
mnemonicLabelContainer.layer.borderWidth = 1
mnemonicLabelContainer.layer.borderColor = Colors.text.cgColor
// Set up call to action label
let callToActionLabel = UILabel()
callToActionLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
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
let callToActionLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonic))
callToActionLabel.addGestureRecognizer(callToActionLabelGestureRecognizer)
callToActionLabel.isUserInteractionEnabled = true
callToActionLabel.isEnabled = true
// Set up spacers
let topSpacer = UIView.vStretchingSpacer()
let bottomSpacer = UIView.vStretchingSpacer()
// Set up copy button container
let copyButtonContainer = UIView()
copyButtonContainer.addSubview(copyButton)
@ -107,6 +123,7 @@ final class SeedVC: BaseVC {
copyButton.pin(.top, to: .top, of: copyButtonContainer)
copyButtonContainer.pin(.trailing, to: .trailing, of: copyButton, withInset: Values.massiveSpacing)
copyButtonContainer.pin(.bottom, to: .bottom, of: copyButton)
// Set up top stack view
let topStackView = UIStackView(arrangedSubviews: [ titleLabel, UIView.spacer(withHeight: isIPhone6OrSmaller ? Values.smallSpacing : Values.largeSpacing), explanationLabel,
UIView.spacer(withHeight: isIPhone6OrSmaller ? Values.smallSpacing + 2 : Values.largeSpacing), mnemonicLabelContainer ])
@ -116,6 +133,7 @@ final class SeedVC: BaseVC {
}
topStackView.axis = .vertical
topStackView.alignment = .fill
// Set up top stack view container
let topStackViewContainer = UIView()
topStackViewContainer.addSubview(topStackView)
@ -123,11 +141,13 @@ final class SeedVC: BaseVC {
topStackView.pin(.top, to: .top, of: topStackViewContainer)
topStackViewContainer.pin(.trailing, to: .trailing, of: topStackView, withInset: Values.veryLargeSpacing)
topStackViewContainer.pin(.bottom, to: .bottom, of: topStackView)
// Set up seed reminder view
view.addSubview(seedReminderView)
seedReminderView.pin(.leading, to: .leading, of: view)
seedReminderView.pin(.top, to: .top, of: view)
seedReminderView.pin(.trailing, to: .trailing, of: view)
// Set up main stack view
let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, copyButtonContainer ])
mainStackView.axis = .vertical
@ -135,6 +155,7 @@ final class SeedVC: BaseVC {
mainStackView.layoutMargins = UIEdgeInsets(top: 0, leading: 0, bottom: Values.mediumSpacing, trailing: 0)
mainStackView.isLayoutMarginsRelativeArrangement = true
view.addSubview(mainStackView)
mainStackView.pin(.leading, to: .leading, of: view)
mainStackView.pin(.top, to: .bottom, of: seedReminderView)
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
}
// MARK: General
// MARK: - General
@objc private func enableCopyButton() {
copyButton.isUserInteractionEnabled = true
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)
}
// MARK: Interaction
// MARK: - Interaction
@objc private func close() {
dismiss(animated: true, completion: nil)
}
@ -160,14 +183,16 @@ final class SeedVC: BaseVC {
self.mnemonicLabel.text = self.mnemonic
self.mnemonicLabel.textColor = Colors.text
}, completion: nil)
UIView.transition(with: seedReminderView.titleLabel, duration: 0.25, options: .transitionCrossDissolve, animations: {
let title = "Account Secured! 100%"
let attributedTitle = NSMutableAttributedString(string: title)
attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "100%"))
self.seedReminderView.title = attributedTitle
}, completion: nil)
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)
seedReminderView.setProgress(1, animated: true)
@ -176,11 +201,15 @@ final class SeedVC: BaseVC {
@objc private func copyMnemonic() {
revealMnemonic()
UIPasteboard.general.string = mnemonic
copyButton.isUserInteractionEnabled = false
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)
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 = {
let result: UILabel = UILabel()
result.textColor = Colors.text
result.setContentHuggingPriority(.required, for: .vertical)
result.font = .boldSystemFont(ofSize: Values.largeFontSize)
result.text = "vc_join_open_group_suggestions_title".localized()
result.numberOfLines = 0
result.textColor = Colors.text
result.lineBreakMode = .byWordWrapping
result.setContentHuggingPriority(.required, for: .vertical)
result.numberOfLines = 0
return result
}()
@ -244,9 +244,9 @@ private final class EnterURLVC: UIViewController, UIGestureRecognizerDelegate, O
view.backgroundColor = .clear
// Next button
let nextButton = Button(style: .prominentOutline, size: .large)
nextButton.setTitle(NSLocalizedString("next", comment: ""), for: UIControl.State.normal)
nextButton.addTarget(self, action: #selector(joinOpenGroup), for: UIControl.Event.touchUpInside)
let nextButton = OutlineButton(style: .regular, size: .large)
nextButton.setTitle("next".localized(), for: .normal)
nextButton.addTarget(self, action: #selector(joinOpenGroup), for: .touchUpInside)
let nextButtonContainer = UIView(
wrapping: nextButton,

View File

@ -1,10 +1,17 @@
import NVActivityIndicatorView
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import UIKit
import NVActivityIndicatorView
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 = {
let result = UIStackView()
result.axis = .vertical
@ -18,21 +25,18 @@ final class PathVC : BaseVC {
return result
}()
private lazy var learnMoreButton: Button = {
let result = Button(style: .prominentOutline, size: .large)
result.setTitle(NSLocalizedString("vc_path_learn_more_button_title", comment: ""), for: UIControl.State.normal)
private lazy var learnMoreButton: OutlineButton = {
let result = OutlineButton(style: .regular, size: .large)
result.setTitle("vc_path_learn_more_button_title".localized(), for: UIControl.State.normal)
result.addTarget(self, action: #selector(learnMore), for: UIControl.Event.touchUpInside)
return result
}()
// MARK: Settings
static let dotSize = CGFloat(8)
static let expandedDotSize = CGFloat(16)
static let rowHeight = isIPhone5OrSmaller ? CGFloat(52) : CGFloat(75)
// MARK: Lifecycle
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setUpGradientBackground()
setUpNavBar()
setUpViewHierarchy()
@ -41,15 +45,15 @@ final class PathVC : BaseVC {
private func setUpNavBar() {
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("vc_path_title", comment: ""))
setNavBarTitle("vc_path_title".localized())
}
private func setUpViewHierarchy() {
// Set up explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
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.textAlignment = .center
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()
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) {

View File

@ -2,6 +2,7 @@
import UIKit
import Curve25519Kit
import SessionUIKit
import SessionUtilitiesKit
final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, OWSQRScannerDelegate {
@ -10,14 +11,15 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
private var targetVCIndex: Int?
private var tabBarTopConstraint: NSLayoutConstraint!
// MARK: Components
// MARK: - Components
private lazy var tabBar: TabBar = {
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 }
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 }
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 = {
let result = ViewMyQRCodeVC()
result.qrCodeVC = self
return result
}()
private lazy var scanQRCodePlaceholderVC: ScanQRCodePlaceholderVC = {
let result = ScanQRCodePlaceholderVC()
result.qrCodeVC = self
return result
}()
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)
result.delegate = self
return result
}()
// MARK: Lifecycle
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("vc_qr_code_title", comment: ""))
let navigationBar = navigationController!.navigationBar
setNavBarTitle("vc_qr_code_title".localized())
// Set up page VC
let hasCameraAccess = (AVCaptureDevice.authorizationStatus(for: .video) == .authorized)
pages = [ viewMyQRCodeVC, (hasCameraAccess ? scanQRCodeWrapperVC : scanQRCodePlaceholderVC) ]
pageVC.dataSource = self
pageVC.delegate = self
pageVC.setViewControllers([ viewMyQRCodeVC ], direction: .forward, animated: false, completion: nil)
// Set up tab bar
view.addSubview(tabBar)
tabBar.pin(.leading, to: .leading, of: view)
tabBarTopConstraint = tabBar.autoPinEdge(toSuperviewSafeArea: .top)
view.pin(.trailing, to: .trailing, of: tabBar)
// Set up page VC constraints
let pageVCView = pageVC.view!
view.addSubview(pageVCView)
pageVCView.pin(.leading, to: .leading, of: view)
pageVCView.pin(.top, to: .bottom, of: tabBar)
view.pin(.trailing, to: .trailing, of: pageVCView)
view.pin(.bottom, to: .bottom, of: pageVCView)
let screen = UIScreen.main.bounds
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)
viewMyQRCodeVC.constrainHeight(to: height)
scanQRCodePlaceholderVC.constrainHeight(to: height)
@ -82,7 +94,8 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
tabBarTopConstraint.constant = navigationController!.navigationBar.height()
}
// MARK: General
// MARK: - General
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let index = pages.firstIndex(of: viewController), index != 0 else { return nil }
return pages[index - 1]
@ -98,7 +111,8 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
pageVC.setViewControllers([ scanQRCodeWrapperVC ], direction: .forward, animated: false, completion: nil)
}
// MARK: Updating
// MARK: - Updating
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
guard let targetVC = pendingViewControllers.first, let index = pages.firstIndex(of: targetVC) else { return }
targetVCIndex = index
@ -109,7 +123,8 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
tabBar.selectTab(at: index)
}
// MARK: Interaction
// MARK: - Interaction
@objc private func close() {
dismiss(animated: true, completion: nil)
}
@ -124,7 +139,7 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
let alert = UIAlertController(
title: "invalid_session_id".localized(),
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)
}
else {
@ -145,7 +160,8 @@ private final class ViewMyQRCodeVC : UIViewController {
weak var qrCodeVC: QRCodeVC!
private var bottomConstraint: NSLayoutConstraint!
// MARK: Lifecycle
// MARK: - Lifecycle
override func viewDidLoad() {
// Remove background color
view.backgroundColor = .clear
@ -181,10 +197,12 @@ private final class ViewMyQRCodeVC : UIViewController {
explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping
// Set up share button
let shareButton = Button(style: .regular, size: .large)
shareButton.setTitle(NSLocalizedString("share", comment: ""), for: UIControl.State.normal)
shareButton.addTarget(self, action: #selector(shareQRCode), for: UIControl.Event.touchUpInside)
let shareButton = OutlineButton(style: .regular, size: .large)
shareButton.setTitle("share".localized(), for: .normal)
shareButton.addTarget(self, action: #selector(shareQRCode), for: .touchUpInside)
// Set up share button container
let shareButtonContainer = UIView()
shareButtonContainer.addSubview(shareButton)
@ -193,6 +211,7 @@ private final class ViewMyQRCodeVC : UIViewController {
shareButtonContainer.pin(.trailing, to: .trailing, of: shareButton, withInset: 80)
shareButtonContainer.pin(.bottom, to: .bottom, of: shareButton, withInset: isIPhone6OrSmaller ? Values.largeSpacing : Values.onboardingButtonBottomOffset)
let spacing = isIPhone5OrSmaller ? Values.mediumSpacing : Values.largeSpacing
// Set up stack view
let stackView = UIStackView(arrangedSubviews: [ titleLabel, UIView.spacer(withHeight: spacing), qrCodeImageViewContainer, UIView.spacer(withHeight: spacing),
explanationLabel, UIView.vStretchingSpacer(), shareButtonContainer ])

View File

@ -33,8 +33,8 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
private lazy var displayNameLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
result.themeTextColor = .textPrimary
result.lineBreakMode = .byTruncatingTail
result.textAlignment = .center
@ -42,7 +42,10 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
}()
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.accessibilityLabel = "Edit display name text field"
@ -51,8 +54,8 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
private lazy var publicKeyLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.font = Fonts.spaceMono(ofSize: isIPhone5OrSmaller ? Values.mediumFontSize : Values.largeFontSize)
result.themeTextColor = .textPrimary
result.numberOfLines = 0
result.textAlignment = .center
result.lineBreakMode = .byCharWrapping
@ -61,14 +64,22 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
return result
}()
private lazy var copyButton: Button = {
let result = Button(style: .prominentOutline, size: .medium)
result.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
private lazy var copyButton: OutlineButton = {
let result = OutlineButton(style: .regular, size: .medium)
result.setTitle("copy".localized(), for: UIControl.State.normal)
result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside)
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 = {
let result = UIStackView()
result.axis = .vertical
@ -77,74 +88,32 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
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 = {
let result = UIImageView()
result.set(.height, to: 24)
let result = UIImageView(
image: UIImage(named: "OxenLightMode")?
.withRenderingMode(.alwaysTemplate)
)
result.themeTintColor = .textPrimary
result.contentMode = .scaleAspectFit
result.set(.height, to: 24)
return result
}()
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()
result.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
result.font = .systemFont(ofSize: Values.verySmallFontSize)
result.text = "Version \(version) (\(buildNumber))"
result.themeTextColor = .textPrimary
result.numberOfLines = 0
result.textAlignment = .center
result.lineBreakMode = .byCharWrapping
let version = Bundle.main.infoDictionary!["CFBundleShortVersionString"]!
let buildNumber = Bundle.main.infoDictionary!["CFBundleVersion"]!
result.text = "Version \(version) (\(buildNumber))"
result.alpha = Values.mediumOpacity
return result
}()
@ -160,7 +129,7 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
setUpGradientBackground()
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("vc_settings_title", comment: ""))
setNavBarTitle("vc_settings_title".localized())
// Navigation bar buttons
updateNavigationBarButtons()
@ -198,12 +167,7 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
headerStackView.alignment = .center
// Separator
let separator = Separator(title: NSLocalizedString("your_session_id", comment: ""))
// 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)
let separator = Separator(title: "your_session_id".localized())
// Button container
let buttonContainer = UIStackView(arrangedSubviews: [ copyButton, shareButton ])
@ -232,7 +196,6 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
}
// Oxen logo
updateLogo()
let logoContainer = UIView()
logoContainer.addSubview(logoImageView)
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
// 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.spacing = Values.largeSpacing
stackView.alignment = .fill
@ -258,47 +221,25 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
}
private func getSettingButtons() -> [UIView] {
func getSeparator() -> UIView {
let result = UIView()
result.backgroundColor = Colors.separator
result.set(.height, to: Values.separatorThickness)
func getSettingButton(
title: String,
color: ThemeValue = .textPrimary,
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
}
func getSettingButton(withTitle title: String, color: UIColor, action selector: Selector) -> UIButton {
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 pathButton = getSettingButton(title: "vc_path_title".localized(), action: #selector(showPath))
let pathStatusView = PathStatusView()
pathStatusView.set(.width, to: PathStatusView.size)
pathStatusView.set(.height, to: PathStatusView.size)
@ -308,21 +249,27 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
pathStatusView.autoVCenterInSuperview()
return [
getSeparator(),
UIView.separator(),
pathButton,
getSeparator(),
getSettingButton(withTitle: NSLocalizedString("vc_settings_privacy_button_title", comment: ""), color: Colors.text, action: #selector(showPrivacySettings)),
getSeparator(),
getSettingButton(withTitle: NSLocalizedString("vc_settings_notifications_button_title", comment: ""), color: Colors.text, action: #selector(showNotificationSettings)),
getSeparator(),
getSettingButton(withTitle: NSLocalizedString("MESSAGE_REQUESTS_TITLE", comment: ""), color: Colors.text, action: #selector(showMessageRequests)),
getSeparator(),
getSettingButton(withTitle: NSLocalizedString("CHATS_TITLE", comment: ""), color: Colors.text, action: #selector(showChatSettings)),
getSeparator(),
getSettingButton(withTitle: NSLocalizedString("vc_settings_recovery_phrase_button_title", comment: ""), color: Colors.text, action: #selector(showSeed)),
getSeparator(),
getSettingButton(withTitle: NSLocalizedString("vc_settings_clear_all_data_button_title", comment: ""), color: Colors.destructive, action: #selector(clearAllData)),
getSeparator()
UIView.separator(),
getSettingButton(title: "vc_settings_privacy_button_title".localized(), action: #selector(showPrivacySettings)),
UIView.separator(),
getSettingButton(title: "vc_settings_notifications_button_title".localized(), action: #selector(showNotificationSettings)),
UIView.separator(),
getSettingButton(title: "CONVERSATIONS_TITLE".localized(), action: #selector(showChatSettings)),
UIView.separator(),
getSettingButton(title: "MESSAGE_REQUESTS_TITLE".localized(), action: #selector(showMessageRequests)),
UIView.separator(),
getSettingButton(title: "APPEARANCE_TITLE".localized(), action: #selector(showAppearanceSettings)),
UIView.separator(),
getSettingButton(title: "vc_settings_invite_a_friend_button_title".localized(), action: #selector(sendInvitation)),
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() {
if isEditingDisplayName {
let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(handleCancelDisplayNameEditingButtonTapped))
cancelButton.tintColor = Colors.text
cancelButton.themeTintColor = .textPrimary
cancelButton.accessibilityLabel = "Cancel button"
cancelButton.isAccessibilityElement = true
navigationItem.leftBarButtonItem = cancelButton
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleSaveDisplayNameButtonTapped))
doneButton.tintColor = Colors.text
doneButton.themeTintColor = .textPrimary
doneButton.accessibilityLabel = "Done button"
doneButton.isAccessibilityElement = true
navigationItem.rightBarButtonItem = doneButton
}
else {
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.isAccessibilityElement = true
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()
qrCodeButton.setImage(qrCodeIcon, for: UIControl.State.normal)
qrCodeButton.tintColor = Colors.text
qrCodeButton.setImage(
UIImage(named: "QRCode")?
.withRenderingMode(.alwaysTemplate),
for: .normal
)
qrCodeButton.themeTintColor = .textPrimary
qrCodeButton.addTarget(self, action: #selector(showQRCode), for: UIControl.Event.touchUpInside)
qrCodeButton.accessibilityLabel = "Show QR code button"
let stackView = UIStackView(arrangedSubviews: [ appModeButton, qrCodeButton ])
let stackView = UIStackView(arrangedSubviews: [ qrCodeButton ])
stackView.axis = .horizontal
stackView.spacing = Values.mediumSpacing
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: stackView)
@ -601,17 +537,17 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
@objc private func showPath() {
let pathVC = PathVC()
navigationController!.pushViewController(pathVC, animated: true)
self.navigationController?.pushViewController(pathVC, animated: true)
}
@objc private func showPrivacySettings() {
let privacySettingsVC = PrivacySettingsTableViewController()
navigationController!.pushViewController(privacySettingsVC, animated: true)
self.navigationController?.pushViewController(privacySettingsVC, animated: true)
}
@objc private func showNotificationSettings() {
let notificationSettingsVC = NotificationSettingsViewController()
navigationController!.pushViewController(notificationSettingsVC, animated: true)
self.navigationController?.pushViewController(notificationSettingsVC, animated: true)
}
@objc private func showMessageRequests() {
@ -621,7 +557,12 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
@objc private func showChatSettings() {
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() {
@ -631,6 +572,10 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
present(seedModal, animated: true, completion: nil)
}
@objc private func showHelp() {
}
@objc private func clearAllData() {
let nukeDataModal = NukeDataModal()
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.
class BaseVC : UIViewController {
private var hasGradient = false
import UIKit
import SessionUIKit
override var preferredStatusBarStyle: UIStatusBarStyle { return isLightMode ? .default : .lightContent }
class BaseVC: UIViewController {
override var preferredStatusBarStyle: UIStatusBarStyle { return ThemeManager.currentTheme.statusBarStyle }
lazy var navBarTitleLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
result.alpha = 1
result.themeTextColor = .textPrimary
result.textAlignment = .center
result.alpha = 1
return result
}()
lazy var crossfadeLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
result.alpha = 0
result.themeTextColor = .textPrimary
result.textAlignment = .center
result.alpha = 0
return result
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.backButtonTitle = ""
view.themeBackgroundColor = .backgroundPrimary
setNeedsStatusBarAppearanceUpdate()
NotificationCenter.default.addObserver(self, selector: #selector(handleAppModeChangedNotification(_:)), name: .appModeChanged, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive(_:)), name: .OWSApplicationDidBecomeActive, object: nil)
@ -69,24 +78,31 @@ class BaseVC : UIViewController {
let container = UIView()
navBarTitleLabel.text = title
crossfadeLabel.text = title
if let customFontSize = customFontSize {
navBarTitleLabel.font = .boldSystemFont(ofSize: customFontSize)
crossfadeLabel.font = .boldSystemFont(ofSize: customFontSize)
}
container.addSubview(navBarTitleLabel)
navBarTitleLabel.pin(to: container)
container.addSubview(crossfadeLabel)
navBarTitleLabel.pin(to: container)
crossfadeLabel.pin(to: container)
navigationItem.titleView = container
}
internal func setUpNavBarSessionHeading() {
let headingImageView = UIImageView()
headingImageView.tintColor = Colors.text
headingImageView.image = UIImage(named: "SessionHeading")?.withRenderingMode(.alwaysTemplate)
let headingImageView = UIImageView(
image: UIImage(named: "SessionHeading")?
.withRenderingMode(.alwaysTemplate)
)
headingImageView.themeTintColor = .textPrimary
headingImageView.contentMode = .scaleAspectFit
headingImageView.set(.width, to: 150)
headingImageView.set(.height, to: Values.mediumFontSize)
navigationItem.titleView = headingImageView
}
@ -96,6 +112,7 @@ class BaseVC : UIViewController {
logoImageView.contentMode = .scaleAspectFit
logoImageView.set(.width, to: 32)
logoImageView.set(.height, to: 32)
navigationItem.titleView = logoImageView
}

View File

@ -54,35 +54,10 @@ NS_ASSUME_NONNULL_BEGIN
[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
{
OWSAssertIsOnMainThread();
OWSAssertDebug(self.delegate);
[SNAppearance switchToImagePickerAppearance];
[self.delegate.fromViewController ows_askForMediaLibraryPermissions:^(BOOL granted) {
if (!granted) {
@ -107,8 +82,6 @@ NS_ASSUME_NONNULL_BEGIN
{
OWSAssertIsOnMainThread();
OWSAssertDebug(self.delegate);
[SNAppearance switchToSessionAppearance];
[self.delegate.fromViewController dismissViewControllerAnimated:YES completion:nil];
}
@ -120,9 +93,6 @@ NS_ASSUME_NONNULL_BEGIN
{
OWSAssertIsOnMainThread();
OWSAssertDebug(self.delegate);
[SNAppearance switchToSessionAppearance];
NSURL* imageURL = [info objectForKey:UIImagePickerControllerImageURL];
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
// 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.threadVariant = .contact
self.threadIsTrusted = false
@ -470,11 +477,11 @@ public extension MessageViewModel {
)
self.rowId = targetId
self.id = targetId
self.variant = .standardOutgoing
self.variant = variant
self.timestampMs = Int64.max
self.authorId = ""
self.authorNameInternal = nil
self.body = nil
self.body = body
self.rawBody = nil
self.expiresStartedAtMs = nil
self.expiresInSeconds = nil
@ -485,7 +492,7 @@ public extension MessageViewModel {
self.isSenderOpenGroupModerator = false
self.isTypingIndicator = isTypingIndicator
self.profile = nil
self.quote = nil
self.quote = quote
self.quoteAttachment = nil
self.linkPreview = nil
self.linkPreviewAttachment = nil
@ -494,7 +501,7 @@ public extension MessageViewModel {
// Post-Query Processing Data
self.attachments = nil
self.cellType = .typingIndicator
self.cellType = cellType
self.authorName = ""
self.senderName = nil
self.shouldShowProfile = false
@ -504,7 +511,7 @@ public extension MessageViewModel {
self.previousVariant = nil
self.positionInCluster = .middle
self.isOnlyMessageInCluster = true
self.isLast = true
self.isLast = isLast
self.currentUserBlindedPublicKey = nil
}
}

View File

@ -77,7 +77,7 @@ public extension Setting.DoubleKey {
}
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
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 defaultNotificationSound: Sound = .note

View File

@ -1,3 +1,6 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import UIKit
import CoreServices
import PromiseKit
import SignalUtilitiesKit

View File

@ -3,6 +3,7 @@
import UIKit
import SessionUIKit
import SessionMessagingKit
import SignalUtilitiesKit
final class SimplifiedConversationCell: UITableViewCell {
// MARK: - Initialization
@ -32,7 +33,7 @@ final class SimplifiedConversationCell: UITableViewCell {
private lazy var accentLineView: UIView = {
let result = UIView()
result.translatesAutoresizingMaskIntoConstraints = false
result.backgroundColor = Colors.destructive
result.themeBackgroundColor = .danger
return result
}()
@ -47,7 +48,7 @@ final class SimplifiedConversationCell: UITableViewCell {
private lazy var displayNameLabel: UILabel = {
let result = UILabel()
result.font = .boldSystemFont(ofSize: Values.mediumFontSize)
result.textColor = Colors.text
result.themeTextColor = .textPrimary
result.lineBreakMode = .byTruncatingTail
return result
@ -56,10 +57,10 @@ final class SimplifiedConversationCell: UITableViewCell {
// MARK: - Initialization
private func setUpViewHierarchy() {
backgroundColor = Colors.cellBackground
themeBackgroundColor = .conversationButton_background
let selectedBackgroundView = UIView()
selectedBackgroundView.backgroundColor = Colors.cellSelected
selectedBackgroundView.themeBackgroundColor = .conversationButton_highlight
self.selectedBackgroundView = selectedBackgroundView
addSubview(stackView)

View File

@ -26,16 +26,16 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView
private lazy var titleLabel: UILabel = {
let titleLabel: UILabel = UILabel()
titleLabel.text = "vc_share_title".localized()
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
titleLabel.text = "vc_share_title".localized()
titleLabel.themeTextColor = .textPrimary
return titleLabel
}()
private lazy var tableView: UITableView = {
let tableView: UITableView = UITableView()
tableView.backgroundColor = .clear
tableView.themeBackgroundColor = .backgroundPrimary
tableView.separatorStyle = .none
tableView.register(view: SimplifiedConversationCell.self)
tableView.showsVerticalScrollIndicator = false
@ -62,8 +62,7 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView
setupNavBar()
// Gradient
view.backgroundColor = .clear
view.setGradient(Gradients.defaultBackground)
view.themeBackgroundColor = .backgroundPrimary
// Title
navigationItem.titleView = titleLabel
@ -130,13 +129,7 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView
// MARK: Layout
private func setupLayout() {
let topInset = 0.15 * view.height()
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

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
public final class Separator : UIView {
private let title: String
public final class Separator: UIView {
private static let height: CGFloat = 24
// MARK: - Components
// MARK: Components
private lazy var titleLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
result.font = .systemFont(ofSize: Values.smallFontSize)
result.themeTextColor = .textPrimary
result.textAlignment = .center
result.alpha = Values.mediumOpacity
return result
}()
private lazy var lineLayer: CAShapeLayer = {
let result = CAShapeLayer()
result.lineWidth = Values.separatorThickness
result.strokeColor = Colors.separator.cgColor
result.fillColor = UIColor.clear.cgColor
result.themeStrokeColor = .borderSeparator
result.themeFillColor = .clear
return result
}()
// MARK: Settings
private static let height: CGFloat = 24
// MARK: - Initialization
// MARK: Initialization
public init(title: String) {
self.title = title
super.init(frame: CGRect.zero)
setUpViewHierarchy()
setUpViewHierarchy(title: title)
}
public override init(frame: CGRect) {
@ -38,18 +42,22 @@ public final class Separator : UIView {
preconditionFailure("Use init(title:) instead.")
}
private func setUpViewHierarchy() {
private func setUpViewHierarchy(title: String) {
titleLabel.text = title
addSubview(titleLabel)
titleLabel.center(.horizontal, in: self)
titleLabel.center(.vertical, in: self)
layer.insertSublayer(lineLayer, at: 0)
set(.height, to: Separator.height)
}
// MARK: Updating
// MARK: - Updating
public override func layoutSubviews() {
super.layoutSubviews()
updateLineLayer()
}
@ -58,13 +66,16 @@ public final class Separator : UIView {
let h = bounds.height
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: h / 2))
let titleLabelFrame = titleLabel.frame.insetBy(dx: -10, dy: -6)
path.addLine(to: CGPoint(x: titleLabelFrame.origin.x, y: h / 2))
let oval = UIBezierPath(roundedRect: titleLabelFrame, cornerRadius: Separator.height / 2)
path.append(oval)
path.move(to: CGPoint(x: titleLabelFrame.origin.x + titleLabelFrame.width, y: h / 2))
path.addLine(to: CGPoint(x: w, y: h / 2))
path.close()
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
@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 static func migrations() -> TargetMigrations {
return TargetMigrations(
@ -25,12 +14,19 @@ public enum SNUtilitiesKit { // Just to make the external API nice
_001_InitialSetupMigration.self,
_002_SetupStandardJobs.self,
_003_YDBToGRDBMigration.self
]
],
[], // Other DB migrations
[], // Legacy DB removal
[]
]
)
}
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
@ -159,7 +160,8 @@ public extension Storage {
subscript(key: Setting.StringKey) -> String? { 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 {
@ -198,7 +200,7 @@ public extension Database {
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 {
guard let rawValue: Int = self[key.rawValue]?.value(as: Int.self) else {
return nil
@ -209,6 +211,17 @@ public extension Database {
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
subscript(key: Setting.DateKey) -> Date? {
get {

View File

@ -19,6 +19,7 @@ public struct TargetMigrations: Comparable {
case utilitiesKit
case snodeKit
case messagingKit
case uiKit
public static func < (lhs: Self, rhs: Self) -> Bool {
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
*/
@objc
public static var kMaxFileSizeAnimatedImage: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
public static var kMaxFileSizeAnimatedImage: UInt { SNUtilitiesKitConfiguration.maxFileSize }
@objc
public static var kMaxFileSizeImage: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
public static var kMaxFileSizeImage: UInt { SNUtilitiesKitConfiguration.maxFileSize }
@objc
public static var kMaxFileSizeVideo: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
public static var kMaxFileSizeVideo: UInt { SNUtilitiesKitConfiguration.maxFileSize }
@objc
public static var kMaxFileSizeAudio: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
public static var kMaxFileSizeAudio: UInt { SNUtilitiesKitConfiguration.maxFileSize }
@objc
public static var kMaxFileSizeGeneric: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
public static var kMaxFileSizeGeneric: UInt { SNUtilitiesKitConfiguration.maxFileSize }
@objc
public static let kMaxVideoDimensions: CGFloat = 3 * 1024

View File

@ -1,6 +1,7 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import SessionUIKit
import SessionSnodeKit
import SessionMessagingKit
@ -13,5 +14,6 @@ public enum Configuration {
SNMessagingKit.configure()
SNSnodeKit.configure()
SUIKit.configure()
}
}

View File

@ -108,7 +108,7 @@ NS_ASSUME_NONNULL_BEGIN
// NOTE: result might end up NO if the super implementation cancels the
// the pop/back.
[super navigationBar:navigationBar shouldPopItem:item];
result = YES;
result = YES;
}
return result;
}
@ -126,14 +126,14 @@ NS_ASSUME_NONNULL_BEGIN
if ([topViewController conformsToProtocol:@protocol(OWSNavigationView)]) {
id<OWSNavigationView> navigationView = (id<OWSNavigationView>)topViewController;
return ![navigationView shouldCancelNavigationBack];
} else {
UIViewController *rootViewController = self.viewControllers.firstObject;
if (topViewController == rootViewController) {
return NO;
} else {
return YES;
}
}
UIViewController *rootViewController = self.viewControllers.firstObject;
if (topViewController == rootViewController) {
return NO;
}
return YES;
}
#pragma mark - NavBarLayoutDelegate
@ -146,14 +146,18 @@ NS_ASSUME_NONNULL_BEGIN
- (UIStatusBarStyle)preferredStatusBarStyle
{
if (!CurrentAppContext().isMainApp) {
return super.preferredStatusBarStyle;
} else if (OWSWindowManager.sharedManager.hasCall) {
if (OWSWindowManager.sharedManager.hasCall) {
// Status bar is overlaying the green "call banner"
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
@ -164,9 +168,11 @@ NS_ASSUME_NONNULL_BEGIN
if (!CurrentAppContext().isMainApp) {
self.additionalSafeAreaInsets = UIEdgeInsetsZero;
} else if (OWSWindowManager.sharedManager.hasCall) {
}
else if (OWSWindowManager.sharedManager.hasCall) {
self.additionalSafeAreaInsets = UIEdgeInsetsMake(20, 0, 0, 0);
} else {
}
else {
self.additionalSafeAreaInsets = UIEdgeInsetsZero;
}

View File

@ -4,6 +4,7 @@ import Foundation
import SessionMessagingKit
import SessionUtilitiesKit
import UIKit
import SessionUIKit
public enum AppSetup {
private static var hasRun: Bool = false
@ -68,7 +69,8 @@ public enum AppSetup {
migrations: [
SNUtilitiesKit.migrations(),
SNSnodeKit.migrations(),
SNMessagingKit.migrations()
SNMessagingKit.migrations(),
SUIKit.migrations()
],
onProgressUpdate: migrationProgressChanged,
onComplete: { error, needsConfigSync in

View File

@ -24,7 +24,7 @@ public extension UIView {
static func separator() -> UIView {
let result = UIView()
result.set(.height, to: Values.separatorThickness)
result.backgroundColor = Colors.separator
result.themeBackgroundColor = .borderSeparator
return result
}
}

View File

@ -1,35 +1,25 @@
import SessionUIKit
@objc(LKViewControllerUtilities)
public final class ViewControllerUtilities : NSObject {
public final class ViewControllerUtilities: NSObject {
private override init() { }
@objc(setUpDefaultSessionStyleForVC:withTitle:customBackButton:)
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
if let title = title {
let titleLabel = UILabel()
titleLabel.text = title
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
titleLabel.text = title
titleLabel.themeTextColor = .textPrimary
vc.navigationItem.titleView = titleLabel
}
// Set up back button
if hasCustomBackButton {
let backButton = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
backButton.tintColor = Colors.text
backButton.themeTintColor = .textPrimary
vc.navigationItem.backBarButtonItem = backButton
}
}