move prev sources
This commit is contained in:
parent
f06208ec66
commit
e210861382
|
@ -136,6 +136,38 @@
|
|||
7E71758E2CECC5C70059F30B /* server_features.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7E71758A2CECC5C70059F30B /* server_features.plist */; };
|
||||
7E71758F2CECC5C70059F30B /* launchscreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7E7175892CECC5C70059F30B /* launchscreen.storyboard */; };
|
||||
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 */; };
|
||||
7E995F252CEAC5D2005B30EE /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E995F222CEAC5D2005B30EE /* RootView.swift */; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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; };
|
||||
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>"; };
|
||||
|
@ -1397,7 +1461,11 @@
|
|||
7E8D7AE42CECD037009AD3DF /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7E8D7AE82CECE9A8009AD3DF /* LoginScreen.swift */,
|
||||
7E8D7AE92CECE9A8009AD3DF /* RegistrationScreen.swift */,
|
||||
7E995F222CEAC5D2005B30EE /* RootView.swift */,
|
||||
7E8D7AEA2CECE9A8009AD3DF /* WelcomeScreen.swift */,
|
||||
7E8D7AF92CECEDB3009AD3DF /* SharedComponents */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1406,15 +1474,56 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
7E6AF38E2CEB9110004328B5 /* MonalXmppWrapper.swift */,
|
||||
7E8D7B2A2CECEEDF009AD3DF /* AppError.swift */,
|
||||
);
|
||||
path = XMPP;
|
||||
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 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7E995F202CEAC5D2005B30EE /* AnotherIMApp.swift */,
|
||||
EA534340732BF66B533E4C0B /* Generated */,
|
||||
7E8D7B152CECEE79009AD3DF /* Helpers */,
|
||||
7ED6F01A2CECC43D0035B3B7 /* Resources */,
|
||||
7E8D7AE42CECD037009AD3DF /* Views */,
|
||||
7E8D7AE52CECD05C009AD3DF /* XMPP */,
|
||||
|
@ -1609,6 +1718,9 @@
|
|||
EA534340732BF66B533E4C0B /* Generated */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7E8D7AEE2CECEB30009AD3DF /* Colors+Generated.swift */,
|
||||
7E8D7AEF2CECEB30009AD3DF /* Images+Generated.swift */,
|
||||
7E8D7AF02CECEB30009AD3DF /* Strings+Generated.swift */,
|
||||
);
|
||||
path = Generated;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2474,6 +2586,38 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
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 */,
|
||||
7E6AF38F2CEB9110004328B5 /* MonalXmppWrapper.swift in Sources */,
|
||||
);
|
||||
|
@ -3415,8 +3559,7 @@
|
|||
baseConfigurationReference = 1936866375CABF471D3CE238 /* Pods-another.im.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_WARN_FRAMEWORK_INCLUDE_PRIVATE_FROM_PUBLIC = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
|
@ -3475,8 +3618,7 @@
|
|||
baseConfigurationReference = F8ACC07B95446BB8052933BF /* Pods-another.im.adhoc.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
|
@ -3567,8 +3709,7 @@
|
|||
baseConfigurationReference = BC9E05245CF07072A35AE126 /* Pods-another.im.alpha.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
|
@ -3659,8 +3800,7 @@
|
|||
baseConfigurationReference = F2082C5D72E8D7D49B31FBEE /* Pods-another.im.appstore.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
|
@ -3751,8 +3891,7 @@
|
|||
baseConfigurationReference = 7D281334DB441077E42E3E89 /* Pods-another.im.appstore-quicksy.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
|
@ -3843,8 +3982,7 @@
|
|||
baseConfigurationReference = 3D8AAFBF5B865907983E9F59 /* Pods-another.im.beta.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
|
|
16
Monal/another.im/Helpers/AVAsset+Thumbnail.swift
Normal file
16
Monal/another.im/Helpers/AVAsset+Thumbnail.swift
Normal 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
|
||||
}
|
||||
}
|
12
Monal/another.im/Helpers/Binding+Extensions.swift
Normal file
12
Monal/another.im/Helpers/Binding+Extensions.swift
Normal 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
|
||||
}
|
||||
}
|
50
Monal/another.im/Helpers/ButtonStyles.swift
Normal file
50
Monal/another.im/Helpers/ButtonStyles.swift
Normal 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)
|
||||
}
|
||||
}
|
13
Monal/another.im/Helpers/Colors+Tappable.swift
Normal file
13
Monal/another.im/Helpers/Colors+Tappable.swift
Normal 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
|
||||
// )
|
||||
// }
|
||||
}
|
51
Monal/another.im/Helpers/Const.swift
Normal file
51
Monal/another.im/Helpers/Const.swift
Normal 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
|
||||
}
|
||||
}
|
15
Monal/another.im/Helpers/EdgeInsets+Extensions.swift
Normal file
15
Monal/another.im/Helpers/EdgeInsets+Extensions.swift
Normal 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)
|
||||
}
|
||||
}
|
16
Monal/another.im/Helpers/Map+Extensions.swift
Normal file
16
Monal/another.im/Helpers/Map+Extensions.swift
Normal 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)"
|
||||
}
|
||||
}
|
43
Monal/another.im/Helpers/PHImageManager+Fetch.swift
Normal file
43
Monal/another.im/Helpers/PHImageManager+Fetch.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
98
Monal/another.im/Helpers/String+Extensions.swift
Normal file
98
Monal/another.im/Helpers/String+Extensions.swift
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
9
Monal/another.im/Helpers/TimeInterval+Extensions.swift
Normal file
9
Monal/another.im/Helpers/TimeInterval+Extensions.swift
Normal 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)
|
||||
}
|
||||
}
|
13
Monal/another.im/Helpers/Typography.swift
Normal file
13
Monal/another.im/Helpers/Typography.swift
Normal 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)
|
||||
}
|
10
Monal/another.im/Helpers/UIApplication+Extensions.swift
Normal file
10
Monal/another.im/Helpers/UIApplication+Extensions.swift
Normal 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)
|
||||
}
|
||||
}
|
30
Monal/another.im/Helpers/UIImage+Crop.swift
Normal file
30
Monal/another.im/Helpers/UIImage+Crop.swift
Normal 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
|
||||
}
|
||||
}
|
13
Monal/another.im/Helpers/URL+Extensions.swift
Normal file
13
Monal/another.im/Helpers/URL+Extensions.swift
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
17
Monal/another.im/Helpers/Vibration.swift
Normal file
17
Monal/another.im/Helpers/Vibration.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
35
Monal/another.im/Helpers/View+Debug.swift
Normal file
35
Monal/another.im/Helpers/View+Debug.swift
Normal 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
|
15
Monal/another.im/Helpers/View+Flip.swift
Normal file
15
Monal/another.im/Helpers/View+Flip.swift
Normal 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())
|
||||
}
|
||||
}
|
11
Monal/another.im/Helpers/View+If.swift
Normal file
11
Monal/another.im/Helpers/View+If.swift
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
27
Monal/another.im/Helpers/View+OnLoad.swift
Normal file
27
Monal/another.im/Helpers/View+OnLoad.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
Monal/another.im/Helpers/View+TappableArea.swift
Normal file
27
Monal/another.im/Helpers/View+TappableArea.swift
Normal 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)
|
||||
}
|
||||
}
|
115
Monal/another.im/Views/LoginScreen.swift
Normal file
115
Monal/another.im/Views/LoginScreen.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
Monal/another.im/Views/RegistrationScreen.swift
Normal file
20
Monal/another.im/Views/RegistrationScreen.swift
Normal 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()
|
||||
}
|
||||
}
|
|
@ -14,14 +14,12 @@ struct RootView: View {
|
|||
.ignoresSafeArea()
|
||||
.onAppear {
|
||||
switch wrapper.accountsAvailability {
|
||||
case .noAccounts:
|
||||
break
|
||||
case .noAccounts, .allDisabled:
|
||||
WelcomeScreen()
|
||||
|
||||
case .someEnabled:
|
||||
break
|
||||
|
||||
case .allDisabled:
|
||||
break
|
||||
// here will be main flow
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
23
Monal/another.im/Views/SharedComponents/LoadingScreen.swift
Normal file
23
Monal/another.im/Views/SharedComponents/LoadingScreen.swift
Normal 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)))
|
||||
}
|
||||
}
|
84
Monal/another.im/Views/SharedComponents/SharedListRow.swift
Normal file
84
Monal/another.im/Views/SharedComponents/SharedListRow.swift
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
79
Monal/another.im/Views/WelcomeScreen.swift
Normal file
79
Monal/another.im/Views/WelcomeScreen.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
12
Monal/another.im/XMPP/AppError.swift
Normal file
12
Monal/another.im/XMPP/AppError.swift
Normal 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
|
||||
}
|
Loading…
Reference in a new issue