mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Progressing on theming functionality
Created the ThemeManager and the system to control the dynamic theming Started updating the main settings screens Added the AppearanceViewController and connected it to the ThemeManager Started adding theme values Started applying theme values throughout
This commit is contained in:
parent
b40752dc78
commit
d56cee8234
91 changed files with 2729 additions and 783 deletions
6
Podfile
6
Podfile
|
@ -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)
|
||||
|
|
|
@ -242,6 +242,6 @@ SPEC CHECKSUMS:
|
|||
YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331
|
||||
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
||||
|
||||
PODFILE CHECKSUM: f0857369c4831b2e5c1946345e76e493f3286805
|
||||
PODFILE CHECKSUM: 3423ad813952f2c56952aa7118df68e7878c3c65
|
||||
|
||||
COCOAPODS: 1.11.3
|
||||
|
|
|
@ -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 */,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 ])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ])
|
||||
|
|
|
@ -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 ])
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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 ])
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
273
Session/Settings/AppearanceViewController.swift
Normal file
273
Session/Settings/AppearanceViewController.swift
Normal 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
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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 ])
|
||||
|
|
|
@ -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
|
||||
|
|
94
Session/Settings/Views/PrimaryColorSelectionView.swift
Normal file
94
Session/Settings/Views/PrimaryColorSelectionView.swift
Normal 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)
|
||||
}
|
||||
}
|
83
Session/Settings/Views/ThemePreviewView.swift
Normal file
83
Session/Settings/Views/ThemePreviewView.swift
Normal 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)
|
||||
}
|
||||
}
|
174
Session/Settings/Views/ThemeSelectionView.swift
Normal file
174
Session/Settings/Views/ThemeSelectionView.swift
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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 ]
|
||||
}
|
||||
}
|
12
Session/Utilities/TraitObservingWindow.swift
Normal file
12
Session/Utilities/TraitObservingWindow.swift
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import CoreServices
|
||||
import PromiseKit
|
||||
import SignalUtilitiesKit
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
90
SessionUIKit/Components/OutlineButton.swift
Normal file
90
SessionUIKit/Components/OutlineButton.swift
Normal 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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
25
SessionUIKit/Configuration.swift
Normal file
25
SessionUIKit/Configuration.swift
Normal 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() {
|
||||
}
|
||||
}
|
34
SessionUIKit/Database/Migrations/_001_ThemePreferences.swift
Normal file
34
SessionUIKit/Database/Migrations/_001_ThemePreferences.swift
Normal 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
|
||||
}
|
||||
}
|
480
SessionUIKit/Style Guide/ThemeManager.swift
Normal file
480
SessionUIKit/Style Guide/ThemeManager.swift
Normal 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
|
||||
}
|
||||
}
|
65
SessionUIKit/Style Guide/Themes/Theme+ClassicDark.swift
Normal file
65
SessionUIKit/Style Guide/Themes/Theme+ClassicDark.swift
Normal 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)
|
||||
]
|
||||
}
|
65
SessionUIKit/Style Guide/Themes/Theme+ClassicLight.swift
Normal file
65
SessionUIKit/Style Guide/Themes/Theme+ClassicLight.swift
Normal 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)
|
||||
]
|
||||
}
|
65
SessionUIKit/Style Guide/Themes/Theme+OceanDark.swift
Normal file
65
SessionUIKit/Style Guide/Themes/Theme+OceanDark.swift
Normal 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)
|
||||
]
|
||||
}
|
65
SessionUIKit/Style Guide/Themes/Theme+OceanLight.swift
Normal file
65
SessionUIKit/Style Guide/Themes/Theme+OceanLight.swift
Normal 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)
|
||||
]
|
||||
}
|
43
SessionUIKit/Style Guide/Themes/Theme+PrimaryColors.swift
Normal file
43
SessionUIKit/Style Guide/Themes/Theme+PrimaryColors.swift
Normal 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
|
||||
})
|
||||
}
|
115
SessionUIKit/Style Guide/Themes/Theme.swift
Normal file
115
SessionUIKit/Style Guide/Themes/Theme.swift
Normal 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
|
||||
}
|
16
SessionUIKit/Utilities/UIColor+Utilities.swift
Normal file
16
SessionUIKit/Utilities/UIColor+Utilities.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue