move prev sources

This commit is contained in:
Woit 2024-11-19 17:07:47 +01:00
parent f06208ec66
commit e210861382
31 changed files with 1322 additions and 18 deletions

View file

@ -136,6 +136,38 @@
7E71758E2CECC5C70059F30B /* server_features.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7E71758A2CECC5C70059F30B /* server_features.plist */; }; 7E71758E2CECC5C70059F30B /* server_features.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7E71758A2CECC5C70059F30B /* server_features.plist */; };
7E71758F2CECC5C70059F30B /* launchscreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7E7175892CECC5C70059F30B /* launchscreen.storyboard */; }; 7E71758F2CECC5C70059F30B /* launchscreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7E7175892CECC5C70059F30B /* launchscreen.storyboard */; };
7E8D7AE32CECD011009AD3DF /* SwiftfulRouting in Frameworks */ = {isa = PBXBuildFile; productRef = 7E8D7AE22CECD011009AD3DF /* SwiftfulRouting */; }; 7E8D7AE32CECD011009AD3DF /* SwiftfulRouting in Frameworks */ = {isa = PBXBuildFile; productRef = 7E8D7AE22CECD011009AD3DF /* SwiftfulRouting */; };
7E8D7AEB2CECE9A8009AD3DF /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7AE82CECE9A8009AD3DF /* LoginScreen.swift */; };
7E8D7AEC2CECE9A8009AD3DF /* RegistrationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7AE92CECE9A8009AD3DF /* RegistrationScreen.swift */; };
7E8D7AED2CECE9A8009AD3DF /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7AEA2CECE9A8009AD3DF /* WelcomeScreen.swift */; };
7E8D7AF12CECEB30009AD3DF /* Images+Generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7AEF2CECEB30009AD3DF /* Images+Generated.swift */; };
7E8D7AF22CECEB30009AD3DF /* Strings+Generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7AF02CECEB30009AD3DF /* Strings+Generated.swift */; };
7E8D7AF32CECEB30009AD3DF /* Colors+Generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7AEE2CECEB30009AD3DF /* Colors+Generated.swift */; };
7E8D7AFA2CECEDB3009AD3DF /* SharedListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7AF52CECEDB3009AD3DF /* SharedListRow.swift */; };
7E8D7AFB2CECEDB3009AD3DF /* SharedSectionTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7AF72CECEDB3009AD3DF /* SharedSectionTitle.swift */; };
7E8D7AFC2CECEDB3009AD3DF /* SharedNavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7AF62CECEDB3009AD3DF /* SharedNavigationBar.swift */; };
7E8D7AFD2CECEDB3009AD3DF /* UniversalInputCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7AF82CECEDB3009AD3DF /* UniversalInputCollection.swift */; };
7E8D7AFE2CECEDB3009AD3DF /* LoadingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7AF42CECEDB3009AD3DF /* LoadingScreen.swift */; };
7E8D7B162CECEE79009AD3DF /* Colors+Tappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B042CECEE79009AD3DF /* Colors+Tappable.swift */; };
7E8D7B172CECEE79009AD3DF /* Vibration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B0F2CECEE79009AD3DF /* Vibration.swift */; };
7E8D7B182CECEE79009AD3DF /* ButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B032CECEE79009AD3DF /* ButtonStyles.swift */; };
7E8D7B192CECEE79009AD3DF /* AVAsset+Thumbnail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B012CECEE79009AD3DF /* AVAsset+Thumbnail.swift */; };
7E8D7B1A2CECEE79009AD3DF /* Const.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B052CECEE79009AD3DF /* Const.swift */; };
7E8D7B1B2CECEE79009AD3DF /* Typography.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B0B2CECEE79009AD3DF /* Typography.swift */; };
7E8D7B1C2CECEE79009AD3DF /* TimeInterval+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B0A2CECEE79009AD3DF /* TimeInterval+Extensions.swift */; };
7E8D7B1D2CECEE79009AD3DF /* EdgeInsets+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B062CECEE79009AD3DF /* EdgeInsets+Extensions.swift */; };
7E8D7B1E2CECEE79009AD3DF /* Binding+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B022CECEE79009AD3DF /* Binding+Extensions.swift */; };
7E8D7B1F2CECEE79009AD3DF /* UIApplication+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B0C2CECEE79009AD3DF /* UIApplication+Extensions.swift */; };
7E8D7B202CECEE79009AD3DF /* View+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B102CECEE79009AD3DF /* View+Debug.swift */; };
7E8D7B212CECEE79009AD3DF /* Map+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B072CECEE79009AD3DF /* Map+Extensions.swift */; };
7E8D7B222CECEE79009AD3DF /* View+Flip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B112CECEE79009AD3DF /* View+Flip.swift */; };
7E8D7B232CECEE79009AD3DF /* View+TappableArea.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B142CECEE79009AD3DF /* View+TappableArea.swift */; };
7E8D7B242CECEE79009AD3DF /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B0E2CECEE79009AD3DF /* URL+Extensions.swift */; };
7E8D7B252CECEE79009AD3DF /* PHImageManager+Fetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B082CECEE79009AD3DF /* PHImageManager+Fetch.swift */; };
7E8D7B262CECEE79009AD3DF /* View+If.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B122CECEE79009AD3DF /* View+If.swift */; };
7E8D7B272CECEE79009AD3DF /* UIImage+Crop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B0D2CECEE79009AD3DF /* UIImage+Crop.swift */; };
7E8D7B282CECEE79009AD3DF /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B092CECEE79009AD3DF /* String+Extensions.swift */; };
7E8D7B292CECEE79009AD3DF /* View+OnLoad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B132CECEE79009AD3DF /* View+OnLoad.swift */; };
7E8D7B2B2CECEEDF009AD3DF /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8D7B2A2CECEEDF009AD3DF /* AppError.swift */; };
7E995F242CEAC5D2005B30EE /* AnotherIMApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E995F202CEAC5D2005B30EE /* AnotherIMApp.swift */; }; 7E995F242CEAC5D2005B30EE /* AnotherIMApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E995F202CEAC5D2005B30EE /* AnotherIMApp.swift */; };
7E995F252CEAC5D2005B30EE /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E995F222CEAC5D2005B30EE /* RootView.swift */; }; 7E995F252CEAC5D2005B30EE /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E995F222CEAC5D2005B30EE /* RootView.swift */; };
7E995F2B2CEAC9A0005B30EE /* monalxmpp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26CC579223A0867400ABB92A /* monalxmpp.framework */; }; 7E995F2B2CEAC9A0005B30EE /* monalxmpp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26CC579223A0867400ABB92A /* monalxmpp.framework */; };
@ -606,6 +638,38 @@
7E7175892CECC5C70059F30B /* launchscreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = launchscreen.storyboard; sourceTree = "<group>"; }; 7E7175892CECC5C70059F30B /* launchscreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = launchscreen.storyboard; sourceTree = "<group>"; };
7E71758A2CECC5C70059F30B /* server_features.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = server_features.plist; sourceTree = "<group>"; }; 7E71758A2CECC5C70059F30B /* server_features.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = server_features.plist; sourceTree = "<group>"; };
7E71758B2CECC5C70059F30B /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; }; 7E71758B2CECC5C70059F30B /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; };
7E8D7AE82CECE9A8009AD3DF /* LoginScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreen.swift; sourceTree = "<group>"; };
7E8D7AE92CECE9A8009AD3DF /* RegistrationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationScreen.swift; sourceTree = "<group>"; };
7E8D7AEA2CECE9A8009AD3DF /* WelcomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreen.swift; sourceTree = "<group>"; };
7E8D7AEE2CECEB30009AD3DF /* Colors+Generated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Colors+Generated.swift"; sourceTree = "<group>"; };
7E8D7AEF2CECEB30009AD3DF /* Images+Generated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Images+Generated.swift"; sourceTree = "<group>"; };
7E8D7AF02CECEB30009AD3DF /* Strings+Generated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Strings+Generated.swift"; sourceTree = "<group>"; };
7E8D7AF42CECEDB3009AD3DF /* LoadingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingScreen.swift; sourceTree = "<group>"; };
7E8D7AF52CECEDB3009AD3DF /* SharedListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedListRow.swift; sourceTree = "<group>"; };
7E8D7AF62CECEDB3009AD3DF /* SharedNavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedNavigationBar.swift; sourceTree = "<group>"; };
7E8D7AF72CECEDB3009AD3DF /* SharedSectionTitle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedSectionTitle.swift; sourceTree = "<group>"; };
7E8D7AF82CECEDB3009AD3DF /* UniversalInputCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniversalInputCollection.swift; sourceTree = "<group>"; };
7E8D7B012CECEE79009AD3DF /* AVAsset+Thumbnail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVAsset+Thumbnail.swift"; sourceTree = "<group>"; };
7E8D7B022CECEE79009AD3DF /* Binding+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Extensions.swift"; sourceTree = "<group>"; };
7E8D7B032CECEE79009AD3DF /* ButtonStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonStyles.swift; sourceTree = "<group>"; };
7E8D7B042CECEE79009AD3DF /* Colors+Tappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Colors+Tappable.swift"; sourceTree = "<group>"; };
7E8D7B052CECEE79009AD3DF /* Const.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Const.swift; sourceTree = "<group>"; };
7E8D7B062CECEE79009AD3DF /* EdgeInsets+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EdgeInsets+Extensions.swift"; sourceTree = "<group>"; };
7E8D7B072CECEE79009AD3DF /* Map+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Map+Extensions.swift"; sourceTree = "<group>"; };
7E8D7B082CECEE79009AD3DF /* PHImageManager+Fetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PHImageManager+Fetch.swift"; sourceTree = "<group>"; };
7E8D7B092CECEE79009AD3DF /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = "<group>"; };
7E8D7B0A2CECEE79009AD3DF /* TimeInterval+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TimeInterval+Extensions.swift"; sourceTree = "<group>"; };
7E8D7B0B2CECEE79009AD3DF /* Typography.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Typography.swift; sourceTree = "<group>"; };
7E8D7B0C2CECEE79009AD3DF /* UIApplication+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Extensions.swift"; sourceTree = "<group>"; };
7E8D7B0D2CECEE79009AD3DF /* UIImage+Crop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Crop.swift"; sourceTree = "<group>"; };
7E8D7B0E2CECEE79009AD3DF /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = "<group>"; };
7E8D7B0F2CECEE79009AD3DF /* Vibration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vibration.swift; sourceTree = "<group>"; };
7E8D7B102CECEE79009AD3DF /* View+Debug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Debug.swift"; sourceTree = "<group>"; };
7E8D7B112CECEE79009AD3DF /* View+Flip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Flip.swift"; sourceTree = "<group>"; };
7E8D7B122CECEE79009AD3DF /* View+If.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+If.swift"; sourceTree = "<group>"; };
7E8D7B132CECEE79009AD3DF /* View+OnLoad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+OnLoad.swift"; sourceTree = "<group>"; };
7E8D7B142CECEE79009AD3DF /* View+TappableArea.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+TappableArea.swift"; sourceTree = "<group>"; };
7E8D7B2A2CECEEDF009AD3DF /* AppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = "<group>"; };
7E995F062CEAC4B8005B30EE /* another.im.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = another.im.app; sourceTree = BUILT_PRODUCTS_DIR; }; 7E995F062CEAC4B8005B30EE /* another.im.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = another.im.app; sourceTree = BUILT_PRODUCTS_DIR; };
7E995F202CEAC5D2005B30EE /* AnotherIMApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnotherIMApp.swift; sourceTree = "<group>"; }; 7E995F202CEAC5D2005B30EE /* AnotherIMApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnotherIMApp.swift; sourceTree = "<group>"; };
7E995F222CEAC5D2005B30EE /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; }; 7E995F222CEAC5D2005B30EE /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; };
@ -1397,7 +1461,11 @@
7E8D7AE42CECD037009AD3DF /* Views */ = { 7E8D7AE42CECD037009AD3DF /* Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7E8D7AE82CECE9A8009AD3DF /* LoginScreen.swift */,
7E8D7AE92CECE9A8009AD3DF /* RegistrationScreen.swift */,
7E995F222CEAC5D2005B30EE /* RootView.swift */, 7E995F222CEAC5D2005B30EE /* RootView.swift */,
7E8D7AEA2CECE9A8009AD3DF /* WelcomeScreen.swift */,
7E8D7AF92CECEDB3009AD3DF /* SharedComponents */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1406,15 +1474,56 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7E6AF38E2CEB9110004328B5 /* MonalXmppWrapper.swift */, 7E6AF38E2CEB9110004328B5 /* MonalXmppWrapper.swift */,
7E8D7B2A2CECEEDF009AD3DF /* AppError.swift */,
); );
path = XMPP; path = XMPP;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
7E8D7AF92CECEDB3009AD3DF /* SharedComponents */ = {
isa = PBXGroup;
children = (
7E8D7AF42CECEDB3009AD3DF /* LoadingScreen.swift */,
7E8D7AF52CECEDB3009AD3DF /* SharedListRow.swift */,
7E8D7AF62CECEDB3009AD3DF /* SharedNavigationBar.swift */,
7E8D7AF72CECEDB3009AD3DF /* SharedSectionTitle.swift */,
7E8D7AF82CECEDB3009AD3DF /* UniversalInputCollection.swift */,
);
path = SharedComponents;
sourceTree = "<group>";
};
7E8D7B152CECEE79009AD3DF /* Helpers */ = {
isa = PBXGroup;
children = (
7E8D7B012CECEE79009AD3DF /* AVAsset+Thumbnail.swift */,
7E8D7B022CECEE79009AD3DF /* Binding+Extensions.swift */,
7E8D7B032CECEE79009AD3DF /* ButtonStyles.swift */,
7E8D7B042CECEE79009AD3DF /* Colors+Tappable.swift */,
7E8D7B052CECEE79009AD3DF /* Const.swift */,
7E8D7B062CECEE79009AD3DF /* EdgeInsets+Extensions.swift */,
7E8D7B072CECEE79009AD3DF /* Map+Extensions.swift */,
7E8D7B082CECEE79009AD3DF /* PHImageManager+Fetch.swift */,
7E8D7B092CECEE79009AD3DF /* String+Extensions.swift */,
7E8D7B0A2CECEE79009AD3DF /* TimeInterval+Extensions.swift */,
7E8D7B0B2CECEE79009AD3DF /* Typography.swift */,
7E8D7B0C2CECEE79009AD3DF /* UIApplication+Extensions.swift */,
7E8D7B0D2CECEE79009AD3DF /* UIImage+Crop.swift */,
7E8D7B0E2CECEE79009AD3DF /* URL+Extensions.swift */,
7E8D7B0F2CECEE79009AD3DF /* Vibration.swift */,
7E8D7B102CECEE79009AD3DF /* View+Debug.swift */,
7E8D7B112CECEE79009AD3DF /* View+Flip.swift */,
7E8D7B122CECEE79009AD3DF /* View+If.swift */,
7E8D7B132CECEE79009AD3DF /* View+OnLoad.swift */,
7E8D7B142CECEE79009AD3DF /* View+TappableArea.swift */,
);
path = Helpers;
sourceTree = "<group>";
};
7E995F232CEAC5D2005B30EE /* another.im */ = { 7E995F232CEAC5D2005B30EE /* another.im */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7E995F202CEAC5D2005B30EE /* AnotherIMApp.swift */, 7E995F202CEAC5D2005B30EE /* AnotherIMApp.swift */,
EA534340732BF66B533E4C0B /* Generated */, EA534340732BF66B533E4C0B /* Generated */,
7E8D7B152CECEE79009AD3DF /* Helpers */,
7ED6F01A2CECC43D0035B3B7 /* Resources */, 7ED6F01A2CECC43D0035B3B7 /* Resources */,
7E8D7AE42CECD037009AD3DF /* Views */, 7E8D7AE42CECD037009AD3DF /* Views */,
7E8D7AE52CECD05C009AD3DF /* XMPP */, 7E8D7AE52CECD05C009AD3DF /* XMPP */,
@ -1609,6 +1718,9 @@
EA534340732BF66B533E4C0B /* Generated */ = { EA534340732BF66B533E4C0B /* Generated */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7E8D7AEE2CECEB30009AD3DF /* Colors+Generated.swift */,
7E8D7AEF2CECEB30009AD3DF /* Images+Generated.swift */,
7E8D7AF02CECEB30009AD3DF /* Strings+Generated.swift */,
); );
path = Generated; path = Generated;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2474,6 +2586,38 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
7E995F242CEAC5D2005B30EE /* AnotherIMApp.swift in Sources */, 7E995F242CEAC5D2005B30EE /* AnotherIMApp.swift in Sources */,
7E8D7AEB2CECE9A8009AD3DF /* LoginScreen.swift in Sources */,
7E8D7AF12CECEB30009AD3DF /* Images+Generated.swift in Sources */,
7E8D7AF22CECEB30009AD3DF /* Strings+Generated.swift in Sources */,
7E8D7AF32CECEB30009AD3DF /* Colors+Generated.swift in Sources */,
7E8D7B162CECEE79009AD3DF /* Colors+Tappable.swift in Sources */,
7E8D7B172CECEE79009AD3DF /* Vibration.swift in Sources */,
7E8D7B182CECEE79009AD3DF /* ButtonStyles.swift in Sources */,
7E8D7B192CECEE79009AD3DF /* AVAsset+Thumbnail.swift in Sources */,
7E8D7B1A2CECEE79009AD3DF /* Const.swift in Sources */,
7E8D7B1B2CECEE79009AD3DF /* Typography.swift in Sources */,
7E8D7B1C2CECEE79009AD3DF /* TimeInterval+Extensions.swift in Sources */,
7E8D7B1D2CECEE79009AD3DF /* EdgeInsets+Extensions.swift in Sources */,
7E8D7B1E2CECEE79009AD3DF /* Binding+Extensions.swift in Sources */,
7E8D7B1F2CECEE79009AD3DF /* UIApplication+Extensions.swift in Sources */,
7E8D7B202CECEE79009AD3DF /* View+Debug.swift in Sources */,
7E8D7B212CECEE79009AD3DF /* Map+Extensions.swift in Sources */,
7E8D7B222CECEE79009AD3DF /* View+Flip.swift in Sources */,
7E8D7B232CECEE79009AD3DF /* View+TappableArea.swift in Sources */,
7E8D7B242CECEE79009AD3DF /* URL+Extensions.swift in Sources */,
7E8D7B252CECEE79009AD3DF /* PHImageManager+Fetch.swift in Sources */,
7E8D7B262CECEE79009AD3DF /* View+If.swift in Sources */,
7E8D7B272CECEE79009AD3DF /* UIImage+Crop.swift in Sources */,
7E8D7B282CECEE79009AD3DF /* String+Extensions.swift in Sources */,
7E8D7B292CECEE79009AD3DF /* View+OnLoad.swift in Sources */,
7E8D7AEC2CECE9A8009AD3DF /* RegistrationScreen.swift in Sources */,
7E8D7AFA2CECEDB3009AD3DF /* SharedListRow.swift in Sources */,
7E8D7B2B2CECEEDF009AD3DF /* AppError.swift in Sources */,
7E8D7AFB2CECEDB3009AD3DF /* SharedSectionTitle.swift in Sources */,
7E8D7AFC2CECEDB3009AD3DF /* SharedNavigationBar.swift in Sources */,
7E8D7AFD2CECEDB3009AD3DF /* UniversalInputCollection.swift in Sources */,
7E8D7AFE2CECEDB3009AD3DF /* LoadingScreen.swift in Sources */,
7E8D7AED2CECE9A8009AD3DF /* WelcomeScreen.swift in Sources */,
7E995F252CEAC5D2005B30EE /* RootView.swift in Sources */, 7E995F252CEAC5D2005B30EE /* RootView.swift in Sources */,
7E6AF38F2CEB9110004328B5 /* MonalXmppWrapper.swift in Sources */, 7E6AF38F2CEB9110004328B5 /* MonalXmppWrapper.swift in Sources */,
); );
@ -3415,8 +3559,7 @@
baseConfigurationReference = 1936866375CABF471D3CE238 /* Pods-another.im.debug.xcconfig */; baseConfigurationReference = 1936866375CABF471D3CE238 /* Pods-another.im.debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_WARN_FRAMEWORK_INCLUDE_PRIVATE_FROM_PUBLIC = YES; CLANG_WARN_FRAMEWORK_INCLUDE_PRIVATE_FROM_PUBLIC = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
@ -3475,8 +3618,7 @@
baseConfigurationReference = F8ACC07B95446BB8052933BF /* Pods-another.im.adhoc.xcconfig */; baseConfigurationReference = F8ACC07B95446BB8052933BF /* Pods-another.im.adhoc.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
@ -3567,8 +3709,7 @@
baseConfigurationReference = BC9E05245CF07072A35AE126 /* Pods-another.im.alpha.xcconfig */; baseConfigurationReference = BC9E05245CF07072A35AE126 /* Pods-another.im.alpha.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
@ -3659,8 +3800,7 @@
baseConfigurationReference = F2082C5D72E8D7D49B31FBEE /* Pods-another.im.appstore.xcconfig */; baseConfigurationReference = F2082C5D72E8D7D49B31FBEE /* Pods-another.im.appstore.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
@ -3751,8 +3891,7 @@
baseConfigurationReference = 7D281334DB441077E42E3E89 /* Pods-another.im.appstore-quicksy.xcconfig */; baseConfigurationReference = 7D281334DB441077E42E3E89 /* Pods-another.im.appstore-quicksy.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
@ -3843,8 +3982,7 @@
baseConfigurationReference = 3D8AAFBF5B865907983E9F59 /* Pods-another.im.beta.xcconfig */; baseConfigurationReference = 3D8AAFBF5B865907983E9F59 /* Pods-another.im.beta.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";

View file

@ -0,0 +1,16 @@
import AVFoundation
import UIKit
extension AVAsset {
func generateVideoThumbnail(_ size: CGSize) async throws -> UIImage {
try await Task {
let assetImgGenerate = AVAssetImageGenerator(asset: self)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMakeWithSeconds(Float64(1), preferredTimescale: 600)
let cgImage = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
let image = UIImage(cgImage: cgImage)
let result = try await image.scaleAndCropImage(size)
return result
}.value
}
}

View file

@ -0,0 +1,12 @@
import SwiftUI
extension Binding where Value == String {
func max(_ limit: Int) -> Self {
if wrappedValue.count > limit {
DispatchQueue.main.async {
wrappedValue = String(wrappedValue.dropLast())
}
}
return self
}
}

View file

@ -0,0 +1,50 @@
import SwiftUI
private enum ButtonSizes {
static let padding = 16.0
static let cornerRadius = 4.0
static let scaleEffect: CGFloat = 0.9
static let opacity: Double = 0.6
}
struct PrimaryButtonStyle: ButtonStyle {
@Environment(\.isEnabled) private var isEnabled
func makeBody(configuration: Configuration) -> some View {
configuration
.label
.font(.head2)
.padding(ButtonSizes.padding)
.frame(maxWidth: .infinity)
.foregroundColor(.Material.Shape.white)
.background {
RoundedRectangle(cornerRadius: ButtonSizes.cornerRadius)
.foregroundColor(isEnabled ? .Material.Elements.active : .Material.Shape.separator)
}
.contentShape(Rectangle())
.scaleEffect(configuration.isPressed ? ButtonSizes.scaleEffect : 1.0)
.opacity(configuration.isPressed ? ButtonSizes.opacity : 1.0)
.animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
}
}
struct SecondaryButtonStyle: ButtonStyle {
@Environment(\.isEnabled) private var isEnabled
func makeBody(configuration: Configuration) -> some View {
configuration
.label
.font(.head2)
.padding(ButtonSizes.padding)
.frame(maxWidth: .infinity)
.foregroundColor(isEnabled ? .Material.Elements.active : .Material.Shape.separator)
.background {
RoundedRectangle(cornerRadius: ButtonSizes.cornerRadius)
.stroke(isEnabled ? Color.Material.Elements.active : Color.Material.Shape.separator)
}
.contentShape(Rectangle())
.scaleEffect(configuration.isPressed ? ButtonSizes.scaleEffect : 1.0)
.opacity(configuration.isPressed ? ButtonSizes.opacity : 1.0)
.animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
}
}

View file

@ -0,0 +1,13 @@
import SwiftUI
public extension Color {
static let clearTappable = Color.white.opacity(0.0001)
// static func random(randomOpacity: Bool = false) -> Color {
// Color(
// red: .random(in: 0 ... 1),
// green: .random(in: 0 ... 1),
// blue: .random(in: 0 ... 1),
// opacity: randomOpacity ? .random(in: 0 ... 1) : 1
// )
// }
}

View file

@ -0,0 +1,51 @@
import Foundation
import UIKit
enum Const {
// App
static var appVersion: String {
let info = Bundle.main.infoDictionary
let appVersion = info?["CFBundleShortVersionString"] as? String ?? "Unknown"
let appBuild = info?[kCFBundleVersionKey as String] as? String ?? "Unknown"
return "v \(appVersion)(\(appBuild))"
}
static var appName: String {
Bundle.main.bundleIdentifier ?? "another.im"
}
// Trusted servers
enum TrustedServers: String {
case narayana = "narayana.im"
case conversations = "conversations.im"
}
// Limit for video for sharing
static let videoDurationLimit = 60.0
// Grid size for gallery preview (3 in a row)
static let galleryGridSize = UIScreen.main.bounds.width / 3
// Size for map preview for location messages
static let mapPreviewSize = UIScreen.main.bounds.width * 0.5
// Size for attachment preview
static let attachmentPreviewSize = UIScreen.main.bounds.width * 0.5
// MAM request page size
static let mamRequestPageSize = 50
}
final class FolderWrapper {
static let shared = FolderWrapper()
let fileFolder: URL
private init() {
// swiftlint:disable:next force_unwrapping
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let subdirectoryURL = documentsURL.appendingPathComponent("Downloads")
if !FileManager.default.fileExists(atPath: subdirectoryURL.path) {
try? FileManager.default.createDirectory(at: subdirectoryURL, withIntermediateDirectories: true, attributes: nil)
}
fileFolder = subdirectoryURL
}
}

View file

@ -0,0 +1,15 @@
import SwiftUI
extension EdgeInsets {
var inverted: EdgeInsets {
.init(top: -top, leading: -leading, bottom: -bottom, trailing: -trailing)
}
static var zero: EdgeInsets {
.init(top: 0, leading: 0, bottom: 0, trailing: 0)
}
static func symmetric(_ value: CGFloat) -> EdgeInsets {
.init(top: value, leading: value, bottom: value, trailing: value)
}
}

View file

@ -0,0 +1,16 @@
import MapKit
extension MKCoordinateRegion: Equatable {
public static func == (lhs: MKCoordinateRegion, rhs: MKCoordinateRegion) -> Bool {
lhs.center.latitude == rhs.center.latitude &&
lhs.center.longitude == rhs.center.longitude &&
lhs.span.latitudeDelta == rhs.span.latitudeDelta &&
lhs.span.longitudeDelta == rhs.span.longitudeDelta
}
}
extension CLLocationCoordinate2D: Identifiable {
public var id: String {
"\(latitude)-\(longitude)"
}
}

View file

@ -0,0 +1,43 @@
import Photos
import UIKit
extension PHImageManager {
func getPhoto(for asset: PHAsset) async throws -> UIImage {
let options = PHImageRequestOptions()
options.version = .original
options.isSynchronous = true
return try await withCheckedThrowingContinuation { continuation in
requestImage(
for: asset,
targetSize: PHImageManagerMaximumSize,
contentMode: .aspectFill,
options: options
) { image, _ in
if let image {
continuation.resume(returning: image)
} else {
continuation.resume(throwing: AppError.imageNotFound)
}
}
}
}
func getVideo(for asset: PHAsset) async throws -> AVAsset {
let options = PHVideoRequestOptions()
options.version = .original
options.deliveryMode = .highQualityFormat
options.isNetworkAccessAllowed = true
return try await withCheckedThrowingContinuation { continuation in
requestAVAsset(
forVideo: asset,
options: options
) { avAsset, _, _ in
if let avAsset {
continuation.resume(returning: avAsset)
} else {
continuation.resume(throwing: AppError.videoNotFound)
}
}
}
}
}

View file

@ -0,0 +1,98 @@
import CoreLocation
import Foundation
import SwiftUI
extension String {
var firstLetter: String {
String(prefix(1)).uppercased()
}
var makeReply: String {
let allLines = components(separatedBy: .newlines)
let nonBlankLines = allLines.filter { !$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
var result = nonBlankLines.joined(separator: "\n")
result = "> \(result)"
return result
}
var isLocation: Bool {
hasPrefix("geo:")
}
var getLatLon: CLLocationCoordinate2D {
let geo = components(separatedBy: ":")[1]
let parts = geo.components(separatedBy: ",")
let lat = Double(parts[0]) ?? 0.0
let lon = Double(parts[1]) ?? 0.0
return CLLocationCoordinate2D(latitude: lat, longitude: lon)
}
var isContact: Bool {
hasPrefix("contact:")
}
var getContactJid: String {
components(separatedBy: ":")[1]
}
}
extension String {
// var attachmentType: AttachmentType {
// let ext = (self as NSString).pathExtension.lowercased()
// if ext.contains("jpeg") || ext.contains("jpg") || ext.contains("png") || ext.contains("gif") {
// return .image
// } else if ext.contains("mov") || ext.contains("mp4") || ext.contains("avi") {
// return .video
// } else if ext.contains("mp3") || ext.contains("wav") || ext.contains("m4a") {
// return .audio
// } else {
// return .file
// }
// }
}
extension String {
var firstLetterColor: Color {
let firstLetter = self.firstLetter
switch firstLetter {
case "A", "M", "Y":
return Color.Rainbow.tortoiseLight500
case "B", "N", "Z":
return Color.Rainbow.orangeLight500
case "C", "O":
return Color.Rainbow.yellowLight500
case "D", "P":
return Color.Rainbow.greenLight500
case "E", "Q":
return Color.Rainbow.blueLight500
case "F", "R":
return Color.Rainbow.magentaLight500
case "G", "S":
return Color.Rainbow.tortoiseDark500
case "H", "T":
return Color.Rainbow.orangeDark500
case "I", "U":
return Color.Rainbow.yellowDark500
case "J", "V":
return Color.Rainbow.greenDark500
case "K", "W":
return Color.Rainbow.blueDark500
case "L", "X":
return Color.Rainbow.magentaDark500
default:
return Color.Rainbow.tortoiseLight500
}
}
}

View file

@ -0,0 +1,9 @@
import Foundation
extension TimeInterval {
var minAndSec: String {
let minutes = Int(self) / 60
let seconds = Int(self) % 60
return String(format: "%02d:%02d", minutes, seconds)
}
}

View file

@ -0,0 +1,13 @@
import Foundation
import SwiftUI
extension Font {
static let head1l = Font.system(size: 34, weight: .light, design: .rounded)
static let head1r = Font.system(size: 34, weight: .regular, design: .rounded)
static let head2 = Font.system(size: 20, weight: .regular, design: .rounded)
static let body1 = Font.system(size: 18, weight: .regular, design: .rounded)
static let body2 = Font.system(size: 16, weight: .regular, design: .rounded)
static let body3 = Font.system(size: 14, weight: .regular, design: .rounded)
static let sub1 = Font.system(size: 10, weight: .regular, design: .rounded)
static let sub2 = Font.system(size: 8, weight: .regular, design: .rounded)
}

View file

@ -0,0 +1,10 @@
import UIKit
func openAppSettings() {
if
let appSettingsUrl = URL(string: UIApplication.openSettingsURLString),
UIApplication.shared.canOpenURL(appSettingsUrl)
{
UIApplication.shared.open(appSettingsUrl, completionHandler: nil)
}
}

View file

@ -0,0 +1,30 @@
import Foundation
import UIKit
extension UIImage {
func scaleAndCropImage(_ size: CGSize) async throws -> UIImage {
try await Task {
let aspect = self.size.width / self.size.height
let targetAspect = size.width / size.height
var newWidth: CGFloat
var newHeight: CGFloat
if aspect < targetAspect {
newWidth = size.width
newHeight = size.width / aspect
} else {
newHeight = size.height
newWidth = size.height * aspect
}
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
self.draw(in: CGRect(x: (size.width - newWidth) / 2, y: (size.height - newHeight) / 2, width: newWidth, height: newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
if let newImage = newImage {
return newImage
} else {
throw NSError(domain: "UIImage", code: -900, userInfo: nil)
}
}.value
}
}

View file

@ -0,0 +1,13 @@
import UniformTypeIdentifiers
extension URL {
var mimeType: String {
let pathExtension = self.pathExtension
if let uti = UTType(filenameExtension: pathExtension) {
return uti.preferredMIMEType ?? "application/octet-stream"
} else {
return "application/octet-stream"
}
}
}

View file

@ -0,0 +1,17 @@
import SwiftUI
import UIKit
enum Vibration: String {
case error
case success
public func vibrate() {
switch self {
case .error:
UINotificationFeedbackGenerator().notificationOccurred(.error)
case .success:
UINotificationFeedbackGenerator().notificationOccurred(.success)
}
}
}

View file

@ -0,0 +1,35 @@
import SwiftUI
#if DEBUG
private let rainbowDebugColors = [
Color.purple,
Color.blue,
Color.green,
Color.yellow,
Color.orange,
Color.red,
Color.pink,
Color.black.opacity(0.5),
Color.teal,
Color.gray,
Color.mint,
Color.cyan
]
public extension Color {
static func random(randomOpacity: Bool = false) -> Color {
Color(
red: .random(in: 0 ... 1),
green: .random(in: 0 ... 1),
blue: .random(in: 0 ... 1),
opacity: randomOpacity ? .random(in: 0 ... 1) : 1
)
}
}
extension View {
func rainbowDebug() -> some View {
background(Color.random())
}
}
#endif

View file

@ -0,0 +1,15 @@
import SwiftUI
struct FlipView: ViewModifier {
func body(content: Content) -> some View {
content
.rotationEffect(.radians(Double.pi))
.scaleEffect(x: -1, y: 1, anchor: .center)
}
}
extension View {
func flip() -> some View {
modifier(FlipView())
}
}

View file

@ -0,0 +1,11 @@
import SwiftUI
public extension View {
@ViewBuilder func `if`<Content: View>(_ condition: @autoclosure () -> Bool, transform: (Self) -> Content) -> some View {
if condition() {
transform(self)
} else {
self
}
}
}

View file

@ -0,0 +1,27 @@
import SwiftUI
// MARK: - On load
extension View {
func onLoad(_ action: @escaping () -> Void) -> some View {
modifier(ViewDidLoadModifier(action))
}
}
private struct ViewDidLoadModifier: ViewModifier {
private let action: () -> Void
@State private var didLoad = false
init(_ action: @escaping () -> Void) {
self.action = action
}
func body(content: Content) -> some View {
content.onAppear {
if !didLoad {
didLoad.toggle()
action()
}
}
}
}

View file

@ -0,0 +1,27 @@
import SwiftUI
extension View {
func tappablePadding(_ insets: EdgeInsets, onTap: @escaping () -> Void) -> some View {
modifier(TappablePadding(insets: insets, onTap: onTap))
}
}
struct TappablePadding: ViewModifier {
let insets: EdgeInsets
let onTap: () -> Void
public init(insets: EdgeInsets, onTap: @escaping () -> Void) {
self.insets = insets
self.onTap = onTap
}
public func body(content: Content) -> some View {
content
.padding(insets)
.contentShape(Rectangle())
.onTapGesture {
onTap()
}
.padding(insets.inverted)
}
}

View file

@ -0,0 +1,115 @@
import Combine
import SwiftUI
struct LoginScreen: View {
@EnvironmentObject var wrapper: MonalXmppWrapper
@Environment(\.router) var router
enum Field {
case userJid
case password
}
@FocusState private var focus: Field?
@State private var jidStr: String = ""
@State private var pass: String = ""
public var body: some View {
ZStack {
// background
Color.Material.Background.light
.ignoresSafeArea()
// content
VStack(spacing: 32) {
// icon
Image(.aimLogo)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 120, height: 120)
// texts
VStack(spacing: 10) {
Text(L10n.Login.title)
.font(.head1l)
.foregroundColor(.Material.Text.main)
.fixedSize(horizontal: true, vertical: false)
Text(L10n.Login.subtitle)
.font(.body2)
.foregroundColor(.Material.Text.sub)
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true)
}
VStack(spacing: 16) {
UniversalInputCollection.TextField(
prompt: L10n.Login.Hint.jid,
text: $jidStr,
focus: $focus,
fieldType: .userJid,
contentType: .emailAddress,
keyboardType: .emailAddress,
submitLabel: .next,
action: {
focus = .password
}
)
UniversalInputCollection.SecureField(
prompt: L10n.Login.Hint.password,
text: $pass,
focus: $focus,
fieldType: .password,
submitLabel: .go,
action: {
focus = nil
}
)
Button {
router.showModal {
LoadingScreen()
}
Task(priority: .background) {
await tryLogin()
}
} label: {
Text(L10n.Login.btn)
}
.buttonStyle(PrimaryButtonStyle())
.disabled(!loginInputValid)
Button {
router.dismissScreen()
} label: {
Text("\(Image(systemName: "chevron.left")) \(L10n.Global.back)")
.foregroundColor(.Material.Elements.active)
.font(.body2)
}
}
}
.padding(.horizontal, 32)
}
}
private var loginInputValid: Bool {
!jidStr.isEmpty && !pass.isEmpty && UniversalInputCollection.Validators.isEmail(jidStr)
}
private func tryLogin() async {
do {
// try await clientsStore.tryLogin(jidStr, pass)
router.dismissScreen()
} catch {
router.showAlert(
.alert,
title: L10n.Global.Error.title,
subtitle: L10n.Login.error
) {
Button(L10n.Global.ok, role: .cancel) {
router.dismissModal()
}
}
}
}
}

View file

@ -0,0 +1,20 @@
import SwiftUI
struct RegistrationScreen: View {
@Environment(\.router) var router
public var body: some View {
ZStack {
Color.Material.Background.light
Button {
router.dismissScreen()
} label: {
VStack {
Text("Not yet implemented")
Text(L10n.Global.back)
}
}
}
.ignoresSafeArea()
}
}

View file

@ -14,14 +14,12 @@ struct RootView: View {
.ignoresSafeArea() .ignoresSafeArea()
.onAppear { .onAppear {
switch wrapper.accountsAvailability { switch wrapper.accountsAvailability {
case .noAccounts: case .noAccounts, .allDisabled:
break WelcomeScreen()
case .someEnabled: case .someEnabled:
break // here will be main flow
EmptyView()
case .allDisabled:
break
} }
} }
} }

View file

@ -0,0 +1,23 @@
import SwiftUI
struct LoadingScreen: View {
var body: some View {
GeometryReader { geo in
ZStack {
// background with opacity
Color.Material.Elements.active.opacity(0.3)
.frame(maxWidth: .infinity, maxHeight: .infinity)
// loader
ProgressView()
.progressViewStyle(
CircularProgressViewStyle(tint: .Material.Elements.active)
)
.position(x: geo.size.width / 2, y: geo.size.height / 2)
.controlSize(.large)
}
}
.ignoresSafeArea()
.transition(AnyTransition.opacity.animation(.easeInOut(duration: 0.1)))
}
}

View file

@ -0,0 +1,84 @@
import SwiftUI
enum SharedListRowIconType {
case charCircle(String)
case image(Image, Color)
case none
}
enum SharedListRowControlType {
case none
case switcher(isOn: Binding<Bool>)
}
struct SharedListRow: View {
let iconType: SharedListRowIconType
let text: String
let controlType: SharedListRowControlType
var body: some View {
VStack(spacing: 0) {
HStack(spacing: 8) {
// Icon
switch iconType {
case .charCircle(let str):
let char = str.firstLetter
let color = str.firstLetterColor
ZStack {
Circle()
.frame(width: 44, height: 44)
.foregroundColor(color)
Text(char)
.foregroundColor(.white)
.font(.body1)
}
case .image(let image, let color):
ZStack {
Circle()
.frame(width: 44, height: 44)
.foregroundColor(.clearTappable)
.overlay {
image
.foregroundColor(color)
}
}
case .none:
Rectangle()
.fill(Color.clear)
.frame(width: 0.1, height: 44)
}
// Text
Text(text)
.foregroundColor(Color.Material.Text.main)
.font(.body2)
Spacer()
// If control is needed
switch controlType {
case .none:
Rectangle()
.fill(Color.clear)
.frame(width: 0.1, height: 44)
case .switcher(let isOn):
Toggle("", isOn: isOn)
.toggleStyle(SwitchToggleStyle(tint: .Material.Elements.active))
.frame(width: 49, height: 31)
}
}
.padding(.horizontal, 16)
.padding(.vertical, 4)
Rectangle()
.frame(maxWidth: .infinity)
.frame(height: 1)
.foregroundColor(.Material.Background.dark)
}
.listRowInsets(.zero)
.listRowSeparator(.hidden)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.Material.Background.light)
}
}

View file

@ -0,0 +1,90 @@
import SwiftUI
struct SharedNavBarButton: View {
let image: Image?
let action: () -> Void
var isEnabled: Bool = true
init(
image: Image,
action: @escaping () -> Void,
isEnabled: Bool = true
) {
self.image = image
self.action = action
self.isEnabled = isEnabled
}
var body: some View {
Button {
action()
} label: {
image
.foregroundColor(isEnabled ? .Material.Elements.active : .Material.Elements.inactive)
.tappablePadding(.symmetric(12)) {
action()
}
}
.disabled(!isEnabled)
}
}
struct SharedNavBarText: View {
let text: String
let action: (() -> Void)?
init(
text: String,
action: (() -> Void)? = nil
) {
self.text = text
self.action = action
}
var body: some View {
Text(text)
.font(.head2)
.foregroundColor(.Material.Text.main)
.tappablePadding(.init(top: 8, leading: 24, bottom: 8, trailing: 24)) {
action?()
}
}
}
struct SharedNavigationBar: View {
var leftButton: SharedNavBarButton?
var centerText: SharedNavBarText?
var rightButton: SharedNavBarButton?
var body: some View {
ZStack {
Color.Material.Background.dark
.ignoresSafeArea()
VStack {
if centerText != nil {
centerText
}
}
Spacer()
HStack(alignment: .center) {
VStack {
if leftButton != nil {
leftButton?.padding()
}
}
.frame(minWidth: 40)
Spacer()
VStack {
if rightButton != nil {
rightButton?
.padding()
}
}
.frame(minWidth: 40)
}
}
.frame(height: 44)
}
}

View file

@ -0,0 +1,21 @@
import SwiftUI
struct SharedSectionTitle: View {
let text: String
var body: some View {
HStack(spacing: 0) {
// Text
Text(text)
.foregroundColor(Color.Material.Text.sub)
.font(.body3)
.padding(.leading, 16)
.padding(.top, 16)
Spacer()
}
.listRowInsets(.zero)
.listRowSeparator(.hidden)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.Material.Background.light)
}
}

View file

@ -0,0 +1,203 @@
import SwiftUI
// MARK: Public
protocol UniversalInputSelectionElement: Identifiable, Equatable, Hashable {
var icon: Image? { get }
var text: String? { get }
}
public enum UniversalInputCollection {
struct TextField<T: Hashable> {
let prompt: String
@Binding var text: String
var focus: FocusState<T?>.Binding
var fieldType: T
let contentType: UITextContentType
let keyboardType: UIKeyboardType
let submitLabel: SubmitLabel
let action: () -> Void
}
struct SecureField<T: Hashable> {
let prompt: String
@Binding var text: String
var focus: FocusState<T?>.Binding
var fieldType: T
let submitLabel: SubmitLabel
let action: () -> Void
}
struct DropDownMenu<T: Hashable, E: UniversalInputSelectionElement> {
let prompt: String
let elements: [E]
@Binding var selected: E?
var focus: FocusState<T?>.Binding
var fieldType: T
}
}
// MARK: Inputs implementations
extension UniversalInputCollection.TextField: View {
var body: some View {
TextField("", text: $text)
.padding(.horizontal, 8)
.focused(focus, equals: fieldType)
.font(.body2)
.foregroundColor(.Material.Text.main)
.autocorrectionDisabled(true)
.autocapitalization(.none)
.textContentType(contentType)
.keyboardType(keyboardType)
.submitLabel(submitLabel)
.textSelection(.enabled)
.onSubmit {
action()
}
.modifier(UniversalInputModifier(
prompt: prompt,
focus: focus,
fieldType: fieldType,
isActive: isFilled
))
}
var isFilled: Bool {
!text.isEmpty || focus.wrappedValue == fieldType
}
}
extension UniversalInputCollection.SecureField: View {
var body: some View {
SecureField("", text: $text)
.padding(.horizontal, 8)
.focused(focus, equals: fieldType)
.font(.body2)
.foregroundColor(.Material.Text.main)
.autocorrectionDisabled(true)
.autocapitalization(.none)
.textContentType(.password)
.submitLabel(submitLabel)
.textSelection(.disabled)
.onSubmit {
action()
}
.modifier(UniversalInputModifier(
prompt: prompt,
focus: focus,
fieldType: fieldType,
isActive: isFilled
))
}
var isFilled: Bool {
!text.isEmpty || focus.wrappedValue == fieldType
}
}
extension UniversalInputCollection.DropDownMenu: View {
var body: some View {
ZStack {
HStack {
Text(text)
.font(.body2)
.foregroundColor(.Material.Text.main)
.padding(.leading, 8)
Spacer()
}
.modifier(UniversalInputModifier(
prompt: prompt,
focus: focus,
fieldType: fieldType,
isActive: selected != nil
))
Menu {
ForEach(elements, id: \.self.id) { element in
Button {
selected = element
} label: {
Text(element.text ?? "")
}
}
} label: {
Label("", image: "")
.labelStyle(TitleOnlyLabelStyle())
.padding(.vertical)
.frame(height: 48)
.frame(maxWidth: .infinity)
}
}
}
var text: String {
if let text = selected?.text {
return text
} else {
return ""
}
}
}
// MARK: Modifiers
private struct UniversalInputModifier<T: Hashable>: ViewModifier {
let prompt: String
var focus: FocusState<T?>.Binding
var fieldType: T
let isActive: Bool
var promptBackground: Color?
var isCentered: Bool?
var customTapAction: (() -> Void)?
func body(content: Content) -> some View {
VStack(spacing: 0) {
ZStack {
HStack {
Text(isActive ? "" : prompt)
.font(.body2)
.foregroundColor(.Material.Shape.separator)
.padding(8)
Spacer()
}
content
.frame(height: 48)
}
}
.frame(height: 48)
.background {
ZStack {
RoundedRectangle(cornerRadius: 4)
.foregroundColor(.Material.Shape.white)
RoundedRectangle(cornerRadius: 4)
.stroke(Color.Material.Shape.separator)
}
}
.contentShape(Rectangle())
.onTapGesture {
if let customTapAction {
customTapAction()
} else {
if focus.wrappedValue != fieldType {
focus.wrappedValue = fieldType
}
}
}
}
}
// MARK: Validators
extension UniversalInputCollection {
enum Validators {
static func isEmail(_ input: String) -> Bool {
if !input.isEmpty {
let mailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
if !NSPredicate(format: "SELF MATCHES %@", mailRegex).evaluate(with: input) {
return false
} else {
return true
}
} else {
return true
}
}
}
}

View file

@ -0,0 +1,79 @@
import SwiftUI
struct WelcomeScreen: View {
@EnvironmentObject var wrapper: MonalXmppWrapper
@Environment(\.router) var router
var body: some View {
ZStack {
// background
Color.Material.Background.light
.ignoresSafeArea()
if wrapper.accountsAvailability == .allDisabled {
VStack {
HStack {
Spacer()
Image(systemName: "gear")
.foregroundColor(.Material.Elements.active)
.tappablePadding(.symmetric(10)) {
router.showScreen(.push) { _ in
EmptyView()
// SettingsScreen()
// .environment(\.settingsParent, .welcome)
// .navigationBarHidden(true)
}
}
}
.padding()
Spacer()
}
}
// content
VStack(spacing: 32) {
// icon
Image.aimLogo
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 120, height: 120)
// texts
VStack(spacing: 10) {
Text(L10n.Global.name)
.font(.head1r)
.foregroundColor(.Material.Text.main)
.fixedSize(horizontal: true, vertical: false)
Text(L10n.Start.subtitle)
.font(.body2)
.foregroundColor(.Material.Text.sub)
.fixedSize(horizontal: false, vertical: true)
.multilineTextAlignment(.center)
}
// buttons
VStack(spacing: 16) {
Button {
router.showScreen(.push) { _ in
LoginScreen()
.navigationBarBackButtonHidden(true)
}
} label: {
Text(L10n.Start.Btn.login)
}
.buttonStyle(SecondaryButtonStyle())
Button {
router.showScreen(.push) { _ in
RegistrationScreen()
.navigationBarBackButtonHidden(true)
}
} label: {
Text(L10n.Start.Btn.register)
}
.buttonStyle(PrimaryButtonStyle())
}
}
.padding(.horizontal, 32)
}
}
}

View file

@ -0,0 +1,12 @@
enum AppError: Error {
case clientNotFound
case rosterNotFound
case imageNotFound
case videoNotFound
case noData
case fileTooBig
case invalidContentType
case invalidLocalName
case featureNotSupported
case securityError
}