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 */; };
|
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";
|
||||||
|
|
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()
|
.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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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