diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/AppFeature.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/AppFeature.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..f456abbc6723aaeea5b0855be66dfd2b2bcc16b8 --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/AppFeature.xcscheme @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1410" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "AppFeature" + BuildableName = "AppFeature" + BlueprintName = "AppFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "AppFeature" + BuildableName = "AppFeature" + BlueprintName = "AppFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/AppNavigation.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/AppNavigation.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..15cbf312c024090afcf8a19dc2a95dc46942b6ae --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/AppNavigation.xcscheme @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1410" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "AppNavigation" + BuildableName = "AppNavigation" + BlueprintName = "AppNavigation" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "AppNavigation" + BuildableName = "AppNavigation" + BlueprintName = "AppNavigation" + ReferencedContainer = "container:"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Defaults.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Defaults.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..8d32953525d76ceaaeafe999fcdec2c2b6a64c44 --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/Defaults.xcscheme @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1410" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "Defaults" + BuildableName = "Defaults" + BlueprintName = "Defaults" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "Defaults" + BuildableName = "Defaults" + BlueprintName = "Defaults" + ReferencedContainer = "container:"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Keychain.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Keychain.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..e1efe3f4ab10b6ff34298955c87ae081d23eafea --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/Keychain.xcscheme @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1410" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "Keychain" + BuildableName = "Keychain" + BlueprintName = "Keychain" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "Keychain" + BuildableName = "Keychain" + BlueprintName = "Keychain" + ReferencedContainer = "container:"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/SettingsFeature.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/SettingsFeature.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..244b1a0ce2d97ea860b30062ec59ec322e831e1a --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/SettingsFeature.xcscheme @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1410" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "SettingsFeature" + BuildableName = "SettingsFeature" + BlueprintName = "SettingsFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "SettingsFeature" + BuildableName = "SettingsFeature" + BlueprintName = "SettingsFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/UpdateErrors.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/UpdateErrors.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..1998517fa48f8d42029ba164bc4429012ad24652 --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/UpdateErrors.xcscheme @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1410" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "UpdateErrors" + BuildableName = "UpdateErrors" + BlueprintName = "UpdateErrors" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "UpdateErrors" + BuildableName = "UpdateErrors" + BlueprintName = "UpdateErrors" + ReferencedContainer = "container:"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/Package.swift b/Package.swift index 79eb6ae391a45399efb0dce157a0239cdfa40c29..b1ec908e6564028f197b352659674d5c971a15ad 100644 --- a/Package.swift +++ b/Package.swift @@ -12,30 +12,37 @@ let package = Package( .library(name: "AppCore", targets: ["AppCore"]), .library(name: "Defaults", targets: ["Defaults"]), .library(name: "Keychain", targets: ["Keychain"]), + .library(name: "Voxophone", targets: ["Voxophone"]), .library(name: "AppFeature", targets: ["AppFeature"]), .library(name: "InputField", targets: ["InputField"]), .library(name: "ScanFeature", targets: ["ScanFeature"]), .library(name: "MenuFeature", targets: ["MenuFeature"]), .library(name: "ChatFeature", targets: ["ChatFeature"]), .library(name: "PushFeature", targets: ["PushFeature"]), + .library(name: "CrashReport", targets: ["CrashReport"]), + .library(name: "UpdateErrors", targets: ["UpdateErrors"]), + .library(name: "CheckVersion", targets: ["CheckVersion"]), .library(name: "AppResources", targets: ["AppResources"]), - .library(name: "CrashService", targets: ["CrashService"]), .library(name: "TermsFeature", targets: ["TermsFeature"]), + .library(name: "AppNavigation", targets: ["AppNavigation"]), .library(name: "BackupFeature", targets: ["BackupFeature"]), .library(name: "LaunchFeature", targets: ["LaunchFeature"]), .library(name: "SearchFeature", targets: ["SearchFeature"]), .library(name: "DrawerFeature", targets: ["DrawerFeature"]), + .library(name: "WebsiteFeature", targets: ["WebsiteFeature"]), .library(name: "RestoreFeature", targets: ["RestoreFeature"]), - .library(name: "CrashReporting", targets: ["CrashReporting"]), .library(name: "ProfileFeature", targets: ["ProfileFeature"]), .library(name: "ContactFeature", targets: ["ContactFeature"]), + .library(name: "FetchBannedList", targets: ["FetchBannedList"]), .library(name: "SettingsFeature", targets: ["SettingsFeature"]), .library(name: "ChatListFeature", targets: ["ChatListFeature"]), .library(name: "RequestsFeature", targets: ["RequestsFeature"]), .library(name: "ReportingFeature", targets: ["ReportingFeature"]), - .library(name: "StatusBarFeature", targets: ["StatusBarFeature"]), .library(name: "ChatInputFeature", targets: ["ChatInputFeature"]), + .library(name: "GroupDraftFeature", targets: ["GroupDraftFeature"]), + .library(name: "ProcessBannedList", targets: ["ProcessBannedList"]), .library(name: "OnboardingFeature", targets: ["OnboardingFeature"]), + .library(name: "CreateGroupFeature", targets: ["CreateGroupFeature"]), .library(name: "CountryListFeature", targets: ["CountryListFeature"]), .library(name: "PermissionsFeature", targets: ["PermissionsFeature"]), .library(name: "ContactListFeature", targets: ["ContactListFeature"]), @@ -118,13 +125,6 @@ let package = Package( url: "https://github.com/pointfreeco/xctest-dynamic-overlay.git", .upToNextMajor(from: "0.3.3") ), - .package( - path: "../xxm-navigation" - ), - .package( - url: "https://git.xx.network/elixxir/xxm-di", - .upToNextMajor(from: "1.0.0") - ) ], targets: [ .target( @@ -135,24 +135,25 @@ let package = Package( .target(name: "ScanFeature"), .target(name: "ChatFeature"), .target(name: "MenuFeature"), + .target(name: "CrashReport"), .target(name: "PushFeature"), .target(name: "TermsFeature"), - .target(name: "CrashService"), .target(name: "BackupFeature"), .target(name: "SearchFeature"), .target(name: "LaunchFeature"), .target(name: "ContactFeature"), + .target(name: "WebsiteFeature"), .target(name: "RestoreFeature"), .target(name: "ProfileFeature"), - .target(name: "CrashReporting"), .target(name: "ChatListFeature"), .target(name: "SettingsFeature"), .target(name: "RequestsFeature"), .target(name: "ReportingFeature"), + .target(name: "GroupDraftFeature"), .target(name: "OnboardingFeature"), + .target(name: "CreateGroupFeature"), .target(name: "ContactListFeature"), .target(name: "RequestPermissionFeature"), - .product(name: "DependencyInjection", package: "xxm-di"), ] ), .testTarget( @@ -166,7 +167,6 @@ let package = Package( dependencies: [ .target(name: "Shared"), .target(name: "AppResources"), - .target(name: "StatusBarFeature"), .product(name: "SnapKit", package: "SnapKit"), .product(name: "Logging", package: "swift-log"), .product(name: "XXModels", package: "client-ios-db"), @@ -177,31 +177,56 @@ let package = Package( .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), ] ), + .target(name: "CheckVersion"), + .target(name: "Voxophone"), + .target(name: "WebsiteFeature"), .target( - name: "CrashReporting" + name: "CrashReport", + dependencies: [ + .product( + name: "FirebaseCrashlytics", + package: "firebase-ios-sdk" + ), + .product( + name: "Dependencies", + package: "swift-composable-architecture" + ), + ] ), .target( - name: "PermissionsFeature", + name: "AppNavigation", dependencies: [ .product( - name: "XCTestDynamicOverlay", - package: "xctest-dynamic-overlay" + name: "XXModels", + package: "client-ios-db" ), .product( - name: "ComposableArchitecture", + name: "Dependencies", package: "swift-composable-architecture" ), ] ), .target( - name: "StatusBarFeature", + name: "CreateGroupFeature", + dependencies: [ + .target(name: "AppCore") + ] + ), + .target( + name: "GroupDraftFeature", + dependencies: [ + .target(name: "AppCore") + ] + ), + .target( + name: "PermissionsFeature", dependencies: [ .product( name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay" ), .product( - name: "ComposableArchitecture", + name: "Dependencies", package: "swift-composable-architecture" ), ] @@ -222,10 +247,12 @@ let package = Package( name: "RequestPermissionFeature", dependencies: [ .target(name: "Shared"), + .target(name: "AppCore"), .target(name: "AppResources"), + .target(name: "AppNavigation"), .target(name: "PermissionsFeature"), .product( - name: "ComposableArchitecture", + name: "Dependencies", package: "swift-composable-architecture" ), ] @@ -233,9 +260,9 @@ let package = Package( .target( name: "PushFeature", dependencies: [ + .target(name: "AppCore"), .target(name: "Defaults"), .target(name: "ReportingFeature"), - .product(name: "DependencyInjection", package: "xxm-di"), .product(name: "XXDatabase", package: "client-ios-db"), .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"), .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"), @@ -249,22 +276,13 @@ let package = Package( ), .target( name: "Defaults", - dependencies: [ - .product(name: "DependencyInjection", package: "xxm-di"), - ] - ), - .target( - name: "CrashService", - dependencies: [ - .target(name: "CrashReporting"), - .product(name: "FirebaseCrashlytics", package: "firebase-ios-sdk"), - ] + dependencies: [] ), .target( name: "CountryListFeature", dependencies: [ .target(name: "Shared"), - .target(name: "StatusBarFeature") + .target(name: "AppCore") ] ), .target( @@ -293,6 +311,9 @@ let package = Package( .target( name: "ChatInputFeature", dependencies: [ + .target( + name: "Voxophone" + ), .product( name: "ComposableArchitecture", package: "swift-composable-architecture" @@ -303,9 +324,8 @@ let package = Package( name: "RestoreFeature", dependencies: [ .target(name: "Shared"), + .target(name: "AppCore"), .product(name: "XXDatabase", package: "client-ios-db"), - .product(name: "Navigation", package: "xxm-navigation"), - .product(name: "DependencyInjection", package: "xxm-di"), .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"), .product(name: "CloudFilesDrive", package: "xxm-cloud-providers"), .product(name: "CloudFilesDropbox", package: "xxm-cloud-providers"), @@ -327,13 +347,14 @@ let package = Package( name: "ChatFeature", dependencies: [ .target(name: "Shared"), + .target(name: "AppCore"), .target(name: "Defaults"), .target(name: "Keychain"), + .target(name: "Voxophone"), .target(name: "DrawerFeature"), .target(name: "ChatInputFeature"), .target(name: "ReportingFeature"), .target(name: "RequestPermissionFeature"), - .product(name: "DependencyInjection", package: "xxm-di"), .product(name: "ChatLayout", package: "ChatLayout"), .product(name: "DifferenceKit", package: "DifferenceKit"), .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"), @@ -351,7 +372,6 @@ let package = Package( .target(name: "CountryListFeature"), .product(name: "Retry", package: "Retry"), .product(name: "XXDatabase", package: "client-ios-db"), - .product(name: "DependencyInjection", package: "xxm-di"), ] ), .target( @@ -360,8 +380,12 @@ let package = Package( .target(name: "Shared"), .target(name: "Defaults"), .target(name: "PushFeature"), + .target(name: "UpdateErrors"), + .target(name: "CheckVersion"), .target(name: "BackupFeature"), + .target(name: "FetchBannedList"), .target(name: "ReportingFeature"), + .target(name: "ProcessBannedList"), .target(name: "RequestPermissionFeature"), .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"), .product(name: "CloudFilesSFTP", package: "xxm-cloud-providers"), @@ -376,7 +400,54 @@ let package = Package( dependencies: [ .target(name: "Shared"), .target(name: "Defaults"), - .product(name: "Navigation", package: "xxm-navigation"), + .target(name: "AppNavigation"), + ] + ), + .target( + name: "UpdateErrors", + dependencies: [ + .product( + name: "XXClient", + package: "elixxir-dapps-sdk-swift" + ), + .product( + name: "XCTestDynamicOverlay", + package: "xctest-dynamic-overlay" + ), + .product( + name: "Dependencies", + package: "swift-composable-architecture" + ), + ] + ), + .target( + name: "ProcessBannedList", + dependencies: [ + .product( + name: "SwiftCSV", + package: "SwiftCSV" + ), + .product( + name: "XCTestDynamicOverlay", + package: "xctest-dynamic-overlay" + ), + .product( + name: "Dependencies", + package: "swift-composable-architecture" + ), + ] + ), + .target( + name: "FetchBannedList", + dependencies: [ + .product( + name: "XCTestDynamicOverlay", + package: "xctest-dynamic-overlay" + ), + .product( + name: "Dependencies", + package: "swift-composable-architecture" + ), ] ), .target( @@ -384,8 +455,10 @@ let package = Package( dependencies: [ .target(name: "Shared"), .target(name: "ContactFeature"), - .product(name: "DependencyInjection", package: "xxm-di"), - .product(name: "DifferenceKit", package: "DifferenceKit"), + .product( + name: "DifferenceKit", + package: "DifferenceKit" + ), ] ), .target( @@ -400,8 +473,6 @@ let package = Package( .target(name: "BackupFeature"), .target(name: "CountryListFeature"), .target(name: "RequestPermissionFeature"), - .product(name: "Navigation", package: "xxm-navigation"), - .product(name: "DependencyInjection", package: "xxm-di"), .product(name: "CombineSchedulers", package: "combine-schedulers"), .product(name: "ScrollViewController", package: "ScrollViewController"), .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"), @@ -418,7 +489,6 @@ let package = Package( .target(name: "ProfileFeature"), .target(name: "SettingsFeature"), .target(name: "ContactListFeature"), - .product(name: "DependencyInjection", package: "xxm-di"), .product(name: "DifferenceKit", package: "DifferenceKit"), ] ), @@ -432,6 +502,7 @@ let package = Package( .target(name: "InputField"), .target(name: "PushFeature"), .target(name: "DrawerFeature"), + .target(name: "AppNavigation"), .target(name: "CountryListFeature"), .target(name: "RequestPermissionFeature"), .product(name: "CombineSchedulers", package: "combine-schedulers"), @@ -443,12 +514,14 @@ let package = Package( name: "MenuFeature", dependencies: [ .target(name: "Shared"), + .target(name: "AppCore"), .target(name: "Defaults"), .target(name: "DrawerFeature"), .target(name: "ReportingFeature"), - .product(name: "Navigation", package: "xxm-navigation"), - .product(name: "DependencyInjection", package: "xxm-di"), - .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"), + .product( + name: "XXClient", + package: "elixxir-dapps-sdk-swift" + ), ] ), .target( @@ -458,10 +531,6 @@ let package = Package( .target(name: "AppCore"), .target(name: "InputField"), .target(name: "DrawerFeature"), - .product( - name: "Navigation", - package: "xxm-navigation" - ), .product( name: "XXClient", package: "elixxir-dapps-sdk-swift" @@ -499,7 +568,6 @@ let package = Package( .target(name: "ContactFeature"), .target(name: "CountryListFeature"), .target(name: "RequestPermissionFeature"), - .product(name: "DependencyInjection", package: "xxm-di"), .product(name: "SnapKit", package: "SnapKit"), ] ), @@ -508,7 +576,6 @@ let package = Package( dependencies: [ .target(name: "Shared"), .target(name: "ContactFeature"), - .product(name: "DependencyInjection", package: "xxm-di"), .product(name: "DifferenceKit", package: "DifferenceKit"), ] ), @@ -521,9 +588,9 @@ let package = Package( .target(name: "InputField"), .target(name: "PushFeature"), .target(name: "MenuFeature"), + .target(name: "CrashReport"), .target(name: "DrawerFeature"), .target(name: "RequestPermissionFeature"), - .product(name: "DependencyInjection", package: "xxm-di"), .product(name: "CombineSchedulers", package: "combine-schedulers"), .product(name: "ScrollViewController", package: "ScrollViewController"), ] @@ -533,7 +600,6 @@ let package = Package( dependencies: [ .target(name: "DrawerFeature"), .target(name: "Shared"), - .product(name: "SwiftCSV", package: "SwiftCSV"), .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), ], resources: [ diff --git a/Sources/AppCore/AppDependencies.swift b/Sources/AppCore/AppDependencies.swift index 1f3ec64e5898e606f93ed1e8957a6f7a2279d3ba..f974596a680bfe8b671315094fc07287bb661787 100644 --- a/Sources/AppCore/AppDependencies.swift +++ b/Sources/AppCore/AppDependencies.swift @@ -1,13 +1,16 @@ +import XXClient import Foundation import XXMessengerClient import XCTestDynamicOverlay import ComposableArchitecture public struct AppDependencies { - public var networkMonitor: NetworkMonitorManager + public var networkMonitor: NetworkMonitor public var toastManager: ToastManager + public var backupHandler: BackupCallbackHandler public var hudManager: HUDManager public var dbManager: DBManager + public var statusBar: StatusBarStylist public var messenger: Messenger public var authHandler: AuthCallbackHandler public var backupStorage: BackupStorage @@ -24,7 +27,11 @@ public struct AppDependencies { extension AppDependencies { public static func live() -> AppDependencies { - let dbManager = DBManager.live() + let dbManager = DBManager.live( + url: FileManager.default.containerURL( + forSecurityApplicationGroupIdentifier: "group.elixxir.messenger" + )! + ) var messengerEnv = MessengerEnvironment.live() messengerEnv.udEnvironment = .init( address: Constants.address, @@ -41,12 +48,20 @@ extension AppDependencies { return AppDependencies( networkMonitor: .live(), toastManager: .live(), + backupHandler: .live( + messenger: messenger + ), hudManager: .live(), dbManager: dbManager, + statusBar: .live(), messenger: messenger, authHandler: .live( messenger: messenger, - handleRequest: .live(db: dbManager.getDB, now: now), + handleRequest: .live( + db: dbManager.getDB, + messenger: messenger, + now: now + ), handleConfirm: .live(db: dbManager.getDB), handleReset: .live(db: dbManager.getDB) ), @@ -81,8 +96,10 @@ extension AppDependencies { public static let unimplemented = AppDependencies( networkMonitor: .unimplemented, toastManager: .unimplemented, + backupHandler: .unimplemented, hudManager: .unimplemented, dbManager: .unimplemented, + statusBar: .unimplemented, messenger: .unimplemented, authHandler: .unimplemented, backupStorage: .unimplemented, @@ -142,3 +159,27 @@ tgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E5 <xxc(2)7mbKFLE201WzH4SGxAOpHjjehwztIV+KGifi5L/PYPcDkAZiB9kZo+Dl3Vc7dD2SdZCFMOJVgwqGzfYRDkjc8RGEllBqNxq2sRRX09iQVef0kJQUgJCHNCOcvm6Ki0JJwvjLceyFh36iwK8oLbhLgqEZY86UScdACTyBCzBIab3ob5mBthYc3mheV88yq5PGF2DQ+dEvueUm+QhOSfwzppAJA/rpW9Wq9xzYcQzaqc3ztAGYfm2BBAHS7HVmkCbvZ/K07Xrl4EBPGHJYq12tWAN/C3mcbbBYUOQXyEzbSl/mO7sL3ORr0B4FMuqCi8EdlD6RO52pVhY+Cg6roRH1t5Ng1JxPt8Mv1yyjbifPhZ5fLKwxBz8UiFORfk0/jnhwgm25LRHqtNRRUlYXLvhv0HhqyYTUt17WNtCLATSVbqLrFGdy2EGadn8mP+kQNHp93f27d/uHgBNNe7LpuYCJMdWpoG6bOqmHEftxt0/MIQA8fTtTm3jJzv+7/QjZJDvQIv0SNdp8HFogpuwde+GuS4BcY7v5xz+ArGWcRR63ct2z83MqQEn9ODr1/gAAAgA7szRpDDQIdFUQo9mkWg8xBA==xxc> """ } + +private enum StoredDummyTrafficKey: DependencyKey { + static var liveValue = Stored<DummyTraffic?>.inMemory() + static var testValue = Stored<DummyTraffic?>.unimplemented() +} + +extension DependencyValues { + public var dummyTraffic: Stored<DummyTraffic?> { + get { self[StoredDummyTrafficKey.self] } + set { self[StoredDummyTrafficKey.self] = newValue } + } +} + +private enum StoredNewGroupChatKey: DependencyKey { + static var liveValue = Stored<GroupChat?>.inMemory() + static var testValue = Stored<GroupChat?>.unimplemented() +} + +extension DependencyValues { + public var groupManager: Stored<GroupChat?> { + get { self[StoredNewGroupChatKey.self] } + set { self[StoredNewGroupChatKey.self] = newValue } + } +} diff --git a/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandler.swift b/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandler.swift index c6e03cbedb7f1d0020d11a94c6fc0172a5fc5247..be0ca1047096e8390ce9b98b03a16b8906b878f2 100644 --- a/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandler.swift +++ b/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandler.swift @@ -1,8 +1,8 @@ -import Foundation -import XCTestDynamicOverlay +import XXModels import XXClient +import Foundation import XXMessengerClient -import XXModels +import XCTestDynamicOverlay public struct AuthCallbackHandler { public typealias OnError = (Error) -> Void diff --git a/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandlerConfirm.swift b/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandlerConfirm.swift index 2aa6787fff741651d649fb8f88397669b448d92f..60d86e9a66eb83a792aaa7d01b664fd62aaa47da 100644 --- a/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandlerConfirm.swift +++ b/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandlerConfirm.swift @@ -18,6 +18,7 @@ extension AuthCallbackHandlerConfirm { guard var dbContact = try db().fetchContacts(.init(id: [id])).first else { return } + dbContact.isRecent = true dbContact.authStatus = .friend dbContact = try db().saveContact(dbContact) } diff --git a/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandlerRequest.swift b/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandlerRequest.swift index 6d1943e923732bc6dbdafe053deeb962191b7d13..4ea820682a7c8c5f772e77fba26b05f3aba94496 100644 --- a/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandlerRequest.swift +++ b/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandlerRequest.swift @@ -1,8 +1,8 @@ -import Foundation -import XCTestDynamicOverlay +import XXModels import XXClient +import Foundation import XXMessengerClient -import XXModels +import XCTestDynamicOverlay public struct AuthCallbackHandlerRequest { public var run: (XXClient.Contact) throws -> Void @@ -15,6 +15,7 @@ public struct AuthCallbackHandlerRequest { extension AuthCallbackHandlerRequest { public static func live( db: DBManagerGetDB, + messenger: Messenger, now: @escaping () -> Date ) -> AuthCallbackHandlerRequest { AuthCallbackHandlerRequest { xxContact in @@ -27,9 +28,21 @@ extension AuthCallbackHandlerRequest { dbContact.username = try xxContact.getFact(.username)?.value dbContact.email = try xxContact.getFact(.email)?.value dbContact.phone = try xxContact.getFact(.phone)?.value - dbContact.authStatus = .stranger + dbContact.authStatus = .verificationInProgress dbContact.createdAt = now() dbContact = try db().saveContact(dbContact) + do { + try messenger.waitForNetwork() + if try messenger.verifyContact(xxContact) { + dbContact.authStatus = .verified + dbContact = try db().saveContact(dbContact) + } else { + try db().deleteContact(dbContact) + } + } catch { + dbContact.authStatus = .verificationFailed + dbContact = try db().saveContact(dbContact) + } } } } diff --git a/Sources/AppCore/BackupCallbackHandler/BackupCallbackHandler.swift b/Sources/AppCore/BackupCallbackHandler/BackupCallbackHandler.swift new file mode 100644 index 0000000000000000000000000000000000000000..d3ddb18feb39077fd2979b0fecc9e65dcd8b1837 --- /dev/null +++ b/Sources/AppCore/BackupCallbackHandler/BackupCallbackHandler.swift @@ -0,0 +1,45 @@ +import XXClient +import Foundation +import XXMessengerClient +import XCTestDynamicOverlay + +public struct BackupCallbackHandler { + public typealias OnError = (Error) -> Void + + public var run: (@escaping OnError) -> Cancellable + + public func callAsFunction(onError: @escaping OnError) -> Cancellable { + run(onError) + } +} + +extension BackupCallbackHandler { + public static func live( + messenger: Messenger + ) -> BackupCallbackHandler { + BackupCallbackHandler { onError in + let callback = UpdateBackupFunc { data in + do { + let url = try FileManager.default.url( + for: .applicationSupportDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: true + ) + .appendingPathComponent("backup") + .appendingPathExtension("xxm") + try data.write(to: url) + } catch { + onError(error) + } + } + return messenger.registerBackupCallback(callback) + } + } +} + +extension BackupCallbackHandler { + public static let unimplemented = BackupCallbackHandler( + run: XCTUnimplemented("\(Self.self)", placeholder: Cancellable {}) + ) +} diff --git a/Sources/AppCore/DBManager/DBManagerMakeDB.swift b/Sources/AppCore/DBManager/DBManagerMakeDB.swift index 4376f12fb3e5630ce0f9d05cd27474b9dd75515c..a3e514ae75ae674e9bf6a10f951fbd4957b6c72e 100644 --- a/Sources/AppCore/DBManager/DBManagerMakeDB.swift +++ b/Sources/AppCore/DBManager/DBManagerMakeDB.swift @@ -21,7 +21,7 @@ extension DBManagerMakeDB { .createDirectory(at: url, withIntermediateDirectories: true) let dbFilePath = url - .appendingPathComponent("db") + .appendingPathComponent("xxm_database") .appendingPathExtension("sqlite") .path diff --git a/Sources/AppCore/Logger/Logger.swift b/Sources/AppCore/Logger/Logger.swift index fbb48f0d2dff23711420e5d7bf80bc3c8a160198..60fc7bf3a56e55125324c3142dea8310e5aa027e 100644 --- a/Sources/AppCore/Logger/Logger.swift +++ b/Sources/AppCore/Logger/Logger.swift @@ -4,6 +4,7 @@ import XCTestDynamicOverlay public struct Logger { public enum Message: Equatable { + case info(String) case error(NSError) } @@ -24,6 +25,13 @@ extension Logger { let logger = Logging.Logger(label: "xx.messenger") return Logger { msg, file, function, line in switch msg { + case .info(let text): + logger.info( + .init(stringLiteral: text), + file: file, + function: function, + line: line + ) case .error(let error): logger.error( .init(stringLiteral: error.localizedDescription), diff --git a/Sources/AppCore/MessageListenerHandler/MessageListenerHandler.swift b/Sources/AppCore/MessageListenerHandler/MessageListenerHandler.swift index 70fbace772986d506de4ee2338445a7c656bae8e..8103b2674704e57f682006e6e23cd4f263fa8bca 100644 --- a/Sources/AppCore/MessageListenerHandler/MessageListenerHandler.swift +++ b/Sources/AppCore/MessageListenerHandler/MessageListenerHandler.swift @@ -1,8 +1,8 @@ -import Foundation -import XCTestDynamicOverlay +import XXModels import XXClient +import Foundation import XXMessengerClient -import XXModels +import XCTestDynamicOverlay public struct MessageListenerHandler { public typealias OnError = (Error) -> Void @@ -32,6 +32,7 @@ extension MessageListenerHandler { status: .received, isUnread: true, text: payload.text, + replyMessageId: payload.replyingTo, roundURL: message.roundURL )) } catch { diff --git a/Sources/AppCore/Models/MessagePayload.swift b/Sources/AppCore/Models/MessagePayload.swift index 67fb94d905b06ad25816ca8c4d11081c72053f19..7ae774337944fdb62b7c5b862cfdd28d7a3a3295 100644 --- a/Sources/AppCore/Models/MessagePayload.swift +++ b/Sources/AppCore/Models/MessagePayload.swift @@ -1,16 +1,22 @@ import Foundation public struct MessagePayload: Equatable { - public init(text: String) { + public init( + text: String, + replyingTo: Data? = nil + ) { self.text = text + self.replyingTo = replyingTo } public var text: String + public var replyingTo: Data? } extension MessagePayload: Codable { enum CodingKeys: String, CodingKey { case text + case replyingTo } public static func decode(_ data: Data) throws -> Self { diff --git a/Sources/AppCore/NetworkMonitor/GetNetworkConnType.swift b/Sources/AppCore/NetworkMonitor/GetNetworkConnType.swift new file mode 100644 index 0000000000000000000000000000000000000000..99b460f76069535017a7cb730c3def9e3888bcf6 --- /dev/null +++ b/Sources/AppCore/NetworkMonitor/GetNetworkConnType.swift @@ -0,0 +1,19 @@ +import XCTestDynamicOverlay + +public struct GetNetworkConnType { + public init(run: @escaping () -> NetworkMonitor.ConnType) { + self.run = run + } + + public var run: () -> NetworkMonitor.ConnType + + public func callAsFunction() -> NetworkMonitor.ConnType { + run() + } +} + +extension GetNetworkConnType { + public static let unimplemented = GetNetworkConnType( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/AppCore/NetworkMonitor/GetNetworkStatus.swift b/Sources/AppCore/NetworkMonitor/GetNetworkStatus.swift new file mode 100644 index 0000000000000000000000000000000000000000..e8d6cfb053ccfb6801e206d2f6beb6976d157e29 --- /dev/null +++ b/Sources/AppCore/NetworkMonitor/GetNetworkStatus.swift @@ -0,0 +1,19 @@ +import XCTestDynamicOverlay + +public struct GetNetworkStatus { + public init(run: @escaping () -> NetworkMonitor.Status) { + self.run = run + } + + public var run: () -> NetworkMonitor.Status + + public func callAsFunction() -> NetworkMonitor.Status { + run() + } +} + +extension GetNetworkStatus { + public static let unimplemented = GetNetworkStatus( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/AppCore/NetworkMonitorManager/NetworkMonitorManager.swift b/Sources/AppCore/NetworkMonitor/NetworkMonitor.swift similarity index 60% rename from Sources/AppCore/NetworkMonitorManager/NetworkMonitorManager.swift rename to Sources/AppCore/NetworkMonitor/NetworkMonitor.swift index e77d4c63087711bff028f6cd78f1b896b399a888..db79b79d26f877fbd3b04470378a4d434f2e6cfd 100644 --- a/Sources/AppCore/NetworkMonitorManager/NetworkMonitorManager.swift +++ b/Sources/AppCore/NetworkMonitor/NetworkMonitor.swift @@ -1,7 +1,7 @@ import Combine import Network -public struct NetworkMonitorManager { +public struct NetworkMonitor { public enum Status: Equatable { case unknown case available @@ -15,14 +15,15 @@ public struct NetworkMonitorManager { case cellular } - public var start: NetworkMonitorStart - public var update: NetworkMonitorUpdate - public var status: NetworkMonitorStatus - public var connType: NetworkMonitorConnType + public var start: StartNetworkMonitor + public var update: UpdateNetworkStatus + public var getStatus: GetNetworkStatus + public var connType: GetNetworkConnType + public var observeStatus: ObserveNetworkStatus } -extension NetworkMonitorManager { - public static func live() -> NetworkMonitorManager { +extension NetworkMonitor { + public static func live() -> NetworkMonitor { class Context { var monitor = NWPathMonitor() let xxAvailability = CurrentValueSubject<Bool?, Never>(nil) @@ -54,7 +55,7 @@ extension NetworkMonitorManager { update: .init { context.xxAvailability.send($0) }, - status: .init { + getStatus: .init { guard let xxAvailability = context.xxAvailability.value else { return .xxNotAvailable } @@ -62,16 +63,34 @@ extension NetworkMonitorManager { }, connType: .init { context.currentConnType.value + }, + observeStatus: .init { + context + .internetAvailability + .combineLatest(context.xxAvailability) + .map { (internet, xx) -> Status in + guard let internet, let xx else { return .unknown} + switch (internet, xx) { + case (true, true): + return .available + case (true, false): + return .xxNotAvailable + case (false, _): + return .internetNotAvailable + } + }.removeDuplicates() + .eraseToAnyPublisher() } ) } } -extension NetworkMonitorManager { - public static let unimplemented = NetworkMonitorManager( +extension NetworkMonitor { + public static let unimplemented = NetworkMonitor( start: .unimplemented, update: .unimplemented, - status: .unimplemented, - connType: .unimplemented + getStatus: .unimplemented, + connType: .unimplemented, + observeStatus: .unimplemented ) } diff --git a/Sources/AppCore/NetworkMonitor/ObserveNetworkStatus.swift b/Sources/AppCore/NetworkMonitor/ObserveNetworkStatus.swift new file mode 100644 index 0000000000000000000000000000000000000000..f6df5d96ebc6f4b243fbd8afa84aa5f45c39bec6 --- /dev/null +++ b/Sources/AppCore/NetworkMonitor/ObserveNetworkStatus.swift @@ -0,0 +1,20 @@ +import Combine +import XCTestDynamicOverlay + +public struct ObserveNetworkStatus { + public init(run: @escaping () -> AnyPublisher<NetworkMonitor.Status, Never>) { + self.run = run + } + + public var run: () -> AnyPublisher<NetworkMonitor.Status, Never> + + public func callAsFunction() -> AnyPublisher<NetworkMonitor.Status, Never> { + run() + } +} + +extension ObserveNetworkStatus { + public static let unimplemented = ObserveNetworkStatus( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/AppCore/NetworkMonitorManager/NetworkMonitorStart.swift b/Sources/AppCore/NetworkMonitor/StartNetworkMonitor.swift similarity index 65% rename from Sources/AppCore/NetworkMonitorManager/NetworkMonitorStart.swift rename to Sources/AppCore/NetworkMonitor/StartNetworkMonitor.swift index 4bb08bdbdd23a28b04c3b7f1a13bfbf53995e0a1..6fa6e45a7f79d810d9ae341293cb44761e03cbb5 100644 --- a/Sources/AppCore/NetworkMonitorManager/NetworkMonitorStart.swift +++ b/Sources/AppCore/NetworkMonitor/StartNetworkMonitor.swift @@ -1,6 +1,6 @@ import XCTestDynamicOverlay -public struct NetworkMonitorStart { +public struct StartNetworkMonitor { public init(run: @escaping () -> Void) { self.run = run } @@ -12,8 +12,8 @@ public struct NetworkMonitorStart { } } -extension NetworkMonitorStart { - public static let unimplemented = NetworkMonitorStart( +extension StartNetworkMonitor { + public static let unimplemented = StartNetworkMonitor( run: XCTUnimplemented("\(Self.self)") ) } diff --git a/Sources/AppCore/NetworkMonitorManager/NetworkMonitorUpdate.swift b/Sources/AppCore/NetworkMonitor/UpdateNetworkStatus.swift similarity index 67% rename from Sources/AppCore/NetworkMonitorManager/NetworkMonitorUpdate.swift rename to Sources/AppCore/NetworkMonitor/UpdateNetworkStatus.swift index 6659aad330ce06f45306cd484c24835d67d83e30..825945096def2084b6bd9c135bba04291ec60a53 100644 --- a/Sources/AppCore/NetworkMonitorManager/NetworkMonitorUpdate.swift +++ b/Sources/AppCore/NetworkMonitor/UpdateNetworkStatus.swift @@ -1,6 +1,6 @@ import XCTestDynamicOverlay -public struct NetworkMonitorUpdate { +public struct UpdateNetworkStatus { public init(run: @escaping (Bool) -> Void) { self.run = run } @@ -12,8 +12,8 @@ public struct NetworkMonitorUpdate { } } -extension NetworkMonitorUpdate { - public static let unimplemented = NetworkMonitorUpdate( +extension UpdateNetworkStatus { + public static let unimplemented = UpdateNetworkStatus( run: XCTUnimplemented("\(Self.self)") ) } diff --git a/Sources/AppCore/NetworkMonitorManager/NetworkMonitorConnType.swift b/Sources/AppCore/NetworkMonitorManager/NetworkMonitorConnType.swift deleted file mode 100644 index 8340f33fd448d9dfd87d76c30dc736a452375c38..0000000000000000000000000000000000000000 --- a/Sources/AppCore/NetworkMonitorManager/NetworkMonitorConnType.swift +++ /dev/null @@ -1,19 +0,0 @@ -import XCTestDynamicOverlay - -public struct NetworkMonitorConnType { - public init(run: @escaping () -> NetworkMonitorManager.ConnType) { - self.run = run - } - - public var run: () -> NetworkMonitorManager.ConnType - - public func callAsFunction() -> NetworkMonitorManager.ConnType { - run() - } -} - -extension NetworkMonitorConnType { - public static let unimplemented = NetworkMonitorConnType( - run: XCTUnimplemented("\(Self.self)") - ) -} diff --git a/Sources/AppCore/NetworkMonitorManager/NetworkMonitorStatus.swift b/Sources/AppCore/NetworkMonitorManager/NetworkMonitorStatus.swift deleted file mode 100644 index afc3169ce97622e94ec6bf74259512cc3e378d44..0000000000000000000000000000000000000000 --- a/Sources/AppCore/NetworkMonitorManager/NetworkMonitorStatus.swift +++ /dev/null @@ -1,19 +0,0 @@ -import XCTestDynamicOverlay - -public struct NetworkMonitorStatus { - public init(run: @escaping () -> NetworkMonitorManager.Status) { - self.run = run - } - - public var run: () -> NetworkMonitorManager.Status - - public func callAsFunction() -> NetworkMonitorManager.Status { - run() - } -} - -extension NetworkMonitorStatus { - public static let unimplemented = NetworkMonitorStatus( - run: XCTUnimplemented("\(Self.self)") - ) -} diff --git a/Sources/AppCore/RootViewController.swift b/Sources/AppCore/RootViewController.swift index eb7db899e63a9c1687ba4b9d6ab22f9abbf58a62..5082447d2980bda881d3c8094ea10a721a638c85 100644 --- a/Sources/AppCore/RootViewController.swift +++ b/Sources/AppCore/RootViewController.swift @@ -1,11 +1,10 @@ import UIKit import Combine -import StatusBarFeature -import ComposableArchitecture +import Dependencies public final class RootViewController: UIViewController { + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist @Dependency(\.app.hudManager) var hudManager: HUDManager - @Dependency(\.statusBar) var statusBar: StatusBarStyleManager @Dependency(\.app.toastManager) var toastManager: ToastManager var hud: HUDView? @@ -24,7 +23,7 @@ public final class RootViewController: UIViewController { required init?(coder: NSCoder) { nil } public override var preferredStatusBarStyle: UIStatusBarStyle { - statusBar.current() + statusBar.get() } public override func viewDidLoad() { @@ -73,8 +72,6 @@ public final class RootViewController: UIViewController { } } -// MARK: - Toaster - extension RootViewController { @objc private func didPanToast(_ sender: UIPanGestureRecognizer) { guard let toastView = sender.view else { return } @@ -167,8 +164,6 @@ extension RootViewController { } } -// MARK: - HUD - extension RootViewController { private func add(hudView: HUDView) { if let hud { @@ -196,62 +191,4 @@ extension RootViewController { hud = hudView } - - // if statusSubject.value.isPresented == true && status.isPresented == true { - // self.errorView = nil - // self.animation = nil - // self.window = nil - // self.actionButton = nil - // self.titleLabel = nil - // - // switch status { - // case .on: - // animation = DotAnimation() - // - // case .onTitle(let text): - // animation = DotAnimation() - // titleLabel = UILabel() - // titleLabel!.text = text - // - // case .onAction(let title): - // animation = DotAnimation() - // actionButton = CapsuleButton() - // actionButton!.set(style: .seeThroughWhite, title: title) - // - // case .error(let error): - // errorView = ErrorView(with: error) - // case .none: - // break - // } - // - // showWindow() - // } - - // if statusSubject.value.isPresented == false && status.isPresented == true { - // switch status { - // case .on: - // animation = DotAnimation() - // - // case .onTitle(let text): - // animation = DotAnimation() - // titleLabel = UILabel() - // titleLabel!.text = text - // - // case .onAction(let title): - // animation = DotAnimation() - // actionButton = CapsuleButton() - // actionButton!.set(style: .seeThroughWhite, title: title) - // - // case .error(let error): - // errorView = ErrorView(with: error) - // case .none: - // break - // } - // - // showWindow() - // } - - // if statusSubject.value.isPresented == true && status.isPresented == false { - // hideWindow() - // } } diff --git a/Sources/AppCore/SendMessage/SendMessage.swift b/Sources/AppCore/SendMessage/SendMessage.swift index f675dc0d2322cbe3b7611d92823eece9b5420d42..d8ad8813c09b85fc9a6c33ef674680cb8943a2a3 100644 --- a/Sources/AppCore/SendMessage/SendMessage.swift +++ b/Sources/AppCore/SendMessage/SendMessage.swift @@ -8,15 +8,16 @@ public struct SendMessage { public typealias OnError = (Error) -> Void public typealias Completion = () -> Void - public var run: (String, Data, @escaping OnError, @escaping Completion) -> Void + public var run: (String, Data?, Data, @escaping OnError, @escaping Completion) -> Void public func callAsFunction( text: String, + replyingTo: Data?, to recipientId: Data, onError: @escaping OnError, completion: @escaping Completion ) { - run(text, recipientId, onError, completion) + run(text, replyingTo, recipientId, onError, completion) } } @@ -26,7 +27,7 @@ extension SendMessage { db: DBManagerGetDB, now: @escaping () -> Date ) -> SendMessage { - SendMessage { text, recipientId, onError, completion in + SendMessage { text, replyingTo, recipientId, onError, completion in do { let myContactId = try messenger.e2e.tryGet().getContact().getId() let message = try db().saveMessage(.init( @@ -36,9 +37,10 @@ extension SendMessage { date: now(), status: .sending, isUnread: false, - text: text + text: text, + replyMessageId: replyingTo )) - let payload = MessagePayload(text: message.text) + let payload = MessagePayload(text: message.text, replyingTo: replyingTo) let report = try messenger.sendMessage( recipientId: recipientId, payload: try payload.encode(), diff --git a/Sources/AppCore/StatusBar/StatusBarStylist.swift b/Sources/AppCore/StatusBar/StatusBarStylist.swift new file mode 100644 index 0000000000000000000000000000000000000000..7eaa87fdcf0db2ac136179bcc86b11cd718fd7c9 --- /dev/null +++ b/Sources/AppCore/StatusBar/StatusBarStylist.swift @@ -0,0 +1,59 @@ +import UIKit +import Combine +import XCTestDynamicOverlay + +public struct StatusBarStylist { + public var set: SetStyle + public var get: GetStyle + public var observe: ObserveStyle + + public static func live() -> StatusBarStylist { + let styleSubject = CurrentValueSubject<UIStatusBarStyle, Never>(.lightContent) + return .init( + set: .init { styleSubject.send($0) }, + get: .init { styleSubject.value }, + observe: .init { styleSubject.eraseToAnyPublisher() } + ) + } + public static let unimplemented = StatusBarStylist( + set: .unimplemented, + get: .unimplemented, + observe: .unimplemented + ) +} + +public struct GetStyle { + public var run: () -> UIStatusBarStyle + + public func callAsFunction() -> UIStatusBarStyle { + run() + } + + public static let unimplemented = GetStyle( + run: XCTUnimplemented("\(Self.self)") + ) +} + +public struct SetStyle { + public var run: (UIStatusBarStyle) -> Void + + public func callAsFunction(_ style: UIStatusBarStyle) -> Void { + run(style) + } + + public static let unimplemented = SetStyle( + run: XCTUnimplemented("\(Self.self)") + ) +} + +public struct ObserveStyle { + public var run: () -> AnyPublisher<UIStatusBarStyle, Never> + + public func callAsFunction() -> AnyPublisher<UIStatusBarStyle, Never> { + run() + } + + public static let unimplemented = ObserveStyle( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/AppFeature/AppDelegate.swift b/Sources/AppFeature/AppDelegate.swift index c4dc80dab5df9e71b580a6e7525fd90858c0d414..7cc572aa26a9f469c6787c4927b29da6b821df77 100644 --- a/Sources/AppFeature/AppDelegate.swift +++ b/Sources/AppFeature/AppDelegate.swift @@ -1,224 +1,19 @@ import UIKit -import BackgroundTasks - -import Shared -import Defaults -import PushFeature +import AppCore import LaunchFeature -import CrashReporting -import DI - -import XXModels -import XXLogger -import XXClient -import Navigation -import XXMessengerClient - -import CloudFiles -import CloudFilesDrive -import CloudFilesICloud -import CloudFilesDropbox public class AppDelegate: UIResponder, UIApplicationDelegate { - @Dependency var navigator: Navigator - @Dependency var pushRouter: PushRouter - @Dependency var pushHandler: PushHandling - @Dependency var crashReporter: CrashReporter - - @KeyObject(.hideAppList, defaultValue: false) var hideAppList: Bool - @KeyObject(.recordingLogs, defaultValue: true) var recordingLogs: Bool - @KeyObject(.crashReporting, defaultValue: true) var isCrashReportingEnabled: Bool - - var calledStopNetwork = false - var forceFailedPendingMessages = false - - var coverView: UIView? - var backgroundTimer: Timer? public var window: UIWindow? public func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - UNUserNotificationCenter.current().delegate = self - setupCloudFilesManagers() - setupCrashReporting() - setupLogging() let navController = UINavigationController(rootViewController: LaunchController()) - let rootViewController = RootViewController(navController) window = UIWindow(frame: UIScreen.main.bounds) - window?.rootViewController = rootViewController + window?.rootViewController = RootViewController(navController) window?.makeKeyAndVisible() - - DependencyRegistrator.registerNavigators(navController) - - DI.Container.shared.register( - PushRouter.live(navigationController: navController) - ) return true } - - public func application(application: UIApplication, shouldAllowExtensionPointIdentifier: String) -> Bool { - false - } - - public func applicationDidEnterBackground(_ application: UIApplication) { - if let messenger = try? DI.Container.shared.resolve() as Messenger, - let database = try? DI.Container.shared.resolve() as Database, - let cMix = try? messenger.cMix.tryGet() { - let backgroundTask = application.beginBackgroundTask(withName: "xx.stop.network") {} - - backgroundTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in - print(">>> .backgroundTimeRemaining: \(UIApplication.shared.backgroundTimeRemaining)") - - guard UIApplication.shared.backgroundTimeRemaining > 8 else { - if !self.calledStopNetwork { - self.calledStopNetwork = true - try! messenger.stop() - print(">>> Called stopNetworkFollower") - } else { - if cMix.hasRunningProcesses() == false { - application.endBackgroundTask(backgroundTask) - timer.invalidate() - } - } - - return - } - - guard UIApplication.shared.backgroundTimeRemaining > 9 else { - if !self.forceFailedPendingMessages { - self.forceFailedPendingMessages = true - let query = Message.Query(status: [.sending]) - let assignment = Message.Assignments(status: .sendingFailed) - _ = try? database.bulkUpdateMessages(query, assignment) - } - return - } - }) - } - } - - public func applicationWillResignActive(_ application: UIApplication) { - if hideAppList { - coverView?.removeFromSuperview() - coverView = UIVisualEffectView(effect: UIBlurEffect(style: .regular)) - coverView?.frame = window?.bounds ?? .zero - window?.addSubview(coverView!) - } - } - - public func applicationWillTerminate(_ application: UIApplication) { - if let messenger = try? DI.Container.shared.resolve() as Messenger { - try? messenger.stop() - } - } - - public func applicationWillEnterForeground(_ application: UIApplication) { - if backgroundTimer != nil { - backgroundTimer?.invalidate() - backgroundTimer = nil - print(">>> Invalidated background timer") - } - - if let messenger = try? DI.Container.shared.resolve() as Messenger, - let cMix = messenger.cMix.get() { - guard self.calledStopNetwork == true else { return } - try? cMix.startNetworkFollower(timeoutMS: 10_000) - print(">>> Called startNetworkFollower") - self.calledStopNetwork = false - } - } - - public func applicationDidBecomeActive(_ application: UIApplication) { - application.applicationIconBadgeNumber = 0 - coverView?.removeFromSuperview() - } - - public func application( - _ app: UIApplication, - open url: URL, - options: [UIApplication.OpenURLOptionsKey : Any] = [:] - ) -> Bool { - handleRedirectURL(url) - } - - public func application( - _ application: UIApplication, - continue userActivity: NSUserActivity, - restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void - ) -> Bool { - guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, - let incomingURL = userActivity.webpageURL, - let username = getUsernameFromInvitationDeepLink(incomingURL), - let router = try? DI.Container.shared.resolve() as PushRouter else { - return false - } - - router.navigateTo(.search(username: username), {}) - return true - } -} - -extension AppDelegate: UNUserNotificationCenterDelegate { - public func userNotificationCenter( - _ center: UNUserNotificationCenter, - didReceive response: UNNotificationResponse, - withCompletionHandler completionHandler: @escaping () -> Void - ) { - let userInfo = response.notification.request.content.userInfo - pushHandler.handleAction(pushRouter, userInfo, completionHandler) - } - - public func application( - _ application: UIApplication, - didReceiveRemoteNotification notification: [AnyHashable: Any], - fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void - ) { - pushHandler.handlePush(notification, completionHandler) - } - - public func application( - _: UIApplication, - didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data - ) { - pushHandler.registerToken(deviceToken) - } -} - -extension AppDelegate { - private func setupCrashReporting() { - crashReporter.configure() - crashReporter.setEnabled(isCrashReportingEnabled) - } - - private func setupCloudFilesManagers() { - CloudFilesManager.all[.icloud] = .iCloud(fileName: "backup.xxm") - CloudFilesManager.all[.dropbox] = .dropbox(appKey: "ppx0de5f16p9aq2", path: "/backup/backup.xxm") - CloudFilesManager.all[.drive] = .drive( - apiKey: "AIzaSyCbI2yQ7pbuVSRvraqanjGcS9CDrjD7lNU", - clientId: "662236151640-herpu89qikpfs9m4kvbi9bs5fpdji5de.apps.googleusercontent.com", - fileName: "backup.xxm" - ) - } - - private func setupLogging() { - if recordingLogs { - XXLogger.start() - } - } -} - -func getUsernameFromInvitationDeepLink(_ url: URL) -> String? { - if let components = URLComponents(url: url, resolvingAgainstBaseURL: false), - components.scheme == "https", - components.host == "elixxir.io", - components.path == "/connect", - let queryItem = components.queryItems?.first(where: { $0.name == "username" }), - let username = queryItem.value { - return username - } - - return nil } diff --git a/Sources/AppFeature/DependencyRegistrator.swift b/Sources/AppFeature/DependencyRegistrator.swift index cd4baf2baf6b1af0cb2946df7c72f45a41437c96..ed05ba40e4225ee6ce3345aca596276c3fc2915e 100644 --- a/Sources/AppFeature/DependencyRegistrator.swift +++ b/Sources/AppFeature/DependencyRegistrator.swift @@ -8,18 +8,14 @@ import MobileCoreServices // MARK: Isolated features import Bindings -import XXLogger import Keychain import Defaults import Voxophone -import Permissions import PushFeature -import CrashService import CrashReporting import VersionChecking import ReportingFeature import CountryListFeature -import DI // MARK: UI Features @@ -33,246 +29,131 @@ import SearchFeature import LaunchFeature import RestoreFeature import ContactFeature +import WebsiteFeature import ProfileFeature import ChatListFeature import SettingsFeature import RequestsFeature +import GroupDraftFeature import OnboardingFeature +import CreateGroupFeature import ContactListFeature +import RequestPermissionFeature import Shared import XXClient -import Navigation +import AppNavigation import KeychainAccess import XXMessengerClient import ComposableArchitecture -struct DependencyRegistrator { - static public func registerNavigators(_ navController: UINavigationController) { -// container.register(CombinedNavigator( -// PresentModalNavigator(), -// DismissModalNavigator(), -// PushNavigator(), -// PopToRootNavigator(), -// PopToNavigator(), -// SetStackNavigator(), -// -// OpenUpNavigator(), -// OpenLeftNavigator(), -// -// PresentOnboardingStartNavigator( -// screen: OnboardingStartController.init, -// navigationController: { navController } -// ), -// PresentChatListNavigator( -// screen: ChatListController.init, -// navigationController: { navController } -// ), -// PresentTermsAndConditionsNavigator( -// screen: TermsConditionsController.init, -// navigationController: { navController } -// ), -// PresentSearchNavigator( -// screen: SearchContainerController.init(_:), -// navigationController: { navController } -// ), -// PresentRequestsNavigator( -// screen: RequestsContainerController.init, -// navigationController: { navController } -// ), -// PresentChatNavigator( -// screen: SingleChatController.init(_:), -// navigationController: { navController } -// ), -// PresentGroupChatNavigator( -// screen: GroupChatController.init(_:), -// navigationController: { navController } -// ), -// PresentOnboardingWelcomeNavigator( -// screen: OnboardingWelcomeController.init, -// navigationController: { navController } -// ), -// PresentOnboardingUsernameNavigator( -// screen: OnboardingUsernameController.init, -// navigationController: { navController } -// ), -// PresentRestoreListNavigator( -// screen: RestoreListController.init, -// navigationController: { navController } -// ), -// PresentOnboardingEmailNavigator( -// screen: OnboardingEmailController.init, -// navigationController: { navController } -// ), -// PresentOnboardingPhoneNavigator( -// screen: OnboardingPhoneController.init, -// navigationController: { navController } -// ), -// PresentOnboardingCodeNavigator( -// screen: OnboardingCodeController.init(_:_:_:), -// navigationController: { navController } -// ), -// PresentDrawerNavigator( -// screen: DrawerController.init(_:), -// navigationController: { navController } -// ), -// PresentContactListNavigator( -// screen: ContactListController.init, -// navigationController: { navController } -// ), -// PresentMenuNavigator( -// screen: MenuController.init(_:), -// navigationController: { navController } -// ), -// PresentScanNavigator( -// screen: ScanContainerController.init, -// navigationController: { navController } -// ), -// PresentNewGroupNavigator( -// screen: CreateGroupController.init, -// navigationController: { navController } -// ), -// PresentCountryListNavigator( -// screen: CountryListController.init(_:), -// navigationController: { navController } -// ), -// PresentProfileNavigator( -// screen: ProfileController.init, -// navigationController: { navController } -// ), -// PresentSettingsNavigator( -// screen: SettingsController.init, -// navigationController: { navController } -// ), -// PresentSettingsAdvancedNavigator( -// screen: SettingsAdvancedController.init, -// navigationController: { navController } -// ), -// PresentSettingsBackupNavigator( -// screen: BackupController.init, -// navigationController: { navController } -// ), -// PresentSettingsAccountDeleteNavigator( -// screen: AccountDeleteController.init, -// navigationController: { navController } -// ), -// PresentContactNavigator( -// screen: ContactController.init(_:), -// navigationController: { navController } -// ), -// PresentActivitySheetNavigator( -// screen: { UIActivityViewController( -// activityItems: $0, -// applicationActivities: nil -// )}, -// navigationController: { navController } -// ), -// PresentProfileEmailNavigator( -// screen: ProfileEmailController.init, -// navigationController: { navController } -// ), -// PresentProfilePhoneNavigator( -// screen: ProfilePhoneController.init, -// navigationController: { navController } -// ), -// PresentPermissionRequestNavigator( -// screen: RequestPermissionController.init, -// navigationController: { navController } -// ), -// PresentPhotoLibraryNavigator( -// screen: UIImagePickerController.init, -// navigationController: { navController } -// ), -// PresentProfileCodeNavigator( -// screen: ProfileCodeController.init(_:_:_:), -// navigationController: { navController } -// ) -// ) as Navigator) - } -} +extension NavigatorKey: DependencyKey { + public static let liveValue: Navigator = CombinedNavigator( + PresentModalNavigator(), + DismissModalNavigator(), + PushNavigator(), + PopToRootNavigator(), + PopToNavigator(), + SetStackNavigator(), + OpenUpNavigator(), + OpenLeftNavigator(), -public struct OtherDependencies { - public var voxophone: Voxophone - public var sendReport: SendReport - public var pushHandler: PushHandler - public var versionCheck: VersionCheck - public var backupService: BackupService - public var hudController: HUDController - public var crashReporter: CrashReporter - public var networkMonitor: NetworkMonitor - public var keyObjectStore: KeyObjectStore - public var fetchBannedList: FetchBannedList - public var toastController: ToastController - public var reportingStatus: ReportingStatus - public var keychainHandler: KeychainHandler - public var makeReportDrawer: MakeReportDrawer - public var statusBarStylist: StatusBarStylist - public var getIdFromContact: GetIdFromContact - public var permissionHandler: PermissionHandler - public var processBannedList: ProcessBannedList - public var makeAppScreenshot: MakeAppScreenshot - public var getFactsFromContact: GetFactsFromContact -} + PresentPhotoLibraryNavigator(), + PresentActivitySheetNavigator(), -extension OtherDependencies { - public static func live() -> OtherDependencies { - .init( - voxophone: .init(), - sendReport: .live, - pushHandler: .init(), - versionCheck: .live, - backupService: .init(), - hudController: .init(), - crashReporter: .live, - networkMonitor: .init(), - keyObjectStore: .userDefaults, - fetchBannedList: .live, - toastController: .init(), - reportingStatus: .live(), - keychainHandler: .init(), - makeReportDrawer: .live, - statusBarStylist: .init(), - getIdFromContact: .live, - permissionHandler: .init(), - processBannedList: .live, - makeAppScreenshot: .live, - getFactsFromContact: .live + PresentWebsiteNavigator( + WebsiteController.init(_:) + ), + PresentCreateGroupNavigator( + CreateGroupController.init(_:) + ), + PresentGroupDraftNavigator( + GroupDraftController.init + ), + PresentMenuNavigator( + MenuController.init(_:_:) + ), + PresentProfileNavigator( + ProfileController.init + ), + PresentChatListNavigator( + ChatListController.init + ), + PresentDrawerNavigator( + DrawerController.init(_:) + ), + PresentScanNavigator( + ScanContainerController.init + ), + PresentChatNavigator( + SingleChatController.init(_:) + ), + PresentContactNavigator( + ContactController.init(_:) + ), + PresentSettingsNavigator( + SettingsMainController.init + ), + PresentSettingsBackupNavigator( + BackupController.init + ), + PresentRestoreListNavigator( + RestoreListController.init + ), + PresentContactListNavigator( + ContactListController.init + ), + PresentGroupChatNavigator( + GroupChatController.init(_:) + ), + PresentProfileEmailNavigator( + ProfileEmailController.init + ), + PresentProfilePhoneNavigator( + ProfilePhoneController.init + ), + PresentSearchNavigator( + SearchContainerController.init(_:) + ), + PresentRequestsNavigator( + RequestsContainerController.init + ), + PresentCountryListNavigator( + CountryListController.init(_:) + ), + PresentOnboardingEmailNavigator( + OnboardingEmailController.init + ), + PresentOnboardingPhoneNavigator( + OnboardingPhoneController.init + ), + PresentProfileCodeNavigator( + ProfileCodeController.init(_:_:_:) + ), + PresentOnboardingStartNavigator( + OnboardingStartController.init + ), + PresentSettingsAdvancedNavigator( + SettingsAdvancedController.init + ), + PresentTermsAndConditionsNavigator( + TermsConditionsController.init + ), + PresentPermissionRequestNavigator( + RequestPermissionController.init + ), + PresentOnboardingWelcomeNavigator( + OnboardingWelcomeController.init + ), + PresentSettingsAccountDeleteNavigator( + SettingsDeleteController.init + ), + PresentOnboardingUsernameNavigator( + OnboardingUsernameController.init + ), + PresentOnboardingCodeNavigator( + OnboardingCodeController.init(_:_:_:) ) - } - - public static let unimplemented = OtherDependencies( - voxophone: .init(), - sendReport: .unimplemented, - pushHandler: .init(), - versionCheck: .unimplemented, - backupService: .init(), - hudController: .init(), - crashReporter: .noop, - networkMonitor: .init(), - keyObjectStore: .mock(dictionary: [:]), - fetchBannedList: .unimplemented, - toastController: .init(), - reportingStatus: .mock(), - keychainHandler: .init(), - makeReportDrawer: .unimplemented, - statusBarStylist: .init(), - getIdFromContact: .live, - permissionHandler: .init(), - processBannedList: .unimplemented, - makeAppScreenshot: .unimplemented, - getFactsFromContact: .live ) } - -private enum OtherDependenciesKey: DependencyKey { - static let liveValue: OtherDependencies = .live() - static let testValue: OtherDependencies = .unimplemented -} - -extension DependencyValues { - public var others: OtherDependencies { - get { self[OtherDependenciesKey.self] } - set { self[OtherDependenciesKey.self] = newValue } - } -} diff --git a/Sources/AppFeature/PushRouter.swift b/Sources/AppFeature/PushRouter.swift index dc790bd8d3af5923dd99ea87e3b051adcf840bad..f36d1e8ab7c9167800fa4557dc3d6fd2b65956a4 100644 --- a/Sources/AppFeature/PushRouter.swift +++ b/Sources/AppFeature/PushRouter.swift @@ -1,4 +1,3 @@ -import DI import UIKit import XXModels import PushFeature @@ -11,47 +10,48 @@ import XXMessengerClient extension PushRouter { static func live(navigationController: UINavigationController) -> PushRouter { - PushRouter { route, completion in - if let launchController = navigationController.viewControllers.last as? LaunchController { - launchController.pendingPushRoute = route - } else { - switch route { - case .requests: - if !(navigationController.viewControllers.last is RequestsContainerController) { - navigationController.setViewControllers([RequestsContainerController()], animated: true) - } - case .search(username: let username): - if let messenger = try? DI.Container.shared.resolve() as Messenger, - let _ = try? messenger.ud.get()?.getContact() { - if !(navigationController.viewControllers.last is SearchContainerController) { - navigationController.setViewControllers([ - ChatListController(), - SearchContainerController(username) - ], animated: true) - } else { - (navigationController.viewControllers.last as? SearchContainerController)?.startSearchingFor(username) - } - } - case .contactChat(id: let id): - if let database: Database = try? DI.Container.shared.resolve(), - let contact = try? database.fetchContacts(.init(id: [id])).first { - navigationController.setViewControllers([ - ChatListController(), - SingleChatController(contact) - ], animated: true) - } - case .groupChat(id: let id): - if let database: Database = try? DI.Container.shared.resolve(), - let info = try? database.fetchGroupInfos(.init(groupId: id)).first { - navigationController.setViewControllers([ - ChatListController(), - GroupChatController(info) - ], animated: true) - } - } - } - - completion() - } + fatalError() +// PushRouter { route, completion in +// if let launchController = navigationController.viewControllers.last as? LaunchController { +// launchController.pendingPushRoute = route +// } else { +// switch route { +// case .requests: +// if !(navigationController.viewControllers.last is RequestsContainerController) { +// navigationController.setViewControllers([RequestsContainerController()], animated: true) +// } +// case .search(username: let username): +// if let messenger = try? DI.Container.shared.resolve() as Messenger, +// let _ = try? messenger.ud.get()?.getContact() { +// if !(navigationController.viewControllers.last is SearchContainerController) { +// navigationController.setViewControllers([ +// ChatListController(), +// SearchContainerController(username) +// ], animated: true) +// } else { +// (navigationController.viewControllers.last as? SearchContainerController)?.startSearchingFor(username) +// } +// } +// case .contactChat(id: let id): +// if let database: Database = try? DI.Container.shared.resolve(), +// let contact = try? dbManager.getDB().fetchContacts(.init(id: [id])).first { +// navigationController.setViewControllers([ +// ChatListController(), +// SingleChatController(contact) +// ], animated: true) +// } +// case .groupChat(id: let id): +// if let database: Database = try? DI.Container.shared.resolve(), +// let info = try? dbManager.getDB().fetchGroupInfos(.init(groupId: id)).first { +// navigationController.setViewControllers([ +// ChatListController(), +// GroupChatController(info) +// ], animated: true) +// } +// } +// } +// +// completion() +// } } } diff --git a/Sources/AppNavigation/Action.swift b/Sources/AppNavigation/Action.swift new file mode 100644 index 0000000000000000000000000000000000000000..ff697eeb21a0937ded7ee2c33961f8cde8592c69 --- /dev/null +++ b/Sources/AppNavigation/Action.swift @@ -0,0 +1,2 @@ +/// Navigation action +public protocol Action {} diff --git a/Sources/AppNavigation/CombinedNavigator.swift b/Sources/AppNavigation/CombinedNavigator.swift new file mode 100644 index 0000000000000000000000000000000000000000..fdbb1741cfe97da7c9295797f57711bfadf17553 --- /dev/null +++ b/Sources/AppNavigation/CombinedNavigator.swift @@ -0,0 +1,30 @@ +/// Combines multiple navigators into a single one +/// +/// - Action is performed using the first navigator that can handle it +/// - When there is no navigator that can handle given action, assertion is thrown +/// - When there are multiple navigators that can handle given action, assertion is thrown +public struct CombinedNavigator: Navigator { + public init(_ navigators: Navigator...) { + self.navigators = navigators + } + + public init(_ navigators: [Navigator]) { + self.navigators = navigators + } + + public func perform(_ action: Action, completion: @escaping () -> Void) { + let navigators = self.navigators.filter { $0.canPerform(action) } + guard let firstNavigator = navigators.first else { + assertionFailure("No navigator to perform action: \(action)", #file, #line) + return + } + guard navigators.count == 1 else { + assertionFailure("Multiple navigators can perform action: \(action), \(navigators)", #file, #line) + return + } + firstNavigator.perform(action, completion: completion) + } + + let navigators: [Navigator] + var assertionFailure: (@autoclosure () -> String, StaticString, UInt) -> Void = Swift.assertionFailure +} diff --git a/Sources/AppNavigation/CustomTransitions/BottomTransition.swift b/Sources/AppNavigation/CustomTransitions/BottomTransition.swift new file mode 100644 index 0000000000000000000000000000000000000000..66b422c1f833a2b6690cea8947ee07f9481ba070 --- /dev/null +++ b/Sources/AppNavigation/CustomTransitions/BottomTransition.swift @@ -0,0 +1,136 @@ +import UIKit +import Combine + +final class BottomTransition: NSObject, UIViewControllerAnimatedTransitioning { + enum Direction { + case present + case dismiss + } + + let isDismissableOnBackground: Bool + var direction: Direction = .present + private let onDismissal: (() -> Void)? + private weak var darkOverlayView: UIControl? + private weak var topConstraint: NSLayoutConstraint? + private weak var bottomConstraint: NSLayoutConstraint? + private var cancellables = Set<AnyCancellable>() + + private var presentedConstraints: [NSLayoutConstraint] = [] + private var dismissedConstraints: [NSLayoutConstraint] = [] + private var presentingController: UIViewController? + + init( + _ isDismissableOnBackground: Bool = true, + onDismissal: (() -> Void)? + ) { + self.onDismissal = onDismissal + self.isDismissableOnBackground = isDismissableOnBackground + super.init() + } + + func transitionDuration( + using context: UIViewControllerContextTransitioning? + ) -> TimeInterval { 0.5 } + + func animateTransition( + using context: UIViewControllerContextTransitioning + ) { + switch direction { + case .present: + present(using: context) + case .dismiss: + dismiss(using: context) + } + } + + private func present(using context: UIViewControllerContextTransitioning) { + guard let presentingController = context.viewController(forKey: .from), + let presentedView = context.view(forKey: .to) else { + context.completeTransition(false) + return + } + + let darkOverlayView = UIControl() + self.darkOverlayView = darkOverlayView + + darkOverlayView.alpha = 0.0 + darkOverlayView.backgroundColor = UIColor.black.withAlphaComponent(0.5) + context.containerView.addSubview(darkOverlayView) + darkOverlayView.frame = context.containerView.bounds + + if isDismissableOnBackground { + darkOverlayView.addTarget(self, action: #selector(didTapOverlay), for: .touchUpInside) + self.presentingController = presentingController + } + + context.containerView.addSubview(presentedView) + presentedView.translatesAutoresizingMaskIntoConstraints = false + + presentedConstraints = [ + presentedView.leftAnchor.constraint(equalTo: context.containerView.leftAnchor), + presentedView.rightAnchor.constraint(equalTo: context.containerView.rightAnchor), + presentedView.bottomAnchor.constraint(equalTo: context.containerView.bottomAnchor), + presentedView.topAnchor.constraint( + greaterThanOrEqualTo: context.containerView.safeAreaLayoutGuide.topAnchor, + constant: 60 + ) + ] + + dismissedConstraints = [ + presentedView.leftAnchor.constraint(equalTo: context.containerView.leftAnchor), + presentedView.rightAnchor.constraint(equalTo: context.containerView.rightAnchor), + presentedView.topAnchor.constraint(equalTo: context.containerView.bottomAnchor) + ] + + NSLayoutConstraint.activate(dismissedConstraints) + + context.containerView.setNeedsLayout() + context.containerView.layoutIfNeeded() + + NSLayoutConstraint.deactivate(dismissedConstraints) + NSLayoutConstraint.activate(presentedConstraints) + + UIView.animate( + withDuration: transitionDuration(using: context), + delay: 0, + usingSpringWithDamping: 1, + initialSpringVelocity: 0, + options: .curveEaseInOut, + animations: { + darkOverlayView.alpha = 1.0 + context.containerView.setNeedsLayout() + context.containerView.layoutIfNeeded() + }, + completion: { _ in + context.completeTransition(true) + }) + } + + private func dismiss(using context: UIViewControllerContextTransitioning) { + NSLayoutConstraint.deactivate(presentedConstraints) + NSLayoutConstraint.activate(dismissedConstraints) + + UIView.animate( + withDuration: transitionDuration(using: context), + delay: 0, + usingSpringWithDamping: 1, + initialSpringVelocity: 0, + options: .curveEaseInOut, + animations: { [weak darkOverlayView] in + darkOverlayView?.alpha = 0.0 + context.containerView.setNeedsLayout() + context.containerView.layoutIfNeeded() + }, + completion: { [weak self] _ in + context.completeTransition(true) + self?.onDismissal?() + } + ) + } + + @objc private func didTapOverlay() { + if let presentingController, isDismissableOnBackground { + presentingController.dismiss(animated: true) + } + } +} diff --git a/Sources/AppNavigation/CustomTransitions/BottomTransitioningDelegate.swift b/Sources/AppNavigation/CustomTransitions/BottomTransitioningDelegate.swift new file mode 100644 index 0000000000000000000000000000000000000000..b30e549738b953c01702f1ca6897b6f50899fa9c --- /dev/null +++ b/Sources/AppNavigation/CustomTransitions/BottomTransitioningDelegate.swift @@ -0,0 +1,25 @@ +import UIKit + +final class BottomTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate { + var isDismissableOnBackgroundTouch: Bool = true + private var transition: BottomTransition? + + func animationController( + forPresented presented: UIViewController, + presenting: UIViewController, + source: UIViewController + ) -> UIViewControllerAnimatedTransitioning? { + transition = BottomTransition(isDismissableOnBackgroundTouch) { [weak self] in + guard let self else { return } + self.transition = nil + } + return transition + } + + func animationController( + forDismissed dismissed: UIViewController + ) -> UIViewControllerAnimatedTransitioning? { + transition?.direction = .dismiss + return transition + } +} diff --git a/Sources/AppNavigation/CustomTransitions/LeftAnimator.swift b/Sources/AppNavigation/CustomTransitions/LeftAnimator.swift new file mode 100644 index 0000000000000000000000000000000000000000..6944551740e8cd93364a9ddf06e2cc8adac38b0d --- /dev/null +++ b/Sources/AppNavigation/CustomTransitions/LeftAnimator.swift @@ -0,0 +1,23 @@ +import UIKit + +protocol LeftAnimating { + func animate(in containerView: UIView, to progress: CGFloat) +} + +struct LeftAnimator: LeftAnimating { + func animate(in containerView: UIView, to progress: CGFloat) { + guard let fromView = containerView.viewWithTag(LeftPresentTransition.fromViewTag) else { return } + + let cornerRadius = progress * 24 + let shadowOpacity = Float(progress) + let offsetX = containerView.bounds.size.width * 0.5 * progress + let offsetY = containerView.bounds.size.height * 0.08 * progress + let scale = 1 - (0.25 * progress) + + fromView.subviews.first?.layer.cornerRadius = cornerRadius + fromView.layer.shadowOpacity = shadowOpacity + fromView.transform = CGAffineTransform.identity + .translatedBy(x: offsetX, y: offsetY) + .scaledBy(x: scale, y: scale) + } +} diff --git a/Sources/AppNavigation/CustomTransitions/LeftDismissInteractor.swift b/Sources/AppNavigation/CustomTransitions/LeftDismissInteractor.swift new file mode 100644 index 0000000000000000000000000000000000000000..c7d7f7868da64721df0ebb52249c7807813f64bb --- /dev/null +++ b/Sources/AppNavigation/CustomTransitions/LeftDismissInteractor.swift @@ -0,0 +1,69 @@ +import UIKit + +protocol LeftDismissInteracting: UIViewControllerInteractiveTransitioning { + var interactionInProgress: Bool { get } + func setup(view: UIView, action: @escaping (() -> Void)) +} + +final class LeftDismissInteractor: + UIPercentDrivenInteractiveTransition, LeftDismissInteracting { + private var action: (() -> Void)? + public var interactionInProgress = false + private var shouldFinishTransition = false + + func setup(view: UIView, action: @escaping (() -> Void)) { + view.addGestureRecognizer(UIPanGestureRecognizer( + target: self, + action: #selector(handlePanGesture(_:)) + )) + view.addGestureRecognizer(UITapGestureRecognizer( + target: self, + action: #selector(handleTapGesture(_:)) + )) + self.action = action + } + + @objc + private func handleTapGesture(_ recognizer: UITapGestureRecognizer) { + action?() + } + + @objc + private func handlePanGesture(_ recognizer: UIPanGestureRecognizer) { + guard let view = recognizer.view, + let containerView = view.superview + else { return } + + let viewWidth = containerView.bounds.size.width + guard viewWidth > 0 else { return } + + let translation = recognizer.translation(in: view) + let progress = min(1, max(0, -translation.x / (viewWidth * 0.8))) + + switch recognizer.state { + case .possible, .failed: + interactionInProgress = false + + case .began: + interactionInProgress = true + shouldFinishTransition = false + action?() + + case .changed: + shouldFinishTransition = progress >= 0.5 + update(progress) + + case .cancelled: + interactionInProgress = false + cancel() + + case .ended: + interactionInProgress = false + shouldFinishTransition ? finish() : cancel() + + @unknown default: + interactionInProgress = false + cancel() + } + } +} diff --git a/Sources/AppNavigation/CustomTransitions/LeftDismissTransition.swift b/Sources/AppNavigation/CustomTransitions/LeftDismissTransition.swift new file mode 100644 index 0000000000000000000000000000000000000000..001ccf8ace53ab9fa411eb2a6014cfe6acaf36b9 --- /dev/null +++ b/Sources/AppNavigation/CustomTransitions/LeftDismissTransition.swift @@ -0,0 +1,34 @@ +import UIKit + +final class LeftDismissTransition: NSObject, UIViewControllerAnimatedTransitioning { + let menuAnimator: LeftAnimating + let viewAnimator: UIViewAnimating.Type + + init( + menuAnimator: LeftAnimating, + viewAnimator: UIViewAnimating.Type + ) { + self.menuAnimator = menuAnimator + self.viewAnimator = viewAnimator + super.init() + } + + func transitionDuration( + using context: UIViewControllerContextTransitioning? + ) -> TimeInterval { 0.25 } + + func animateTransition( + using context: UIViewControllerContextTransitioning + ) { + viewAnimator.animate( + withDuration: transitionDuration(using: context), + animations: { + self.menuAnimator.animate(in: context.containerView, to: 0) + }, + completion: { _ in + let isCancelled = context.transitionWasCancelled + context.completeTransition(isCancelled == false) + } + ) + } +} diff --git a/Sources/AppNavigation/CustomTransitions/LeftPresentTransition.swift b/Sources/AppNavigation/CustomTransitions/LeftPresentTransition.swift new file mode 100644 index 0000000000000000000000000000000000000000..3af434fff9f4d7248e28c44ba56d00c93b2e6ad1 --- /dev/null +++ b/Sources/AppNavigation/CustomTransitions/LeftPresentTransition.swift @@ -0,0 +1,68 @@ +import UIKit + +final class LeftPresentTransition: NSObject, UIViewControllerAnimatedTransitioning { + let dismissInteractor: LeftDismissInteracting + let menuAnimator: LeftAnimating + let viewAnimator: UIViewAnimating.Type + + static let fromViewTag = UUID().hashValue + + init( + dismissInteractor: LeftDismissInteracting, + menuAnimator: LeftAnimating, + viewAnimator: UIViewAnimating.Type + ) { + self.dismissInteractor = dismissInteractor + self.menuAnimator = menuAnimator + self.viewAnimator = viewAnimator + super.init() + } + + func transitionDuration( + using context: UIViewControllerContextTransitioning? + ) -> TimeInterval { 0.25 } + + func animateTransition( + using context: UIViewControllerContextTransitioning + ) { + guard let fromVC = context.viewController(forKey: .from), + let fromSnapshot = fromVC.view.snapshotView(afterScreenUpdates: true), + let toVC = context.viewController(forKey: .to) + else { + context.completeTransition(false) + return + } + + context.containerView.addSubview(toVC.view) + toVC.view.frame = context.containerView.bounds + + let fromView = UIView() + fromView.tag = Self.fromViewTag + context.containerView.addSubview(fromView) + fromView.frame = context.containerView.bounds + fromView.layer.shadowColor = UIColor.black.cgColor + fromView.layer.shadowOpacity = 1 + fromView.layer.shadowOffset = .zero + fromView.layer.shadowRadius = 32 + fromView.addSubview(fromSnapshot) + fromSnapshot.frame = fromView.bounds + fromSnapshot.layer.cornerRadius = 0 + fromSnapshot.layer.masksToBounds = true + + dismissInteractor.setup( + view: fromView, + action: { fromVC.dismiss(animated: true) } + ) + + viewAnimator.animate( + withDuration: transitionDuration(using: context), + animations: { + self.menuAnimator.animate(in: context.containerView, to: 1) + }, + completion: { _ in + let isCancelled = context.transitionWasCancelled + context.completeTransition(isCancelled == false) + } + ) + } +} diff --git a/Sources/AppNavigation/CustomTransitions/LeftTransitioningDelegate.swift b/Sources/AppNavigation/CustomTransitions/LeftTransitioningDelegate.swift new file mode 100644 index 0000000000000000000000000000000000000000..7b918587e8b49a67227fd4d04a9a08aec095a979 --- /dev/null +++ b/Sources/AppNavigation/CustomTransitions/LeftTransitioningDelegate.swift @@ -0,0 +1,55 @@ +import UIKit + +protocol UIViewAnimating { + static func animate( + withDuration duration: TimeInterval, + animations: @escaping (() -> Void), + completion: ((Bool) -> Void)? + ) +} + +extension UIView: UIViewAnimating {} + +final class LeftTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate { + let menuAnimator: LeftAnimating + let viewAnimator: UIViewAnimating.Type + let dismissInteractor: LeftDismissInteracting + + init( + dismissInteractor: LeftDismissInteracting = LeftDismissInteractor(), + menuAnimator: LeftAnimating = LeftAnimator(), + viewAnimator: UIViewAnimating.Type = UIView.self + ) { + self.dismissInteractor = dismissInteractor + self.menuAnimator = menuAnimator + self.viewAnimator = viewAnimator + super.init() + } + + func animationController( + forPresented presented: UIViewController, + presenting: UIViewController, + source: UIViewController + ) -> UIViewControllerAnimatedTransitioning? { + LeftPresentTransition( + dismissInteractor: dismissInteractor, + menuAnimator: menuAnimator, + viewAnimator: viewAnimator + ) + } + + func animationController( + forDismissed dismissed: UIViewController + ) -> UIViewControllerAnimatedTransitioning? { + LeftDismissTransition( + menuAnimator: menuAnimator, + viewAnimator: viewAnimator + ) + } + + func interactionControllerForDismissal( + using animator: UIViewControllerAnimatedTransitioning + ) -> UIViewControllerInteractiveTransitioning? { + dismissInteractor.interactionInProgress ? dismissInteractor : nil + } +} diff --git a/Sources/AppNavigation/Dependency.swift b/Sources/AppNavigation/Dependency.swift new file mode 100644 index 0000000000000000000000000000000000000000..c11e4ce52060f110c1a42a5436eed5a3bde96740 --- /dev/null +++ b/Sources/AppNavigation/Dependency.swift @@ -0,0 +1,26 @@ +import Dependencies +import XCTestDynamicOverlay + +public enum NavigatorKey: TestDependencyKey { + public static let testValue: Navigator = UnimplementedNavigator() +} + +public extension DependencyValues { + var navigator: Navigator { + get { self[NavigatorKey.self] } + set { self[NavigatorKey.self] = newValue } + } +} + +public struct UnimplementedNavigator: Navigator { + public init() {} + + public func perform(_ action: Action, completion: @escaping () -> Void) { + XCTestDynamicOverlay.XCTFail("UnimplementedNavigator.perform not implemented") + } + + public func canPerform(_ action: Action) -> Bool { + XCTestDynamicOverlay.XCTFail("UnimplementedNavigator.canPerform not implemented") + return false + } +} diff --git a/Sources/AppNavigation/Generic/DismissModal.swift b/Sources/AppNavigation/Generic/DismissModal.swift new file mode 100644 index 0000000000000000000000000000000000000000..5511e991db9441133e56b4336bfe71eb5a6e4591 --- /dev/null +++ b/Sources/AppNavigation/Generic/DismissModal.swift @@ -0,0 +1,33 @@ +import UIKit + +/// Dismiss view controller presented from provided view controller +public struct DismissModal: Action { + /// - Parameters: + /// - parent: Parent view controller from which dismiss happens + /// - animated: Animate the transition + public init( + from parent: UIViewController, + animated: Bool = true + ) { + self.parent = parent + self.animated = animated + } + + /// Parent view controller from which dismiss happens + public var parent: UIViewController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `DismissModal` action +public struct DismissModalNavigator: TypedNavigator { + public init() {} + + public func perform(_ action: DismissModal, completion: @escaping () -> Void) { + action.parent.dismiss( + animated: action.animated, + completion: completion + ) + } +} diff --git a/Sources/AppNavigation/Generic/OpenLeft.swift b/Sources/AppNavigation/Generic/OpenLeft.swift new file mode 100644 index 0000000000000000000000000000000000000000..90302e792da6cc98fa2456e800820b4d4e00088b --- /dev/null +++ b/Sources/AppNavigation/Generic/OpenLeft.swift @@ -0,0 +1,45 @@ +import UIKit + +/// Open left view controller on provided parent view controller +public struct OpenLeft: Action { + /// - Parameters: + /// - viewController: View controller to present + /// - parent: Parent view controller from which presentation should happen + /// - animated: Animate the transition + public init( + _ viewController: UIViewController, + from parent: UIViewController, + animated: Bool = true + ) { + self.viewController = viewController + self.parent = parent + self.animated = animated + } + + /// View controller to present + public var viewController: UIViewController + + /// Parent view controller from which presentation should happen + public var parent: UIViewController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `OpenLeft` action +public struct OpenLeftNavigator: TypedNavigator { + let transitioningDelegate = LeftTransitioningDelegate() + + public init() {} + + public func perform(_ action: OpenLeft, completion: @escaping () -> Void) { + action.viewController.transitioningDelegate = transitioningDelegate + action.viewController.modalPresentationStyle = .overFullScreen + + action.parent.present( + action.viewController, + animated: action.animated, + completion: completion + ) + } +} diff --git a/Sources/AppNavigation/Generic/OpenUp.swift b/Sources/AppNavigation/Generic/OpenUp.swift new file mode 100644 index 0000000000000000000000000000000000000000..dfcedc3fafa521134e3ca871e803538503b43f02 --- /dev/null +++ b/Sources/AppNavigation/Generic/OpenUp.swift @@ -0,0 +1,52 @@ +import UIKit + +/// Open up view controller on provided parent view controller +public struct OpenUp: Action { + /// - Parameters: + /// - viewController: View controller to present + /// - parent: Parent view controller from which presentation should happen + /// - animated: Animate the transition + /// - dismissable: Dismissable upon background touch flag + public init( + _ viewController: UIViewController, + from parent: UIViewController, + animated: Bool = true, + dismissable: Bool = true + ) { + self.viewController = viewController + self.parent = parent + self.animated = animated + self.dismissable = dismissable + } + + /// View controller to present + public var viewController: UIViewController + + /// Parent view controller from which presentation should happen + public var parent: UIViewController + + /// Animate the transition + public var animated: Bool + + /// Dismissable upon background touch flag + public var dismissable: Bool +} + +/// Performs `OpenUp` action +public struct OpenUpNavigator: TypedNavigator { + let transitioningDelegate = BottomTransitioningDelegate() + + public init() {} + + public func perform(_ action: OpenUp, completion: @escaping () -> Void) { + transitioningDelegate.isDismissableOnBackgroundTouch = action.dismissable + action.viewController.transitioningDelegate = transitioningDelegate + action.viewController.modalPresentationStyle = .overFullScreen + + action.parent.present( + action.viewController, + animated: action.animated, + completion: completion + ) + } +} diff --git a/Sources/AppNavigation/Generic/PopTo.swift b/Sources/AppNavigation/Generic/PopTo.swift new file mode 100644 index 0000000000000000000000000000000000000000..15af276aa8f70d29b7a3266be72555c0bd69bbb9 --- /dev/null +++ b/Sources/AppNavigation/Generic/PopTo.swift @@ -0,0 +1,44 @@ +import UIKit + +/// Pop to the view controller on a given navigation controller +public struct PopTo: Action { + /// - Parameters: + /// - viewController: View controller to which should pop + /// - navigationController: Navigation controller on which pop should happen + /// - animated: Animate the transition + public init( + _ viewController: UIViewController, + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.viewController = viewController + self.navigationController = navigationController + self.animated = animated + } + + /// View controller to which should pop + public var viewController: UIViewController + + /// Navigation controller on which pop should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PopTo` action +public struct PopToNavigator: TypedNavigator { + public init() {} + + public func perform(_ action: PopTo, completion: @escaping () -> Void) { + action.navigationController.popToViewController( + action.viewController, + animated: action.animated + ) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/Generic/PopToRoot.swift b/Sources/AppNavigation/Generic/PopToRoot.swift new file mode 100644 index 0000000000000000000000000000000000000000..5918a3476f9ba0a23d6413173dc764e7df7a7052 --- /dev/null +++ b/Sources/AppNavigation/Generic/PopToRoot.swift @@ -0,0 +1,35 @@ +import UIKit + +/// Pops to root view controller on a given navigation controller +public struct PopToRoot: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which pop should happen + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which pop should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PopToRoot` action +public struct PopToRootNavigator: TypedNavigator { + public init() {} + + public func perform(_ action: PopToRoot, completion: @escaping () -> Void) { + action.navigationController.popToRootViewController(animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/Generic/PresentModal.swift b/Sources/AppNavigation/Generic/PresentModal.swift new file mode 100644 index 0000000000000000000000000000000000000000..23db172f067f1131a7e557f70c356c00c27d70cd --- /dev/null +++ b/Sources/AppNavigation/Generic/PresentModal.swift @@ -0,0 +1,40 @@ +import UIKit + +/// Present view controller on provided parent view controller +public struct PresentModal: Action { + /// - Parameters: + /// - viewController: View controller to present + /// - parent: Parent view controller from which presentation should happen + /// - animated: Animate the transition + public init( + _ viewController: UIViewController, + from parent: UIViewController, + animated: Bool = true + ) { + self.viewController = viewController + self.parent = parent + self.animated = animated + } + + /// View controller to present + public var viewController: UIViewController + + /// Parent view controller from which presentation should happen + public var parent: UIViewController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentModal` action +public struct PresentModalNavigator: TypedNavigator { + public init() {} + + public func perform(_ action: PresentModal, completion: @escaping () -> Void) { + action.parent.present( + action.viewController, + animated: action.animated, + completion: completion + ) + } +} diff --git a/Sources/AppNavigation/Generic/Push.swift b/Sources/AppNavigation/Generic/Push.swift new file mode 100644 index 0000000000000000000000000000000000000000..a0a01ea670a9bfe255e128b30547eeb79dfbcca6 --- /dev/null +++ b/Sources/AppNavigation/Generic/Push.swift @@ -0,0 +1,41 @@ +import UIKit + +/// Push view controller on a given navigation controller +public struct Push: Action { + /// - Parameters: + /// - viewController: View controller to which should be pushed + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + _ viewController: UIViewController, + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.viewController = viewController + self.navigationController = navigationController + self.animated = animated + } + + /// View controller to which should be pushed + public var viewController: UIViewController + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `Push` action +public struct PushNavigator: TypedNavigator { + public init() {} + + public func perform(_ action: Push, completion: @escaping () -> Void) { + action.navigationController.pushViewController(action.viewController, animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/Generic/SetStack.swift b/Sources/AppNavigation/Generic/SetStack.swift new file mode 100644 index 0000000000000000000000000000000000000000..23ca3a679bf6ddbdb6869aff208a776172161594 --- /dev/null +++ b/Sources/AppNavigation/Generic/SetStack.swift @@ -0,0 +1,44 @@ +import UIKit + +/// Sets view controllers on a given navigation controller +public struct SetStack: Action { + /// - Parameters: + /// - viewControllers: View controllers that should be set + /// - navigationController: Navigation controller on which view controllers should be set + /// - animated: Animate the transition + public init( + _ viewControllers: [UIViewController], + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.viewControllers = viewControllers + self.navigationController = navigationController + self.animated = animated + } + + /// View controllers that should be set + public var viewControllers: [UIViewController] + + /// Navigation controller on which view controllers should be set + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `SetStack` action +public struct SetStackNavigator: TypedNavigator { + public init() {} + + public func perform(_ action: SetStack, completion: @escaping () -> Void) { + action.navigationController.setViewControllers( + action.viewControllers, + animated: action.animated + ) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/Navigator.swift b/Sources/AppNavigation/Navigator.swift new file mode 100644 index 0000000000000000000000000000000000000000..c37ca8d190ffd9f19fb667dcf37fd1718a9f9e64 --- /dev/null +++ b/Sources/AppNavigation/Navigator.swift @@ -0,0 +1,23 @@ +/// Navigator that performs a navigation action +public protocol Navigator { + /// Returns true if the navigator can perform the action + /// - Default implementation returns true for any action + /// - Parameter action: navigation action + func canPerform(_ action: Action) -> Bool + + /// Performs the navigation action + /// - Parameters: + /// - action: navigation action + /// - completion: closure that will be executed after performing the action + func perform(_ action: Action, completion: @escaping () -> Void) +} + +public extension Navigator { + func canPerform(_ action: Action) -> Bool { true } + + /// Performs the navigation action with empty completion closure + /// - Parameter action: navigation action + func perform(_ action: Action) { + perform(action, completion: {}) + } +} diff --git a/Sources/AppNavigation/PresentActivitySheet.swift b/Sources/AppNavigation/PresentActivitySheet.swift new file mode 100644 index 0000000000000000000000000000000000000000..1ba69910f41607fc78fd86d99f75ee46f02a6de7 --- /dev/null +++ b/Sources/AppNavigation/PresentActivitySheet.swift @@ -0,0 +1,43 @@ +import UIKit + +/// Presents `UIActivityViewController` on a given parent view controller +public struct PresentActivitySheet: Action { + /// - Parameters: + /// - items: Items to be displayed at the activity sheet controller + /// - parent: Parent view controller from which presentation should happen + /// - animated: Animate the transition + public init( + items: [Any], + from parent: UIViewController, + animated: Bool = true + ) { + self.items = items + self.parent = parent + self.animated = animated + } + + /// Items to be displayed at the activity sheet controller + public var items: [Any] + + /// Parent view controller from which presentation should happen + public var parent: UIViewController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentActivitySheet` action +public struct PresentActivitySheetNavigator: TypedNavigator { + public init() {} + + public func perform(_ action: PresentActivitySheet, completion: @escaping () -> Void) { + action.parent.present( + UIActivityViewController( + activityItems: action.items, + applicationActivities: nil + ), + animated: action.animated, + completion: completion + ) + } +} diff --git a/Sources/AppNavigation/PresentCamera.swift b/Sources/AppNavigation/PresentCamera.swift new file mode 100644 index 0000000000000000000000000000000000000000..f5fa18ef1f7f2446a637e0a35fadc906590b3534 --- /dev/null +++ b/Sources/AppNavigation/PresentCamera.swift @@ -0,0 +1,37 @@ +import UIKit + +/// Presents `Camera` on provided parent view controller +public struct PresentCamera: Action { + /// - Parameters: + /// - parent: Parent view controller from which presentation should happen + /// - animated: Animate the transition + public init( + from parent: UIViewController, + animated: Bool = true + ) { + self.parent = parent + self.animated = animated + } + + /// Parent view controller from which presentation should happen + public var parent: UIViewController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentCamera` action +public struct PresentCameraNavigator: TypedNavigator { + public init() {} + + public func perform(_ action: PresentCamera, completion: @escaping () -> Void) { + let imagePickerController = UIImagePickerController() + imagePickerController.sourceType = .camera + imagePickerController.delegate = action.parent as? UIImagePickerControllerDelegate & UINavigationControllerDelegate + action.parent.present( + imagePickerController, + animated: action.animated, + completion: completion + ) + } +} diff --git a/Sources/AppNavigation/PresentChat.swift b/Sources/AppNavigation/PresentChat.swift new file mode 100644 index 0000000000000000000000000000000000000000..ef6ab52f5e7d00be89a1d0b5b329aa7496727e7a --- /dev/null +++ b/Sources/AppNavigation/PresentChat.swift @@ -0,0 +1,49 @@ +import UIKit +import XXModels + +/// Pushes `Chat` on a given navigation controller +public struct PresentChat: Action { + /// - Parameters: + /// - contact: Model to build the view controller which will be pushed + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + contact: Contact, + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.contact = contact + self.navigationController = navigationController + self.animated = animated + } + + /// Model to build the view controller which will be pushed + public var contact: Contact + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentChat` action +public struct PresentChatNavigator: TypedNavigator { + /// View controller which should be pushed + var viewController: (Contact) -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed + public init(_ viewController: @escaping (Contact) -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentChat, completion: @escaping () -> Void) { + action.navigationController.pushViewController(viewController(action.contact), animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentChatList.swift b/Sources/AppNavigation/PresentChatList.swift new file mode 100644 index 0000000000000000000000000000000000000000..1cdca2246f9a9de7e3796caa57cef56f67c65270 --- /dev/null +++ b/Sources/AppNavigation/PresentChatList.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Sets `ChatList` on a given navigation controller stack +public struct PresentChatList: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which stack should be set + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which stack should be set + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentChatList` action +public struct PresentChatListNavigator: TypedNavigator { + /// View controller which should be set in navigation stack + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be set in navigation stack + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentChatList, completion: @escaping () -> Void) { + action.navigationController.setViewControllers([viewController()], animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentContact.swift b/Sources/AppNavigation/PresentContact.swift new file mode 100644 index 0000000000000000000000000000000000000000..dd1fca4beb590248d1baff41b5c70e1c735182bc --- /dev/null +++ b/Sources/AppNavigation/PresentContact.swift @@ -0,0 +1,49 @@ +import UIKit +import XXModels + +/// Pushes `Contact` on a given navigation controller +public struct PresentContact: Action { + /// - Parameters: + /// - contact: Model to build the view controller which will be pushed + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + contact: Contact, + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.contact = contact + self.navigationController = navigationController + self.animated = animated + } + + /// Model to build the view controller which will be pushed + public var contact: Contact + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentContact` action +public struct PresentContactNavigator: TypedNavigator { + /// View controller which should be pushed + var viewController: (Contact) -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed + public init(_ viewController: @escaping (Contact) -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentContact, completion: @escaping () -> Void) { + action.navigationController.pushViewController(viewController(action.contact), animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentContactList.swift b/Sources/AppNavigation/PresentContactList.swift new file mode 100644 index 0000000000000000000000000000000000000000..bdf9d30fc3a9118ced62cb0ae8e6c44322f48f0e --- /dev/null +++ b/Sources/AppNavigation/PresentContactList.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Sets `ContactList` on a given navigation controller stack +public struct PresentContactList: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which stack should be set + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which stack should be set + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentContactList` action +public struct PresentContactListNavigator: TypedNavigator { + /// View controller to which should be set in navigation stack + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be set in navigation stack + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentContactList, completion: @escaping () -> Void) { + action.navigationController.setViewControllers([viewController()], animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentCountryList.swift b/Sources/AppNavigation/PresentCountryList.swift new file mode 100644 index 0000000000000000000000000000000000000000..5365cf6b1e2c925fdf8fee7dd7a34b17efe0c499 --- /dev/null +++ b/Sources/AppNavigation/PresentCountryList.swift @@ -0,0 +1,47 @@ +import UIKit + +/// Presents `CountryList` on a given parent view controller +public struct PresentCountryList: Action { + /// - Parameters: + /// - completion: Completion closure with the selected country model + /// - parent: Parent view controller from which presentation should happen + /// - animated: Animate the transition + public init( + completion: @escaping (Any) -> Void, + from parent: UIViewController, + animated: Bool = true + ) { + self.completion = completion + self.parent = parent + self.animated = animated + } + + /// Completion closure with the selected country model + public var completion: (Any) -> Void + + /// Parent view controller from which presentation should happen + public var parent: UIViewController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentCountryList` action +public struct PresentCountryListNavigator: TypedNavigator { + /// View controller which should be presented + var viewController: (@escaping (Any) -> Void) -> UIViewController + + /// - Parameters: + /// - viewController: view controller which should be presented + public init(_ viewController: @escaping (@escaping (Any) -> Void) -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentCountryList, completion: @escaping () -> Void) { + action.parent.present( + viewController(action.completion), + animated: action.animated, + completion: completion + ) + } +} diff --git a/Sources/AppNavigation/PresentCreateGroupDrawer.swift b/Sources/AppNavigation/PresentCreateGroupDrawer.swift new file mode 100644 index 0000000000000000000000000000000000000000..8abf07fdaa88842ef7d9ef9bdc8b657d375dc872 --- /dev/null +++ b/Sources/AppNavigation/PresentCreateGroupDrawer.swift @@ -0,0 +1,56 @@ +import UIKit +import XXModels + +/// Opens up `CreateGroup` on a given parent view controller +public struct PresentCreateGroup: Action { + /// - Parameters: + /// - members: Collection of contacts that will be in the group + /// - parent: Parent view controller from which presentation should happen + /// - animated: Animate the transition + public init( + members: [Contact], + from parent: UIViewController, + animated: Bool = true + ) { + self.members = members + self.parent = parent + self.animated = animated + } + + /// Collection of contacts that will be in the group + public var members: [Contact] + + /// Parent view controller from which presentation should happen + public var parent: UIViewController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentCreateGroup` action +public struct PresentCreateGroupNavigator: TypedNavigator { + /// Custom transitioning delegate + let transitioningDelegate = BottomTransitioningDelegate() + + /// View controller which should be opened up + var viewController: ([Contact]) -> UIViewController + + /// - Parameters: + /// - viewController: view controller which should be presented + public init(_ viewController: @escaping ([Contact]) -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentCreateGroup, completion: @escaping () -> Void) { + transitioningDelegate.isDismissableOnBackgroundTouch = true + let controller = viewController(action.members) + controller.transitioningDelegate = transitioningDelegate + controller.modalPresentationStyle = .overFullScreen + + action.parent.present( + controller, + animated: action.animated, + completion: completion + ) + } +} diff --git a/Sources/AppNavigation/PresentDrawer.swift b/Sources/AppNavigation/PresentDrawer.swift new file mode 100644 index 0000000000000000000000000000000000000000..8e49f814efafa6552f2c3f2b8c37ef79a83bef8f --- /dev/null +++ b/Sources/AppNavigation/PresentDrawer.swift @@ -0,0 +1,61 @@ +import UIKit + +/// Opens up `Drawer` on a given parent view controller +public struct PresentDrawer: Action { + /// - Parameters: + /// - items: Collection of drawer items that will be present on the view controller + /// - isDismissable: Flag that differentiates whether this presentation is dismissable on background touch + /// - parent: Parent view controller from which presentation should happen + /// - animated: Animate the transition + public init( + items: [Any], + isDismissable: Bool, + from parent: UIViewController, + animated: Bool = true + ) { + self.items = items + self.isDismissable = isDismissable + self.parent = parent + self.animated = animated + } + + /// Collection of drawer items that will be present on the view controller + public var items: [Any] + + /// Flag that differentiates whether this presentation is dismissable on background touch + public var isDismissable: Bool + + /// Parent view controller from which presentation should happen + public var parent: UIViewController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentDrawer` action +public struct PresentDrawerNavigator: TypedNavigator { + /// Custom transitioning delegate + let transitioningDelegate = BottomTransitioningDelegate() + + /// View controller which should be opened up + var viewController: ([Any]) -> UIViewController + + /// - Parameters: + /// - viewController: view controller which should be presented + public init(_ viewController: @escaping ([Any]) -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentDrawer, completion: @escaping () -> Void) { + transitioningDelegate.isDismissableOnBackgroundTouch = action.isDismissable + let controller = viewController(action.items) + controller.transitioningDelegate = transitioningDelegate + controller.modalPresentationStyle = .overFullScreen + + action.parent.present( + controller, + animated: action.animated, + completion: completion + ) + } +} diff --git a/Sources/AppNavigation/PresentGroupChat.swift b/Sources/AppNavigation/PresentGroupChat.swift new file mode 100644 index 0000000000000000000000000000000000000000..e5695a778eca7820292b449d0209d697fa006162 --- /dev/null +++ b/Sources/AppNavigation/PresentGroupChat.swift @@ -0,0 +1,49 @@ +import UIKit +import XXModels + +/// Pushes `GroupChat` on a given navigation controller +public struct PresentGroupChat: Action { + /// - Parameters: + /// - groupInfo: Model to build the view controller which will be pushed + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + groupInfo: GroupInfo, + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.groupInfo = groupInfo + self.navigationController = navigationController + self.animated = animated + } + + /// Model to build the view controller which will be pushed + public var groupInfo: GroupInfo + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentGroupChat` action +public struct PresentGroupChatNavigator: TypedNavigator { + /// View controller which should be pushed + var viewController: (GroupInfo) -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed + public init(_ viewController: @escaping (GroupInfo) -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentGroupChat, completion: @escaping () -> Void) { + action.navigationController.pushViewController(viewController(action.groupInfo), animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentGroupDraft.swift b/Sources/AppNavigation/PresentGroupDraft.swift new file mode 100644 index 0000000000000000000000000000000000000000..9190507f7e89baf10c89357c3245162d6f1126fe --- /dev/null +++ b/Sources/AppNavigation/PresentGroupDraft.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Pushes `GroupDraft` on a given navigation controller +public struct PresentGroupDraft: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentGroupDraft` action +public struct PresentGroupDraftNavigator: TypedNavigator { + /// View controller which should be pushed + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentGroupDraft, completion: @escaping () -> Void) { + action.navigationController.pushViewController(viewController(), animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentMemberList.swift b/Sources/AppNavigation/PresentMemberList.swift new file mode 100644 index 0000000000000000000000000000000000000000..3fa6582f85d7e92b9d476248454c3dda3629efc8 --- /dev/null +++ b/Sources/AppNavigation/PresentMemberList.swift @@ -0,0 +1,14 @@ +import XXModels + +public struct PresentMemberList: Action { + public var members: [Contact] + public var animated: Bool + + public init( + members: [Contact], + animated: Bool = true + ) { + self.members = members + self.animated = animated + } +} diff --git a/Sources/AppNavigation/PresentMenu.swift b/Sources/AppNavigation/PresentMenu.swift new file mode 100644 index 0000000000000000000000000000000000000000..59561cc1acf0f0f5121f26d89f1b5b14a22a4cd1 --- /dev/null +++ b/Sources/AppNavigation/PresentMenu.swift @@ -0,0 +1,66 @@ +import UIKit + +/// Options that can be lead to a flow on the menu UI +public enum MenuItem { + case join + case scan + case chats + case share + case profile + case contacts + case requests + case settings + case dashboard +} + +/// Opens left `Menu` on a given parent view controller +public struct PresentMenu: Action { + /// - Parameters: + /// - currentItem: A correlation with the flow that this controller is being presented + /// - parent: Parent view controller from which presentation should happen + /// - animated: Animate the transition + public init( + currentItem: MenuItem, + from parent: UIViewController, + animated: Bool = true + ) { + self.currentItem = currentItem + self.parent = parent + self.animated = animated + } + + /// A correlation with the flow that this controller is being presented + public var currentItem: MenuItem + + /// Parent view controller from which presentation should happen + public var parent: UIViewController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentMenu` action +public struct PresentMenuNavigator: TypedNavigator { + /// Custom transitioning delegate + let transitioningDelegate = LeftTransitioningDelegate() + + /// View controller which should be opened left + var viewController: (MenuItem, UINavigationController?) -> UIViewController + + /// - Parameters: + /// - viewController: view controller which should be presented + public init(_ viewController: @escaping (MenuItem, UINavigationController?) -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentMenu, completion: @escaping () -> Void) { + let controller = viewController(action.currentItem, action.parent.navigationController) + controller.transitioningDelegate = transitioningDelegate + controller.modalPresentationStyle = .overFullScreen + action.parent.present( + controller, + animated: action.animated, + completion: completion + ) + } +} diff --git a/Sources/AppNavigation/PresentNickname.swift b/Sources/AppNavigation/PresentNickname.swift new file mode 100644 index 0000000000000000000000000000000000000000..8c6bd57c346a9beed71e8b40595e2b7367590600 --- /dev/null +++ b/Sources/AppNavigation/PresentNickname.swift @@ -0,0 +1,60 @@ +import UIKit + +/// Opens up `Nickname` on a given parent view controller +public struct PresentNickname: Action { + /// - Parameters: + /// - prefilled: Optional value to be set as placeholder/pre-existent text + /// - completion: Closure that passes the value of the text set + /// - parent: Parent view controller from which presentation should happen + /// - animated: Animate the transition + public init( + prefilled: String?, + completion: @escaping (String) -> Void, + from parent: UIViewController, + animated: Bool = true + ) { + self.prefilled = prefilled + self.completion = completion + self.parent = parent + self.animated = animated + } + + /// Optional value to be set as placeholder/pre-existent text + public var prefilled: String? + + /// Closure that passes the value of the text set + public var completion: (String) -> Void + + /// Parent view controller from which presentation should happen + public var parent: UIViewController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentNickname` action +public struct PresentNicknameNavigator: TypedNavigator { + /// Custom transitioning delegate + let transitioningDelegate = BottomTransitioningDelegate() + + /// View controller which should be opened up + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: view controller which should be presented + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentNickname, completion: @escaping () -> Void) { + let controller = viewController() + controller.transitioningDelegate = transitioningDelegate + controller.modalPresentationStyle = .overFullScreen + + action.parent.present( + controller, + animated: action.animated, + completion: completion + ) + } +} diff --git a/Sources/AppNavigation/PresentOnboardingCode.swift b/Sources/AppNavigation/PresentOnboardingCode.swift new file mode 100644 index 0000000000000000000000000000000000000000..7ed2f50c8dd725db9673ff22e71762cbef3d8ecf --- /dev/null +++ b/Sources/AppNavigation/PresentOnboardingCode.swift @@ -0,0 +1,61 @@ +import UIKit + +/// Pushes `OnboardingCode` on a given navigation controller +public struct PresentOnboardingCode: Action { + /// - Parameters: + /// - isEmail: Flag to differentiate email or phone code + /// - content: Content that is being set if confirmation code gets validated + /// - confirmationId: Confirmation id to validate with third-party + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + isEmail: Bool, + content: String, + confirmationId: String, + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.isEmail = isEmail + self.content = content + self.confirmationId = confirmationId + self.navigationController = navigationController + self.animated = animated + } + + /// Flag to differentiate email or phone code + public var isEmail: Bool + + /// Content that is being set if confirmation code gets validated + public var content: String + + /// Confirmation id to validate with third-party + public var confirmationId: String + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentOnboardingCode` action +public struct PresentOnboardingCodeNavigator: TypedNavigator { + /// View controller which should be pushed + var viewController: (Bool, String, String) -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed + public init(_ viewController: @escaping (Bool, String, String) -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentOnboardingCode, completion: @escaping () -> Void) { + let controller = viewController(action.isEmail, action.content, action.confirmationId) + action.navigationController.pushViewController(controller, animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentOnboardingEmail.swift b/Sources/AppNavigation/PresentOnboardingEmail.swift new file mode 100644 index 0000000000000000000000000000000000000000..4f57119e0157d8b480cc55c41fea90bde38eac2e --- /dev/null +++ b/Sources/AppNavigation/PresentOnboardingEmail.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Sets `OnboardingEmail` on a given navigation controller stack +public struct PresentOnboardingEmail: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which stack should be set + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which stack should be set + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentOnboardingEmail` action +public struct PresentOnboardingEmailNavigator: TypedNavigator { + /// View controller which should be set in navigation stack + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be set in navigation stack + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentOnboardingEmail, completion: @escaping () -> Void) { + action.navigationController.setViewControllers([viewController()], animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentOnboardingPhone.swift b/Sources/AppNavigation/PresentOnboardingPhone.swift new file mode 100644 index 0000000000000000000000000000000000000000..eca7c89605f99e1f6ec8e982150907f7f7ef80d0 --- /dev/null +++ b/Sources/AppNavigation/PresentOnboardingPhone.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Sets `OnboardingPhone` on a given navigation controller stack +public struct PresentOnboardingPhone: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which stack should be set + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which stack should be set + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentOnboardingPhone` action +public struct PresentOnboardingPhoneNavigator: TypedNavigator { + /// View controller which should be set in navigation stack + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be set in navigation stack + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentOnboardingPhone, completion: @escaping () -> Void) { + action.navigationController.setViewControllers([viewController()], animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentOnboardingStart.swift b/Sources/AppNavigation/PresentOnboardingStart.swift new file mode 100644 index 0000000000000000000000000000000000000000..e531c6909ea2e1087dfea80e608dcd384c7dafef --- /dev/null +++ b/Sources/AppNavigation/PresentOnboardingStart.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Sets `OnboardingStart` on a given navigation controller stack +public struct PresentOnboardingStart: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which stack should be set + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which stack should be set + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentOnboardingStart` action +public struct PresentOnboardingStartNavigator: TypedNavigator { + /// View controller which should be set in navigation stack + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be set in navigation stack + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentOnboardingStart, completion: @escaping () -> Void) { + action.navigationController.setViewControllers([viewController()], animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentOnboardingUsername.swift b/Sources/AppNavigation/PresentOnboardingUsername.swift new file mode 100644 index 0000000000000000000000000000000000000000..428dc24a0b84c014e3473e575d7f2058ff92180e --- /dev/null +++ b/Sources/AppNavigation/PresentOnboardingUsername.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Pushes `OnboardingUsername` on a given navigation controller +public struct PresentOnboardingUsername: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentOnboardingUsername` action +public struct PresentOnboardingUsernameNavigator: TypedNavigator { + /// View controller which should be pushed + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentOnboardingUsername, completion: @escaping () -> Void) { + action.navigationController.pushViewController(viewController(), animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentOnboardingWelcome.swift b/Sources/AppNavigation/PresentOnboardingWelcome.swift new file mode 100644 index 0000000000000000000000000000000000000000..0df955e7e77208e5dcc675220621b7f800403f82 --- /dev/null +++ b/Sources/AppNavigation/PresentOnboardingWelcome.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Sets `OnboardingWelcome` on a given navigation controller stack +public struct PresentOnboardingWelcome: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which stack should be set + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which stack should be set + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentOnboardingWelcome` action +public struct PresentOnboardingWelcomeNavigator: TypedNavigator { + /// View controller which should be set in navigation stack + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be set in navigation stack + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentOnboardingWelcome, completion: @escaping () -> Void) { + action.navigationController.setViewControllers([viewController()], animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentPassphrase.swift b/Sources/AppNavigation/PresentPassphrase.swift new file mode 100644 index 0000000000000000000000000000000000000000..6e208f26b048d5649c4badf2cafd5a3d43fd9d0e --- /dev/null +++ b/Sources/AppNavigation/PresentPassphrase.swift @@ -0,0 +1,15 @@ +public struct PresentPassphrase: Action { + public var onCancel: () -> Void + public var onPasspharse: (String) -> Void + public var animated: Bool + + public init( + onCancel: @escaping () -> Void, + onPassphrase: @escaping (String) -> Void, + animated: Bool = true + ) { + self.onCancel = onCancel + self.onPasspharse = onPassphrase + self.animated = animated + } +} diff --git a/Sources/AppNavigation/PresentPermissionRequest.swift b/Sources/AppNavigation/PresentPermissionRequest.swift new file mode 100644 index 0000000000000000000000000000000000000000..76c89f1c94a814061473dfcb003466dc9795cddb --- /dev/null +++ b/Sources/AppNavigation/PresentPermissionRequest.swift @@ -0,0 +1,59 @@ +import UIKit + +/// Types of permissions that can be requested to the user +public enum PermissionType { + /// Device camera permission type + case camera + + /// Camera roll and library permission type + case library + + /// Device microphone permission type + case microphone +} + +/// Presents `PermissionRequest` on provided parent view controller +public struct PresentPermissionRequest: Action { + /// - Parameters: + /// - type: Type of permission that is being requested + /// - parent: Parent view controller from which presentation should happen + /// - animated: Animate the transition + public init( + type: PermissionType, + from parent: UIViewController, + animated: Bool = true + ) { + self.type = type + self.parent = parent + self.animated = animated + } + + /// Type of permission that is being requested + public var type: PermissionType + + /// Parent view controller from which presentation should happen + public var parent: UIViewController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentPermissionRequest` action +public struct PresentPermissionRequestNavigator: TypedNavigator { + /// View controller which should be presented + var viewController: (PermissionType) -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be presented + public init(_ viewController: @escaping (PermissionType) -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentPermissionRequest, completion: @escaping () -> Void) { + action.parent.present( + viewController(action.type), + animated: action.animated, + completion: completion + ) + } +} diff --git a/Sources/AppNavigation/PresentPhotoLibrary.swift b/Sources/AppNavigation/PresentPhotoLibrary.swift new file mode 100644 index 0000000000000000000000000000000000000000..886b3c8b3c5bcf4468f02113d20865e3f2387d4d --- /dev/null +++ b/Sources/AppNavigation/PresentPhotoLibrary.swift @@ -0,0 +1,36 @@ +import UIKit + +/// Presents `PhotoLibrary` on provided parent view controller +public struct PresentPhotoLibrary: Action { + /// - Parameters: + /// - parent: Parent view controller from which presentation should happen + /// - animated: Animate the transition + public init( + from parent: UIViewController, + animated: Bool = true + ) { + self.parent = parent + self.animated = animated + } + + /// Parent view controller from which presentation should happen + public var parent: UIViewController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentPhotoLibrary` action +public struct PresentPhotoLibraryNavigator: TypedNavigator { + public init() {} + + public func perform(_ action: PresentPhotoLibrary, completion: @escaping () -> Void) { + let imagePickerController = UIImagePickerController() + imagePickerController.delegate = action.parent as? UIImagePickerControllerDelegate & UINavigationControllerDelegate + action.parent.present( + imagePickerController, + animated: action.animated, + completion: completion + ) + } +} diff --git a/Sources/AppNavigation/PresentProfile.swift b/Sources/AppNavigation/PresentProfile.swift new file mode 100644 index 0000000000000000000000000000000000000000..42363e49108b8bb363987fd17c998240e8fc9554 --- /dev/null +++ b/Sources/AppNavigation/PresentProfile.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Sets `Profile` on a given navigation controller stack +public struct PresentProfile: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which stack should be set + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which stack should be set + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentProfile` action +public struct PresentProfileNavigator: TypedNavigator { + /// View controller which should be set in navigation stack + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be set in navigation stack + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentProfile, completion: @escaping () -> Void) { + action.navigationController.setViewControllers([viewController()], animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentProfileCode.swift b/Sources/AppNavigation/PresentProfileCode.swift new file mode 100644 index 0000000000000000000000000000000000000000..db50a72d8f3ef9a5e40e6188025a68d44b924d5b --- /dev/null +++ b/Sources/AppNavigation/PresentProfileCode.swift @@ -0,0 +1,61 @@ +import UIKit + +/// Pushes `ProfileCode` on a given navigation controller +public struct PresentProfileCode: Action { + /// - Parameters: + /// - isEmail: Flag to differentiate email or phone code + /// - content: Content that is being set if confirmation code gets validated + /// - confirmationId: Confirmation id to validate with third-party + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + isEmail: Bool, + content: String, + confirmationId: String, + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.isEmail = isEmail + self.content = content + self.confirmationId = confirmationId + self.navigationController = navigationController + self.animated = animated + } + + /// Flag to differentiate email or phone code + public var isEmail: Bool + + /// Content that is being set if confirmation code gets validated + public var content: String + + /// Confirmation id to validate with third-party + public var confirmationId: String + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentProfileCode` action +public struct PresentProfileCodeNavigator: TypedNavigator { + /// View controller which should be pushed + var viewController: (Bool, String, String) -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed + public init(_ viewController: @escaping (Bool, String, String) -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentProfileCode, completion: @escaping () -> Void) { + let controller = viewController(action.isEmail, action.content, action.confirmationId) + action.navigationController.pushViewController(controller, animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentProfileEmail.swift b/Sources/AppNavigation/PresentProfileEmail.swift new file mode 100644 index 0000000000000000000000000000000000000000..0ea2c524781d1046b806c65edd9be0931d0f7bc7 --- /dev/null +++ b/Sources/AppNavigation/PresentProfileEmail.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Pushes `ProfileEmail` on a given navigation controller +public struct PresentProfileEmail: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentProfileEmail` action +public struct PresentProfileEmailNavigator: TypedNavigator { + /// View controller which should be pushed + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentProfileEmail, completion: @escaping () -> Void) { + action.navigationController.pushViewController(viewController(), animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentProfilePhone.swift b/Sources/AppNavigation/PresentProfilePhone.swift new file mode 100644 index 0000000000000000000000000000000000000000..c669afe41c355ee6ef39fa1c38dbea2dae2a7583 --- /dev/null +++ b/Sources/AppNavigation/PresentProfilePhone.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Pushes `ProfilePhone` on a given navigation controller +public struct PresentProfilePhone: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentProfilePhone` action +public struct PresentProfilePhoneNavigator: TypedNavigator { + /// View controller which should be pushed + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentProfilePhone, completion: @escaping () -> Void) { + action.navigationController.pushViewController(viewController(), animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentRequests.swift b/Sources/AppNavigation/PresentRequests.swift new file mode 100644 index 0000000000000000000000000000000000000000..aa346ff45292234cc1576135d517b49625f04916 --- /dev/null +++ b/Sources/AppNavigation/PresentRequests.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Sets `Requests` on a given navigation controller stack +public struct PresentRequests: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which stack should be set + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which stack should be set + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentRequests` action +public struct PresentRequestsNavigator: TypedNavigator { + /// View controller which should be set in navigation stack + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be set in navigation stack + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentRequests, completion: @escaping () -> Void) { + action.navigationController.setViewControllers([viewController()], animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentRestoreList.swift b/Sources/AppNavigation/PresentRestoreList.swift new file mode 100644 index 0000000000000000000000000000000000000000..977c224537b219dff54e4ed87f87115c01af945d --- /dev/null +++ b/Sources/AppNavigation/PresentRestoreList.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Pushes `RestoreList` on a given navigation controller +public struct PresentRestoreList: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentRestoreList` action +public struct PresentRestoreListNavigator: TypedNavigator { + /// View controller which should be pushed + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentRestoreList, completion: @escaping () -> Void) { + action.navigationController.pushViewController(viewController(), animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentSFTP.swift b/Sources/AppNavigation/PresentSFTP.swift new file mode 100644 index 0000000000000000000000000000000000000000..dee1bde64055af2fb2fa7fa4e8006bed8270c20b --- /dev/null +++ b/Sources/AppNavigation/PresentSFTP.swift @@ -0,0 +1,49 @@ +import UIKit + +/// Pushes `SFTP` on a given navigation controller +public struct PresentSFTP: Action { + /// - Parameters: + /// - completion: Completion closure with host, username and password + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + completion: @escaping (String, String, String) -> Void, + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.completion = completion + self.navigationController = navigationController + self.animated = animated + } + + /// Completion closure with host, username and password + public var completion: (String, String, String) -> Void + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentSFTP` action +public struct PresentSFTPNavigator: TypedNavigator { + /// View controller which should be pushed + var viewController: (@escaping (String, String, String) -> Void) -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed + public init(_ viewController: @escaping (@escaping (String, String, String) -> Void) -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentSFTP, completion: @escaping () -> Void) { + let controller = viewController(action.completion) + action.navigationController.pushViewController(controller, animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentScan.swift b/Sources/AppNavigation/PresentScan.swift new file mode 100644 index 0000000000000000000000000000000000000000..d4fb08cb2be013598154342439480972f26f4e23 --- /dev/null +++ b/Sources/AppNavigation/PresentScan.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Sets `Scan` on a given navigation controller stack +public struct PresentScan: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which stack should be set + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which stack should be set + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentScan` action +public struct PresentScanNavigator: TypedNavigator { + /// View controller which should be set in navigation stack + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be set in navigation stack + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentScan, completion: @escaping () -> Void) { + action.navigationController.setViewControllers([viewController()], animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentSearch.swift b/Sources/AppNavigation/PresentSearch.swift new file mode 100644 index 0000000000000000000000000000000000000000..4dc89878f03d712d3ccf5486a4be4c3dcab82673 --- /dev/null +++ b/Sources/AppNavigation/PresentSearch.swift @@ -0,0 +1,59 @@ +import UIKit + +/// Sets or Pushes `Search` on a given navigation controller +public struct PresentSearch: Action { + /// - Parameters: + /// - searching: Optional string to be searched upon further viewModel intialization + /// - replacing: Flag to differentiate if should be a push or a set stack + /// - navigationController: Navigation controller on which will be pushed or stack should be set + /// - animated: Animate the transition + public init( + searching: String?, + replacing: Bool, + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.searching = searching + self.replacing = replacing + self.navigationController = navigationController + self.animated = animated + } + + /// Optional string to be searched upon further viewModel intialization + public var searching: String? + + /// Flag to differentiate if should be a push or a set stack + public var replacing: Bool + + /// Navigation controller on which stack should be set + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentSearch` action +public struct PresentSearchNavigator: TypedNavigator { + /// View controller which should be pushed or set in navigation stack + var viewController: (String?) -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed or set in navigation stack + public init(_ viewController: @escaping (String?) -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentSearch, completion: @escaping () -> Void) { + if action.replacing { + action.navigationController.setViewControllers([viewController(action.searching)], animated: action.animated) + } else { + action.navigationController.pushViewController(viewController(action.searching), animated: action.animated) + } + + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentSettings.swift b/Sources/AppNavigation/PresentSettings.swift new file mode 100644 index 0000000000000000000000000000000000000000..e6a4871612234c122da83bdd6be5869b895062e8 --- /dev/null +++ b/Sources/AppNavigation/PresentSettings.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Sets `Settings` on a given navigation controller stack +public struct PresentSettings: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which stack should be set + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which stack should be set + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentSettings` action +public struct PresentSettingsNavigator: TypedNavigator { + /// View controller which should be set in navigation stack + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be set in navigation stack + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentSettings, completion: @escaping () -> Void) { + action.navigationController.setViewControllers([viewController()], animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentSettingsAccountDelete.swift b/Sources/AppNavigation/PresentSettingsAccountDelete.swift new file mode 100644 index 0000000000000000000000000000000000000000..e5691b4cda89a52c223c2c463fd96743465b1cd4 --- /dev/null +++ b/Sources/AppNavigation/PresentSettingsAccountDelete.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Pushes `SettingsAccountDelete` on a given navigation controller +public struct PresentSettingsAccountDelete: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentSettingsAccountDelete` action +public struct PresentSettingsAccountDeleteNavigator: TypedNavigator { + /// View controller which should be pushed + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentSettingsAccountDelete, completion: @escaping () -> Void) { + action.navigationController.pushViewController(viewController(), animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentSettingsAdvanced.swift b/Sources/AppNavigation/PresentSettingsAdvanced.swift new file mode 100644 index 0000000000000000000000000000000000000000..737f02fa2f6d97a8da754f821d1860502dcfa957 --- /dev/null +++ b/Sources/AppNavigation/PresentSettingsAdvanced.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Pushes `SettingsAdvanced` on a given navigation controller +public struct PresentSettingsAdvanced: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentSettingsAdvanced` action +public struct PresentSettingsAdvancedNavigator: TypedNavigator { + /// View controller which should be pushed + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentSettingsAdvanced, completion: @escaping () -> Void) { + action.navigationController.pushViewController(viewController(), animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentSettingsBackup.swift b/Sources/AppNavigation/PresentSettingsBackup.swift new file mode 100644 index 0000000000000000000000000000000000000000..f1d2ec5e3808f5bc45e8a968a03f8a4c0272f89a --- /dev/null +++ b/Sources/AppNavigation/PresentSettingsBackup.swift @@ -0,0 +1,42 @@ +import UIKit + +/// Pushes `SettingsBackup` on a given navigation controller +public struct PresentSettingsBackup: Action { + /// - Parameters: + /// - navigationController: Navigation controller on which push should happen + /// - animated: Animate the transition + public init( + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.navigationController = navigationController + self.animated = animated + } + + /// Navigation controller on which push should happen + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentSettingsBackup` action +public struct PresentSettingsBackupNavigator: TypedNavigator { + /// View controller which should be pushed + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentSettingsBackup, completion: @escaping () -> Void) { + action.navigationController.pushViewController(viewController(), animated: action.animated) + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentTermsAndConditions.swift b/Sources/AppNavigation/PresentTermsAndConditions.swift new file mode 100644 index 0000000000000000000000000000000000000000..8baa2e06fb19f87e770eb5792b898f97e94bee65 --- /dev/null +++ b/Sources/AppNavigation/PresentTermsAndConditions.swift @@ -0,0 +1,53 @@ +import UIKit + +/// Sets or Pushes `TermsAndConditions` on a given navigation controller +public struct PresentTermsAndConditions: Action { + /// - Parameters: + /// - replacing: Flag to differentiate if should be a push or a set stack + /// - navigationController: Navigation controller on which will be pushed or stack should be set + /// - animated: Animate the transition + public init( + replacing: Bool, + on navigationController: UINavigationController, + animated: Bool = true + ) { + self.replacing = replacing + self.navigationController = navigationController + self.animated = animated + } + + /// Flag to differentiate if should be a push or a set stack + public var replacing: Bool + + /// Navigation controller on which stack should be set + public var navigationController: UINavigationController + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentTermsAndConditions` action +public struct PresentTermsAndConditionsNavigator: TypedNavigator { + /// View controller which should be pushed or set in navigation stack + var viewController: () -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be pushed or set in navigation stack + public init(_ viewController: @escaping () -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentTermsAndConditions, completion: @escaping () -> Void) { + if action.replacing { + action.navigationController.setViewControllers([viewController()], animated: action.animated) + } else { + action.navigationController.pushViewController(viewController(), animated: action.animated) + } + + if action.animated, let coordinator = action.navigationController.transitionCoordinator { + coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) + } else { + completion() + } + } +} diff --git a/Sources/AppNavigation/PresentWebsite.swift b/Sources/AppNavigation/PresentWebsite.swift new file mode 100644 index 0000000000000000000000000000000000000000..02abd2eef9dfb3709e1704d66c662b93524d6471 --- /dev/null +++ b/Sources/AppNavigation/PresentWebsite.swift @@ -0,0 +1,48 @@ +import UIKit +import WebKit + +/// Presents `Website` on a given parent view controller +public struct PresentWebsite: Action { + /// - Parameters: + /// - urlString: Url that will be loaded on the web view + /// - parent: Parent view controller from which presentation should happen + /// - animated: Animate the transition + public init( + urlString: String, + from parent: UIViewController, + animated: Bool = true + ) { + self.urlString = urlString + self.parent = parent + self.animated = animated + } + + /// Parent view controller from which presentation should happen + public var parent: UIViewController + + /// Url that will be loaded on the web view + public var urlString: String + + /// Animate the transition + public var animated: Bool +} + +/// Performs `PresentWebsite` action +public struct PresentWebsiteNavigator: TypedNavigator { + /// View controller which should be presented + var viewController: (String) -> UIViewController + + /// - Parameters: + /// - viewController: View controller which should be presented + public init(_ viewController: @escaping (String) -> UIViewController) { + self.viewController = viewController + } + + public func perform(_ action: PresentWebsite, completion: @escaping () -> Void) { + action.parent.present( + viewController(action.urlString), + animated: action.animated, + completion: completion + ) + } +} diff --git a/Sources/AppNavigation/TypedNavigator.swift b/Sources/AppNavigation/TypedNavigator.swift new file mode 100644 index 0000000000000000000000000000000000000000..c351c98a2257a610fd7bbd291dcdab614597ee0d --- /dev/null +++ b/Sources/AppNavigation/TypedNavigator.swift @@ -0,0 +1,46 @@ +/// Navigation that can perform action of a concrete type +public protocol TypedNavigator: Navigator { + /// Type of the action that the navigator can perform + associatedtype ActionType: Action + + /// Returns true if the action can be performed by the navigator + /// - Default implementation returns true for any action + /// - Parameter action: navigation action + func canPerform(_ action: ActionType) -> Bool + + /// Performs the navigation action + /// - Parameters: + /// - action: navigation action + /// - completion: closure that will be executed after performing the action + func perform(_ action: ActionType, completion: @escaping () -> Void) +} + +public extension TypedNavigator { + /// Returns true if the navigation action is of the type handled by the navigator + /// - Parameter action: navigation action + /// - Returns: true if action can be performed + func canPerform(_ action: Action) -> Bool { + if let action = action as? ActionType { + return canPerform(action) + } + return false + } + + func canPerform(_ action: ActionType) -> Bool { true } + + /// Performs the navigation action with empty completion closure + /// - Parameter action: navigation action + func perform(_ action: ActionType) { + perform(action, completion: {}) + } + + /// Performs the navigation action if its type matches `ActionType` handled by the navigator + /// - Parameters: + /// - action: navigation action + /// - completion: closure that will be executed after performing the action + func perform(_ action: Action, completion: @escaping () -> Void) { + if let action = action as? ActionType { + perform(action, completion: completion) + } + } +} diff --git a/Sources/Shared/swiftgen.yml b/Sources/AppResources/swiftgen.yml similarity index 77% rename from Sources/Shared/swiftgen.yml rename to Sources/AppResources/swiftgen.yml index 1811db25daa24e73f82a0022cc191d14e15f664b..3e4548e0eb8a79eefb0bb9f37d8d3ef7cc9338da 100644 --- a/Sources/Shared/swiftgen.yml +++ b/Sources/AppResources/swiftgen.yml @@ -2,7 +2,7 @@ strings: inputs: Resources/en.lproj outputs: templateName: structured-swift5 - output: AutoGenerated/Strings.swift + output: Strings.swift params: enumName: Localized publicAccess: true @@ -11,7 +11,7 @@ xcassets: inputs: Resources/Assets.xcassets outputs: templateName: swift5 - output: AutoGenerated/Assets.swift + output: Assets.swift params: publicAccess: true @@ -19,7 +19,7 @@ fonts: inputs: Resources/Fonts outputs: templateName: swift5 - output: AutoGenerated/Fonts.swift + output: Fonts.swift params: enumName: Fonts publicAccess: true diff --git a/Sources/BackupFeature/Controllers/BackupConfigController.swift b/Sources/BackupFeature/Controllers/BackupConfigController.swift index 25772de76f0a5a0bc13a45ddc2789088b3307982..f2e0220380a545b5d8a79cc5c7517d7676930ba1 100644 --- a/Sources/BackupFeature/Controllers/BackupConfigController.swift +++ b/Sources/BackupFeature/Controllers/BackupConfigController.swift @@ -1,14 +1,14 @@ -import DI import UIKit import Shared import Combine import CloudFiles -import Navigation -import DrawerFeature import AppResources +import AppNavigation +import DrawerFeature +import ComposableArchitecture final class BackupConfigController: UIViewController { - @Dependency var navigator: Navigator + @Dependency(\.navigator) var navigator: Navigator private lazy var screenView = BackupConfigView() diff --git a/Sources/BackupFeature/Controllers/BackupController.swift b/Sources/BackupFeature/Controllers/BackupController.swift index b01e3865f6aee5765f0cdb18423b553c266de428..ca82c50e8c1d470f1c0433f16e3a6d182ddd0604 100644 --- a/Sources/BackupFeature/Controllers/BackupController.swift +++ b/Sources/BackupFeature/Controllers/BackupController.swift @@ -1,4 +1,3 @@ -import DI import UIKit import Shared import Combine diff --git a/Sources/BackupFeature/Controllers/BackupPassphraseController.swift b/Sources/BackupFeature/Controllers/BackupPassphraseController.swift index 9ec594c8680d4bc8162cfbc47c077bbbea26b127..35b97b35cab02ea52f5bc5b9f800ccca3a4cba17 100644 --- a/Sources/BackupFeature/Controllers/BackupPassphraseController.swift +++ b/Sources/BackupFeature/Controllers/BackupPassphraseController.swift @@ -17,13 +17,13 @@ public final class BackupPassphraseController: UIViewController { } } - private let cancelClosure: EmptyClosure - private let stringClosure: StringClosure + private let cancelClosure: () -> Void + private let stringClosure: (String) -> Void private var cancellables = Set<AnyCancellable>() public init( - _ cancelClosure: @escaping EmptyClosure, - _ stringClosure: @escaping StringClosure + _ cancelClosure: @escaping () -> Void, + _ stringClosure: @escaping (String) -> Void ) { self.stringClosure = stringClosure self.cancelClosure = cancelClosure diff --git a/Sources/BackupFeature/Controllers/BackupSetupController.swift b/Sources/BackupFeature/Controllers/BackupSetupController.swift index efb0cab4a130ed620275c19319eb5302f4638624..7e697535ebfba84a6ef932e582882f89cc8718eb 100644 --- a/Sources/BackupFeature/Controllers/BackupSetupController.swift +++ b/Sources/BackupFeature/Controllers/BackupSetupController.swift @@ -1,6 +1,5 @@ import UIKit import Combine -import DI final class BackupSetupController: UIViewController { private lazy var screenView = BackupSetupView() diff --git a/Sources/BackupFeature/Service/BackupService.swift b/Sources/BackupFeature/Service/BackupService.swift index c471287bad37f16d3cda7d81dfb7ff4f1fcc0f07..c024227d5e77cc240f456052e6b4cc96beff4543 100644 --- a/Sources/BackupFeature/Service/BackupService.swift +++ b/Sources/BackupFeature/Service/BackupService.swift @@ -10,8 +10,8 @@ import XXMessengerClient import ComposableArchitecture public final class BackupService { - @Dependency(\.app.messenger) var messenger: Messenger - @Dependency(\.app.networkMonitor) var networkMonitor: NetworkMonitorManager + @Dependency(\.app.messenger) var messenger + @Dependency(\.app.networkMonitor) var networkMonitor @KeyObject(.email, defaultValue: nil) var email: String? @KeyObject(.phone, defaultValue: nil) var phone: String? @@ -255,3 +255,4 @@ public final class BackupService { .appendingPathExtension("xxm") } } + diff --git a/Sources/BackupFeature/Service/Dependency.swift b/Sources/BackupFeature/Service/Dependency.swift new file mode 100644 index 0000000000000000000000000000000000000000..9d2b0df290ff5e9e90937473f0e6e4ccd0d26955 --- /dev/null +++ b/Sources/BackupFeature/Service/Dependency.swift @@ -0,0 +1,13 @@ +import Dependencies + +private enum BackupServiceDependencyKey: DependencyKey { + static let liveValue: BackupService = .init() + static let testValue: BackupService = .init() +} + +extension DependencyValues { + public var backupService: BackupService { + get { self[BackupServiceDependencyKey.self] } + set { self[BackupServiceDependencyKey.self] = newValue } + } +} diff --git a/Sources/BackupFeature/ViewModels/BackupConfigViewModel.swift b/Sources/BackupFeature/ViewModels/BackupConfigViewModel.swift index 5318952dcadddd6a33bf7c257a4d51ae96851523..410535e63a8f348ef7882aa9dfad4bd90674ae4a 100644 --- a/Sources/BackupFeature/ViewModels/BackupConfigViewModel.swift +++ b/Sources/BackupFeature/ViewModels/BackupConfigViewModel.swift @@ -5,7 +5,7 @@ import Combine import XXClient import Defaults import CloudFiles -import Navigation +import AppNavigation import ComposableArchitecture enum BackupActionState { @@ -32,8 +32,8 @@ struct BackupConfigViewModel { extension BackupConfigViewModel { static func live() -> Self { class Context { - //@Dependency var service: BackupService @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.backupService) var service: BackupService @Dependency(\.app.hudManager) var hudManager: HUDManager } @@ -42,9 +42,9 @@ extension BackupConfigViewModel { return .init( didTapBackupNow: { context.service.didForceBackup() - context.hudController.show() + context.hudManager.show() DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { - context.hudController.dismiss() + context.hudManager.hide() } }, didChooseWifiOnly: context.service.didSetWiFiOnly(enabled:), @@ -58,12 +58,12 @@ extension BackupConfigViewModel { context.navigator.perform(PresentPassphrase(onCancel: { context.service.toggle(service: service, enabling: false) }, onPassphrase: { passphrase in - context.hudController.show(.init( + context.hudManager.show(.init( content: "Initializing and securing your backup file will take few seconds, please keep the app open." )) context.service.toggle(service: service, enabling: enabling) context.service.initializeBackup(passphrase: passphrase) - context.hudController.dismiss() + context.hudManager.hide() })) }, didTapService: { service, controller in diff --git a/Sources/BackupFeature/ViewModels/BackupSFTPViewModel.swift b/Sources/BackupFeature/ViewModels/BackupSFTPViewModel.swift index 80cdb78497baf010eb0e64c04b53660595af35a9..bd18ee5f294fafcdf2ad8d28eb8465b36429f916 100644 --- a/Sources/BackupFeature/ViewModels/BackupSFTPViewModel.swift +++ b/Sources/BackupFeature/ViewModels/BackupSFTPViewModel.swift @@ -6,7 +6,9 @@ import Combine import Foundation import CloudFiles import CloudFilesSFTP -import DI + +import AppCore +import ComposableArchitecture struct SFTPViewState { var host: String = "" @@ -16,7 +18,7 @@ struct SFTPViewState { } final class BackupSFTPViewModel { - @Dependency var hudController: HUDController + @Dependency(\.app.hudManager) var hudManager: HUDManager var statePublisher: AnyPublisher<SFTPViewState, Never> { stateSubject.eraseToAnyPublisher() @@ -45,7 +47,7 @@ final class BackupSFTPViewModel { } func didTapLogin() { - hudController.show() + hudManager.show() let host = stateSubject.value.host let username = stateSubject.value.username @@ -64,7 +66,7 @@ final class BackupSFTPViewModel { ).link(anyController) { switch $0 { case .success: - self.hudController.dismiss() + self.hudManager.hide() self.authSubject.send((host, username, password)) case .failure(let error): var message = "An error occurred while trying to link SFTP: " @@ -81,11 +83,11 @@ final class BackupSFTPViewModel { message.append(error.localizedDescription) } - self.hudController.show(.init(content: message)) + self.hudManager.show(.init(content: message)) } } } catch { - self.hudController.show(.init(error: error)) + self.hudManager.show(.init(error: error)) } } } diff --git a/Sources/BackupFeature/ViewModels/BackupSetupViewModel.swift b/Sources/BackupFeature/ViewModels/BackupSetupViewModel.swift index 35575aff0578c40a0f57b419740fb7ca5fe3bda1..a470e6c647c987e1af9668fe3a57807075377980 100644 --- a/Sources/BackupFeature/ViewModels/BackupSetupViewModel.swift +++ b/Sources/BackupFeature/ViewModels/BackupSetupViewModel.swift @@ -2,19 +2,19 @@ import UIKit import Shared import Combine import CloudFiles -import DI +import ComposableArchitecture struct BackupSetupViewModel { - var didTapService: (CloudService, UIViewController) -> Void + var didTapService: (CloudService, UIViewController) -> Void } extension BackupSetupViewModel { - static func live() -> Self { - class Context { - @Dependency var service: BackupService - } - - let context = Context() - return .init(didTapService: context.service.authorize) + static func live() -> Self { + class Context { + @Dependency(\.backupService) var service: BackupService } + + let context = Context() + return .init(didTapService: context.service.authorize) + } } diff --git a/Sources/BackupFeature/ViewModels/BackupViewModel.swift b/Sources/BackupFeature/ViewModels/BackupViewModel.swift index d25ddc5ebfbd3778131f15b111ad57e89f4c6733..378590c3edadce9b7036e2f262fcedc6f36c9626 100644 --- a/Sources/BackupFeature/ViewModels/BackupViewModel.swift +++ b/Sources/BackupFeature/ViewModels/BackupViewModel.swift @@ -1,5 +1,5 @@ -import DI import Combine +import ComposableArchitecture enum BackupViewState: Equatable { case setup @@ -16,7 +16,7 @@ struct BackupViewModel { extension BackupViewModel { static func live() -> Self { class Context { - @Dependency var service: BackupService + @Dependency(\.backupService) var service: BackupService } let context = Context() diff --git a/Sources/ChatFeature/CellFactory.swift b/Sources/ChatFeature/CellFactory.swift index bc7bc9bec131565d07dc5805865bdcc13e0959dc..2f7767ebe4902933f0615d000ca48e6ebf9ed8ee 100644 --- a/Sources/ChatFeature/CellFactory.swift +++ b/Sources/ChatFeature/CellFactory.swift @@ -1,76 +1,76 @@ -import UIKit -import XCTestDynamicOverlay - -public struct CellFactory<Model> { - public struct Registrar { - public init(register: @escaping (UICollectionView) -> Void) { - self.register = register - } - - public var register: (UICollectionView) -> Void - - public func callAsFunction(in view: UICollectionView) { - register(view) - } - } - - public struct Builder { - public init(build: @escaping (Model, UICollectionView, IndexPath) -> UICollectionViewCell?) { - self.build = build - } - - public var build: (Model, UICollectionView, IndexPath) -> UICollectionViewCell? - - public func callAsFunction( - for model: Model, - in view: UICollectionView, - at indexPath: IndexPath - ) -> UICollectionViewCell? { - build(model, view, indexPath) - } - } - - public init( - register: Registrar, - build: Builder - ) { - self.register = register - self.build = build - } - - public var register: Registrar - public var build: Builder -} - -extension CellFactory { - public static func combined(_ factories: CellFactory...) -> CellFactory { - combined(factories) - } - - public static func combined(_ factories: [CellFactory]) -> CellFactory { - CellFactory( - register: .init { collectionView in - factories.forEach { $0.register(in: collectionView) } - }, - build: .init { model, collectionView, indexPath in - for factory in factories { - if let cell = factory.build(for: model, in: collectionView, at: indexPath) { - return cell - } - } - return nil - } - ) - } -} - -#if DEBUG -extension CellFactory { - public static func unimplemented() -> CellFactory { - CellFactory( - register: .init(register: XCTUnimplemented("\(Self.self).Registrar")), - build: .init(build: XCTUnimplemented("\(Self.self).Builder")) - ) - } -} -#endif +//import UIKit +//import XCTestDynamicOverlay +// +//public struct CellFactory<Model> { +// public struct Registrar { +// public init(register: @escaping (UICollectionView) -> Void) { +// self.register = register +// } +// +// public var register: (UICollectionView) -> Void +// +// public func callAsFunction(in view: UICollectionView) { +// register(view) +// } +// } +// +// public struct Builder { +// public init(build: @escaping (Model, UICollectionView, IndexPath) -> UICollectionViewCell?) { +// self.build = build +// } +// +// public var build: (Model, UICollectionView, IndexPath) -> UICollectionViewCell? +// +// public func callAsFunction( +// for model: Model, +// in view: UICollectionView, +// at indexPath: IndexPath +// ) -> UICollectionViewCell? { +// build(model, view, indexPath) +// } +// } +// +// public init( +// register: Registrar, +// build: Builder +// ) { +// self.register = register +// self.build = build +// } +// +// public var register: Registrar +// public var build: Builder +//} +// +//extension CellFactory { +// public static func combined(_ factories: CellFactory...) -> CellFactory { +// combined(factories) +// } +// +// public static func combined(_ factories: [CellFactory]) -> CellFactory { +// CellFactory( +// register: .init { collectionView in +// factories.forEach { $0.register(in: collectionView) } +// }, +// build: .init { model, collectionView, indexPath in +// for factory in factories { +// if let cell = factory.build(for: model, in: collectionView, at: indexPath) { +// return cell +// } +// } +// return nil +// } +// ) +// } +//} +// +//#if DEBUG +//extension CellFactory { +// public static func unimplemented() -> CellFactory { +// CellFactory( +// register: .init(register: XCTUnimplemented("\(Self.self).Registrar")), +// build: .init(build: XCTUnimplemented("\(Self.self).Builder")) +// ) +// } +//} +//#endif diff --git a/Sources/ChatFeature/Controllers/GroupChatController.swift b/Sources/ChatFeature/Controllers/GroupChatController.swift index 7a5090ef483860e517d5f128dfcc8e758d86d4b8..43ed50008506da291cc8d9233bd468423e4ad8b3 100644 --- a/Sources/ChatFeature/Controllers/GroupChatController.swift +++ b/Sources/ChatFeature/Controllers/GroupChatController.swift @@ -1,11 +1,13 @@ -import DI import UIKit import Shared import Combine +import AppCore import XXModels import Voxophone import ChatLayout -import Navigation +import Dependencies +import AppResources +import AppNavigation import DrawerFeature import DifferenceKit import ReportingFeature @@ -19,12 +21,13 @@ typealias OutgoingFailedGroupTextCell = CollectionCell<FlexibleSpace, StackMessa typealias OutgoingFailedGroupReplyCell = CollectionCell<FlexibleSpace, ReplyStackMessageView> public final class GroupChatController: UIViewController { - @Dependency var database: Database - @Dependency var navigator: Navigator - @Dependency var barStylist: StatusBarStylist - @Dependency var reportingStatus: ReportingStatus - @Dependency var makeReportDrawer: MakeReportDrawer - @Dependency var makeAppScreenshot: MakeAppScreenshot + @Dependency(\.navigator) var navigator + @Dependency(\.app.dbManager) var dbManager + @Dependency(\.app.statusBar) var statusBar + @Dependency(\.reportingStatus) var reportingStatus + +// @Dependency var makeReportDrawer: MakeReportDrawer +// @Dependency var makeAppScreenshot: MakeAppScreenshot private var collectionView: UICollectionView! private lazy var header = GroupHeaderView() @@ -50,7 +53,7 @@ public final class GroupChatController: UIViewController { initialState: .init(canAddAttachments: false), reducer: chatInputReducer, environment: .init( - voxophone: try! DI.Container.shared.resolve() as Voxophone, + voxophone: Voxophone(), //try! DI.Container.shared.resolve() as Voxophone, sendAudio: { _ in }, didTapCamera: {}, didTapLibrary: {}, @@ -76,7 +79,7 @@ public final class GroupChatController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - barStylist.styleSubject.send(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar.customize( backgroundColor: Asset.neutralWhite.color, shadowColor: Asset.neutralDisabled.color @@ -199,7 +202,7 @@ public final class GroupChatController: UIViewController { ], isDismissable: false, from: self)) case .webview(let urlString): - navigator.perform(PresentWebsite(url: URL(string: urlString)!)) + navigator.perform(PresentWebsite(urlString: urlString, from: self)) } }.store(in: &cancellables) @@ -222,10 +225,10 @@ public final class GroupChatController: UIViewController { navigator.perform(DismissModal(from: self)) { [weak self] in guard let self else { return } self.drawerCancellables.removeAll() - let screenshot = try! self.makeAppScreenshot() - self.viewModel.report(contact: contact, screenshot: screenshot) { - self.collectionView.reloadData() - } +// let screenshot = try! self.makeAppScreenshot() +// self.viewModel.report(contact: contact, screenshot: screenshot) { +// self.collectionView.reloadData() +// } } }.store(in: &drawerCancellables) @@ -392,7 +395,7 @@ extension GroupChatController: UICollectionViewDataSource { var isSenderBanned = false - if let sender = try? database.fetchContacts(.init(id: [item.senderId])).first { + if let sender = try? dbManager.getDB().fetchContacts(.init(id: [item.senderId])).first { isSenderBanned = sender.isBanned } @@ -640,7 +643,10 @@ extension GroupChatController: UICollectionViewDelegate { previewProvider: nil ) { [weak self] suggestedActions in - guard let self else { return nil } + guard let self else { + fatalError() + //return nil + } let item = self.sections[indexPath.section].elements[indexPath.item] diff --git a/Sources/ChatFeature/Controllers/MembersController.swift b/Sources/ChatFeature/Controllers/MembersController.swift index 435ec4dfb63beb7869794dc51e15566942b6a16b..086114eaafc2da90ceec2c80a61d926d255d62c9 100644 --- a/Sources/ChatFeature/Controllers/MembersController.swift +++ b/Sources/ChatFeature/Controllers/MembersController.swift @@ -1,82 +1,83 @@ import UIKit import Shared import XXModels +import AppResources final class MembersController: UIViewController { - private lazy var stackView = UIStackView() - - private let members: [Contact] - - init(with members: [Contact]) { - self.members = members - super.init(nibName: nil, bundle: nil) + private lazy var stackView = UIStackView() + + private let members: [Contact] + + init(with members: [Contact]) { + self.members = members + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { nil } + + public override func viewDidLoad() { + super.viewDidLoad() + + view.layer.cornerRadius = 15 + view.layer.masksToBounds = true + view.backgroundColor = Asset.neutralWhite.color + + stackView.axis = .vertical + stackView.distribution = .fillEqually + view.addSubview(stackView) + + stackView.snp.makeConstraints { + $0.top.equalToSuperview().offset(10) + $0.left.right.equalToSuperview() + $0.bottom.equalTo(view.safeAreaLayoutGuide) } - - required init?(coder: NSCoder) { nil } - - public override func viewDidLoad() { - super.viewDidLoad() - - view.layer.cornerRadius = 15 - view.layer.masksToBounds = true - view.backgroundColor = Asset.neutralWhite.color - - stackView.axis = .vertical - stackView.distribution = .fillEqually - view.addSubview(stackView) - - stackView.snp.makeConstraints { - $0.top.equalToSuperview().offset(10) - $0.left.right.equalToSuperview() - $0.bottom.equalTo(view.safeAreaLayoutGuide) - } - - members.forEach { - let memberView = MemberView() - let assignedTitle = ($0.nickname ?? $0.username) ?? "Fetching username..." - memberView.titleLabel.text = assignedTitle - memberView.avatarView.setupProfile(title: assignedTitle, image: $0.photo, size: .small) - stackView.addArrangedSubview(memberView) - } + + members.forEach { + let memberView = MemberView() + let assignedTitle = ($0.nickname ?? $0.username) ?? "Fetching username..." + memberView.titleLabel.text = assignedTitle + memberView.avatarView.setupProfile(title: assignedTitle, image: $0.photo, size: .small) + stackView.addArrangedSubview(memberView) } + } } private final class MemberView: UIView { - let titleLabel = UILabel() - let avatarView = AvatarView() - let separatorView = UIView() - - init() { - super.init(frame: .zero) - backgroundColor = Asset.neutralWhite.color - titleLabel.textColor = Asset.neutralBody.color - titleLabel.font = Fonts.Mulish.bold.font(size: 12.0) - separatorView.backgroundColor = Asset.neutralLine.color - - addSubview(titleLabel) - addSubview(avatarView) - addSubview(separatorView) - - avatarView.snp.makeConstraints { - $0.top.equalToSuperview().offset(10) - $0.width.height.equalTo(30) - $0.left.equalToSuperview().offset(25) - $0.centerY.equalToSuperview() - } - - titleLabel.snp.makeConstraints { - $0.centerY.equalTo(avatarView) - $0.left.equalTo(avatarView.snp.right).offset(14) - $0.right.lessThanOrEqualToSuperview().offset(-10) - } - - separatorView.snp.makeConstraints { - $0.height.equalTo(1) - $0.left.equalToSuperview().offset(25) - $0.right.equalToSuperview() - $0.bottom.equalToSuperview() - } + let titleLabel = UILabel() + let avatarView = AvatarView() + let separatorView = UIView() + + init() { + super.init(frame: .zero) + backgroundColor = Asset.neutralWhite.color + titleLabel.textColor = Asset.neutralBody.color + titleLabel.font = Fonts.Mulish.bold.font(size: 12.0) + separatorView.backgroundColor = Asset.neutralLine.color + + addSubview(titleLabel) + addSubview(avatarView) + addSubview(separatorView) + + avatarView.snp.makeConstraints { + $0.top.equalToSuperview().offset(10) + $0.width.height.equalTo(30) + $0.left.equalToSuperview().offset(25) + $0.centerY.equalToSuperview() } - - required init?(coder: NSCoder) { nil } + + titleLabel.snp.makeConstraints { + $0.centerY.equalTo(avatarView) + $0.left.equalTo(avatarView.snp.right).offset(14) + $0.right.lessThanOrEqualToSuperview().offset(-10) + } + + separatorView.snp.makeConstraints { + $0.height.equalTo(1) + $0.left.equalToSuperview().offset(25) + $0.right.equalToSuperview() + $0.bottom.equalToSuperview() + } + } + + required init?(coder: NSCoder) { nil } } diff --git a/Sources/ChatFeature/Controllers/SingleChatController.swift b/Sources/ChatFeature/Controllers/SingleChatController.swift index a8052cb16ae611a98ff44c37c29102e1fc442701..4ae089ad95fab266d3300d0f73c1b19345e59c81 100644 --- a/Sources/ChatFeature/Controllers/SingleChatController.swift +++ b/Sources/ChatFeature/Controllers/SingleChatController.swift @@ -1,15 +1,15 @@ -import DI import UIKit import Shared import Combine -import XXLogger +import AppCore import XXModels import QuickLook import Voxophone -import Navigation import ChatLayout -import Navigation +import Dependencies +import AppResources import DrawerFeature +import AppNavigation import DifferenceKit import ChatInputFeature import ReportingFeature @@ -24,13 +24,15 @@ extension Message: Differentiable { } public final class SingleChatController: UIViewController { - @Dependency var logger: XXLogger - @Dependency var navigator: Navigator - @Dependency var voxophone: Voxophone - @Dependency var barStylist: StatusBarStylist - @Dependency var reportingStatus: ReportingStatus - @Dependency var makeReportDrawer: MakeReportDrawer - @Dependency var makeAppScreenshot: MakeAppScreenshot +// @Dependency var voxophone: Voxophone +// @Dependency var makeReportDrawer: MakeReportDrawer +// @Dependency var makeAppScreenshot: MakeAppScreenshot + + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist + @Dependency(\.reportingStatus) var reportingStatus: ReportingStatus + + let voxophone = Voxophone() private lazy var infoView = UIControl() private lazy var nameLabel = UILabel() @@ -66,7 +68,7 @@ public final class SingleChatController: UIViewController { initialState: .init(canAddAttachments: true), reducer: chatInputReducer, environment: .init( - voxophone: try! DI.Container.shared.resolve() as Voxophone, + voxophone: Voxophone(), //try! DI.Container.shared.resolve() as Voxophone, sendAudio: { viewModel.didSendAudio(url: $0) }, didTapCamera: { viewModel.didTest(permission: .camera) }, didTapLibrary: { viewModel.didTest(permission: .library) }, @@ -83,7 +85,7 @@ public final class SingleChatController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - barStylist.styleSubject.send(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar.customize( backgroundColor: Asset.neutralWhite.color, shadowColor: Asset.neutralDisabled.color @@ -213,49 +215,49 @@ public final class SingleChatController: UIViewController { viewModel.navigation .receive(on: DispatchQueue.main) .removeDuplicates() - .sink { [unowned self] _ in -// switch $0 { -// case .library: -// navigator.perform(PresentPhotoLibrary()) -// case .camera: -// navigator.perform(PresentCamera()) -// case .cameraPermission: -// navigator.perform(PresentPermissionRequest(type: .camera)) -// case .microphonePermission: -// navigator.perform(PresentPermissionRequest(type: .microphone)) -// case .libraryPermission: -// navigator.perform(PresentPermissionRequest(type: .library)) -// case .webview(let urlString): -// navigator.perform(PresentWebsite(url: URL(string: urlString)!)) -// case .waitingRound: -// let button = DrawerCapsuleButton(model: .init( -// title: Localized.Chat.RoundDrawer.action, -// style: .brandColored -// )) -// -// button -// .action -// .receive(on: DispatchQueue.main) -// .sink { [unowned self] in -// navigator.perform(DismissModal(from: self)) { [weak self] in -// guard let self else { return } -// self.drawerCancellables.removeAll() -// } -// }.store(in: &drawerCancellables) -// -// navigator.perform(PresentDrawer(items: [ -// DrawerText( -// font: Fonts.Mulish.semiBold.font(size: 14.0), -// text: Localized.Chat.RoundDrawer.title, -// color: Asset.neutralWeak.color, -// lineHeightMultiple: 1.35, -// spacingAfter: 25 -// ), -// button -// ])) -// case .none: -// break -// } + .sink { [unowned self] in + switch $0 { + case .library: + navigator.perform(PresentPhotoLibrary(from: self)) + case .camera: + navigator.perform(PresentCamera(from: self)) + case .cameraPermission: + navigator.perform(PresentPermissionRequest(type: .camera, from: self)) + case .microphonePermission: + navigator.perform(PresentPermissionRequest(type: .microphone, from: self)) + case .libraryPermission: + navigator.perform(PresentPermissionRequest(type: .library, from: self)) + case .webview(let urlString): + navigator.perform(PresentWebsite(urlString: urlString, from: self)) + case .waitingRound: + let button = DrawerCapsuleButton(model: .init( + title: Localized.Chat.RoundDrawer.action, + style: .brandColored + )) + + button + .action + .receive(on: DispatchQueue.main) + .sink { [unowned self] in + navigator.perform(DismissModal(from: self)) { [weak self] in + guard let self else { return } + self.drawerCancellables.removeAll() + } + }.store(in: &drawerCancellables) + + navigator.perform(PresentDrawer(items: [ + DrawerText( + font: Fonts.Mulish.semiBold.font(size: 14.0), + text: Localized.Chat.RoundDrawer.title, + color: Asset.neutralWeak.color, + lineHeightMultiple: 1.35, + spacingAfter: 25 + ), + button + ], isDismissable: true, from: self)) + case .none: + break + } viewModel.didNavigateSomewhere() }.store(in: &cancellables) @@ -408,11 +410,11 @@ public final class SingleChatController: UIViewController { navigator.perform(DismissModal(from: self)) { [weak self] in guard let self else { return } self.drawerCancellables.removeAll() - let screenshot = try! self.makeAppScreenshot() - self.viewModel.report(screenshot: screenshot) { success in - guard success else { return } - self.navigationController?.popViewController(animated: true) - } +// let screenshot = try! self.makeAppScreenshot() +// self.viewModel.report(screenshot: screenshot) { success in +// guard success else { return } +// self.navigationController?.popViewController(animated: true) +// } } }.store(in: &drawerCancellables) diff --git a/Sources/ChatFeature/Controllers/WebController.swift b/Sources/ChatFeature/Controllers/WebController.swift deleted file mode 100644 index f3099b7a5bcb8cea0b867f5cbdeeb9e5327f8b46..0000000000000000000000000000000000000000 --- a/Sources/ChatFeature/Controllers/WebController.swift +++ /dev/null @@ -1,62 +0,0 @@ -import UIKit -import WebKit - -public final class WebScreen: UIViewController { - private let url: URL - private lazy var screenView = WebView() - - public init(_ urlString: String) { - self.url = .init(string: urlString)! - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { nil } - - public override func loadView() { - view = screenView - } - - public override func viewDidLoad() { - super.viewDidLoad() - screenView.webView.load(URLRequest(url: url)) - screenView.closeButton = UIBarButtonItem( - title: "Close", - style: .done, - target: self, - action: #selector(didTappedClose)) - } - - @objc private func didTappedClose() { - dismiss(animated: true) - } -} - -final class WebView: UIView { - let webView = WKWebView() - let navBar = UINavigationBar() - var closeButton: UIBarButtonItem! { - didSet { navBar.topItem?.leftBarButtonItem = closeButton } - } - - init() { - super.init(frame: .zero) - backgroundColor = .white - navBar.items = [UINavigationItem(title: "")] - addSubview(webView) - addSubview(navBar) - - navBar.snp.makeConstraints { - $0.top.equalTo(safeAreaLayoutGuide) - $0.left.equalToSuperview() - $0.right.equalToSuperview() - } - webView.snp.makeConstraints { - $0.bottom.equalToSuperview() - $0.left.equalToSuperview() - $0.right.equalToSuperview() - $0.top.equalTo(navBar.snp.bottom) - } - } - - required init?(coder: NSCoder) { nil } -} diff --git a/Sources/ChatFeature/Helpers/BubbleBuilder.swift b/Sources/ChatFeature/Helpers/BubbleBuilder.swift index e6d71c81ee3557385b2d9ec3e349ece13948ac33..e9e53d9355f380032cf7e835c7c8531a85c8233c 100644 --- a/Sources/ChatFeature/Helpers/BubbleBuilder.swift +++ b/Sources/ChatFeature/Helpers/BubbleBuilder.swift @@ -1,6 +1,7 @@ import UIKit import Shared import XXModels +import AppResources final class Bubbler { static func build( diff --git a/Sources/ChatFeature/Helpers/CellConfigurator.swift b/Sources/ChatFeature/Helpers/CellConfigurator.swift index d9b61fd9d3be46405ab170259dbe82aa0b1961a0..63b7998cc93808fe34d6d97d32f6c9ddecb903e2 100644 --- a/Sources/ChatFeature/Helpers/CellConfigurator.swift +++ b/Sources/ChatFeature/Helpers/CellConfigurator.swift @@ -3,442 +3,443 @@ import Shared import Combine import XXModels import Voxophone +import AppResources import AVFoundation struct CellFactory { - var canBuild: (Message) -> Bool - - var build: (Message, UICollectionView, IndexPath) -> UICollectionViewCell - - func callAsFunction( - item: Message, - collectionView: UICollectionView, - indexPath: IndexPath - ) -> UICollectionViewCell { - build(item, collectionView, indexPath) - } + var canBuild: (Message) -> Bool + + var build: (Message, UICollectionView, IndexPath) -> UICollectionViewCell + + func callAsFunction( + item: Message, + collectionView: UICollectionView, + indexPath: IndexPath + ) -> UICollectionViewCell { + build(item, collectionView, indexPath) + } } extension CellFactory { - static func combined(factories: [CellFactory]) -> Self { - .init( - canBuild: { _ in true }, - build: { item, collectionView, indexPath in - guard let factory = factories.first(where: { $0.canBuild(item)}) else { - fatalError("Couldn't find a factory for \(item). Did you forget to implement?") - } - - return factory( - item: item, - collectionView: collectionView, - indexPath: indexPath - ) - } + static func combined(factories: [CellFactory]) -> Self { + .init( + canBuild: { _ in true }, + build: { item, collectionView, indexPath in + guard let factory = factories.first(where: { $0.canBuild(item)}) else { + fatalError("Couldn't find a factory for \(item). Did you forget to implement?") + } + + return factory( + item: item, + collectionView: collectionView, + indexPath: indexPath ) - } + } + ) + } } extension CellFactory { - static func incomingAudio( - voxophone: Voxophone, - transfer: @escaping (Data) -> FileTransfer - ) -> Self { - .init( - canBuild: { item in - guard (item.status == .received || item.status == .receiving), - item.replyMessageId == nil, - item.fileTransferId != nil else { return false } - - return transfer(item.fileTransferId!).type == "m4a" - - }, build: { item, collectionView, indexPath in - let ft = transfer(item.fileTransferId!) - let cell: IncomingAudioCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - let url = FileManager.url(for: "\(ft.name).\(ft.type)")! - - var model = AudioMessageCellState( - date: item.date, - audioURL: url, - isPlaying: false, - transferProgress: ft.progress, - isLoudspeaker: false, - duration: (try? AVAudioPlayer(contentsOf: url).duration) ?? 0.0, - playbackTime: 0.0 - ) - - cell.leftView.setup(with: model) - cell.canReply = false - cell.performReply = {} - - Bubbler.build(audioBubble: cell.leftView, with: item) - - voxophone.$state - .sink { - switch $0 { - case .playing(url, _, time: let time, _): - model.isPlaying = true - model.playbackTime = time - default: - model.isPlaying = false - model.playbackTime = 0.0 - } - - model.isLoudspeaker = $0.isLoudspeaker - - cell.leftView.setup(with: model) - }.store(in: &cell.leftView.cancellables) - - cell.leftView.didTapRight = { - guard item.status != .receiving else { return } - - voxophone.toggleLoudspeaker() - } - - cell.leftView.didTapLeft = { - guard item.status != .receiving else { return } - - if case .playing(url, _, _, _) = voxophone.state { - voxophone.reset() - } else { - voxophone.load(url) - voxophone.play() - } - } - - return cell - } + static func incomingAudio( + voxophone: Voxophone, + transfer: @escaping (Data) -> FileTransfer + ) -> Self { + .init( + canBuild: { item in + guard (item.status == .received || item.status == .receiving), + item.replyMessageId == nil, + item.fileTransferId != nil else { return false } + + return transfer(item.fileTransferId!).type == "m4a" + + }, build: { item, collectionView, indexPath in + let ft = transfer(item.fileTransferId!) + let cell: IncomingAudioCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) + let url = FileManager.url(for: "\(ft.name).\(ft.type)")! + + var model = AudioMessageCellState( + date: item.date, + audioURL: url, + isPlaying: false, + transferProgress: ft.progress, + isLoudspeaker: false, + duration: (try? AVAudioPlayer(contentsOf: url).duration) ?? 0.0, + playbackTime: 0.0 ) - } - - static func outgoingAudio( - voxophone: Voxophone, - transfer: @escaping (Data) -> FileTransfer - ) -> Self { - .init( - canBuild: { item in - guard (item.status == .sent || - item.status == .sending || - item.status == .sendingFailed || - item.status == .sendingTimedOut) - && item.replyMessageId == nil - && item.fileTransferId != nil else { - return false - } - - return transfer(item.fileTransferId!).type == "m4a" - - }, build: { item, collectionView, indexPath in - let ft = transfer(item.fileTransferId!) - let cell: OutgoingAudioCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - let url = FileManager.url(for: "\(ft.name).\(ft.type)")! - var model = AudioMessageCellState( - date: item.date, - audioURL: url, - isPlaying: false, - transferProgress: ft.progress, - isLoudspeaker: false, - duration: (try? AVAudioPlayer(contentsOf: url).duration) ?? 0.0, - playbackTime: 0.0 - ) - - cell.rightView.setup(with: model) - cell.canReply = false - cell.performReply = {} - - Bubbler.build(audioBubble: cell.rightView, with: item) - - voxophone.$state - .sink { - switch $0 { - case .playing(url, _, time: let time, _): - model.isPlaying = true - model.playbackTime = time - default: - model.isPlaying = false - model.playbackTime = 0.0 - } - - model.isLoudspeaker = $0.isLoudspeaker - - cell.rightView.setup(with: model) - }.store(in: &cell.rightView.cancellables) - - cell.rightView.didTapRight = { - voxophone.toggleLoudspeaker() - } - - cell.rightView.didTapLeft = { - if case .playing(url, _, _, _) = voxophone.state { - voxophone.reset() - } else { - voxophone.load(url) - voxophone.play() - } - } - - return cell + + cell.leftView.setup(with: model) + cell.canReply = false + cell.performReply = {} + + Bubbler.build(audioBubble: cell.leftView, with: item) + + voxophone.$state + .sink { + switch $0 { + case .playing(url, _, time: let time, _): + model.isPlaying = true + model.playbackTime = time + default: + model.isPlaying = false + model.playbackTime = 0.0 } + + model.isLoudspeaker = $0.isLoudspeaker + + cell.leftView.setup(with: model) + }.store(in: &cell.leftView.cancellables) + + cell.leftView.didTapRight = { + guard item.status != .receiving else { return } + + voxophone.toggleLoudspeaker() + } + + cell.leftView.didTapLeft = { + guard item.status != .receiving else { return } + + if case .playing(url, _, _, _) = voxophone.state { + voxophone.reset() + } else { + voxophone.load(url) + voxophone.play() + } + } + + return cell + } + ) + } + + static func outgoingAudio( + voxophone: Voxophone, + transfer: @escaping (Data) -> FileTransfer + ) -> Self { + .init( + canBuild: { item in + guard (item.status == .sent || + item.status == .sending || + item.status == .sendingFailed || + item.status == .sendingTimedOut) + && item.replyMessageId == nil + && item.fileTransferId != nil else { + return false + } + + return transfer(item.fileTransferId!).type == "m4a" + + }, build: { item, collectionView, indexPath in + let ft = transfer(item.fileTransferId!) + let cell: OutgoingAudioCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) + let url = FileManager.url(for: "\(ft.name).\(ft.type)")! + var model = AudioMessageCellState( + date: item.date, + audioURL: url, + isPlaying: false, + transferProgress: ft.progress, + isLoudspeaker: false, + duration: (try? AVAudioPlayer(contentsOf: url).duration) ?? 0.0, + playbackTime: 0.0 ) - } + + cell.rightView.setup(with: model) + cell.canReply = false + cell.performReply = {} + + Bubbler.build(audioBubble: cell.rightView, with: item) + + voxophone.$state + .sink { + switch $0 { + case .playing(url, _, time: let time, _): + model.isPlaying = true + model.playbackTime = time + default: + model.isPlaying = false + model.playbackTime = 0.0 + } + + model.isLoudspeaker = $0.isLoudspeaker + + cell.rightView.setup(with: model) + }.store(in: &cell.rightView.cancellables) + + cell.rightView.didTapRight = { + voxophone.toggleLoudspeaker() + } + + cell.rightView.didTapLeft = { + if case .playing(url, _, _, _) = voxophone.state { + voxophone.reset() + } else { + voxophone.load(url) + voxophone.play() + } + } + + return cell + } + ) + } } extension CellFactory { - static func outgoingImage( - transfer: @escaping (Data) -> FileTransfer - ) -> Self { - .init( - canBuild: { item in - guard (item.status == .sent || - item.status == .sending || - item.status == .sendingFailed || - item.status == .sendingTimedOut) - && item.replyMessageId == nil - && item.fileTransferId != nil else { - return false - } - - return transfer(item.fileTransferId!).type == "jpeg" - - }, build: { item, collectionView, indexPath in - let ft = transfer(item.fileTransferId!) - let cell: OutgoingImageCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - - Bubbler.build(imageBubble: cell.rightView, with: item, with: transfer(item.fileTransferId!)) - cell.canReply = false - cell.performReply = {} - - if let image = UIImage(data: ft.data!) { - cell.rightView.imageView.image = UIImage(cgImage: image.cgImage!, scale: image.scale, orientation: .up) - } - - return cell - } - ) - } - - static func incomingImage( - transfer: @escaping (Data) -> FileTransfer - ) -> Self { - .init( - canBuild: { item in - guard (item.status == .received || item.status == .receiving) - && item.replyMessageId == nil - && item.fileTransferId != nil else { - return false - } - - return transfer(item.fileTransferId!).type == "jpeg" - - }, build: { item, collectionView, indexPath in - let ft = transfer(item.fileTransferId!) - let cell: IncomingImageCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - - Bubbler.build(imageBubble: cell.leftView, with: item, with: ft) - cell.canReply = false - cell.performReply = {} - - if let data = ft.data { - cell.leftView.imageView.image = UIImage(data: data) - } else { - cell.leftView.imageView.image = Asset.transferImagePlaceholder.image - } - - return cell - } - ) - } + static func outgoingImage( + transfer: @escaping (Data) -> FileTransfer + ) -> Self { + .init( + canBuild: { item in + guard (item.status == .sent || + item.status == .sending || + item.status == .sendingFailed || + item.status == .sendingTimedOut) + && item.replyMessageId == nil + && item.fileTransferId != nil else { + return false + } + + return transfer(item.fileTransferId!).type == "jpeg" + + }, build: { item, collectionView, indexPath in + let ft = transfer(item.fileTransferId!) + let cell: OutgoingImageCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) + + Bubbler.build(imageBubble: cell.rightView, with: item, with: transfer(item.fileTransferId!)) + cell.canReply = false + cell.performReply = {} + + if let image = UIImage(data: ft.data!) { + cell.rightView.imageView.image = UIImage(cgImage: image.cgImage!, scale: image.scale, orientation: .up) + } + + return cell + } + ) + } + + static func incomingImage( + transfer: @escaping (Data) -> FileTransfer + ) -> Self { + .init( + canBuild: { item in + guard (item.status == .received || item.status == .receiving) + && item.replyMessageId == nil + && item.fileTransferId != nil else { + return false + } + + return transfer(item.fileTransferId!).type == "jpeg" + + }, build: { item, collectionView, indexPath in + let ft = transfer(item.fileTransferId!) + let cell: IncomingImageCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) + + Bubbler.build(imageBubble: cell.leftView, with: item, with: ft) + cell.canReply = false + cell.performReply = {} + + if let data = ft.data { + cell.leftView.imageView.image = UIImage(data: data) + } else { + cell.leftView.imageView.image = Asset.transferImagePlaceholder.image + } + + return cell + } + ) + } } extension CellFactory { - static func outgoingReply( - performReply: @escaping () -> Void, - replyContent: @escaping (Data) -> (String, String), - showRound: @escaping (String?) -> Void - ) -> Self { - .init( - canBuild: { item in - (item.status == .sent || item.status == .sending) - && item.replyMessageId != nil - - }, build: { item, collectionView, indexPath in - let cell: OutgoingReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - - Bubbler.buildReply( - bubble: cell.rightView, - with: item, - reply: replyContent(item.replyMessageId!) - ) - - cell.canReply = item.status == .sent - cell.performReply = performReply - cell.rightView.didTapShowRound = { showRound(item.roundURL) } - return cell - } + static func outgoingReply( + performReply: @escaping () -> Void, + replyContent: @escaping (Data) -> (String, String), + showRound: @escaping (String?) -> Void + ) -> Self { + .init( + canBuild: { item in + (item.status == .sent || item.status == .sending) + && item.replyMessageId != nil + + }, build: { item, collectionView, indexPath in + let cell: OutgoingReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) + + Bubbler.buildReply( + bubble: cell.rightView, + with: item, + reply: replyContent(item.replyMessageId!) ) - } - - static func incomingReply( - performReply: @escaping () -> Void, - replyContent: @escaping (Data) -> (String, String), - showRound: @escaping (String?) -> Void - ) -> Self { - .init( - canBuild: { item in - item.status == .received - && item.replyMessageId != nil - - }, build: { item, collectionView, indexPath in - let cell: IncomingReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - - Bubbler.buildReply( - bubble: cell.leftView, - with: item, - reply: replyContent(item.replyMessageId!) - ) - cell.canReply = item.status == .received - cell.performReply = performReply - cell.leftView.didTapShowRound = { showRound(item.roundURL) } - cell.leftView.revertBottomStackOrder() - return cell - } + + cell.canReply = item.status == .sent + cell.performReply = performReply + cell.rightView.didTapShowRound = { showRound(item.roundURL) } + return cell + } + ) + } + + static func incomingReply( + performReply: @escaping () -> Void, + replyContent: @escaping (Data) -> (String, String), + showRound: @escaping (String?) -> Void + ) -> Self { + .init( + canBuild: { item in + item.status == .received + && item.replyMessageId != nil + + }, build: { item, collectionView, indexPath in + let cell: IncomingReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) + + Bubbler.buildReply( + bubble: cell.leftView, + with: item, + reply: replyContent(item.replyMessageId!) ) - } - - static func outgoingFailedReply( - performReply: @escaping () -> Void, - replyContent: @escaping (Data) -> (String, String) - ) -> Self { - .init( - canBuild: { item in - (item.status == .sendingFailed || item.status == .sendingTimedOut) - && item.replyMessageId != nil - - }, build: { item, collectionView, indexPath in - let cell: OutgoingFailedReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - - Bubbler.buildReply( - bubble: cell.rightView, - with: item, - reply: replyContent(item.replyMessageId!) - ) - - cell.canReply = false - cell.performReply = performReply - return cell - } + cell.canReply = item.status == .received + cell.performReply = performReply + cell.leftView.didTapShowRound = { showRound(item.roundURL) } + cell.leftView.revertBottomStackOrder() + return cell + } + ) + } + + static func outgoingFailedReply( + performReply: @escaping () -> Void, + replyContent: @escaping (Data) -> (String, String) + ) -> Self { + .init( + canBuild: { item in + (item.status == .sendingFailed || item.status == .sendingTimedOut) + && item.replyMessageId != nil + + }, build: { item, collectionView, indexPath in + let cell: OutgoingFailedReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) + + Bubbler.buildReply( + bubble: cell.rightView, + with: item, + reply: replyContent(item.replyMessageId!) ) - } + + cell.canReply = false + cell.performReply = performReply + return cell + } + ) + } } extension CellFactory { - static func incomingText( - performReply: @escaping () -> Void, - showRound: @escaping (String?) -> Void - ) -> Self { - .init( - canBuild: { item in - item.status == .received - && item.replyMessageId == nil - - }, build: { item, collectionView, indexPath in - let cell: IncomingTextCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - - Bubbler.build(bubble: cell.leftView, with: item) - cell.canReply = item.status == .received - cell.performReply = performReply - cell.leftView.didTapShowRound = { showRound(item.roundURL) } - cell.leftView.revertBottomStackOrder() - return cell - } - ) - } - - static func outgoingText( - performReply: @escaping () -> Void, - showRound: @escaping (String?) -> Void - ) -> Self { - .init( - canBuild: { item in - (item.status == .sending || item.status == .sent) - && item.replyMessageId == nil - - }, build: { item, collectionView, indexPath in - let cell: OutgoingTextCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - - Bubbler.build(bubble: cell.rightView, with: item) - cell.canReply = item.status == .sent - cell.performReply = performReply - cell.rightView.didTapShowRound = { showRound(item.roundURL) } - - return cell - } - ) - } - - static func outgoingFailedText(performReply: @escaping () -> Void) -> Self { - .init( - canBuild: { item in - (item.status == .sendingFailed || item.status == .sendingTimedOut) - && item.replyMessageId == nil - - }, build: { item, collectionView, indexPath in - let cell: OutgoingFailedTextCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - - Bubbler.build(bubble: cell.rightView, with: item) - cell.canReply = false - cell.performReply = performReply - return cell - } - ) - } + static func incomingText( + performReply: @escaping () -> Void, + showRound: @escaping (String?) -> Void + ) -> Self { + .init( + canBuild: { item in + item.status == .received + && item.replyMessageId == nil + + }, build: { item, collectionView, indexPath in + let cell: IncomingTextCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) + + Bubbler.build(bubble: cell.leftView, with: item) + cell.canReply = item.status == .received + cell.performReply = performReply + cell.leftView.didTapShowRound = { showRound(item.roundURL) } + cell.leftView.revertBottomStackOrder() + return cell + } + ) + } + + static func outgoingText( + performReply: @escaping () -> Void, + showRound: @escaping (String?) -> Void + ) -> Self { + .init( + canBuild: { item in + (item.status == .sending || item.status == .sent) + && item.replyMessageId == nil + + }, build: { item, collectionView, indexPath in + let cell: OutgoingTextCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) + + Bubbler.build(bubble: cell.rightView, with: item) + cell.canReply = item.status == .sent + cell.performReply = performReply + cell.rightView.didTapShowRound = { showRound(item.roundURL) } + + return cell + } + ) + } + + static func outgoingFailedText(performReply: @escaping () -> Void) -> Self { + .init( + canBuild: { item in + (item.status == .sendingFailed || item.status == .sendingTimedOut) + && item.replyMessageId == nil + + }, build: { item, collectionView, indexPath in + let cell: OutgoingFailedTextCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) + + Bubbler.build(bubble: cell.rightView, with: item) + cell.canReply = false + cell.performReply = performReply + return cell + } + ) + } } struct ActionFactory { - enum Action { - case copy - case retry - case reply - case delete - case report - - var title: String { - switch self { - - case .copy: - return Localized.Chat.BubbleMenu.copy - case .retry: - return Localized.Chat.BubbleMenu.retry - case .reply: - return Localized.Chat.BubbleMenu.reply - case .delete: - return Localized.Chat.BubbleMenu.delete - case .report: - return Localized.Chat.BubbleMenu.report - } - } + enum Action { + case copy + case retry + case reply + case delete + case report + + var title: String { + switch self { + + case .copy: + return Localized.Chat.BubbleMenu.copy + case .retry: + return Localized.Chat.BubbleMenu.retry + case .reply: + return Localized.Chat.BubbleMenu.reply + case .delete: + return Localized.Chat.BubbleMenu.delete + case .report: + return Localized.Chat.BubbleMenu.report + } } - - static func build( - from item: Message, - action: Action, - closure: @escaping (Message) -> Void - ) -> UIAction? { - - switch action { - case .report: - guard item.status == .received else { return nil } - case .reply: - guard item.status == .received || item.status == .sent else { return nil } - case .retry: - guard item.status == .sendingFailed || item.status == .sendingTimedOut else { return nil } - case .delete, .copy: - break - } - - return UIAction( - title: action.title, - state: .off, - handler: { _ in closure(item) } - ) + } + + static func build( + from item: Message, + action: Action, + closure: @escaping (Message) -> Void + ) -> UIAction? { + + switch action { + case .report: + guard item.status == .received else { return nil } + case .reply: + guard item.status == .received || item.status == .sent else { return nil } + case .retry: + guard item.status == .sendingFailed || item.status == .sendingTimedOut else { return nil } + case .delete, .copy: + break } + + return UIAction( + title: action.title, + state: .off, + handler: { _ in closure(item) } + ) + } } diff --git a/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift b/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift index aa2481e2d7fef99b50edf6989bc9cc3898218ef6..2456b811a8af7c96ced78c76b14f1e88e4a606bc 100644 --- a/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift +++ b/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift @@ -1,13 +1,15 @@ import UIKit import Shared +import AppCore import Combine import XXModels import Defaults import Foundation +import AppResources import DifferenceKit import ReportingFeature -import DI import XXMessengerClient +import ComposableArchitecture import struct XXModels.Message import XXClient @@ -18,13 +20,13 @@ enum GroupChatNavigationRoutes: Equatable { } final class GroupChatViewModel { - @Dependency var database: Database - @Dependency var messenger: Messenger - @Dependency var sendReport: SendReport - @Dependency var groupManager: GroupChat - @Dependency var hudController: HUDController - @Dependency var reportingStatus: ReportingStatus - @Dependency var toastController: ToastController + @Dependency(\.sendReport) var sendReport + @Dependency(\.app.dbManager) var dbManager + @Dependency(\.app.messenger) var messenger + @Dependency(\.groupManager) var groupManager + @Dependency(\.app.hudManager) var hudManager + @Dependency(\.app.toastManager) var toastManager + @Dependency(\.reportingStatus) var reportingStatus @KeyObject(.username, defaultValue: nil) var username: String? @@ -52,7 +54,7 @@ final class GroupChatViewModel { private let routesSubject = PassthroughSubject<GroupChatNavigationRoutes, Never>() var messages: AnyPublisher<[ArraySection<ChatSection, Message>], Never> { - database.fetchMessagesPublisher(.init(chat: .group(info.group.id))) + try! dbManager.getDB().fetchMessagesPublisher(.init(chat: .group(info.group.id))) .replaceError(with: []) .map { messages -> [ArraySection<ChatSection, Message>] in let groupedByDate = Dictionary(grouping: messages) { domainModel -> Date in @@ -78,153 +80,90 @@ final class GroupChatViewModel { func readAll() { let assignment = Message.Assignments(isUnread: false) let query = Message.Query(chat: .group(info.group.id)) - _ = try? database.bulkUpdateMessages(query, assignment) + _ = try? dbManager.getDB().bulkUpdateMessages(query, assignment) } func didRequestDelete(_ messages: [Message]) { - _ = try? database.deleteMessages(.init(id: Set(messages.map(\.id)))) + _ = try? dbManager.getDB().deleteMessages(.init(id: Set(messages.map(\.id)))) } func didRequestReport(_ message: Message) { - if let contact = try? database.fetchContacts(.init(id: [message.senderId])).first { + if let contact = try? dbManager.getDB().fetchContacts(.init(id: [message.senderId])).first { reportPopupSubject.send(contact) } } func send(_ text: String) { - var message = Message( - senderId: myId, - recipientId: nil, - groupId: info.group.id, - date: Date(), - status: .sending, - isUnread: false, - text: text.trimmingCharacters(in: .whitespacesAndNewlines), - replyMessageId: stagedReply?.messageId - ) - - print("") - print("Outgoing GroupMessage:") - print("- groupId: \(info.group.id.base64EncodedString().prefix(10))...") - print("- senderId: \(myId.base64EncodedString().prefix(10))...") - print("- payload.text: \(message.text)") - do { - message = try database.saveMessage(message) - - let payload = Payload( - text: text.trimmingCharacters(in: .whitespacesAndNewlines), - reply: stagedReply - ).asData() - - let report = try groupManager.send( + var message = Message( + senderId: try messenger.e2e.get()!.getContact().getId(), + recipientId: nil, groupId: info.group.id, - message: payload + date: Date(), + status: .sending, + isUnread: false, + text: text.trimmingCharacters(in: .whitespacesAndNewlines), + replyMessageId: stagedReply?.messageId ) - - print("- messageId: \(report.messageId.base64EncodedString().prefix(10))...") - - if let foo = stagedReply { - print("- payload.reply.messageId: \(foo.messageId.base64EncodedString().prefix(10))...") - print("- payload.reply.senderId: \(foo.senderId.base64EncodedString().prefix(10))...") - } else { - print("- payload.reply: ∅") - } - - message.networkId = report.messageId - - try messenger.cMix.get()!.waitForRoundResult( - roundList: try report.encode(), + message = try dbManager.getDB().saveMessage(message) + let report = try groupManager.get()?.send( + groupId: info.id, + message: MessagePayload( + text: text.trimmingCharacters(in: .whitespacesAndNewlines), + replyingTo: stagedReply?.messageId + ).encode() + ) + message.networkId = report!.messageId + message.date = Date.fromTimestamp(Int(report!.timestamp)) + message = try dbManager.getDB().saveMessage(message) + try messenger.cMix.get()?.waitForRoundResult( + roundList: try report!.encode(), timeoutMS: 15_000, - callback: .init(handle: { - switch $0 { + callback: .init(handle: { result in + switch result { case .delivered: message.status = .sent - if let foo = try? self.database.saveMessage(message) { - message = foo - } case .notDelivered(timedOut: let timedOut): - if timedOut { - message.status = .sendingTimedOut - } else { - message.status = .sendingFailed - } - - if let foo = try? self.database.saveMessage(message) { - message = foo - } + message.status = timedOut ? .sendingTimedOut : .sendingFailed } + _ = try? self.dbManager.getDB().saveMessage(message) }) ) - - print("") - - message.roundURL = report.roundURL - message.date = Date.fromTimestamp(Int(report.timestamp)) - message = try database.saveMessage(message) } catch { - message.status = .sendingFailed - if let foo = try? database.saveMessage(message) { - message = foo - } + print(error.localizedDescription) } - - stagedReply = nil } func retry(_ message: Message) { - var message = message - do { + var message = message message.status = .sending - message = try database.saveMessage(message) - - var reply: Reply? - - if let replyId = message.replyMessageId { - reply = Reply(messageId: replyId, senderId: myId) - } - - let report = try groupManager.send( - groupId: message.groupId!, - message: Payload( - text: message.text, - reply: reply - ).asData() + message = try dbManager.getDB().saveMessage(message) + let report = try groupManager.get()?.send( + groupId: info.id, + message: MessagePayload( + text: message.text.trimmingCharacters(in: .whitespacesAndNewlines), + replyingTo: stagedReply?.messageId + ).encode() ) - - try messenger.cMix.get()!.waitForRoundResult( - roundList: try report.encode(), + message.networkId = report!.messageId + message.date = Date.fromTimestamp(Int(report!.timestamp)) + message = try dbManager.getDB().saveMessage(message) + try messenger.cMix.get()?.waitForRoundResult( + roundList: try report!.encode(), timeoutMS: 15_000, - callback: .init(handle: { - switch $0 { + callback: .init(handle: { result in + switch result { case .delivered: message.status = .sent - if let foo = try? self.database.saveMessage(message) { - message = foo - } case .notDelivered(timedOut: let timedOut): - if timedOut { - message.status = .sendingTimedOut - } else { - message.status = .sendingFailed - } - - if let foo = try? self.database.saveMessage(message) { - message = foo - } + message.status = timedOut ? .sendingTimedOut : .sendingFailed } + _ = try? self.dbManager.getDB().saveMessage(message) }) ) - - message.networkId = report.messageId - message.date = Date.fromTimestamp(Int(report.timestamp)) - message = try database.saveMessage(message) } catch { - message.status = .sendingFailed - if let foo = try? database.saveMessage(message) { - message = foo - } + print(error.localizedDescription) } } @@ -241,7 +180,7 @@ final class GroupChatViewModel { } func getReplyContent(for messageId: Data) -> (String, String) { - guard let message = try? database.fetchMessages(.init(networkId: messageId)).first else { + guard let message = try? dbManager.getDB().fetchMessages(.init(networkId: messageId)).first else { return ("[DELETED]", "[DELETED]") } @@ -251,7 +190,7 @@ final class GroupChatViewModel { func getName(from senderId: Data) -> String { guard senderId != myId else { return "You" } - guard let contact = try? database.fetchContacts(.init(id: [senderId])).first else { + guard let contact = try? dbManager.getDB().fetchContacts(.init(id: [senderId])).first else { return "[DELETED]" } @@ -290,18 +229,18 @@ final class GroupChatViewModel { } ) - hudController.show() + hudManager.show() sendReport(report) { result in switch result { case .failure(let error): DispatchQueue.main.async { - self.hudController.show(.init(error: error)) + self.hudManager.show(.init(error: error)) } case .success(_): self.blockContact(contact) DispatchQueue.main.async { - self.hudController.dismiss() + self.hudManager.hide() self.presentReportConfirmation(contact: contact) completion() } @@ -312,12 +251,12 @@ final class GroupChatViewModel { private func blockContact(_ contact: XXModels.Contact) { var contact = contact contact.isBlocked = true - _ = try? database.saveContact(contact) + _ = try? dbManager.getDB().saveContact(contact) } private func presentReportConfirmation(contact: XXModels.Contact) { let name = (contact.nickname ?? contact.username) ?? "the contact" - toastController.enqueueToast(model: .init( + toastManager.enqueue(.init( title: "Your report has been sent and \(name) is now blocked.", leftImage: Asset.requestSentToaster.image )) diff --git a/Sources/ChatFeature/ViewModels/SingleChatViewModel.swift b/Sources/ChatFeature/ViewModels/SingleChatViewModel.swift index adc8743e058ea95d384a5a60bb522f2312f139c2..92b93761bcf599f72275369d966ee39e05ef22f7 100644 --- a/Sources/ChatFeature/ViewModels/SingleChatViewModel.swift +++ b/Sources/ChatFeature/ViewModels/SingleChatViewModel.swift @@ -1,17 +1,17 @@ -import DI import UIKit import Shared +import AppCore import Combine -import XXLogger import XXModels import XXClient import Defaults -import Navigation -import Foundation -import Permissions +import AppResources +import Dependencies +import AppNavigation import DifferenceKit import ReportingFeature import XXMessengerClient +import PermissionsFeature import struct XXModels.Message import struct XXModels.FileTransfer @@ -28,16 +28,16 @@ enum SingleChatNavigationRoutes: Equatable { } final class SingleChatViewModel: NSObject { - @Dependency var logger: XXLogger - @Dependency var database: Database - @Dependency var messenger: Messenger - @Dependency var sendReport: SendReport - @Dependency var hudController: HUDController - @Dependency var permissions: PermissionHandling - @Dependency var toastController: ToastController - @Dependency var networkMonitor: NetworkMonitoring - @Dependency var transferManager: XXClient.FileTransfer - + @Dependency(\.sendReport) var sendReport + @Dependency(\.permissions) var permissions + @Dependency(\.app.dbManager) var dbManager + @Dependency(\.app.sendImage) var sendImage + @Dependency(\.app.messenger) var messenger + @Dependency(\.app.hudManager) var hudManager + @Dependency(\.app.sendMessage) var sendMessage + @Dependency(\.app.toastManager) var toastManager + @Dependency(\.app.networkMonitor) var networkMonitor + @KeyObject(.username, defaultValue: nil) var username: String? var contact: XXModels.Contact { contactSubject.value } @@ -53,12 +53,11 @@ final class SingleChatViewModel: NSObject { var isOnline: AnyPublisher<Bool, Never> { networkMonitor - .statusPublisher + .observeStatus() .map { $0 == .available } .eraseToAnyPublisher() } - - + var myId: Data { try! messenger.e2e.get()!.getContact().getId() } @@ -84,7 +83,7 @@ final class SingleChatViewModel: NSObject { if contact.isRecent == true { var contact = contact contact.isRecent = false - _ = try? database.saveContact(contact) + _ = try? dbManager.getDB().saveContact(contact) } } @@ -98,13 +97,13 @@ final class SingleChatViewModel: NSObject { updateRecentState(contact) - database.fetchContactsPublisher(Contact.Query(id: [contact.id])) + try! dbManager.getDB().fetchContactsPublisher(Contact.Query(id: [contact.id])) .replaceError(with: []) .compactMap { $0.first } .sink { [unowned self] in contactSubject.send($0) } .store(in: &cancellables) - database.fetchMessagesPublisher(.init(chat: .direct(myId, contact.id))) + try! dbManager.getDB().fetchMessagesPublisher(.init(chat: .direct(myId, contact.id))) .replaceError(with: []) .map { let groupedByDate = Dictionary(grouping: $0) { domainModel -> Date in @@ -125,296 +124,102 @@ final class SingleChatViewModel: NSObject { })) } - // MARK: Public - func getFileTransferWith(id: Data) -> FileTransfer { - guard let transfer = try? database.fetchFileTransfers(.init(id: [id])).first else { + guard let transfer = try? dbManager.getDB().fetchFileTransfers(.init(id: [id])).first else { fatalError() } return transfer } - func didSendAudio(url: URL) { -// do { -// let _ = try transferManager.send( -// params: .init( -// payload: .init( -// name: "", -// type: "", -// preview: Data(), -// contents: Data() -// ), -// recipientId: contact.id, -// retry: 1, -// period: 1_000 -// ), -// callback: .init(handle: { -// switch $0 { -// case .success(let progressCallback): -// print(progressCallback.progress.total) -// case .failure(let error): -// print(error.localizedDescription) -// } -// }) -// ) -// -// // transferId -// } catch { -// -// } - } + func didSendAudio(url: URL) {} func didSend(image: UIImage) { guard let imageData = image.orientedUp().jpegData(compressionQuality: 1.0) else { return } - hudController.show() - - let transferName = UUID().uuidString - - do { -// let tid = try transferManager.send( -// params: .init( -// payload: .init( -// name: transferName, -// type: "jpeg", -// preview: Data(), -// contents: imageData -// ), -// recipientId: contact.id, -// retry: 10, -// period: 1_000 -// ), -// callback: .init(handle: { -// switch $0 { -// case .success(let progressCallback): -// -// if progressCallback.progress.completed { -// print(">>> Outgoing transfer finished successfully") -// } else { -// print(">>> Outgoing transfer. (\(progressCallback.progress.transmitted)/\(progressCallback.progress.total))") -// } -// -// /// THIS IS TOO COMPLEX, NEEDS HELP FROM DARIUSZ -// -// case .failure(let error): -// print(">>> Transfer.error: \(error.localizedDescription)") -// } -// }) -//// ) -// -// let transferModel = FileTransfer( -// id: tid, -// contactId: contact.id, -// name: transferName, -// type: "jpeg", -// data: imageData, -// progress: 0.0, -// isIncoming: false, -// createdAt: Date() -// ) -// -// let transferMessage = Message( -// senderId: myId, -// recipientId: contact.id, -// groupId: nil, -// date: Date(), -// status: .sending, -// isUnread: false, -// text: "", -// replyMessageId: nil, -// roundURL: nil, -// fileTransferId: tid -// ) -// -// try database.saveFileTransfer(transferModel) -// try database.saveMessage(transferMessage) - - hudController.dismiss() - } catch { - hudController.show(.init(error: error)) + + sendImage(imageData, to: contact.id, onError: { + print("\($0.localizedDescription)") + }) { + print("finished") } } - + func readAll() { let assignment = Message.Assignments(isUnread: false) let query = Message.Query(chat: .direct(myId, contact.id)) - _ = try? database.bulkUpdateMessages(query, assignment) + _ = try? dbManager.getDB().bulkUpdateMessages(query, assignment) } - + func didRequestDeleteAll() { - _ = try? database.deleteMessages(.init(chat: .direct(myId, contact.id))) + _ = try? dbManager.getDB().deleteMessages(.init(chat: .direct(myId, contact.id))) } - + func didRequestRetry(_ message: Message) { - var message = message - - do { - message.status = .sending - message = try database.saveMessage(message) - - var reply: Reply? - - if let replyId = message.replyMessageId { - reply = Reply(messageId: replyId, senderId: myId) - } - - let report = try messenger.e2e.get()!.send( - messageType: 2, - recipientId: contact.id, - payload: Payload( - text: message.text, - reply: reply - ).asData(), - e2eParams: GetE2EParams.liveDefault() - ) - - try messenger.cMix.get()!.waitForRoundResult( - roundList: try report.encode(), - timeoutMS: 15_000, - callback: .init(handle: { - switch $0 { - case .delivered: - message.status = .sent - _ = try? self.database.saveMessage(message) - - case .notDelivered(timedOut: let timedOut): - if timedOut { - message.status = .sendingTimedOut - } else { - message.status = .sendingFailed - } - - _ = try? self.database.saveMessage(message) - } - }) - ) - - message.roundURL = report.roundURL - message.networkId = report.messageId - if let timestamp = report.timestamp { - message.date = Date.fromTimestamp(Int(timestamp)) - } - - message = try database.saveMessage(message) - } catch { - print(error.localizedDescription) - message.status = .sendingFailed - _ = try? database.saveMessage(message) - } + // TODO } - + func didNavigateSomewhere() { navigationRoutes.send(.none) } - + @discardableResult func didTest(permission: PermissionType) -> Bool { switch permission { case .camera: - if permissions.isCameraAllowed { + if permissions.camera.status() { navigationRoutes.send(.camera) } else { navigationRoutes.send(.cameraPermission) } case .library: - if permissions.isPhotosAllowed { + if permissions.library.status() { navigationRoutes.send(.library) } else { navigationRoutes.send(.libraryPermission) } case .microphone: - if permissions.isMicrophoneAllowed { + if permissions.microphone.status() { return true } else { navigationRoutes.send(.microphonePermission) } } - + return false } - + func didRequestCopy(_ model: Message) { UIPasteboard.general.string = model.text } - + func didRequestDeleteSingle(_ model: Message) { didRequestDelete([model]) } - + func didRequestReport(_: Message) { reportPopupSubject.send() } - + func abortReply() { stagedReply = nil } - + func send(_ string: String) { - var message: Message = .init( - senderId: myId, - recipientId: contact.id, - groupId: nil, - date: Date(), - status: .sending, - isUnread: false, + sendMessage( text: string.trimmingCharacters(in: .whitespacesAndNewlines), - replyMessageId: stagedReply?.messageId - ) - - DispatchQueue.global().async { [weak self] in - guard let self else { return } - - do { - message = try self.database.saveMessage(message) - - let report = try self.messenger.e2e.get()!.send( - messageType: 2, - recipientId: self.contact.id, - payload: Payload(text: message.text, reply: self.stagedReply).asData(), - e2eParams: GetE2EParams.liveDefault() - ) - - try self.messenger.cMix.get()!.waitForRoundResult( - roundList: try report.encode(), - timeoutMS: 15_000, - callback: .init(handle: { - switch $0 { - case .delivered: - message.status = .sent - _ = try? self.database.saveMessage(message) - - case .notDelivered(timedOut: let timedOut): - if timedOut { - message.status = .sendingTimedOut - } else { - message.status = .sendingFailed - } - - _ = try? self.database.saveMessage(message) - } - }) - ) - - message.roundURL = report.roundURL - message.networkId = report.messageId - if let timestamp = report.timestamp { - message.date = Date.fromTimestamp(Int(timestamp)) - } - - message = try self.database.saveMessage(message) - } catch { - print(error.localizedDescription) - message.status = .sendingFailed - _ = try? self.database.saveMessage(message) + replyingTo: stagedReply?.messageId, + to: contact.id, + onError: { + print("\($0.localizedDescription)") + }, completion: { + print("completed") } - - self.stagedReply = nil - } + ) } - + func didRequestReply(_ message: Message) { guard let networkId = message.networkId else { return } - + let senderTitle: String = { if message.senderId == myId { return "You" @@ -422,24 +227,24 @@ final class SingleChatViewModel: NSObject { return (contact.nickname ?? contact.username) ?? "Fetching username..." } }() - + replySubject.send((senderTitle, message.text)) stagedReply = Reply(messageId: networkId, senderId: message.senderId) } - + func getReplyContent(for messageId: Data) -> (String, String) { - guard let message = try? database.fetchMessages(.init(networkId: messageId)).first else { + guard let message = try? dbManager.getDB().fetchMessages(.init(networkId: messageId)).first else { return ("[DELETED]", "[DELETED]") } - - guard let contact = try? database.fetchContacts(.init(id: [message.senderId])).first else { + + guard let contact = try? dbManager.getDB().fetchContacts(.init(id: [message.senderId])).first else { fatalError() } - + let contactTitle = (contact.nickname ?? contact.username) ?? "You" return (contactTitle, message.text) } - + func showRoundFrom(_ roundURL: String?) { if let urlString = roundURL, !urlString.isEmpty { navigationRoutes.send(.webview(urlString)) @@ -447,26 +252,26 @@ final class SingleChatViewModel: NSObject { navigationRoutes.send(.waitingRound) } } - + func didRequestDelete(_ items: [Message]) { - _ = try? database.deleteMessages(.init(id: Set(items.compactMap(\.id)))) + _ = try? dbManager.getDB().deleteMessages(.init(id: Set(items.compactMap(\.id)))) } - + func itemWith(id: Int64) -> Message? { sectionsRelay.value.flatMap(\.elements).first(where: { $0.id == id }) } - + func itemAt(indexPath: IndexPath) -> Message? { guard sectionsRelay.value.count > indexPath.section else { return nil } - + let items = sectionsRelay.value[indexPath.section].elements return items.count > indexPath.row ? items[indexPath.row] : nil } - + func section(at index: Int) -> ChatSection? { sectionsRelay.value.count > 0 ? sectionsRelay.value[index].model : nil } - + func report(screenshot: UIImage, completion: @escaping (Bool) -> Void) { let report = Report( sender: .init( @@ -480,36 +285,36 @@ final class SingleChatViewModel: NSObject { type: .dm, screenshot: screenshot.pngData()! ) - - hudController.show() + + hudManager.show() sendReport(report) { result in switch result { case .failure(let error): DispatchQueue.main.async { - self.hudController.show(.init(error: error)) + self.hudManager.show(.init(error: error)) completion(false) } - + case .success(_): self.blockContact() DispatchQueue.main.async { - self.hudController.dismiss() + self.hudManager.hide() self.presentReportConfirmation() completion(true) } } } } - + private func blockContact() { var contact = contact contact.isBlocked = true - _ = try? database.saveContact(contact) + _ = try? dbManager.getDB().saveContact(contact) } - + private func presentReportConfirmation() { let name = (contact.nickname ?? contact.username) ?? "the contact" - toastController.enqueueToast(model: .init( + toastManager.enqueue(.init( title: "Your report has been sent and \(name) is now blocked.", leftImage: Asset.requestSentToaster.image )) diff --git a/Sources/ChatFeature/Views/Cells/AudioMessageView.swift b/Sources/ChatFeature/Views/Cells/AudioMessageView.swift index 5982ffc49253c7d5b7a21939290b11f0f48e2f20..88b866abe701f4d3af9aa4041616dc1fa1a02d34 100644 --- a/Sources/ChatFeature/Views/Cells/AudioMessageView.swift +++ b/Sources/ChatFeature/Views/Cells/AudioMessageView.swift @@ -1,6 +1,7 @@ import UIKit import Shared import Combine +import AppResources typealias OutgoingAudioCell = CollectionCell<FlexibleSpace, AudioMessageView> typealias IncomingAudioCell = CollectionCell<AudioMessageView, FlexibleSpace> diff --git a/Sources/ChatFeature/Views/Cells/AudioView.swift b/Sources/ChatFeature/Views/Cells/AudioView.swift index 2a46a7a0309bf1f4a184e3d0cbfe88023f7f653d..e3d26bcbd683907b94073cfbea6aa0250e2ec0ef 100644 --- a/Sources/ChatFeature/Views/Cells/AudioView.swift +++ b/Sources/ChatFeature/Views/Cells/AudioView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class AudioView: UIView { // MARK: UI diff --git a/Sources/ChatFeature/Views/Cells/DocumentMessageView.swift b/Sources/ChatFeature/Views/Cells/DocumentMessageView.swift index 2a8cf025f16d524858da87af728cbdd7bcbf1de7..8556277cc8bbe73aabf5511be4fb59333f9d2509 100644 --- a/Sources/ChatFeature/Views/Cells/DocumentMessageView.swift +++ b/Sources/ChatFeature/Views/Cells/DocumentMessageView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources typealias OutgoingDocumentCell = CollectionCell<FlexibleSpace, DocumentMessageView> typealias IncomingDocumentCell = CollectionCell<DocumentMessageView, FlexibleSpace> diff --git a/Sources/ChatFeature/Views/Cells/ImageMessageView.swift b/Sources/ChatFeature/Views/Cells/ImageMessageView.swift index 88efa910ce3ef46a3b1f8012382b1287117b477d..92cfc9f3151cd14dccee4f52aa9bd3f14cebcf81 100644 --- a/Sources/ChatFeature/Views/Cells/ImageMessageView.swift +++ b/Sources/ChatFeature/Views/Cells/ImageMessageView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources typealias OutgoingImageCell = CollectionCell<FlexibleSpace, ImageMessageView> typealias IncomingImageCell = CollectionCell<ImageMessageView, FlexibleSpace> diff --git a/Sources/ChatFeature/Views/Cells/ReplyStackMessageView.swift b/Sources/ChatFeature/Views/Cells/ReplyStackMessageView.swift index 20a74276710b492a845ad7b276fc79665a6a1baf..73e063e850229f3ba0f62f6105d6851a6ea4c55c 100644 --- a/Sources/ChatFeature/Views/Cells/ReplyStackMessageView.swift +++ b/Sources/ChatFeature/Views/Cells/ReplyStackMessageView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources typealias IncomingReplyCell = CollectionCell<ReplyStackMessageView, FlexibleSpace> typealias OutgoingReplyCell = CollectionCell<FlexibleSpace, ReplyStackMessageView> diff --git a/Sources/ChatFeature/Views/Cells/ReplyView.swift b/Sources/ChatFeature/Views/Cells/ReplyView.swift index e64def34d02055d356efefd14ce5b6a4e5cab05e..3f8e1f8dcac7bfd7edcc07e62b464d27b4000370 100644 --- a/Sources/ChatFeature/Views/Cells/ReplyView.swift +++ b/Sources/ChatFeature/Views/Cells/ReplyView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class ReplyView: UIView { let space = UIView() diff --git a/Sources/ChatFeature/Views/Cells/StackMessageView.swift b/Sources/ChatFeature/Views/Cells/StackMessageView.swift index 2c5a3c9f3f6c9e86735c28eb4edb64efcfd5d44f..df89a30363624c426e6119942907437a7ad9ef79 100644 --- a/Sources/ChatFeature/Views/Cells/StackMessageView.swift +++ b/Sources/ChatFeature/Views/Cells/StackMessageView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources typealias IncomingTextCell = CollectionCell<StackMessageView, FlexibleSpace> typealias OutgoingTextCell = CollectionCell<FlexibleSpace, StackMessageView> diff --git a/Sources/ChatFeature/Views/ChatMenuView.swift b/Sources/ChatFeature/Views/ChatMenuView.swift index 6b6f06bfe6ad54326de469f45b50ecf928aa2a3c..5ed237dd36f3d3f616337e88ab34f8ca81b2a805 100644 --- a/Sources/ChatFeature/Views/ChatMenuView.swift +++ b/Sources/ChatFeature/Views/ChatMenuView.swift @@ -1,6 +1,7 @@ import UIKit import Shared import Combine +import AppResources final class ChatMenuView: UIToolbar { enum Action { diff --git a/Sources/ChatFeature/Views/ChatView.swift b/Sources/ChatFeature/Views/ChatView.swift index 0d3529709d1b646a43c3d3fa5b41cdf05d016821..34026c0cc6aa77955b0363a12e1f5bc5f6ffa244 100644 --- a/Sources/ChatFeature/Views/ChatView.swift +++ b/Sources/ChatFeature/Views/ChatView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class ChatView: UIView { let titleLabel = UILabel() diff --git a/Sources/ChatFeature/Views/GroupHeaderView.swift b/Sources/ChatFeature/Views/GroupHeaderView.swift index b33415707304f070f172764a5ee8a49d89a1a10d..fdd4940153806a413e0362ac55d11afde886e31e 100644 --- a/Sources/ChatFeature/Views/GroupHeaderView.swift +++ b/Sources/ChatFeature/Views/GroupHeaderView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources struct Member { let title: String diff --git a/Sources/ChatFeature/Views/RetrySheetView.swift b/Sources/ChatFeature/Views/RetrySheetView.swift index 44c1de62c74e6ab1f19eacb18a9fd22ff02166f1..1b79665bf9acf383edfa3fc7dfa5239447c9b962 100644 --- a/Sources/ChatFeature/Views/RetrySheetView.swift +++ b/Sources/ChatFeature/Views/RetrySheetView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class RetrySheetView: UIView { // MARK: UI diff --git a/Sources/ChatFeature/Views/SectionHeaderView.swift b/Sources/ChatFeature/Views/SectionHeaderView.swift index 564ad98d840772945e8dfcda3a8921d81b23f4cd..9e78f977d123f628f72300350953dcad2aa9f1d0 100644 --- a/Sources/ChatFeature/Views/SectionHeaderView.swift +++ b/Sources/ChatFeature/Views/SectionHeaderView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class SectionHeaderView: UICollectionReusableView { // MARK: UI diff --git a/Sources/ChatFeature/Views/SheetButton.swift b/Sources/ChatFeature/Views/SheetButton.swift index c20ec8931d7b7b303dc35c819f3b6ad7ba09394c..f24e8a06a62e1689957c6e570d08a19d5787faf3 100644 --- a/Sources/ChatFeature/Views/SheetButton.swift +++ b/Sources/ChatFeature/Views/SheetButton.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class SheetButton: UIControl { enum Style { diff --git a/Sources/ChatFeature/Views/SheetView.swift b/Sources/ChatFeature/Views/SheetView.swift index a4cdfeb1348a6cd8b369115d6c34272df7649d87..6e305a6f5b1ee5ce1b47b1b1d1c58fa46fc359b4 100644 --- a/Sources/ChatFeature/Views/SheetView.swift +++ b/Sources/ChatFeature/Views/SheetView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class SheetView: UIView { let stackView = UIStackView() diff --git a/Sources/ChatFeature/Views/TextView.swift b/Sources/ChatFeature/Views/TextView.swift index 1fbc36974b706d916aa6be5b7ce358b322a1cad5..ea7b30e3cfc95e45f677bbf750d8d27eb8c2c275 100644 --- a/Sources/ChatFeature/Views/TextView.swift +++ b/Sources/ChatFeature/Views/TextView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources /// UITextView avoiding selection diff --git a/Sources/ChatInputFeature/ChatInputView.swift b/Sources/ChatInputFeature/ChatInputView.swift index eef904e58e8f57277a57a3789bd3db15f7ac7adc..ba81355868eb9ec1347d9b58f21a3e3edf1a7e88 100644 --- a/Sources/ChatInputFeature/ChatInputView.swift +++ b/Sources/ChatInputFeature/ChatInputView.swift @@ -2,7 +2,6 @@ import UIKit import Shared import Combine import CasePaths -import Voxophone import AppResources import ComposableArchitecture diff --git a/Sources/ChatListFeature/Controller/ChatListController.swift b/Sources/ChatListFeature/Controller/ChatListController.swift index c141c1ae99af73758f9acb538e98b4e4d7ae94f2..8ad43f6efa8fea9ea98d3894f6726d9d7b75bbe4 100644 --- a/Sources/ChatListFeature/Controller/ChatListController.swift +++ b/Sources/ChatListFeature/Controller/ChatListController.swift @@ -1,13 +1,15 @@ import UIKit import Shared import Combine +import AppCore import XXModels -import Navigation -import DI +import AppResources +import Dependencies +import AppNavigation public final class ChatListController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var barStylist: StatusBarStylist + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = ChatListView() private lazy var topLeftView = ChatListTopLeftNavView() @@ -47,7 +49,7 @@ public final class ChatListController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - barStylist.styleSubject.send(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar.customize(backgroundColor: Asset.neutralWhite.color) } @@ -69,9 +71,15 @@ public final class ChatListController: UIViewController { .sink { [unowned self] in switch $0 { case .didTapSearch: - navigator.perform(PresentSearch(searching: nil, replacing: false, on: navigationController!)) + navigator.perform(PresentSearch( + searching: nil, + replacing: false, + on: navigationController! + )) case .didTapNewGroup: - navigator.perform(PresentNewGroup(on: navigationController!)) + navigator.perform( + PresentGroupDraft(on: navigationController!) + ) } }.store(in: &cancellables) diff --git a/Sources/ChatListFeature/Controller/ChatListSearchTableController.swift b/Sources/ChatListFeature/Controller/ChatListSearchTableController.swift index b9a877cbf6f4942ba12d547a1fdef94aa8cf502d..284a571745afde1d577fa0a34afe48be13fd3d76 100644 --- a/Sources/ChatListFeature/Controller/ChatListSearchTableController.swift +++ b/Sources/ChatListFeature/Controller/ChatListSearchTableController.swift @@ -1,8 +1,9 @@ import UIKit import Shared import Combine -import Navigation -import DI +import AppResources +import Dependencies +import AppNavigation class ChatSearchListTableViewDiffableDataSource: UITableViewDiffableDataSource<SearchSection, SearchItem> { override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { @@ -16,7 +17,7 @@ class ChatSearchListTableViewDiffableDataSource: UITableViewDiffableDataSource<S } final class ChatSearchTableController: UITableViewController { - @Dependency var navigator: Navigator + @Dependency(\.navigator) var navigator: Navigator private let viewModel: ChatListViewModel private let cellHeight: CGFloat = 83.0 diff --git a/Sources/ChatListFeature/Controller/ChatListTableController.swift b/Sources/ChatListFeature/Controller/ChatListTableController.swift index 8db077fa8c454239b6d1b5553c9c25632ed7e38d..e1c2e06b98954ab0f99d4fa6452fd781a6ede8bd 100644 --- a/Sources/ChatListFeature/Controller/ChatListTableController.swift +++ b/Sources/ChatListFeature/Controller/ChatListTableController.swift @@ -2,17 +2,18 @@ import UIKit import Shared import Combine import XXModels -import Navigation +import AppNavigation import DifferenceKit import DrawerFeature -import DI +import Dependencies +import AppResources extension ChatInfo: Differentiable { public var differenceIdentifier: ChatInfo.ID { id } } final class ChatListTableController: UITableViewController { - @Dependency var navigator: Navigator + @Dependency(\.navigator) var navigator: Navigator private var rows = [ChatInfo]() private let viewModel: ChatListViewModel diff --git a/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift b/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift index 1838460803a0b4e6b2068247db601d4038c6f708..0e4c28520f58202435ff65d4d2c0a9069fd537fd 100644 --- a/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift +++ b/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift @@ -3,9 +3,10 @@ import Shared import Combine import XXModels import Defaults +import AppCore +import Dependencies import XXMessengerClient import ReportingFeature -import DI import struct XXModels.Group import XXClient @@ -24,11 +25,11 @@ typealias RecentsSnapshot = NSDiffableDataSourceSnapshot<SectionId, XXModels.Con typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem> final class ChatListViewModel { - @Dependency var database: Database - @Dependency var messenger: Messenger - @Dependency var groupManager: GroupChat - @Dependency var hudController: HUDController - @Dependency var reportingStatus: ReportingStatus + @Dependency(\.app.dbManager) var dbManager + @Dependency(\.app.messenger) var messenger + @Dependency(\.groupManager) var groupManager + @Dependency(\.app.hudManager) var hudManager + @Dependency(\.reportingStatus) var reportingStatus // TO REFACTOR: var isOnline: AnyPublisher<Bool, Never> { @@ -51,7 +52,7 @@ final class ChatListViewModel { isBanned: reportingStatus.isEnabled() ? false : nil ) - return database.fetchContactsPublisher(query) + return try! dbManager.getDB().fetchContactsPublisher(query) .replaceError(with: []) .map { let section = SectionId() @@ -69,7 +70,7 @@ final class ChatListViewModel { ) return Publishers.CombineLatest3( - database.fetchContactsPublisher(contactsQuery) + try! dbManager.getDB().fetchContactsPublisher(contactsQuery) .replaceError(with: []) .map { $0.filter { $0.id != self.myId }}, chatsPublisher, @@ -135,8 +136,8 @@ final class ChatListViewModel { ) return Publishers.CombineLatest( - database.fetchContactsPublisher(contactsQuery).replaceError(with: []), - database.fetchGroupsPublisher(groupQuery).replaceError(with: []) + try! dbManager.getDB().fetchContactsPublisher(contactsQuery).replaceError(with: []), + try! dbManager.getDB().fetchGroupsPublisher(groupQuery).replaceError(with: []) ) .map { $0.0.count + $0.1.count } .eraseToAnyPublisher() @@ -147,7 +148,7 @@ final class ChatListViewModel { private let chatsSubject = CurrentValueSubject<[ChatInfo], Never>([]) init() { - database.fetchChatInfosPublisher( + try! dbManager.getDB().fetchChatInfosPublisher( ChatInfo.Query( contactChatInfoQuery: .init( userId: myId, @@ -174,25 +175,27 @@ final class ChatListViewModel { } func leave(_ group: Group) { - hudController.show() - + guard let manager = groupManager.get() else { + return + } + hudManager.show() do { - try groupManager.leaveGroup(groupId: group.id) - try database.deleteMessages(.init(chat: .group(group.id))) - try database.deleteGroup(group) - hudController.dismiss() + try manager.leaveGroup(groupId: group.id) + try dbManager.getDB().deleteMessages(.init(chat: .group(group.id))) + try dbManager.getDB().deleteGroup(group) + hudManager.hide() } catch { - hudController.show(.init(error: error)) + hudManager.show(.init(error: error)) } } func clear(_ contact: XXModels.Contact) { - _ = try? database.deleteMessages(.init(chat: .direct(myId, contact.id))) + _ = try? dbManager.getDB().deleteMessages(.init(chat: .direct(myId, contact.id))) } func groupInfo(from group: Group) -> GroupInfo? { let query = GroupInfo.Query(groupId: group.id) - guard let info = try? database.fetchGroupInfos(query).first else { + guard let info = try? dbManager.getDB().fetchGroupInfos(query).first else { return nil } diff --git a/Sources/ChatListFeature/Views/ChatListCell.swift b/Sources/ChatListFeature/Views/ChatListCell.swift index e097c7fc6c3731d78c76fbbab9a58cdfb6548b64..9ee550dad267464a979b43926222b85cfdf467fb 100644 --- a/Sources/ChatListFeature/Views/ChatListCell.swift +++ b/Sources/ChatListFeature/Views/ChatListCell.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class ChatListCell: UITableViewCell { let titleLabel = UILabel() diff --git a/Sources/ChatListFeature/Views/ChatListContainerView.swift b/Sources/ChatListFeature/Views/ChatListContainerView.swift index b34fc50bb3690707488c0842058c818b1092de6a..77f35111eecf168a96a51a56996f91bb5e0d23a1 100644 --- a/Sources/ChatListFeature/Views/ChatListContainerView.swift +++ b/Sources/ChatListFeature/Views/ChatListContainerView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class ChatListContainerView: UIView { let separatorView = UIView() diff --git a/Sources/ChatListFeature/Views/ChatListEmptyView.swift b/Sources/ChatListFeature/Views/ChatListEmptyView.swift index 449c71451b679c18f6ae8fda508d9678b648773d..4004ad713f9fb2e96c43ba4274b61305ce892108 100644 --- a/Sources/ChatListFeature/Views/ChatListEmptyView.swift +++ b/Sources/ChatListFeature/Views/ChatListEmptyView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class ChatListEmptyView: UIView { let titleLabel = UILabel() diff --git a/Sources/ChatListFeature/Views/ChatListMenuView.swift b/Sources/ChatListFeature/Views/ChatListMenuView.swift index 4c700f31414cb2a0d107c076c282bafd6380e16c..94c18bdfca28937a535117f9ad18a90c76d06caf 100644 --- a/Sources/ChatListFeature/Views/ChatListMenuView.swift +++ b/Sources/ChatListFeature/Views/ChatListMenuView.swift @@ -1,6 +1,7 @@ import UIKit import Shared import Combine +import AppResources final class ChatListMenuView: UIToolbar { enum Action { diff --git a/Sources/ChatListFeature/Views/ChatListRecentContactCell.swift b/Sources/ChatListFeature/Views/ChatListRecentContactCell.swift index 6f452d4e51b3a0a39cee4ded26339cc143fd894a..1174d4019d14daffa80bfc2887962efdb43fde93 100644 --- a/Sources/ChatListFeature/Views/ChatListRecentContactCell.swift +++ b/Sources/ChatListFeature/Views/ChatListRecentContactCell.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class ChatListRecentContactCell: UICollectionViewCell { let titleLabel = UILabel() diff --git a/Sources/ChatListFeature/Views/ChatListTopLeftNavView.swift b/Sources/ChatListFeature/Views/ChatListTopLeftNavView.swift index 7ed956f4f3736c5c96bebdeaf59f8b7d198bc78f..9e35a63d2cabef7ec6561a174a4e39238e6773ca 100644 --- a/Sources/ChatListFeature/Views/ChatListTopLeftNavView.swift +++ b/Sources/ChatListFeature/Views/ChatListTopLeftNavView.swift @@ -1,6 +1,7 @@ import UIKit import Shared import Combine +import AppResources final class ChatListTopLeftNavView: UIView { let titleLabel = UILabel() diff --git a/Sources/ChatListFeature/Views/ChatListTopRightNavView.swift b/Sources/ChatListFeature/Views/ChatListTopRightNavView.swift index 182187b997840d7d7d7164f29b5fe4501cabf551..1e7426425483439a8e884c224aa74e59ef30a65a 100644 --- a/Sources/ChatListFeature/Views/ChatListTopRightNavView.swift +++ b/Sources/ChatListFeature/Views/ChatListTopRightNavView.swift @@ -1,6 +1,7 @@ import UIKit import Shared import Combine +import AppResources final class ChatListTopRightNavView: UIView { enum Action { diff --git a/Sources/ChatListFeature/Views/ChatListView.swift b/Sources/ChatListFeature/Views/ChatListView.swift index 4bfe400fe287e385551605d1b9c9f343f168f241..9b90b60a55e5db25a5844ac604162080c969e825 100644 --- a/Sources/ChatListFeature/Views/ChatListView.swift +++ b/Sources/ChatListFeature/Views/ChatListView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class ChatListView: UIView { let snackBar = SnackBar() diff --git a/Sources/ChatListFeature/Views/ChatSearchEmptyView.swift b/Sources/ChatListFeature/Views/ChatSearchEmptyView.swift index ec55193886cf161efbdf1159737ce16281fb334a..7853c79e5eb9e3ad88377e7db9a030f861797b1f 100644 --- a/Sources/ChatListFeature/Views/ChatSearchEmptyView.swift +++ b/Sources/ChatListFeature/Views/ChatSearchEmptyView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class ChatSearchEmptyView: UIView { let titleLabel = UILabel() diff --git a/Sources/CheckVersion/CheckVersion.swift b/Sources/CheckVersion/CheckVersion.swift new file mode 100644 index 0000000000000000000000000000000000000000..32cd955542ab8311ae679007e73aaa29f8c1819f --- /dev/null +++ b/Sources/CheckVersion/CheckVersion.swift @@ -0,0 +1,62 @@ +import Foundation +import XCTestDynamicOverlay + +public struct CheckVersion { + public enum VersionState { + case updated + case outdated(String) + case wayTooOld(String, String) + } + + public enum Error: Swift.Error { + case noLocalVersion + case failureFetchingRemote(FetchRemoteVersion.Error) + } + + public typealias Completion = (Result<VersionState, Error>) -> Void + + public var run: (@escaping Completion) -> Void + + public func callAsFunction(_ completion: @escaping Completion) -> Void { + run(completion) + } +} + +extension CheckVersion { + public static func live( + local: FetchLocalVersion = .live, + remote: FetchRemoteVersion = .live + ) -> CheckVersion { + .init { completion in + remote { + switch $0 { + case .success(let remoteModel): + guard let localVersion = local() else { + completion(.failure(.noLocalVersion)) + return + } + if localVersion >= remoteModel.details.recommendedVersion { + completion(.success(.updated)) + } else { + if localVersion < remoteModel.details.minimumVersion { + completion(.success(.wayTooOld( + remoteModel.details.appUrl, + remoteModel.details.minimumVersionMessage + ))) + return + } + completion(.success(.outdated(remoteModel.details.appUrl))) + } + case .failure(let error): + completion(.failure(.failureFetchingRemote(error))) + } + } + } + } +} + +extension CheckVersion { + public static let unimplemented = CheckVersion( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/CheckVersion/Dependency.swift b/Sources/CheckVersion/Dependency.swift new file mode 100644 index 0000000000000000000000000000000000000000..636a64c83d37eb9f639076b1ec80dfd6f2e2a7ed --- /dev/null +++ b/Sources/CheckVersion/Dependency.swift @@ -0,0 +1,13 @@ +import Dependencies + +private enum CheckVersionDependencyKey: DependencyKey { + static let liveValue: CheckVersion = .live() + static let testValue: CheckVersion = .unimplemented +} + +extension DependencyValues { + public var checkVersion: CheckVersion { + get { self[CheckVersionDependencyKey.self] } + set { self[CheckVersionDependencyKey.self] = newValue } + } +} diff --git a/Sources/CheckVersion/FetchLocalVersion.swift b/Sources/CheckVersion/FetchLocalVersion.swift new file mode 100644 index 0000000000000000000000000000000000000000..1954fe744e5a70f30c6d57c121b4218c73dba992 --- /dev/null +++ b/Sources/CheckVersion/FetchLocalVersion.swift @@ -0,0 +1,22 @@ +import Foundation +import XCTestDynamicOverlay + +public struct FetchLocalVersion { + public var run: () -> String? + + public func callAsFunction() -> String? { + run() + } +} + +extension FetchLocalVersion { + public static let live = FetchLocalVersion { + Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String + } +} + +extension FetchLocalVersion { + public static let unimplemented = FetchLocalVersion( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/CheckVersion/FetchRemoteVersion.swift b/Sources/CheckVersion/FetchRemoteVersion.swift new file mode 100644 index 0000000000000000000000000000000000000000..e3c19f924df47afb64a01887f6ccae05f63824fa --- /dev/null +++ b/Sources/CheckVersion/FetchRemoteVersion.swift @@ -0,0 +1,51 @@ +import Foundation +import XCTestDynamicOverlay + +public struct FetchRemoteVersion { + public enum Error: Swift.Error { + case noData + case requestError + case decodeFailure + } + + public typealias Completion = (Result<Remote, Error>) -> Void + + public var run: (@escaping Completion) -> Void + + public func callAsFunction(_ completion: @escaping Completion) -> Void { + run(completion) + } +} + +extension FetchRemoteVersion { + public static let live = FetchRemoteVersion { completion in + let urlString = "https://elixxir-bins.s3-us-west-1.amazonaws.com/client/dapps/appdb.json" + let request = URLRequest( + url: URL(string: urlString)!, + cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, + timeoutInterval: 5 + ) + URLSession.shared.dataTask(with: request) { data, _, error in + guard error == nil else { + completion(.failure(.requestError)) + return + } + guard let data else { + completion(.failure(.noData)) + return + } + do { + let model = try JSONDecoder().decode(Remote.self, from: data) + completion(.success(model)) + } catch { + completion(.failure(.decodeFailure)) + } + }.resume() + } +} + +extension FetchRemoteVersion { + public static let unimplemented = FetchRemoteVersion( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/CheckVersion/RemoteDetailsModel.swift b/Sources/CheckVersion/RemoteDetailsModel.swift new file mode 100644 index 0000000000000000000000000000000000000000..d549eb6359ed1c38b4403eae55bf5b45384b77fd --- /dev/null +++ b/Sources/CheckVersion/RemoteDetailsModel.swift @@ -0,0 +1,13 @@ +public struct RemoteDetails: Codable { + public var appUrl: String + public var minimumVersion: String + public var recommendedVersion: String + public var minimumVersionMessage: String + + private enum CodingKeys: String, CodingKey { + case appUrl = "new_ios_app_url" + case minimumVersion = "new_ios_min_version" + case minimumVersionMessage = "new_minimum_popup_msg" + case recommendedVersion = "new_ios_recommended_version" + } +} diff --git a/Sources/CheckVersion/RemoteModel.swift b/Sources/CheckVersion/RemoteModel.swift new file mode 100644 index 0000000000000000000000000000000000000000..f2a65522f1fe974fb1f31e618c55b6c5956eba7f --- /dev/null +++ b/Sources/CheckVersion/RemoteModel.swift @@ -0,0 +1,7 @@ +public struct Remote: Codable { + var details: RemoteDetails + + private enum CodingKeys: String, CodingKey { + case details = "dapp-id" + } +} diff --git a/Sources/ContactFeature/Controllers/ContactController.swift b/Sources/ContactFeature/Controllers/ContactController.swift index b1e667cecb38dc02c386fcc875317c961674f786..ee9bb011bd4eff7300dcff42ea5af2524434f6ce 100644 --- a/Sources/ContactFeature/Controllers/ContactController.swift +++ b/Sources/ContactFeature/Controllers/ContactController.swift @@ -2,14 +2,16 @@ import UIKit import Shared import Combine import XXModels -import Navigation +import AppCore +import Dependencies +import AppResources +import AppNavigation import DrawerFeature -import DI import ScrollViewController public final class ContactController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var barStylist: StatusBarStylist + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = ContactView() private lazy var scrollViewController = ScrollViewController() @@ -28,7 +30,7 @@ public final class ContactController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationItem.backButtonTitle = "" - barStylist.styleSubject.send(.lightContent) + statusBar.set(.lightContent) navigationController?.navigationBar .customize( backgroundColor: Asset.neutralBody.color, diff --git a/Sources/ContactFeature/Controllers/NicknameController.swift b/Sources/ContactFeature/Controllers/NicknameController.swift index 509f2cba9aba1b2c02699dbb96341908d1b6d5fd..a36ad67dcd7f761d4df829535d17b1d140e2194f 100644 --- a/Sources/ContactFeature/Controllers/NicknameController.swift +++ b/Sources/ContactFeature/Controllers/NicknameController.swift @@ -5,83 +5,83 @@ import InputField import ScrollViewController public final class NicknameController: UIViewController { - private lazy var screenView = NicknameView() - - private let prefilled: String - private let completion: StringClosure - private let viewModel = NicknameViewModel() - private var cancellables = Set<AnyCancellable>() - private let keyboardListener = KeyboardFrameChangeListener(notificationCenter: .default) - - public init(_ prefilled: String, _ completion: @escaping StringClosure) { - self.prefilled = prefilled - self.completion = completion - super.init(nibName: nil, bundle: nil) + private lazy var screenView = NicknameView() + + private let prefilled: String + private let completion: (String) -> Void + private let viewModel = NicknameViewModel() + private var cancellables = Set<AnyCancellable>() + private let keyboardListener = KeyboardFrameChangeListener(notificationCenter: .default) + + public init(_ prefilled: String, _ completion: @escaping (String) -> Void) { + self.prefilled = prefilled + self.completion = completion + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { nil } + + public override func loadView() { + let view = UIView() + view.addSubview(screenView) + + screenView.snp.makeConstraints { + $0.top.equalToSuperview() + $0.left.equalToSuperview() + $0.right.equalToSuperview() + $0.bottom.equalToSuperview().offset(0) } - required init?(coder: NSCoder) { nil } + self.view = view + } - public override func loadView() { - let view = UIView() - view.addSubview(screenView) + public override func viewDidLoad() { + super.viewDidLoad() + setupKeyboard() + setupBindings() - screenView.snp.makeConstraints { - $0.top.equalToSuperview() - $0.left.equalToSuperview() - $0.right.equalToSuperview() - $0.bottom.equalToSuperview().offset(0) - } + screenView.inputField.update(content: prefilled) + viewModel.didInput(prefilled) + } - self.view = view - } - - public override func viewDidLoad() { - super.viewDidLoad() - setupKeyboard() - setupBindings() - - screenView.inputField.update(content: prefilled) - viewModel.didInput(prefilled) - } + private func setupKeyboard() { + keyboardListener.keyboardFrameWillChange = { [weak self] keyboard in + guard let self else { return } - private func setupKeyboard() { - keyboardListener.keyboardFrameWillChange = { [weak self] keyboard in - guard let self else { return } + let inset = self.view.frame.height - self.view.convert(keyboard.frame, from: nil).minY - let inset = self.view.frame.height - self.view.convert(keyboard.frame, from: nil).minY + self.screenView.snp.updateConstraints { + $0.bottom.equalToSuperview().offset(-inset) + } - self.screenView.snp.updateConstraints { - $0.bottom.equalToSuperview().offset(-inset) - } - - self.view.setNeedsLayout() - - UIView.animate(withDuration: keyboard.animationDuration) { - self.view.layoutIfNeeded() - } - } - } + self.view.setNeedsLayout() - private func setupBindings() { - viewModel.state - .map(\.status) - .receive(on: DispatchQueue.main) - .sink { [weak screenView] in screenView?.update(status: $0) } - .store(in: &cancellables) - - viewModel.done - .receive(on: DispatchQueue.main) - .sink { [unowned self] in - dismiss(animated: true) - completion($0) - }.store(in: &cancellables) - - screenView.inputField.textPublisher - .sink { [weak viewModel] in viewModel?.didInput($0) } - .store(in: &cancellables) - - screenView.saveButton.publisher(for: .touchUpInside) - .sink { [weak viewModel] in viewModel?.didTapSave() } - .store(in: &cancellables) + UIView.animate(withDuration: keyboard.animationDuration) { + self.view.layoutIfNeeded() + } } + } + + private func setupBindings() { + viewModel.state + .map(\.status) + .receive(on: DispatchQueue.main) + .sink { [weak screenView] in screenView?.update(status: $0) } + .store(in: &cancellables) + + viewModel.done + .receive(on: DispatchQueue.main) + .sink { [unowned self] in + dismiss(animated: true) + completion($0) + }.store(in: &cancellables) + + screenView.inputField.textPublisher + .sink { [weak viewModel] in viewModel?.didInput($0) } + .store(in: &cancellables) + + screenView.saveButton.publisher(for: .touchUpInside) + .sink { [weak viewModel] in viewModel?.didTapSave() } + .store(in: &cancellables) + } } diff --git a/Sources/ContactFeature/ViewModels/ContactViewModel.swift b/Sources/ContactFeature/ViewModels/ContactViewModel.swift index 1434570019f0a2be2e3afdc346380944d7f50442..6e20c16e65aea219159179fcea4739f8447f858f 100644 --- a/Sources/ContactFeature/ViewModels/ContactViewModel.swift +++ b/Sources/ContactFeature/ViewModels/ContactViewModel.swift @@ -1,12 +1,13 @@ import UIKit import Shared import Combine +import AppCore import XXModels import Defaults import XXClient +import Dependencies import CombineSchedulers import XXMessengerClient -import DI struct ContactViewState: Equatable { var title: String? @@ -18,10 +19,9 @@ struct ContactViewState: Equatable { } final class ContactViewModel { - @Dependency var database: Database - @Dependency var messenger: Messenger - @Dependency var hudController: HUDController - @Dependency var getFactsFromContact: GetFactsFromContact + @Dependency(\.app.dbManager) var dbManager: DBManager + @Dependency(\.app.messenger) var messenger: Messenger + @Dependency(\.app.hudManager) var hudManager: HUDManager @KeyObject(.username, defaultValue: nil) var username: String? @KeyObject(.sharingEmail, defaultValue: false) var sharingEmail: Bool @@ -47,15 +47,11 @@ final class ContactViewModel { init(_ contact: XXModels.Contact) { self.contact = contact - - let facts = try? getFactsFromContact(contact.marshaled!) - let email = facts?.first(where: { $0.type == .email })?.value - let phone = facts?.first(where: { $0.type == .phone })?.value - + stateRelay.value = .init( title: contact.nickname ?? contact.username, - email: email, - phone: phone, + email: contact.email, + phone: contact.phone, photo: contact.photo != nil ? UIImage(data: contact.photo!) : nil, username: contact.username, nickname: contact.nickname @@ -65,50 +61,50 @@ final class ContactViewModel { func didChoosePhoto(_ photo: UIImage) { stateRelay.value.photo = photo contact.photo = photo.jpegData(compressionQuality: 0.0) - _ = try? database.saveContact(contact) + _ = try? dbManager.getDB().saveContact(contact) } func didTapDelete() { - hudController.show() + hudManager.show() do { try messenger.e2e.get()!.deleteRequest.partnerId(contact.id) - try database.deleteContact(contact) + try dbManager.getDB().deleteContact(contact) - hudController.dismiss() + hudManager.hide() popToRootRelay.send() } catch { - hudController.show(.init(error: error)) + hudManager.show(.init(error: error)) } } func didTapReject() { // TODO: Reject function on the API? - _ = try? database.deleteContact(contact) + _ = try? dbManager.getDB().deleteContact(contact) popRelay.send() } func didTapClear() { - _ = try? database.deleteMessages(.init(chat: .direct(myId, contact.id))) + _ = try? dbManager.getDB().deleteMessages(.init(chat: .direct(myId, contact.id))) } func didUpdateNickname(_ string: String) { contact.nickname = string.isEmpty ? nil : string stateRelay.value.title = string.isEmpty ? contact.username : string - _ = try? database.saveContact(contact) + _ = try? dbManager.getDB().saveContact(contact) stateRelay.value.nickname = contact.nickname } func didTapResend() { - hudController.show() + hudManager.show() contact.authStatus = .requesting backgroundScheduler.schedule { [weak self] in guard let self else { return } do { - try self.database.saveContact(self.contact) + try self.dbManager.getDB().saveContact(self.contact) var includedFacts: [Fact] = [] let myFacts = try self.messenger.ud.get()!.getFacts() @@ -131,20 +127,20 @@ final class ContactViewModel { ) self.contact.authStatus = .requested - try self.database.saveContact(self.contact) + try self.dbManager.getDB().saveContact(self.contact) - self.hudController.dismiss() + self.hudManager.hide() self.popRelay.send() } catch { self.contact.authStatus = .requestFailed - _ = try? self.database.saveContact(self.contact) - self.hudController.show(.init(error: error)) + _ = try? self.dbManager.getDB().saveContact(self.contact) + self.hudManager.show(.init(error: error)) } } } func didTapRequest(with nickname: String) { - hudController.show() + hudManager.show() contact.nickname = nickname contact.authStatus = .requesting @@ -152,7 +148,7 @@ final class ContactViewModel { guard let self else { return } do { - try self.database.saveContact(self.contact) + try self.dbManager.getDB().saveContact(self.contact) var includedFacts: [Fact] = [] let myFacts = try self.messenger.ud.get()!.getFacts() @@ -175,20 +171,20 @@ final class ContactViewModel { ) self.contact.authStatus = .requested - try self.database.saveContact(self.contact) + try self.dbManager.getDB().saveContact(self.contact) - self.hudController.dismiss() + self.hudManager.hide() self.successRelay.send() } catch { self.contact.authStatus = .requestFailed - _ = try? self.database.saveContact(self.contact) - self.hudController.show(.init(error: error)) + _ = try? self.dbManager.getDB().saveContact(self.contact) + self.hudManager.show(.init(error: error)) } } } func didTapAccept(_ nickname: String) { - hudController.show() + hudManager.show() contact.nickname = nickname contact.authStatus = .confirming @@ -196,19 +192,19 @@ final class ContactViewModel { guard let self else { return } do { - try self.database.saveContact(self.contact) + try self.dbManager.getDB().saveContact(self.contact) let _ = try self.messenger.e2e.get()!.confirmReceivedRequest(partner: XXClient.Contact.live(self.contact.marshaled!)) self.contact.authStatus = .friend - try self.database.saveContact(self.contact) + try self.dbManager.getDB().saveContact(self.contact) - self.hudController.dismiss() + self.hudManager.hide() self.popRelay.send() } catch { self.contact.authStatus = .confirmationFailed - _ = try? self.database.saveContact(self.contact) - self.hudController.show(.init(error: error)) + _ = try? self.dbManager.getDB().saveContact(self.contact) + self.hudManager.show(.init(error: error)) } } } diff --git a/Sources/ContactFeature/ViewModels/NicknameViewModel.swift b/Sources/ContactFeature/ViewModels/NicknameViewModel.swift index 25897d83c215e59b6cfd6233bcb28dc503feb616..6f4cd52b331188a2d10fac1ddff5e0ebaf3d5f80 100644 --- a/Sources/ContactFeature/ViewModels/NicknameViewModel.swift +++ b/Sources/ContactFeature/ViewModels/NicknameViewModel.swift @@ -1,6 +1,7 @@ import Shared import Combine import InputField +import AppResources struct NicknameViewState { var nickname: String = "" diff --git a/Sources/ContactFeature/Views/ContactConfirmedView.swift b/Sources/ContactFeature/Views/ContactConfirmedView.swift index 472513842beaf5914e6924bf56f56d1b5f2f5b34..7a99fb9f0be8995158fcf17c0f230eb424a32c2d 100644 --- a/Sources/ContactFeature/Views/ContactConfirmedView.swift +++ b/Sources/ContactFeature/Views/ContactConfirmedView.swift @@ -1,38 +1,39 @@ import UIKit import Shared +import AppResources final class ContactConfirmedView: UIView { - let stackView = UIStackView() - let clearButton = CapsuleButton() - let buttons = SheetCardComponent() - - init() { - super.init(frame: .zero) - - clearButton.setStyle(.seeThrough) - clearButton.setTitle(Localized.Contact.Confirmed.clear, for: .normal) - - buttons.set(buttons: [clearButton]) - - stackView.axis = .vertical - stackView.spacing = 25 - - addSubview(stackView) - addSubview(buttons) - - stackView.snp.makeConstraints { make in - make.top.equalToSuperview().offset(24) - make.left.equalToSuperview().offset(24) - make.right.equalToSuperview().offset(-24) - } - - buttons.snp.makeConstraints { make in - make.top.greaterThanOrEqualTo(stackView.snp.bottom).offset(24) - make.left.equalToSuperview() - make.right.equalToSuperview() - make.bottom.equalToSuperview() - } + let stackView = UIStackView() + let clearButton = CapsuleButton() + let buttons = SheetCardComponent() + + init() { + super.init(frame: .zero) + + clearButton.setStyle(.seeThrough) + clearButton.setTitle(Localized.Contact.Confirmed.clear, for: .normal) + + buttons.set(buttons: [clearButton]) + + stackView.axis = .vertical + stackView.spacing = 25 + + addSubview(stackView) + addSubview(buttons) + + stackView.snp.makeConstraints { + $0.top.equalToSuperview().offset(24) + $0.left.equalToSuperview().offset(24) + $0.right.equalToSuperview().offset(-24) + } + + buttons.snp.makeConstraints { + $0.top.greaterThanOrEqualTo(stackView.snp.bottom).offset(24) + $0.left.equalToSuperview() + $0.right.equalToSuperview() + $0.bottom.equalToSuperview() } + } - required init?(coder: NSCoder) { nil } + required init?(coder: NSCoder) { nil } } diff --git a/Sources/ContactFeature/Views/ContactInProgressView.swift b/Sources/ContactFeature/Views/ContactInProgressView.swift index 533b5fe893b6f2ab5637dacaab9c2b38cf20881b..2866b6d5d586b1fbc94dfe6479e41d530448980b 100644 --- a/Sources/ContactFeature/Views/ContactInProgressView.swift +++ b/Sources/ContactFeature/Views/ContactInProgressView.swift @@ -1,6 +1,7 @@ import UIKit import Shared import XXModels +import AppResources final class ContactAlmostView: UIView { let stack = UIStackView() diff --git a/Sources/ContactFeature/Views/ContactReceivedView.swift b/Sources/ContactFeature/Views/ContactReceivedView.swift index 2d29fd46123aab7b37e615fcc31c22cbbd486c06..4678fdb7eb4a2147e18db552fcf2f1705cd9db24 100644 --- a/Sources/ContactFeature/Views/ContactReceivedView.swift +++ b/Sources/ContactFeature/Views/ContactReceivedView.swift @@ -1,67 +1,62 @@ import UIKit import Shared +import AppResources final class ContactReceivedView: UIView { - // MARK: UI + let title = UILabel() + let icon = UIImageView() + let stack = UIStackView() + let accept = CapsuleButton() + let reject = CapsuleButton() - let title = UILabel() - let icon = UIImageView() - let stack = UIStackView() - let accept = CapsuleButton() - let reject = CapsuleButton() + init() { + super.init(frame: .zero) + setup() + } - // MARK: Lifecycle + required init?(coder: NSCoder) { nil } - init() { - super.init(frame: .zero) - setup() - } - - required init?(coder: NSCoder) { nil } + private func setup() { + icon.contentMode = .center - // MARK: Private + title.textAlignment = .center + title.textColor = Asset.neutralBody.color + title.text = Localized.Contact.Received.title + title.font = Fonts.Mulish.bold.font(size: 24.0) - private func setup() { - icon.contentMode = .center + icon.image = Asset.contactRequestPlaceholder.image - title.textAlignment = .center - title.textColor = Asset.neutralBody.color - title.text = Localized.Contact.Received.title - title.font = Fonts.Mulish.bold.font(size: 24.0) + accept.setStyle(.brandColored) + accept.setTitle(Localized.Contact.Received.accept, for: .normal) - icon.image = Asset.contactRequestPlaceholder.image + reject.setStyle(.seeThrough) + reject.setTitle(Localized.Contact.Received.reject, for: .normal) - accept.setStyle(.brandColored) - accept.setTitle(Localized.Contact.Received.accept, for: .normal) + stack.axis = .vertical + stack.addArrangedSubview(title) + stack.addArrangedSubview(accept) + stack.addArrangedSubview(reject) - reject.setStyle(.seeThrough) - reject.setTitle(Localized.Contact.Received.reject, for: .normal) + stack.setCustomSpacing(24, after: title) + stack.setCustomSpacing(20, after: accept) - stack.axis = .vertical - stack.addArrangedSubview(title) - stack.addArrangedSubview(accept) - stack.addArrangedSubview(reject) + addSubview(icon) + addSubview(stack) - stack.setCustomSpacing(24, after: title) - stack.setCustomSpacing(20, after: accept) + setupConstraints() + } - addSubview(icon) - addSubview(stack) - - setupConstraints() + private func setupConstraints() { + icon.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalTo(stack.snp.top).offset(-30) } - private func setupConstraints() { - icon.snp.makeConstraints { make in - make.centerX.equalToSuperview() - make.bottom.equalTo(stack.snp.top).offset(-30) - } - - stack.snp.makeConstraints { make in - make.top.greaterThanOrEqualToSuperview().offset(20) - make.left.equalToSuperview().offset(24) - make.right.equalToSuperview().offset(-24) - make.bottom.equalToSuperview().offset(-34) - } + stack.snp.makeConstraints { make in + make.top.greaterThanOrEqualToSuperview().offset(20) + make.left.equalToSuperview().offset(24) + make.right.equalToSuperview().offset(-24) + make.bottom.equalToSuperview().offset(-34) } + } } diff --git a/Sources/ContactFeature/Views/ContactScannedView.swift b/Sources/ContactFeature/Views/ContactScannedView.swift index 840e445cacfbc228705a5616c1b198c86bd534fe..8dd6a118c79bf2e1b736d4214d45a90d2450c5b3 100644 --- a/Sources/ContactFeature/Views/ContactScannedView.swift +++ b/Sources/ContactFeature/Views/ContactScannedView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class ContactScannedView: UIView { let title = UILabel() diff --git a/Sources/ContactFeature/Views/ContactSuccessView.swift b/Sources/ContactFeature/Views/ContactSuccessView.swift index 616d1b0621274dc6d7b04fb710cef4d4b28140d6..dc610b3ca85802443e376c37ace40c68900f024f 100644 --- a/Sources/ContactFeature/Views/ContactSuccessView.swift +++ b/Sources/ContactFeature/Views/ContactSuccessView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class ContactSuccessView: UIView { // MARK: UI diff --git a/Sources/ContactFeature/Views/ContactView.swift b/Sources/ContactFeature/Views/ContactView.swift index 08b4a4c6d445f66af68e5ba2637fe26e3c46efe6..059686511eaee49e47ccb3fb9b77156fc5a2427b 100644 --- a/Sources/ContactFeature/Views/ContactView.swift +++ b/Sources/ContactFeature/Views/ContactView.swift @@ -1,6 +1,7 @@ import UIKit import Shared import XXModels +import AppResources final class ContactView: UIView { let container = UIView() diff --git a/Sources/ContactFeature/Views/NicknameView.swift b/Sources/ContactFeature/Views/NicknameView.swift index 731bdd70a67f940d16e570b7f25b78cd8f1e8e1e..637f5a1fa17a451cc2cc5514a6f2acf268febe64 100644 --- a/Sources/ContactFeature/Views/NicknameView.swift +++ b/Sources/ContactFeature/Views/NicknameView.swift @@ -1,6 +1,7 @@ import UIKit import Shared import InputField +import AppResources final class NicknameView: UIView { let titleLabel = UILabel() diff --git a/Sources/ContactListFeature/Controllers/ContactListController.swift b/Sources/ContactListFeature/ContactListController.swift similarity index 93% rename from Sources/ContactListFeature/Controllers/ContactListController.swift rename to Sources/ContactListFeature/ContactListController.swift index c5a2dbb059953c0a8ed6c5b680ec98a6851261ee..f44b63c0d760b2c0cd55d3a00f972dcb2d1b0e0d 100644 --- a/Sources/ContactListFeature/Controllers/ContactListController.swift +++ b/Sources/ContactListFeature/ContactListController.swift @@ -1,12 +1,14 @@ import UIKit import Shared import Combine -import Navigation -import DI +import AppCore +import Dependencies +import AppResources +import AppNavigation public final class ContactListController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var barStylist: StatusBarStylist + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = ContactListView() private lazy var tableController = ContactListTableController(viewModel) @@ -20,7 +22,7 @@ public final class ContactListController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - barStylist.styleSubject.send(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar.customize(backgroundColor: Asset.neutralWhite.color) } @@ -105,7 +107,7 @@ public final class ContactListController: UIViewController { .publisher(for: .touchUpInside) .receive(on: DispatchQueue.main) .sink { [unowned self] in - navigator.perform(PresentNewGroup(on: navigationController!)) + navigator.perform(PresentGroupDraft(on: navigationController!)) }.store(in: &cancellables) screenView diff --git a/Sources/ContactListFeature/ContactListItemButton.swift b/Sources/ContactListFeature/ContactListItemButton.swift new file mode 100644 index 0000000000000000000000000000000000000000..009260c7c3a11e54ca25670b56cd6aa3960c9643 --- /dev/null +++ b/Sources/ContactListFeature/ContactListItemButton.swift @@ -0,0 +1,61 @@ +import UIKit +import Shared +import AppResources + +final class ItemButton: UIControl { + let titleLabel = UILabel() + let iconImageView = UIImageView() + let separatorView = UIView() + let stackView = UIStackView() + let notificationLabel = UILabel() + + init() { + super.init(frame: .zero) + + titleLabel.textColor = Asset.brandPrimary.color + titleLabel.font = Fonts.Mulish.semiBold.font(size: 14.0) + separatorView.backgroundColor = Asset.neutralLine.color + + notificationLabel.isHidden = true + notificationLabel.layer.cornerRadius = 5 + notificationLabel.layer.masksToBounds = true + notificationLabel.textColor = Asset.neutralWhite.color + notificationLabel.backgroundColor = Asset.brandPrimary.color + notificationLabel.font = Fonts.Mulish.bold.font(size: 12.0) + + stackView.spacing = 16 + stackView.addArrangedSubview(iconImageView) + stackView.addArrangedSubview(titleLabel) + stackView.addArrangedSubview(notificationLabel) + stackView.setCustomSpacing(6, after: titleLabel) + + stackView.isUserInteractionEnabled = false + addSubview(stackView) + addSubview(separatorView) + + stackView.snp.makeConstraints { make in + make.top.equalToSuperview().offset(12) + make.left.equalToSuperview().offset(24) + make.bottom.equalTo(separatorView.snp.top).offset(-12) + } + + separatorView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(24) + make.right.equalToSuperview().offset(-24) + make.bottom.equalToSuperview() + make.height.equalTo(1) + } + } + + required init?(coder: NSCoder) { nil } + + func setup(title: String, image: UIImage) { + titleLabel.text = title + iconImageView.image = image + } + + func updateNotification(_ count: Int) { + notificationLabel.isHidden = count < 1 + notificationLabel.text = " \(count) " + } +} diff --git a/Sources/ContactListFeature/ContactListTableController.swift b/Sources/ContactListFeature/ContactListTableController.swift new file mode 100644 index 0000000000000000000000000000000000000000..8215991d49982bf7f3780636947fcd7adf575adb --- /dev/null +++ b/Sources/ContactListFeature/ContactListTableController.swift @@ -0,0 +1,83 @@ +import UIKit +import Shared +import Combine +import XXModels +import AppResources + +final class ContactListTableController: UITableViewController { + private var collation = UILocalizedIndexedCollation.current() + private var sections: [[Contact]] = [] { + didSet { self.tableView.reloadData() } + } + + private let viewModel: ContactListViewModel + private var cancellables = Set<AnyCancellable>() + private let tapRelay = PassthroughSubject<Contact, Never>() + + var didTap: AnyPublisher<Contact, Never> { tapRelay.eraseToAnyPublisher() } + + override func viewDidLoad() { + super.viewDidLoad() + setupTableView() + } + + init(_ viewModel: ContactListViewModel) { + self.viewModel = viewModel + super.init(style: .grouped) + } + + required init?(coder: NSCoder) { nil } + + private func setupTableView() { + tableView.separatorStyle = .none + tableView.register(AvatarCell.self) + tableView.backgroundColor = Asset.neutralWhite.color + tableView.sectionIndexColor = Asset.neutralDark.color + tableView.contentInset = UIEdgeInsets(top: -20, left: 0, bottom: 0, right: 0) + + viewModel.contacts + .receive(on: DispatchQueue.main) + .sink { [unowned self] in + let results = IndexedListCollator().sectioned(items: $0) + self.collation = results.collation + self.sections = results.sections + }.store(in: &cancellables) + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell: AvatarCell = tableView.dequeueReusableCell(forIndexPath: indexPath) + let contact = sections[indexPath.section][indexPath.row] + let name = (contact.nickname ?? contact.username) ?? "Fetching username..." + + cell.setup(title: name, image: contact.photo) + return cell + } + + override func numberOfSections(in: UITableView) -> Int { + sections.count + } + + override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int { + sections[section].count + } + + override func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) { + tapRelay.send(sections[indexPath.section][indexPath.row]) + } + + override func sectionIndexTitles(for: UITableView) -> [String]? { + collation.sectionIndexTitles + } + + override func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? { + collation.sectionTitles[section] + } + + override func tableView(_: UITableView, sectionForSectionIndexTitle: String, at index: Int) -> Int { + collation.section(forSectionIndexTitle: index) + } + + override func tableView(_: UITableView, heightForRowAt: IndexPath) -> CGFloat { + 64 + } +} diff --git a/Sources/ContactListFeature/ContactListView.swift b/Sources/ContactListFeature/ContactListView.swift new file mode 100644 index 0000000000000000000000000000000000000000..75ce0cb079969051c4fef0598a417dfcf7279ec6 --- /dev/null +++ b/Sources/ContactListFeature/ContactListView.swift @@ -0,0 +1,73 @@ +import UIKit +import Shared +import AppResources + +final class ContactListView: UIView { + let newGroupButton = ItemButton() + let requestsButton = ItemButton() + let topStackView = UIStackView() + let stackView = UIStackView() + let emptyTitleLabel = UILabel() + let searchButton = CapsuleButton() + + init() { + super.init(frame: .zero) + setup() + } + + required init?(coder: NSCoder) { nil } + + private func setup() { + backgroundColor = Asset.neutralWhite.color + + requestsButton.separatorView.isHidden = true + requestsButton.setup(title: "Requests", image: Asset.contactListRequests.image) + newGroupButton.setup(title: Localized.ContactList.newGroup, image: Asset.contactListNewGroup.image) + + let paragraph = NSMutableParagraphStyle() + paragraph.lineHeightMultiple = 1.2 + paragraph.alignment = .center + + emptyTitleLabel.attributedText = NSAttributedString( + string: Localized.ContactList.Empty.title, + attributes: [ + .paragraphStyle: paragraph, + .foregroundColor: Asset.neutralActive.color, + .font: Fonts.Mulish.bold.font(size: 24.0) as UIFont + ] + ) + emptyTitleLabel.numberOfLines = 0 + + searchButton.setStyle(.brandColored) + searchButton.setTitle(Localized.ContactList.Empty.action, for: .normal) + + stackView.spacing = 24 + stackView.axis = .vertical + stackView.alignment = .center + stackView.addArrangedSubview(emptyTitleLabel) + stackView.addArrangedSubview(searchButton) + + topStackView.axis = .vertical + topStackView.addArrangedSubview(newGroupButton) + topStackView.addArrangedSubview(requestsButton) + + addSubview(topStackView) + addSubview(stackView) + + setupConstraints() + } + + private func setupConstraints() { + topStackView.snp.makeConstraints { make in + make.top.equalToSuperview().offset(20) + make.left.equalToSuperview() + make.right.equalToSuperview() + } + + stackView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(24) + make.right.equalToSuperview().offset(-24) + } + } +} diff --git a/Sources/ContactListFeature/ContactListViewModel.swift b/Sources/ContactListFeature/ContactListViewModel.swift new file mode 100644 index 0000000000000000000000000000000000000000..3e509accc5cfd7c71e76ec7e6dc814da5a63af9a --- /dev/null +++ b/Sources/ContactListFeature/ContactListViewModel.swift @@ -0,0 +1,63 @@ +import Combine +import XXModels +import Defaults +import ReportingFeature +import XXMessengerClient + +import Foundation +import XXClient + +import AppCore +import Dependencies + +final class ContactListViewModel { + @Dependency(\.app.dbManager) var dbManager: DBManager + @Dependency(\.app.messenger) var messenger: Messenger + @Dependency(\.reportingStatus) var reportingStatus: ReportingStatus + + var myId: Data { + try! messenger.e2e.get()!.getContact().getId() + } + + var contacts: AnyPublisher<[XXModels.Contact], Never> { + let query = Contact.Query( + authStatus: [.friend], + isBlocked: reportingStatus.isEnabled() ? false : nil, + isBanned: reportingStatus.isEnabled() ? false: nil + ) + + return try! dbManager.getDB().fetchContactsPublisher(query) + .replaceError(with: []) + .map { $0.filter { $0.id != self.myId }} + .eraseToAnyPublisher() + } + + var requestCount: AnyPublisher<Int, Never> { + let groupQuery = Group.Query( + authStatus: [.pending], + isLeaderBlocked: reportingStatus.isEnabled() ? false : nil, + isLeaderBanned: reportingStatus.isEnabled() ? false : nil + ) + + let contactsQuery = Contact.Query( + authStatus: [ + .verified, + .confirming, + .confirmationFailed, + .verificationFailed, + .verificationInProgress + ], + isBlocked: reportingStatus.isEnabled() ? false : nil, + isBanned: reportingStatus.isEnabled() ? false : nil + ) + + return Publishers.CombineLatest( + try! dbManager.getDB().fetchContactsPublisher(contactsQuery) + .replaceError(with: []), + try! dbManager.getDB().fetchGroupsPublisher(groupQuery) + .replaceError(with: []) + ) + .map { $0.0.count + $0.1.count } + .eraseToAnyPublisher() + } +} diff --git a/Sources/ContactListFeature/Controllers/ContactListTableController.swift b/Sources/ContactListFeature/Controllers/ContactListTableController.swift deleted file mode 100644 index 0e456781aadc10fe5ce5c8e98b2444720a540546..0000000000000000000000000000000000000000 --- a/Sources/ContactListFeature/Controllers/ContactListTableController.swift +++ /dev/null @@ -1,82 +0,0 @@ -import UIKit -import Shared -import Combine -import XXModels - -final class ContactListTableController: UITableViewController { - private var collation = UILocalizedIndexedCollation.current() - private var sections: [[Contact]] = [] { - didSet { self.tableView.reloadData() } - } - - private let viewModel: ContactListViewModel - private var cancellables = Set<AnyCancellable>() - private let tapRelay = PassthroughSubject<Contact, Never>() - - var didTap: AnyPublisher<Contact, Never> { tapRelay.eraseToAnyPublisher() } - - override func viewDidLoad() { - super.viewDidLoad() - setupTableView() - } - - init(_ viewModel: ContactListViewModel) { - self.viewModel = viewModel - super.init(style: .grouped) - } - - required init?(coder: NSCoder) { nil } - - private func setupTableView() { - tableView.separatorStyle = .none - tableView.register(AvatarCell.self) - tableView.backgroundColor = Asset.neutralWhite.color - tableView.sectionIndexColor = Asset.neutralDark.color - tableView.contentInset = UIEdgeInsets(top: -20, left: 0, bottom: 0, right: 0) - - viewModel.contacts - .receive(on: DispatchQueue.main) - .sink { [unowned self] in - let results = IndexedListCollator().sectioned(items: $0) - self.collation = results.collation - self.sections = results.sections - }.store(in: &cancellables) - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell: AvatarCell = tableView.dequeueReusableCell(forIndexPath: indexPath) - let contact = sections[indexPath.section][indexPath.row] - let name = (contact.nickname ?? contact.username) ?? "Fetching username..." - - cell.setup(title: name, image: contact.photo) - return cell - } - - override func numberOfSections(in: UITableView) -> Int { - sections.count - } - - override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int { - sections[section].count - } - - override func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) { - tapRelay.send(sections[indexPath.section][indexPath.row]) - } - - override func sectionIndexTitles(for: UITableView) -> [String]? { - collation.sectionIndexTitles - } - - override func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? { - collation.sectionTitles[section] - } - - override func tableView(_: UITableView, sectionForSectionIndexTitle: String, at index: Int) -> Int { - collation.section(forSectionIndexTitle: index) - } - - override func tableView(_: UITableView, heightForRowAt: IndexPath) -> CGFloat { - 64 - } -} diff --git a/Sources/ContactListFeature/Controllers/CreateDrawerController.swift b/Sources/ContactListFeature/Controllers/CreateDrawerController.swift deleted file mode 100644 index 4ba7213d8a117cd7650fa992f40ccc8e43ab2924..0000000000000000000000000000000000000000 --- a/Sources/ContactListFeature/Controllers/CreateDrawerController.swift +++ /dev/null @@ -1,89 +0,0 @@ -import UIKit -import Shared -import Combine - -public final class CreateDrawerController: UIViewController { - private lazy var screenView = CreateDrawerView() - - private let selectedCount: Int - private let viewModel = CreateDrawerViewModel() - private let completion: (String, String?) -> Void - private var cancellables = Set<AnyCancellable>() - - public init(_ count: Int, _ completion: @escaping (String, String?) -> Void) { - self.selectedCount = count - self.completion = completion - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { nil } - - public override func loadView() { - let view = UIView() - view.addSubview(screenView) - - screenView.snp.makeConstraints { - $0.top.equalToSuperview() - $0.left.equalToSuperview() - $0.right.equalToSuperview() - $0.bottom.equalToSuperview().offset(0) - } - - self.view = view - } - - public override func viewDidLoad() { - super.viewDidLoad() - screenView.set(count: selectedCount) { - // TODO: ⚠️ - } - - setupBindings() - } - - private func setupBindings() { - viewModel.statePublisher - .map(\.status) - .receive(on: DispatchQueue.main) - .sink { [weak screenView] in screenView?.update(status: $0) } - .store(in: &cancellables) - - viewModel.donePublisher - .receive(on: DispatchQueue.main) - .sink { [unowned self] in - dismiss(animated: true) - completion($0.0, $0.1) - }.store(in: &cancellables) - - screenView.cancelButton - .publisher(for: .touchUpInside) - .receive(on: DispatchQueue.main) - .sink { [unowned self] in dismiss(animated: true) } - .store(in: &cancellables) - - screenView.inputField - .textPublisher - .sink { [weak viewModel] in viewModel?.didInput($0) } - .store(in: &cancellables) - - screenView.otherInputField - .textPublisher - .sink { [weak viewModel] in viewModel?.didOtherInput($0) } - .store(in: &cancellables) - - screenView.inputField - .returnPublisher - .sink { [unowned self] in screenView.inputField.endEditing(true) } - .store(in: &cancellables) - - screenView.otherInputField - .returnPublisher - .sink { [unowned self] in screenView.otherInputField.endEditing(true) } - .store(in: &cancellables) - - screenView.createButton - .publisher(for: .touchUpInside) - .sink { [weak viewModel] in viewModel?.didTapCreate() } - .store(in: &cancellables) - } -} diff --git a/Sources/ContactListFeature/ViewModels/ContactListViewModel.swift b/Sources/ContactListFeature/ViewModels/ContactListViewModel.swift deleted file mode 100644 index 838a7b12557279a3833c1c2d955e3719d6c74561..0000000000000000000000000000000000000000 --- a/Sources/ContactListFeature/ViewModels/ContactListViewModel.swift +++ /dev/null @@ -1,61 +0,0 @@ -import Combine -import XXModels -import Defaults -import ReportingFeature -import DI -import XXMessengerClient - -import Foundation -import XXClient - -final class ContactListViewModel { - @Dependency var database: Database - @Dependency var messenger: Messenger - @Dependency var reportingStatus: ReportingStatus - - var myId: Data { - try! messenger.e2e.get()!.getContact().getId() - } - - var contacts: AnyPublisher<[XXModels.Contact], Never> { - let query = Contact.Query( - authStatus: [.friend], - isBlocked: reportingStatus.isEnabled() ? false : nil, - isBanned: reportingStatus.isEnabled() ? false: nil - ) - - return database.fetchContactsPublisher(query) - .replaceError(with: []) - .map { $0.filter { $0.id != self.myId }} - .eraseToAnyPublisher() - } - - var requestCount: AnyPublisher<Int, Never> { - let groupQuery = Group.Query( - authStatus: [.pending], - isLeaderBlocked: reportingStatus.isEnabled() ? false : nil, - isLeaderBanned: reportingStatus.isEnabled() ? false : nil - ) - - let contactsQuery = Contact.Query( - authStatus: [ - .verified, - .confirming, - .confirmationFailed, - .verificationFailed, - .verificationInProgress - ], - isBlocked: reportingStatus.isEnabled() ? false : nil, - isBanned: reportingStatus.isEnabled() ? false : nil - ) - - return Publishers.CombineLatest( - database.fetchContactsPublisher(contactsQuery) - .replaceError(with: []), - database.fetchGroupsPublisher(groupQuery) - .replaceError(with: []) - ) - .map { $0.0.count + $0.1.count } - .eraseToAnyPublisher() - } -} diff --git a/Sources/ContactListFeature/ViewModels/CreateDrawerViewModel.swift b/Sources/ContactListFeature/ViewModels/CreateDrawerViewModel.swift deleted file mode 100644 index b47569594d82edaf5b9b92dc9ab0fc6d6fef040a..0000000000000000000000000000000000000000 --- a/Sources/ContactListFeature/ViewModels/CreateDrawerViewModel.swift +++ /dev/null @@ -1,53 +0,0 @@ -import Shared -import Combine -import InputField - -struct CreateDrawerViewState { - var welcome: String? - var groupName: String = "" - var status: InputField.ValidationStatus = .unknown(nil) -} - -final class CreateDrawerViewModel { - var statePublisher: AnyPublisher<CreateDrawerViewState, Never> { - stateSubject.eraseToAnyPublisher() - } - - var donePublisher: AnyPublisher<(String, String?), Never> { - doneSubject.eraseToAnyPublisher() - } - - private let doneSubject = PassthroughSubject<(String, String?), Never>() - private let stateSubject = CurrentValueSubject<CreateDrawerViewState, Never>(.init()) - - func didInput(_ string: String) { - stateSubject.value.groupName = string - validate() - } - - func didOtherInput(_ string: String) { - stateSubject.value.welcome = string - } - - func didTapCreate() { - let name = stateSubject.value.groupName.trimmingCharacters(in: .whitespacesAndNewlines) - let welcome = stateSubject.value.welcome - doneSubject.send((name, welcome)) - } - - private func validate() { - let value = stateSubject.value.groupName.trimmingCharacters(in: .whitespacesAndNewlines) - - guard value.count >= 4 else { - stateSubject.value.status = .invalid(Localized.CreateGroup.Drawer.minimum) - return - } - - guard value.count < 21 else { - stateSubject.value.status = .invalid(Localized.CreateGroup.Drawer.maximum) - return - } - - stateSubject.value.status = .valid(nil) - } -} diff --git a/Sources/ContactListFeature/ViewModels/CreateGroupViewModel.swift b/Sources/ContactListFeature/ViewModels/CreateGroupViewModel.swift deleted file mode 100644 index 74d9745b35ebd188dff4cf9a74115bb50e4d7c55..0000000000000000000000000000000000000000 --- a/Sources/ContactListFeature/ViewModels/CreateGroupViewModel.swift +++ /dev/null @@ -1,141 +0,0 @@ -import UIKit -import Shared -import Combine -import XXModels -import Defaults -import XXClient -import ReportingFeature -import CombineSchedulers -import XXMessengerClient -import DI - -final class CreateGroupViewModel { - @KeyObject(.username, defaultValue: "") var username: String - - @Dependency var database: Database - @Dependency var messenger: Messenger - @Dependency var groupManager: GroupChat - @Dependency var hudController: HUDController - @Dependency var reportingStatus: ReportingStatus - - var myId: Data { - try! messenger.e2e.get()!.getContact().getId() - } - - var selected: AnyPublisher<[XXModels.Contact], Never> { - selectedContactsRelay.eraseToAnyPublisher() - } - - var contacts: AnyPublisher<[XXModels.Contact], Never> { - contactsRelay.eraseToAnyPublisher() - } - - var info: AnyPublisher<GroupInfo, Never> { - infoRelay.eraseToAnyPublisher() - } - - var backgroundScheduler: AnySchedulerOf<DispatchQueue> - = DispatchQueue.global().eraseToAnyScheduler() - - private var allContacts = [XXModels.Contact]() - private var cancellables = Set<AnyCancellable>() - private let infoRelay = PassthroughSubject<GroupInfo, Never>() - private let contactsRelay = CurrentValueSubject<[XXModels.Contact], Never>([]) - private let selectedContactsRelay = CurrentValueSubject<[XXModels.Contact], Never>([]) - - init() { - let query = Contact.Query( - authStatus: [.friend], - isBlocked: reportingStatus.isEnabled() ? false : nil, - isBanned: reportingStatus.isEnabled() ? false : nil - ) - - database.fetchContactsPublisher(query) - .replaceError(with: []) - .map { $0.filter { $0.id != self.myId }} - .map { $0.sorted(by: { $0.username! < $1.username! })} - .sink { [unowned self] in - allContacts = $0 - contactsRelay.send($0) - }.store(in: &cancellables) - } - - // MARK: Public - - func didSelect(contact: XXModels.Contact) { - if selectedContactsRelay.value.contains(contact) { - selectedContactsRelay.value.removeAll { $0.username == contact.username } - } else { - selectedContactsRelay.value.append(contact) - } - } - - func filter(_ text: String) { - guard text.isEmpty == false else { - contactsRelay.send(allContacts) - return - } - - contactsRelay.send( - allContacts.filter { - ($0.username ?? "").contains(text.lowercased()) - } - ) - } - - func create(name: String, welcome: String?, members: [XXModels.Contact]) { - hudController.show() - - backgroundScheduler.schedule { [weak self] in - guard let self else { return } - - do { - let report = try self.groupManager.makeGroup( - membership: members.map(\.id), - message: welcome?.data(using: .utf8), - name: name.data(using: .utf8) - ) - - let group = Group( - id: report.id, - name: name, - leaderId: self.myId, - createdAt: Date(), - authStatus: .participating, - serialized: try report.encode() // ? - ) - - _ = try self.database.saveGroup(group) - - if let welcomeMessage = welcome { - try self.database.saveMessage( - Message( - senderId: self.myId, - recipientId: nil, - groupId: group.id, - date: group.createdAt, - status: .sent, - isUnread: false, - text: welcomeMessage, - replyMessageId: nil, - roundURL: nil, - fileTransferId: nil - ) - ) - } - - try members - .map { GroupMember(groupId: group.id, contactId: $0.id) } - .forEach { try self.database.saveGroupMember($0) } - - let query = GroupInfo.Query(groupId: group.id) - let info = try self.database.fetchGroupInfos(query).first - - self.infoRelay.send(info!) - self.hudController.dismiss() - } catch { - self.hudController.show(.init(error: error)) - } - } - } -} diff --git a/Sources/ContactListFeature/Views/ContactListItemButton.swift b/Sources/ContactListFeature/Views/ContactListItemButton.swift deleted file mode 100644 index c498f27ab9429fade4721e95b5fca6f3a73bdca7..0000000000000000000000000000000000000000 --- a/Sources/ContactListFeature/Views/ContactListItemButton.swift +++ /dev/null @@ -1,60 +0,0 @@ -import UIKit -import Shared - -final class ItemButton: UIControl { - let titleLabel = UILabel() - let iconImageView = UIImageView() - let separatorView = UIView() - let stackView = UIStackView() - let notificationLabel = UILabel() - - init() { - super.init(frame: .zero) - - titleLabel.textColor = Asset.brandPrimary.color - titleLabel.font = Fonts.Mulish.semiBold.font(size: 14.0) - separatorView.backgroundColor = Asset.neutralLine.color - - notificationLabel.isHidden = true - notificationLabel.layer.cornerRadius = 5 - notificationLabel.layer.masksToBounds = true - notificationLabel.textColor = Asset.neutralWhite.color - notificationLabel.backgroundColor = Asset.brandPrimary.color - notificationLabel.font = Fonts.Mulish.bold.font(size: 12.0) - - stackView.spacing = 16 - stackView.addArrangedSubview(iconImageView) - stackView.addArrangedSubview(titleLabel) - stackView.addArrangedSubview(notificationLabel) - stackView.setCustomSpacing(6, after: titleLabel) - - stackView.isUserInteractionEnabled = false - addSubview(stackView) - addSubview(separatorView) - - stackView.snp.makeConstraints { make in - make.top.equalToSuperview().offset(12) - make.left.equalToSuperview().offset(24) - make.bottom.equalTo(separatorView.snp.top).offset(-12) - } - - separatorView.snp.makeConstraints { make in - make.left.equalToSuperview().offset(24) - make.right.equalToSuperview().offset(-24) - make.bottom.equalToSuperview() - make.height.equalTo(1) - } - } - - required init?(coder: NSCoder) { nil } - - func setup(title: String, image: UIImage) { - titleLabel.text = title - iconImageView.image = image - } - - func updateNotification(_ count: Int) { - notificationLabel.isHidden = count < 1 - notificationLabel.text = " \(count) " - } -} diff --git a/Sources/ContactListFeature/Views/ContactListView.swift b/Sources/ContactListFeature/Views/ContactListView.swift deleted file mode 100644 index e7a52a717346d43bb2551eac86839d796a9c78c1..0000000000000000000000000000000000000000 --- a/Sources/ContactListFeature/Views/ContactListView.swift +++ /dev/null @@ -1,72 +0,0 @@ -import UIKit -import Shared - -final class ContactListView: UIView { - let newGroupButton = ItemButton() - let requestsButton = ItemButton() - let topStackView = UIStackView() - let stackView = UIStackView() - let emptyTitleLabel = UILabel() - let searchButton = CapsuleButton() - - init() { - super.init(frame: .zero) - setup() - } - - required init?(coder: NSCoder) { nil } - - private func setup() { - backgroundColor = Asset.neutralWhite.color - - requestsButton.separatorView.isHidden = true - requestsButton.setup(title: "Requests", image: Asset.contactListRequests.image) - newGroupButton.setup(title: Localized.ContactList.newGroup, image: Asset.contactListNewGroup.image) - - let paragraph = NSMutableParagraphStyle() - paragraph.lineHeightMultiple = 1.2 - paragraph.alignment = .center - - emptyTitleLabel.attributedText = NSAttributedString( - string: Localized.ContactList.Empty.title, - attributes: [ - .paragraphStyle: paragraph, - .foregroundColor: Asset.neutralActive.color, - .font: Fonts.Mulish.bold.font(size: 24.0) as UIFont - ] - ) - emptyTitleLabel.numberOfLines = 0 - - searchButton.setStyle(.brandColored) - searchButton.setTitle(Localized.ContactList.Empty.action, for: .normal) - - stackView.spacing = 24 - stackView.axis = .vertical - stackView.alignment = .center - stackView.addArrangedSubview(emptyTitleLabel) - stackView.addArrangedSubview(searchButton) - - topStackView.axis = .vertical - topStackView.addArrangedSubview(newGroupButton) - topStackView.addArrangedSubview(requestsButton) - - addSubview(topStackView) - addSubview(stackView) - - setupConstraints() - } - - private func setupConstraints() { - topStackView.snp.makeConstraints { make in - make.top.equalToSuperview().offset(20) - make.left.equalToSuperview() - make.right.equalToSuperview() - } - - stackView.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.left.equalToSuperview().offset(24) - make.right.equalToSuperview().offset(-24) - } - } -} diff --git a/Sources/ContactListFeature/Views/CreateDrawerView.swift b/Sources/ContactListFeature/Views/CreateDrawerView.swift deleted file mode 100644 index 618ce7c94b4ed11109294b82bda47c8e17d98820..0000000000000000000000000000000000000000 --- a/Sources/ContactListFeature/Views/CreateDrawerView.swift +++ /dev/null @@ -1,106 +0,0 @@ -import UIKit -import Shared -import InputField - -final class CreateDrawerView: UIView { - let titleLabel = UILabel() - let subtitleView = TextWithInfoView() - let inputField = InputField() - let otherInputField = InputField() - let stackView = UIStackView() - let createButton = CapsuleButton() - let cancelButton = CapsuleButton() - - var didTapInfo: (() -> Void)? - - init() { - super.init(frame: .zero) - - layer.cornerRadius = 40 - backgroundColor = Asset.neutralWhite.color - layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] - - titleLabel.textAlignment = .left - titleLabel.text = Localized.CreateGroup.Drawer.title - titleLabel.font = Fonts.Mulish.bold.font(size: 26.0) - titleLabel.textColor = Asset.neutralActive.color - - inputField.setup( - style: .regular, - title: Localized.CreateGroup.Drawer.input, - placeholder: Localized.CreateGroup.Drawer.placeholder, - leftView: .image(Asset.personGray.image), - accessibility: Localized.Accessibility.CreateGroup.Drawer.input, - subtitleColor: Asset.neutralDisabled.color - ) - - otherInputField.setup( - style: .regular, - title: Localized.CreateGroup.Drawer.otherInput, - placeholder: Localized.CreateGroup.Drawer.otherPlaceholder, - leftView: .image(Asset.balloon.image), - accessibility: Localized.Accessibility.CreateGroup.Drawer.otherInput, - subtitleColor: Asset.neutralDisabled.color - ) - - createButton.set( - style: .brandColored, - title: Localized.CreateGroup.Drawer.action, - accessibility: Localized.Accessibility.CreateGroup.Drawer.create - ) - - cancelButton.set( - style: .seeThrough, - title: Localized.CreateGroup.Drawer.cancel - ) - - stackView.spacing = 20 - stackView.axis = .vertical - stackView.addArrangedSubview(titleLabel) - stackView.addArrangedSubview(subtitleView) - stackView.addArrangedSubview(inputField) - stackView.addArrangedSubview(otherInputField) - stackView.addArrangedSubview(createButton) - stackView.addArrangedSubview(cancelButton) - - addSubview(stackView) - - stackView.snp.makeConstraints { - $0.top.equalToSuperview().offset(60) - $0.left.equalToSuperview().offset(50) - $0.right.equalToSuperview().offset(-50) - $0.bottom.equalToSuperview().offset(-70) - } - } - - required init?(coder: NSCoder) { nil } - - func set(count: Int, didTap: @escaping () -> Void) { - self.didTapInfo = didTap - - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.alignment = .left - paragraphStyle.lineHeightMultiple = 1.1 - - subtitleView.setup( - text: Localized.CreateGroup.Drawer.subtitle("\(count)"), - attributes: [ - .paragraphStyle: paragraphStyle, - .foregroundColor: Asset.neutralBody.color, - .font: Fonts.Mulish.semiBold.font(size: 14.0) as Any - ], - didTapInfo: { [weak self] in self?.didTapInfo?() } - ) - } - - func update(status: InputField.ValidationStatus) { - inputField.update(status: status) - - switch status { - case .valid: - createButton.isEnabled = true - case .invalid, .unknown: - createButton.isEnabled = false - } - } -} diff --git a/Sources/ContactListFeature/Views/CreateGroupCollectionCell.swift b/Sources/ContactListFeature/Views/CreateGroupCollectionCell.swift deleted file mode 100644 index 1717365300ab0ae52380cc47e6073f8118684957..0000000000000000000000000000000000000000 --- a/Sources/ContactListFeature/Views/CreateGroupCollectionCell.swift +++ /dev/null @@ -1,80 +0,0 @@ -import UIKit -import Shared -import Combine - -final class CreateGroupCollectionCell: UICollectionViewCell { - let titleLabel = UILabel() - let removeButton = UIButton() - let upperView = UIView() - let avatarView = AvatarView() - - var didTapRemove: (() -> Void)? - var cancellables = Set<AnyCancellable>() - - override init(frame: CGRect) { - super.init(frame: frame) - - titleLabel.numberOfLines = 2 - titleLabel.lineBreakMode = .byWordWrapping - titleLabel.textAlignment = .center - titleLabel.textColor = Asset.neutralDark.color - titleLabel.font = Fonts.Mulish.semiBold.font(size: 14.0) - - removeButton.layer.cornerRadius = 9 - removeButton.backgroundColor = Asset.accentDanger.color - removeButton.setImage(Asset.contactListAvatarRemove.image, for: .normal) - - upperView.addSubview(avatarView) - contentView.addSubview(titleLabel) - contentView.addSubview(upperView) - contentView.addSubview(removeButton) - - upperView.snp.makeConstraints { - $0.top.equalToSuperview() - $0.left.equalToSuperview() - $0.right.equalToSuperview() - } - - avatarView.snp.makeConstraints { - $0.width.equalTo(48) - $0.height.equalTo(48) - $0.top.equalToSuperview().offset(4) - $0.left.equalToSuperview().offset(4) - $0.right.equalToSuperview().offset(-4) - $0.bottom.equalToSuperview().offset(-4) - } - - removeButton.snp.makeConstraints { - $0.centerY.equalTo(avatarView.snp.top).offset(5) - $0.centerX.equalTo(avatarView.snp.right).offset(-5) - $0.width.equalTo(18) - $0.height.equalTo(18) - } - - titleLabel.snp.makeConstraints { - $0.top.equalTo(upperView.snp.bottom) - $0.left.equalToSuperview() - $0.right.equalToSuperview() - $0.bottom.equalToSuperview() - } - } - - required init?(coder: NSCoder) { nil } - - override func prepareForReuse() { - super.prepareForReuse() - titleLabel.text = nil - avatarView.prepareForReuse() - cancellables.removeAll() - } - - func setup(title: String, image: Data?) { - titleLabel.text = title - avatarView.setupProfile(title: title, image: image, size: .large) - cancellables.removeAll() - - removeButton.publisher(for: .touchUpInside) - .sink { [unowned self] in didTapRemove?() } - .store(in: &cancellables) - } -} diff --git a/Sources/ContactListFeature/Views/CreateGroupView.swift b/Sources/ContactListFeature/Views/CreateGroupView.swift deleted file mode 100644 index 0f7bd57962bed1fd6930bb6e1cd62fe2c618b01b..0000000000000000000000000000000000000000 --- a/Sources/ContactListFeature/Views/CreateGroupView.swift +++ /dev/null @@ -1,62 +0,0 @@ -import UIKit -import Shared -import SnapKit - -final class CreateGroupView: UIView { - let stackView = UIStackView() - let tableView = UITableView() - let searchComponent = SearchComponent() - lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) - - let layout: UICollectionViewFlowLayout = { - let layout = UICollectionViewFlowLayout() - layout.minimumInteritemSpacing = 45 - layout.itemSize = CGSize(width: 56, height: 100) - layout.scrollDirection = .horizontal - return layout - }() - - init() { - super.init(frame: .zero) - backgroundColor = Asset.neutralWhite.color - - tableView.separatorStyle = .none - tableView.tintColor = Asset.brandPrimary.color - tableView.backgroundColor = Asset.neutralWhite.color - tableView.allowsMultipleSelectionDuringEditing = true - tableView.setEditing(true, animated: true) - - searchComponent.set( - placeholder: "Search connections", - imageAtRight: UIImage.color(.clear) - ) - - collectionView.backgroundColor = Asset.neutralWhite.color - collectionView.contentInset = UIEdgeInsets(top: 0, left: 30, bottom: 0, right: 30) - - stackView.spacing = 31 - stackView.axis = .vertical - stackView.addArrangedSubview(collectionView) - stackView.addArrangedSubview(tableView) - - addSubview(stackView) - addSubview(searchComponent) - - searchComponent.snp.makeConstraints { make in - make.top.equalToSuperview().offset(20) - make.left.equalToSuperview().offset(20) - make.right.equalToSuperview().offset(-20) - } - - stackView.snp.makeConstraints { make in - make.top.equalTo(searchComponent.snp.bottom).offset(20) - make.left.equalToSuperview() - make.right.equalToSuperview() - make.bottom.equalToSuperview() - } - - collectionView.snp.makeConstraints { $0.height.equalTo(100) } - } - - required init?(coder: NSCoder) { nil } -} diff --git a/Sources/CountryListFeature/CountryListController.swift b/Sources/CountryListFeature/CountryListController.swift index f7c11b93853371b1ccf191b32abfe367ca59b237..dd6ad4e65985464e7ae3c323a30df35da6033ab9 100644 --- a/Sources/CountryListFeature/CountryListController.swift +++ b/Sources/CountryListFeature/CountryListController.swift @@ -1,12 +1,12 @@ import UIKit import Shared import Combine +import AppCore import AppResources -import StatusBarFeature -import ComposableArchitecture +import Dependencies public final class CountryListController: UIViewController, UITableViewDelegate { - @Dependency(\.statusBar) var statusBar: StatusBarStyleManager + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = CountryListView() @@ -24,7 +24,7 @@ public final class CountryListController: UIViewController, UITableViewDelegate public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - statusBar.update(.darkContent) + statusBar.set(.darkContent) } public override func loadView() { diff --git a/Sources/CrashReport/CrashReport.swift b/Sources/CrashReport/CrashReport.swift new file mode 100644 index 0000000000000000000000000000000000000000..b5eceb577f093b6d61aafe6290cfe943c597ed36 --- /dev/null +++ b/Sources/CrashReport/CrashReport.swift @@ -0,0 +1,26 @@ +import Firebase +import CrashReporting +import FirebaseCrashlytics +import XCTestDynamicOverlay + +public struct CrashReport { + public var configure: () -> Void + public var sendError: (NSError) -> Void + public var setEnabled: (Bool) -> Void +} + +extension CrashReport { + public static let live = CrashReport( + configure: FirebaseApp.configure, + sendError: Crashlytics.crashlytics().record(error:), + setEnabled: Crashlytics.crashlytics().setCrashlyticsCollectionEnabled + ) +} + +extension CrashReport { + public static let unimplemented = CrashReport( + configure: XCTUnimplemented("\(Self.self)"), + sendError: XCTUnimplemented("\(Self.self)"), + setEnabled: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/CrashReport/Dependency.swift b/Sources/CrashReport/Dependency.swift new file mode 100644 index 0000000000000000000000000000000000000000..ee81581b77eac6a15c8ca9e4d5044a644a7b4c13 --- /dev/null +++ b/Sources/CrashReport/Dependency.swift @@ -0,0 +1,13 @@ +import Dependencies + +private enum CrashReportDependencyKey: DependencyKey { + static let liveValue: CrashReport = .live + static let testValue: CrashReport = .unimplemented +} + +extension DependencyValues { + public var crashReport: CrashReport { + get { self[CrashReportDependencyKey.self] } + set { self[CrashReportDependencyKey.self] = newValue } + } +} diff --git a/Sources/CrashReporting/CrashReporter.swift b/Sources/CrashReporting/CrashReporter.swift deleted file mode 100644 index e83c1c54b0f765442b9f31c5c779c86096a92db7..0000000000000000000000000000000000000000 --- a/Sources/CrashReporting/CrashReporter.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Foundation - -public struct CrashReporter { - public var configure: () -> Void - public var sendError: (NSError) -> Void - public var setEnabled: (Bool) -> Void - - public init( - configure: @escaping () -> Void, - sendError: @escaping (NSError) -> Void, - setEnabled: @escaping (Bool) -> Void - ) { - self.configure = configure - self.sendError = sendError - self.setEnabled = setEnabled - } -} - -public extension CrashReporter { - static let noop = Self( - configure: {}, - sendError: { _ in }, - setEnabled: { _ in } - ) -} diff --git a/Sources/CrashService/CrashService.swift b/Sources/CrashService/CrashService.swift deleted file mode 100644 index 4226369deeb3db598fee81058dbd139ff0dc1c2b..0000000000000000000000000000000000000000 --- a/Sources/CrashService/CrashService.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Firebase -import CrashReporting -import FirebaseCrashlytics - -public extension CrashReporter { - static let live = Self( - configure: { FirebaseApp.configure() }, - sendError: { Crashlytics.crashlytics().record(error: $0) }, - setEnabled: { Crashlytics.crashlytics().setCrashlyticsCollectionEnabled($0) } - ) -} diff --git a/Sources/CreateGroupFeature/CreateGroupController.swift b/Sources/CreateGroupFeature/CreateGroupController.swift new file mode 100644 index 0000000000000000000000000000000000000000..ad7303364f56d0dbd7b840f3803cb066f8be2578 --- /dev/null +++ b/Sources/CreateGroupFeature/CreateGroupController.swift @@ -0,0 +1,87 @@ +import UIKit +import Combine +import XXModels + +public final class CreateGroupController: UIViewController { + private lazy var screenView = CreateGroupView() + + private let groupMembers: [Contact] + private let viewModel = CreateGroupViewModel() + private var cancellables = Set<AnyCancellable>() + + public init(_ groupMembers: [Contact]) { + self.groupMembers = groupMembers + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { nil } + + public override func loadView() { + view = screenView + } + + public override func viewDidLoad() { + super.viewDidLoad() + screenView.set(count: groupMembers.count, didTap: {}) + + viewModel + .statePublisher + .map(\.status) + .receive(on: DispatchQueue.main) + .sink { [unowned self] in + screenView.update(status: $0) + }.store(in: &cancellables) + + viewModel + .statePublisher + .map(\.shouldDismiss) + .filter { $0 == true } + .receive(on: DispatchQueue.main) + .sink { [unowned self] _ in + dismiss(animated: true) + }.store(in: &cancellables) + + screenView + .cancelButton + .publisher(for: .touchUpInside) + .receive(on: DispatchQueue.main) + .sink { [unowned self] in + dismiss(animated: true) + }.store(in: &cancellables) + + screenView + .inputField + .textPublisher + .sink { [unowned self] in + viewModel.didInput($0) + }.store(in: &cancellables) + + screenView + .otherInputField + .textPublisher + .sink { [unowned self] in + viewModel.didOtherInput($0) + }.store(in: &cancellables) + + screenView + .inputField + .returnPublisher + .sink { [unowned self] in + screenView.inputField.endEditing(true) + }.store(in: &cancellables) + + screenView + .otherInputField + .returnPublisher + .sink { [unowned self] in + screenView.otherInputField.endEditing(true) + }.store(in: &cancellables) + + screenView + .createButton + .publisher(for: .touchUpInside) + .sink { [unowned self] in + viewModel.didTapCreate(groupMembers) + }.store(in: &cancellables) + } +} diff --git a/Sources/CreateGroupFeature/CreateGroupView.swift b/Sources/CreateGroupFeature/CreateGroupView.swift new file mode 100644 index 0000000000000000000000000000000000000000..8d38493433607e66a2ac836e8ae8e07d473be134 --- /dev/null +++ b/Sources/CreateGroupFeature/CreateGroupView.swift @@ -0,0 +1,107 @@ +import UIKit +import Shared +import InputField +import AppResources + +final class CreateGroupView: UIView { + let titleLabel = UILabel() + let subtitleView = TextWithInfoView() + let inputField = InputField() + let otherInputField = InputField() + let stackView = UIStackView() + let createButton = CapsuleButton() + let cancelButton = CapsuleButton() + + var didTapInfo: (() -> Void)? + + init() { + super.init(frame: .zero) + + layer.cornerRadius = 40 + backgroundColor = Asset.neutralWhite.color + layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + + titleLabel.textAlignment = .left + titleLabel.text = Localized.CreateGroup.Drawer.title + titleLabel.font = Fonts.Mulish.bold.font(size: 26.0) + titleLabel.textColor = Asset.neutralActive.color + + inputField.setup( + style: .regular, + title: Localized.CreateGroup.Drawer.input, + placeholder: Localized.CreateGroup.Drawer.placeholder, + leftView: .image(Asset.personGray.image), + accessibility: Localized.Accessibility.CreateGroup.Drawer.input, + subtitleColor: Asset.neutralDisabled.color + ) + + otherInputField.setup( + style: .regular, + title: Localized.CreateGroup.Drawer.otherInput, + placeholder: Localized.CreateGroup.Drawer.otherPlaceholder, + leftView: .image(Asset.balloon.image), + accessibility: Localized.Accessibility.CreateGroup.Drawer.otherInput, + subtitleColor: Asset.neutralDisabled.color + ) + + createButton.set( + style: .brandColored, + title: Localized.CreateGroup.Drawer.action, + accessibility: Localized.Accessibility.CreateGroup.Drawer.create + ) + + cancelButton.set( + style: .seeThrough, + title: Localized.CreateGroup.Drawer.cancel + ) + + stackView.spacing = 20 + stackView.axis = .vertical + stackView.addArrangedSubview(titleLabel) + stackView.addArrangedSubview(subtitleView) + stackView.addArrangedSubview(inputField) + stackView.addArrangedSubview(otherInputField) + stackView.addArrangedSubview(createButton) + stackView.addArrangedSubview(cancelButton) + + addSubview(stackView) + + stackView.snp.makeConstraints { + $0.top.equalToSuperview().offset(60) + $0.left.equalToSuperview().offset(50) + $0.right.equalToSuperview().offset(-50) + $0.bottom.equalToSuperview().offset(-70) + } + } + + required init?(coder: NSCoder) { nil } + + func set(count: Int, didTap: @escaping () -> Void) { + self.didTapInfo = didTap + + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.alignment = .left + paragraphStyle.lineHeightMultiple = 1.1 + + subtitleView.setup( + text: Localized.CreateGroup.Drawer.subtitle("\(count)"), + attributes: [ + .paragraphStyle: paragraphStyle, + .foregroundColor: Asset.neutralBody.color, + .font: Fonts.Mulish.semiBold.font(size: 14.0) as Any + ], + didTapInfo: { [weak self] in self?.didTapInfo?() } + ) + } + + func update(status: InputField.ValidationStatus) { + inputField.update(status: status) + + switch status { + case .valid: + createButton.isEnabled = true + case .invalid, .unknown: + createButton.isEnabled = false + } + } +} diff --git a/Sources/CreateGroupFeature/CreateGroupViewModel.swift b/Sources/CreateGroupFeature/CreateGroupViewModel.swift new file mode 100644 index 0000000000000000000000000000000000000000..f63eccede0feef855c3b973d948627160c6d6f89 --- /dev/null +++ b/Sources/CreateGroupFeature/CreateGroupViewModel.swift @@ -0,0 +1,103 @@ +import Shared +import Combine +import AppCore +import XXModels +import InputField +import AppResources +import Dependencies + +import Foundation // ? + +struct CreateGroupViewModel { + struct ViewState { + var welcome: String? + var groupName: String = "" + var status: InputField.ValidationStatus = .unknown(nil) + var shouldDismiss: Bool = false + } + + @Dependency(\.app.bgQueue) var bgQueue + @Dependency(\.app.dbManager) var dbManager + @Dependency(\.app.messenger) var messenger + @Dependency(\.groupManager) var groupManager + @Dependency(\.app.hudManager) var hudManager + + var statePublisher: AnyPublisher<ViewState, Never> { + stateSubject.eraseToAnyPublisher() + } + + private let stateSubject = CurrentValueSubject<ViewState, Never>(.init()) + + func didInput(_ string: String) { + stateSubject.value.groupName = string + validate() + } + + func didOtherInput(_ string: String) { + stateSubject.value.welcome = string + } + + func didTapCreate(_ members: [Contact]) { + hudManager.show() + let welcome = stateSubject.value.welcome + let name = stateSubject.value.groupName.trimmingCharacters(in: .whitespacesAndNewlines) + + bgQueue.schedule { + do { + guard let manager = groupManager.get() else { + fatalError("Can't create a group w/out a manager") + } + let report = try manager.makeGroup( + membership: members.map(\.id), + message: welcome?.data(using: .utf8), + name: name.data(using: .utf8) + ) + let group = Group( + id: report.id, + name: name, + leaderId: try messenger.e2e.get()!.getContact().getId(), + createdAt: Date(), + authStatus: .participating, + serialized: try report.encode() + ) + try dbManager.getDB().saveGroup(group) + if let welcome { + try dbManager.getDB().saveMessage(.init( + senderId: try messenger.e2e.get()!.getContact().getId(), + recipientId: nil, + groupId: group.id, + date: group.createdAt, + status: .sent, + isUnread: false, + text: welcome + )) + } + try members.map { + GroupMember(groupId: group.id, contactId: $0.id) + }.forEach { + try dbManager.getDB().saveGroupMember($0) + } + _ = try dbManager.getDB().fetchGroupInfos( + .init(groupId: group.id) + ).first + hudManager.hide() + stateSubject.value.shouldDismiss = true + } catch { + hudManager.show(.init(error: error)) + } + } + } + + private func validate() { + let value = stateSubject.value.groupName.trimmingCharacters(in: .whitespacesAndNewlines) + guard value.count >= 4 else { + stateSubject.value.status = .invalid(Localized.CreateGroup.Drawer.minimum) + return + } + guard value.count < 21 else { + stateSubject.value.status = .invalid(Localized.CreateGroup.Drawer.maximum) + return + } + stateSubject.value.status = .valid(nil) + } +} diff --git a/Sources/Defaults/Dependencies.swift b/Sources/Defaults/Dependencies.swift new file mode 100644 index 0000000000000000000000000000000000000000..e0446cf6f3a8c8d952b419df86a95cb7043d52fd --- /dev/null +++ b/Sources/Defaults/Dependencies.swift @@ -0,0 +1,13 @@ +import Dependencies + +private enum KeyObjectStoreDependencyKey: DependencyKey { + static let liveValue: KeyObjectStore = .live + static let testValue: KeyObjectStore = .unimplemented +} + +extension DependencyValues { + public var store: KeyObjectStore { + get { self[KeyObjectStoreDependencyKey.self] } + set { self[KeyObjectStoreDependencyKey.self] = newValue } + } +} diff --git a/Sources/Defaults/KeyObject.swift b/Sources/Defaults/KeyObject.swift index 1072e3c1cca9e9e195e99f892f489d52b4d6150f..290a567e68cb5e72f81ed09f2ada856e1bfe16c3 100644 --- a/Sources/Defaults/KeyObject.swift +++ b/Sources/Defaults/KeyObject.swift @@ -1,5 +1,5 @@ -import DI import Foundation +import ComposableArchitecture public enum Key: String { case email @@ -23,42 +23,12 @@ public enum Key: String { case askedDummyTrafficOnce } -public struct KeyObjectStore { - var objectForKey: (String) -> Any? - var setObjectForKey: (Any?, String) -> Void - var removeObjectForKey: (String) -> Void - - public init( - objectForKey: @escaping (String) -> Any?, - setObjectForKey: @escaping (Any?, String) -> Void, - removeObjectForKey: @escaping (String) -> Void - ) { - self.objectForKey = objectForKey - self.setObjectForKey = setObjectForKey - self.removeObjectForKey = removeObjectForKey - } -} - -public extension KeyObjectStore { - static func mock(dictionary: NSMutableDictionary) -> Self { - Self(objectForKey: { dictionary[$0] }, - setObjectForKey: { dictionary[$1] = $0 }, - removeObjectForKey: { dictionary[$0] = nil }) - } - - static let userDefaults = Self( - objectForKey: UserDefaults.standard.object(forKey:), - setObjectForKey: UserDefaults.standard.set(_:forKey:), - removeObjectForKey: UserDefaults.standard.removeObject(forKey:) - ) -} - @propertyWrapper public struct KeyObject<T> { let key: String let defaultValue: T - @Dependency var store: KeyObjectStore + @Dependency(\.store) var store: KeyObjectStore public init(_ key: Key, defaultValue: T) { self.key = key.rawValue @@ -67,13 +37,13 @@ public struct KeyObject<T> { public var wrappedValue: T { get { - store.objectForKey(key) as? T ?? defaultValue + store.get(key) as? T ?? defaultValue } set { if let value = newValue as? OptionalProtocol, value.isNil() { - store.removeObjectForKey(key) + store.remove(key) } else { - store.setObjectForKey(newValue, key) + store.set(newValue, for: key) } } } diff --git a/Sources/Defaults/KeyObjectStore.swift b/Sources/Defaults/KeyObjectStore.swift new file mode 100644 index 0000000000000000000000000000000000000000..38588f92da0ba9f28ed3c389afb8f9c9469a049a --- /dev/null +++ b/Sources/Defaults/KeyObjectStore.swift @@ -0,0 +1,21 @@ +public struct KeyObjectStore { + public var get: ObjectForKey + public var set: SetObjectForKey + public var remove: RemoveObjectForKey +} + +extension KeyObjectStore { + public static let live = KeyObjectStore( + get: .live, + set: .live, + remove: .live + ) +} + +extension KeyObjectStore { + public static let unimplemented = KeyObjectStore( + get: .unimplemented, + set: .unimplemented, + remove: .unimplemented + ) +} diff --git a/Sources/Defaults/ObjectForKey.swift b/Sources/Defaults/ObjectForKey.swift new file mode 100644 index 0000000000000000000000000000000000000000..e2c51f0699a95fd32f636dc58d099f2f4afe0c2c --- /dev/null +++ b/Sources/Defaults/ObjectForKey.swift @@ -0,0 +1,22 @@ +import Foundation +import XCTestDynamicOverlay + +public struct ObjectForKey { + public var run: (String) -> Any? + + public func callAsFunction(_ key: String) -> Any? { + run(key) + } +} + +extension ObjectForKey { + public static let live = ObjectForKey { + UserDefaults.standard.object(forKey: $0) + } +} + +extension ObjectForKey { + public static let unimplemented = ObjectForKey( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/Defaults/RemoveObjectForKey.swift b/Sources/Defaults/RemoveObjectForKey.swift new file mode 100644 index 0000000000000000000000000000000000000000..f8108bcffc0d978558f38b017e2cc9f0de848cc3 --- /dev/null +++ b/Sources/Defaults/RemoveObjectForKey.swift @@ -0,0 +1,22 @@ +import Foundation +import XCTestDynamicOverlay + +public struct RemoveObjectForKey { + public var run: (String) -> Void + + public func callAsFunction(_ key: String) -> Void { + run(key) + } +} + +extension RemoveObjectForKey { + public static let live = RemoveObjectForKey { + UserDefaults.standard.removeObject(forKey: $0) + } +} + +extension RemoveObjectForKey { + public static let unimplemented = RemoveObjectForKey( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/Defaults/SetObjectForKey.swift b/Sources/Defaults/SetObjectForKey.swift new file mode 100644 index 0000000000000000000000000000000000000000..c2f7d625d607474cdb4f7ba673fe9ead94673651 --- /dev/null +++ b/Sources/Defaults/SetObjectForKey.swift @@ -0,0 +1,22 @@ +import Foundation +import XCTestDynamicOverlay + +public struct SetObjectForKey { + public var run: (Any?, String) -> Void + + public func callAsFunction(_ value: Any?, for key: String) -> Void { + run(value, key) + } +} + +extension SetObjectForKey { + public static let live = SetObjectForKey { value, key in + UserDefaults.standard.set(value, forKey: key) + } +} + +extension SetObjectForKey { + public static let unimplemented = SetObjectForKey( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/FetchBannedList/Dependency.swift b/Sources/FetchBannedList/Dependency.swift new file mode 100644 index 0000000000000000000000000000000000000000..8eb9ad1696a663a47bcb257e3b71d09fc6225e11 --- /dev/null +++ b/Sources/FetchBannedList/Dependency.swift @@ -0,0 +1,14 @@ +import Dependencies + +private enum FetchBannedListDependencyKey: DependencyKey { + static let liveValue: FetchBannedList = .live + static let testValue: FetchBannedList = .unimplemented +} + +extension DependencyValues { + public var fetchBannedList: FetchBannedList { + get { self[FetchBannedListDependencyKey.self] } + set { self[FetchBannedListDependencyKey.self] = newValue } + } +} + diff --git a/Sources/FetchBannedList/FetchBannedList.swift b/Sources/FetchBannedList/FetchBannedList.swift new file mode 100644 index 0000000000000000000000000000000000000000..9315cd589c431e3ed010d34e7c321f2dc9c5c787 --- /dev/null +++ b/Sources/FetchBannedList/FetchBannedList.swift @@ -0,0 +1,46 @@ +import Foundation +import XCTestDynamicOverlay + +public struct FetchBannedList { + public enum Error: Swift.Error, Equatable { + case network(URLError) + case invalidResponse + } + + public typealias Completion = (Result<Data, Error>) -> Void + + public var run: (@escaping Completion) -> Void + + public func callAsFunction(completion: @escaping Completion) { + run(completion) + } +} + +extension FetchBannedList { + public static let live = FetchBannedList { completion in + let url = URL(string: "https://elixxir-bins.s3.us-west-1.amazonaws.com/client/bannedUsers/banned.csv")! + let session = URLSession.shared + let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData) + let task = session.dataTask(with: request) { data, response, error in + if let error = error { + completion(.failure(.network(error as! URLError))) + return + } + guard let response = response as? HTTPURLResponse, + (200..<300).contains(response.statusCode), + let data = data + else { + completion(.failure(.invalidResponse)) + return + } + completion(.success(data)) + } + task.resume() + } +} + +extension FetchBannedList { + public static let unimplemented = FetchBannedList( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/GroupDraftFeature/GroupDraftCollectionCell.swift b/Sources/GroupDraftFeature/GroupDraftCollectionCell.swift new file mode 100644 index 0000000000000000000000000000000000000000..cdcb1393c7460df0410e352ca7ab3347d359dbaf --- /dev/null +++ b/Sources/GroupDraftFeature/GroupDraftCollectionCell.swift @@ -0,0 +1,83 @@ +import UIKit +import Shared +import Combine +import AppResources + +final class GroupDraftCollectionCell: UICollectionViewCell { + let titleLabel = UILabel() + let removeButton = UIButton() + let upperView = UIView() + let avatarView = AvatarView() + + var didTapRemove: (() -> Void)? + var cancellables = Set<AnyCancellable>() + + override init(frame: CGRect) { + super.init(frame: frame) + + titleLabel.numberOfLines = 2 + titleLabel.lineBreakMode = .byWordWrapping + titleLabel.textAlignment = .center + titleLabel.textColor = Asset.neutralDark.color + titleLabel.font = Fonts.Mulish.semiBold.font(size: 14.0) + + removeButton.layer.cornerRadius = 9 + removeButton.backgroundColor = Asset.accentDanger.color + removeButton.setImage(Asset.contactListAvatarRemove.image, for: .normal) + + upperView.addSubview(avatarView) + contentView.addSubview(titleLabel) + contentView.addSubview(upperView) + contentView.addSubview(removeButton) + + upperView.snp.makeConstraints { + $0.top.equalToSuperview() + $0.left.equalToSuperview() + $0.right.equalToSuperview() + } + + avatarView.snp.makeConstraints { + $0.width.equalTo(48) + $0.height.equalTo(48) + $0.top.equalToSuperview().offset(4) + $0.left.equalToSuperview().offset(4) + $0.right.equalToSuperview().offset(-4) + $0.bottom.equalToSuperview().offset(-4) + } + + removeButton.snp.makeConstraints { + $0.centerY.equalTo(avatarView.snp.top).offset(5) + $0.centerX.equalTo(avatarView.snp.right).offset(-5) + $0.width.equalTo(18) + $0.height.equalTo(18) + } + + titleLabel.snp.makeConstraints { + $0.top.equalTo(upperView.snp.bottom) + $0.left.equalToSuperview() + $0.right.equalToSuperview() + $0.bottom.equalToSuperview() + } + } + + required init?(coder: NSCoder) { nil } + + override func prepareForReuse() { + super.prepareForReuse() + titleLabel.text = nil + avatarView.prepareForReuse() + cancellables.removeAll() + } + + func setup(title: String, image: Data?) { + titleLabel.text = title + avatarView.setupProfile(title: title, image: image, size: .large) + cancellables.removeAll() + + removeButton + .publisher(for: .touchUpInside) + .sink { [unowned self] in + didTapRemove?() + }.store(in: &cancellables) + } +} diff --git a/Sources/ContactListFeature/Controllers/CreateGroupController.swift b/Sources/GroupDraftFeature/GroupDraftController.swift similarity index 88% rename from Sources/ContactListFeature/Controllers/CreateGroupController.swift rename to Sources/GroupDraftFeature/GroupDraftController.swift index 34539b857c33cec6bbf1ba0a074db9f228493e48..27dd858fa3abc733e288f4495d53f0aaad629741 100644 --- a/Sources/ContactListFeature/Controllers/CreateGroupController.swift +++ b/Sources/GroupDraftFeature/GroupDraftController.swift @@ -2,20 +2,21 @@ import UIKit import Shared import Combine import XXModels -import Navigation -import DI +import AppResources +import Dependencies +import AppNavigation -public final class CreateGroupController: UIViewController { - @Dependency var navigator: Navigator +public final class GroupDraftController: UIViewController { + @Dependency(\.navigator) var navigator: Navigator private lazy var titleLabel = UILabel() private lazy var createButton = UIButton() - private lazy var screenView = CreateGroupView() + private lazy var screenView = GroupDraftView() private var selectedElements = [Contact]() { didSet { screenView.tableView.reloadData() } } - private let viewModel = CreateGroupViewModel() + private let viewModel = GroupDraftViewModel() private var cancellables = Set<AnyCancellable>() private var tableDataSource: UITableViewDiffableDataSource<SectionId, Contact>! private var collectionDataSource: UICollectionViewDiffableDataSource<SectionId, Contact>! @@ -72,12 +73,12 @@ public final class CreateGroupController: UIViewController { private func setupTableAndCollection() { screenView.tableView.rowHeight = 64.0 screenView.tableView.register(AvatarCell.self) - screenView.collectionView.register(CreateGroupCollectionCell.self) + screenView.collectionView.register(GroupDraftCollectionCell.self) collectionDataSource = UICollectionViewDiffableDataSource<SectionId, Contact>( collectionView: screenView.collectionView ) { [weak viewModel] collectionView, indexPath, contact in - let cell: CreateGroupCollectionCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) + let cell: GroupDraftCollectionCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) let title = (contact.nickname ?? contact.username) ?? "" cell.setup(title: title, image: contact.photo) @@ -167,21 +168,15 @@ public final class CreateGroupController: UIViewController { .publisher(for: .touchUpInside) .receive(on: DispatchQueue.main) .sink { [unowned self] in -// coordinator.toGroupDrawer( -// with: count, -// from: self, { (name, welcome) in -// self.viewModel.create( -// name: name, -// welcome: welcome, -// members: self.selectedElements -// ) -// } -// ) + navigator.perform(PresentCreateGroup( + members: selectedElements, + from: self + )) }.store(in: &cancellables) } } -extension CreateGroupController: UITableViewDelegate { +extension GroupDraftController: UITableViewDelegate { public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let contact = tableDataSource.itemIdentifier(for: indexPath) { viewModel.didSelect(contact: contact) diff --git a/Sources/GroupDraftFeature/GroupDraftView.swift b/Sources/GroupDraftFeature/GroupDraftView.swift new file mode 100644 index 0000000000000000000000000000000000000000..972cd50602ddac171f203a01b4fef82d04ab8a37 --- /dev/null +++ b/Sources/GroupDraftFeature/GroupDraftView.swift @@ -0,0 +1,65 @@ +import UIKit +import Shared +import SnapKit +import AppResources + +final class GroupDraftView: UIView { + let stackView = UIStackView() + let tableView = UITableView() + let searchComponent = SearchComponent() + lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + + let layout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.minimumInteritemSpacing = 45 + layout.itemSize = CGSize(width: 56, height: 100) + layout.scrollDirection = .horizontal + return layout + }() + + init() { + super.init(frame: .zero) + backgroundColor = Asset.neutralWhite.color + + tableView.separatorStyle = .none + tableView.tintColor = Asset.brandPrimary.color + tableView.backgroundColor = Asset.neutralWhite.color + tableView.allowsMultipleSelectionDuringEditing = true + tableView.setEditing(true, animated: true) + + searchComponent.set( + placeholder: "Search connections", + imageAtRight: UIImage.color(.clear) + ) + + collectionView.backgroundColor = Asset.neutralWhite.color + collectionView.contentInset = UIEdgeInsets(top: 0, left: 30, bottom: 0, right: 30) + + stackView.spacing = 31 + stackView.axis = .vertical + stackView.addArrangedSubview(collectionView) + stackView.addArrangedSubview(tableView) + + addSubview(stackView) + addSubview(searchComponent) + + searchComponent.snp.makeConstraints { + $0.top.equalToSuperview().offset(20) + $0.left.equalToSuperview().offset(20) + $0.right.equalToSuperview().offset(-20) + } + + stackView.snp.makeConstraints { + $0.top.equalTo(searchComponent.snp.bottom).offset(20) + $0.left.equalToSuperview() + $0.right.equalToSuperview() + $0.bottom.equalToSuperview() + } + + collectionView.snp.makeConstraints { + $0.height.equalTo(100) + } + } + + required init?(coder: NSCoder) { nil } +} diff --git a/Sources/GroupDraftFeature/GroupDraftViewModel.swift b/Sources/GroupDraftFeature/GroupDraftViewModel.swift new file mode 100644 index 0000000000000000000000000000000000000000..476dd69ad26eaab2de5a17ed501289f830799374 --- /dev/null +++ b/Sources/GroupDraftFeature/GroupDraftViewModel.swift @@ -0,0 +1,76 @@ +import Combine +import AppCore +import XXModels +import Defaults +import Foundation +import Dependencies +import ReportingFeature + +final class GroupDraftViewModel { + @Dependency(\.app.bgQueue) var bgQueue + @Dependency(\.app.dbManager) var dbManager + @Dependency(\.app.messenger) var messenger + @Dependency(\.reportingStatus) var reportingStatus + + @KeyObject(.username, defaultValue: "") var username: String + + var myId: Data { + try! messenger.e2e.get()!.getContact().getId() + } + + var selected: AnyPublisher<[XXModels.Contact], Never> { + selectedContactsRelay.eraseToAnyPublisher() + } + + var contacts: AnyPublisher<[XXModels.Contact], Never> { + contactsRelay.eraseToAnyPublisher() + } + + var info: AnyPublisher<GroupInfo, Never> { + infoRelay.eraseToAnyPublisher() + } + + private var allContacts = [XXModels.Contact]() + private var cancellables = Set<AnyCancellable>() + private let infoRelay = PassthroughSubject<GroupInfo, Never>() + private let contactsRelay = CurrentValueSubject<[XXModels.Contact], Never>([]) + private let selectedContactsRelay = CurrentValueSubject<[XXModels.Contact], Never>([]) + + init() { + let query = Contact.Query( + authStatus: [.friend], + isBlocked: reportingStatus.isEnabled() ? false : nil, + isBanned: reportingStatus.isEnabled() ? false : nil + ) + + try! dbManager.getDB().fetchContactsPublisher(query) + .replaceError(with: []) + .map { $0.filter { $0.id != self.myId }} + .map { $0.sorted(by: { $0.username! < $1.username! })} + .sink { [unowned self] in + allContacts = $0 + contactsRelay.send($0) + }.store(in: &cancellables) + } + + func didSelect(contact: XXModels.Contact) { + if selectedContactsRelay.value.contains(contact) { + selectedContactsRelay.value.removeAll { $0.username == contact.username } + } else { + selectedContactsRelay.value.append(contact) + } + } + + func filter(_ text: String) { + guard text.isEmpty == false else { + contactsRelay.send(allContacts) + return + } + + contactsRelay.send( + allContacts.filter { + ($0.username ?? "").contains(text.lowercased()) + } + ) + } +} diff --git a/Sources/Keychain/Dependency.swift b/Sources/Keychain/Dependency.swift new file mode 100644 index 0000000000000000000000000000000000000000..02ca45a9267826025a38dc77c4826eb034113bb1 --- /dev/null +++ b/Sources/Keychain/Dependency.swift @@ -0,0 +1,13 @@ +import Dependencies + +private enum KeychainDependencyKey: DependencyKey { + static let liveValue: KeychainManager = .live + static let testValue: KeychainManager = .unimplemented +} + +extension DependencyValues { + public var keychain: KeychainManager { + get { self[KeychainDependencyKey.self] } + set { self[KeychainDependencyKey.self] = newValue } + } +} diff --git a/Sources/Keychain/DestroyKeychain.swift b/Sources/Keychain/DestroyKeychain.swift new file mode 100644 index 0000000000000000000000000000000000000000..8bb24fe094745fa5b65e2c3e9e68b6cfc2fabc08 --- /dev/null +++ b/Sources/Keychain/DestroyKeychain.swift @@ -0,0 +1,22 @@ +import KeychainAccess +import XCTestDynamicOverlay + +public struct DestroyKeychain { + public var run: () throws -> Void + + public func callAsFunction() throws -> Void { + try run() + } +} + +extension DestroyKeychain { + public static let live = DestroyKeychain { + try Keychain(service: "XXM").removeAll() + } +} + +extension DestroyKeychain { + public static let unimplemented = DestroyKeychain( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/Keychain/GetValueForKey.swift b/Sources/Keychain/GetValueForKey.swift new file mode 100644 index 0000000000000000000000000000000000000000..31fe3ca81fc53279bf5f1d0a99ca10704927839e --- /dev/null +++ b/Sources/Keychain/GetValueForKey.swift @@ -0,0 +1,22 @@ +import KeychainAccess +import XCTestDynamicOverlay + +public struct GetValueForKey { + public var run: (String) throws -> String? + + public func callAsFunction(_ key: String) throws -> String? { + try run(key) + } +} + +extension GetValueForKey { + public static let live = GetValueForKey { + try Keychain(service: "XXM").get($0) + } +} + +extension GetValueForKey { + public static let unimplemented = GetValueForKey( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/Keychain/KeychainHandler.swift b/Sources/Keychain/KeychainHandler.swift index 3ed93be94118e9ec41f2ab86d80cf177d7fe0225..6b55051142ba5937692e491e1620460469cf0b6e 100644 --- a/Sources/Keychain/KeychainHandler.swift +++ b/Sources/Keychain/KeychainHandler.swift @@ -1,51 +1,24 @@ -import Foundation -import KeychainAccess - -public enum KeychainSFTP: String { - case pwd - case host - case username +public struct KeychainManager { + public var set: SetValueForKey + public var get: GetValueForKey + public var remove: RemoveValueForKey + public var destroy: DestroyKeychain } -public protocol KeychainHandling { - func clear() throws - func getPassword() throws -> Data? - func remove(_ key: String) throws - func store(password pwd: Data) throws - - func get(key: KeychainSFTP) throws -> String? - func store(key: KeychainSFTP, value: String) throws +extension KeychainManager { + public static let live = KeychainManager( + set: .live, + get: .live, + remove: .live, + destroy: .live + ) } -public struct KeychainHandler: KeychainHandling { - private let keychain: Keychain - private let password = "password" - - public init() { - self.keychain = Keychain(service: "XXM") - } - - public func remove(_ key: String) throws { - try keychain.remove(key) - } - - public func clear() throws { - try keychain.removeAll() - } - - public func store(password pwd: Data) throws { - try keychain.set(pwd, key: password) - } - - public func getPassword() throws -> Data? { - try keychain.getData(password) - } - - public func get(key: KeychainSFTP) throws -> String? { - try keychain.get(key.rawValue) - } - - public func store(key: KeychainSFTP, value: String) throws { - try keychain.set(value, key: key.rawValue) - } +extension KeychainManager { + public static let unimplemented = KeychainManager( + set: .unimplemented, + get: .unimplemented, + remove: .unimplemented, + destroy: .unimplemented + ) } diff --git a/Sources/Keychain/MockKeychainHandler.swift b/Sources/Keychain/MockKeychainHandler.swift deleted file mode 100644 index ea2af5d6467860b5c13d79f01dbdf223862115e1..0000000000000000000000000000000000000000 --- a/Sources/Keychain/MockKeychainHandler.swift +++ /dev/null @@ -1,12 +0,0 @@ -import Foundation - -public struct MockKeychainHandler: KeychainHandling { - public init() {} - - public func clear() throws {} - public func remove(_ key: String) throws {} - public func store(password pwd: Data) throws {} - public func getPassword() throws -> Data? { Data() } - public func get(key: KeychainSFTP) throws -> String? { nil } - public func store(key: KeychainSFTP, value: String) throws {} -} diff --git a/Sources/Keychain/RemoveValueForKey.swift b/Sources/Keychain/RemoveValueForKey.swift new file mode 100644 index 0000000000000000000000000000000000000000..9868a6f6d6bed828d64881877a8d082a90d8afe1 --- /dev/null +++ b/Sources/Keychain/RemoveValueForKey.swift @@ -0,0 +1,22 @@ +import KeychainAccess +import XCTestDynamicOverlay + +public struct RemoveValueForKey { + public var run: (String) throws -> Void + + public func callAsFunction(_ key: String) throws -> Void { + try run(key) + } +} + +extension RemoveValueForKey { + public static let live = RemoveValueForKey { + try Keychain(service: "XXM").remove($0) + } +} + +extension RemoveValueForKey { + public static let unimplemented = RemoveValueForKey( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/Keychain/SetValueForKey.swift b/Sources/Keychain/SetValueForKey.swift new file mode 100644 index 0000000000000000000000000000000000000000..c0cc66ba4816f3156798705ef2bfc34275698a58 --- /dev/null +++ b/Sources/Keychain/SetValueForKey.swift @@ -0,0 +1,22 @@ +import KeychainAccess +import XCTestDynamicOverlay + +public struct SetValueForKey { + public var run: (String, String) throws -> Void + + public func callAsFunction(_ value: String, for key: String) throws -> Void { + try run(value, key) + } +} + +extension SetValueForKey { + public static let live = SetValueForKey { value, key in + try Keychain(service: "XXM").set(value, key: key) + } +} + +extension SetValueForKey { + public static let unimplemented = SetValueForKey( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/LaunchFeature/BackendVersionInformation.swift b/Sources/LaunchFeature/BackendVersionInformation.swift deleted file mode 100644 index bb52e089d69a5bdc73672d70b936d0afe941c341..0000000000000000000000000000000000000000 --- a/Sources/LaunchFeature/BackendVersionInformation.swift +++ /dev/null @@ -1,7 +0,0 @@ -struct BackendVersionInformation: Codable { - var info: DappVersionInformation - - private enum CodingKeys: String, CodingKey { - case info = "dapp-id" - } -} diff --git a/Sources/LaunchFeature/DappVersionInformation.swift b/Sources/LaunchFeature/DappVersionInformation.swift deleted file mode 100644 index c6d49f32bf3f211ea669cf003a9cc663c86adc76..0000000000000000000000000000000000000000 --- a/Sources/LaunchFeature/DappVersionInformation.swift +++ /dev/null @@ -1,14 +0,0 @@ -public struct DappVersionInformation: Codable { - public var appUrl: String - public var minimum: String - public var isRequired: Bool? - public var recommended: String - public var minimumMessage: String - - private enum CodingKeys: String, CodingKey { - case appUrl = "new_ios_app_url" - case minimum = "new_ios_min_version" - case recommended = "new_ios_recommended_version" - case minimumMessage = "new_minimum_popup_msg" - } -} diff --git a/Sources/LaunchFeature/LaunchController.swift b/Sources/LaunchFeature/LaunchController.swift index dbc4067bbfbdfeb388cf52a84b05eff5c24ff01e..9371455d9dc07804847e6642d731cccb3d452ec8 100644 --- a/Sources/LaunchFeature/LaunchController.swift +++ b/Sources/LaunchFeature/LaunchController.swift @@ -1,51 +1,24 @@ import UIKit import Shared import Combine -import Navigation import PushFeature -import DrawerFeature +import Dependencies import AppResources -import ComposableArchitecture +import DrawerFeature +import AppNavigation public final class LaunchController: UIViewController { @Dependency(\.navigator) var navigator: Navigator private lazy var screenView = LaunchView() - private let viewModel = LaunchViewModel() - public var pendingPushRoute: PushRouter.Route? private var cancellables = Set<AnyCancellable>() private var drawerCancellables = Set<AnyCancellable>() - public override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - viewModel.viewDidAppear() - } - public override func loadView() { view = screenView } - public override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - navigationController?.navigationBar.customize(translucent: true) - } - - public override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - let gradient = CAGradientLayer() - gradient.colors = [ - UIColor(red: 122/255, green: 235/255, blue: 239/255, alpha: 1).cgColor, - UIColor(red: 56/255, green: 204/255, blue: 232/255, alpha: 1).cgColor, - UIColor(red: 63/255, green: 186/255, blue: 253/255, alpha: 1).cgColor, - UIColor(red: 98/255, green: 163/255, blue: 255/255, alpha: 1).cgColor - ] - gradient.frame = screenView.bounds - gradient.startPoint = CGPoint(x: 1, y: 0) - gradient.endPoint = CGPoint(x: 0, y: 1) - screenView.layer.insertSublayer(gradient, at: 0) - } - public override func viewDidLoad() { super.viewDidLoad() @@ -58,10 +31,10 @@ public final class LaunchController: UIViewController { navigator.perform(PresentTermsAndConditions(replacing: true, on: navigationController!)) return } - if let route = pendingPushRoute { - hasPendingPushRoute(route) - return - } +// if let route = pendingPushRoute { +// hasPendingPushRoute(route) +// return +// } navigator.perform(PresentChatList(on: navigationController!)) return } @@ -73,6 +46,8 @@ public final class LaunchController: UIViewController { offerUpdate(model: update) } }.store(in: &cancellables) + + viewModel.startLaunch() } private func hasPendingPushRoute(_ route: PushRouter.Route) { diff --git a/Sources/LaunchFeature/LaunchView.swift b/Sources/LaunchFeature/LaunchView.swift index b1c3982f558dc0b79c2e369c68f1e5b79f442723..40f6fb44b8b23520c062774adca2df350b98a409 100644 --- a/Sources/LaunchFeature/LaunchView.swift +++ b/Sources/LaunchFeature/LaunchView.swift @@ -1,16 +1,28 @@ import UIKit -import Shared import AppResources final class LaunchView: UIView { let imageView = UIImageView() + let gradientLayer = CAGradientLayer() init() { super.init(frame: .zero) + + gradientLayer.colors = [ + UIColor(red: 122/255, green: 235/255, blue: 239/255, alpha: 1).cgColor, + UIColor(red: 56/255, green: 204/255, blue: 232/255, alpha: 1).cgColor, + UIColor(red: 63/255, green: 186/255, blue: 253/255, alpha: 1).cgColor, + UIColor(red: 98/255, green: 163/255, blue: 255/255, alpha: 1).cgColor + ] + gradientLayer.startPoint = CGPoint(x: 1, y: 0) + gradientLayer.endPoint = CGPoint(x: 0, y: 1) + layer.insertSublayer(gradientLayer, at: 0) imageView.image = Asset.splash.image imageView.contentMode = .scaleAspectFit backgroundColor = Asset.neutralWhite.color + addSubview(imageView) + imageView.snp.makeConstraints { $0.center.equalToSuperview() $0.left.equalToSuperview().offset(100) @@ -18,4 +30,8 @@ final class LaunchView: UIView { } required init?(coder: NSCoder) { nil } + + override func layoutSubviews() { + gradientLayer.frame = bounds + } } diff --git a/Sources/LaunchFeature/LaunchViewModel+Banned.swift b/Sources/LaunchFeature/LaunchViewModel+Banned.swift deleted file mode 100644 index 1449fb675e7e190cc257785b55f43517f39ac518..0000000000000000000000000000000000000000 --- a/Sources/LaunchFeature/LaunchViewModel+Banned.swift +++ /dev/null @@ -1,60 +0,0 @@ -import Shared -import XXModels -import Foundation - -extension LaunchViewModel { - func updateBannedList(completion: @escaping () -> Void) { - fetchBannedList { result in - switch result { - case .failure(_): - DispatchQueue.main.asyncAfter(deadline: .now() + 2) { - self.updateBannedList(completion: completion) - } - case .success(let data): - self.processBannedList(data, completion: completion) - } - } - } - - func processBannedList(_ data: Data, completion: @escaping () -> Void) { - processBannedList( - data: data, - forEach: { result in - switch result { - case .success(let userId): - let query = Contact.Query(id: [userId]) - if var contact = try! database.fetchContacts(query).first { - if contact.isBanned == false { - contact.isBanned = true - try! database.saveContact(contact) - self.enqueueBanWarning(contact: contact) - } - } else { - try! database.saveContact(.init(id: userId, isBanned: true)) - } - - case .failure(_): - break - } - }, - completion: { result in - switch result { - case .failure(_): - DispatchQueue.main.asyncAfter(deadline: .now() + 2) { - self.updateBannedList(completion: completion) - } - case .success(_): - completion() - } - } - ) - } - - func enqueueBanWarning(contact: XXModels.Contact) { - let name = (contact.nickname ?? contact.username) ?? "One of your contacts" - toastController.enqueueToast(model: .init( - title: "\(name) has been banned for offensive content.", - leftImage: Asset.requestSentToaster.image - )) - } -} diff --git a/Sources/LaunchFeature/LaunchViewModel+Database.swift b/Sources/LaunchFeature/LaunchViewModel+Database.swift deleted file mode 100644 index 15ae6c42c88abf3f095c05d666720761707ffd22..0000000000000000000000000000000000000000 --- a/Sources/LaunchFeature/LaunchViewModel+Database.swift +++ /dev/null @@ -1,74 +0,0 @@ -import XXModels -import Foundation -import XXDatabase -import XXLegacyDatabaseMigrator - -extension LaunchViewModel { - func setupDatabase() throws { - let legacyOldPath = NSSearchPathForDirectoriesInDomains( - .documentDirectory, .userDomainMask, true - )[0].appending("/xxmessenger.sqlite") - - let legacyPath = FileManager.default - .containerURL(forSecurityApplicationGroupIdentifier: "group.elixxir.messenger")! - .appendingPathComponent("database") - .appendingPathExtension("sqlite").path - - let dbExistsInLegacyOldPath = FileManager.default.fileExists(atPath: legacyOldPath) - let dbExistsInLegacyPath = FileManager.default.fileExists(atPath: legacyPath) - - if dbExistsInLegacyOldPath && !dbExistsInLegacyPath { - try? FileManager.default.moveItem(atPath: legacyOldPath, toPath: legacyPath) - } - - let dbPath = FileManager.default - .containerURL(forSecurityApplicationGroupIdentifier: "group.elixxir.messenger")! - .appendingPathComponent("xxm_database") - .appendingPathExtension("sqlite").path - - let database = try Database.onDisk(path: dbPath) - - if dbExistsInLegacyPath { - try Migrator.live()( - try .init(path: legacyPath), - to: database, - myContactId: Data(), //client.bindings.myId, - meMarshaled: Data() //client.bindings.meMarshalled - ) - - try FileManager.default.moveItem(atPath: legacyPath, toPath: legacyPath.appending("-backup")) - } - - DI.Container.shared.register(database) - - _ = try? database.bulkUpdateContacts(.init(authStatus: [.requesting]), .init(authStatus: .requestFailed)) - _ = try? database.bulkUpdateContacts(.init(authStatus: [.confirming]), .init(authStatus: .confirmationFailed)) - _ = try? database.bulkUpdateContacts(.init(authStatus: [.verificationInProgress]), .init(authStatus: .verificationFailed)) - } - - func getContactWith(userId: Data) -> XXModels.Contact? { - let query = Contact.Query( - id: [userId], - isBlocked: reportingStatus.isEnabled() ? false : nil, - isBanned: reportingStatus.isEnabled() ? false : nil - ) - - guard let database: Database = try? DI.Container.shared.resolve(), - let contact = try? database.fetchContacts(query).first else { - return nil - } - - return contact - } - - func getGroupInfoWith(groupId: Data) -> GroupInfo? { - let query = GroupInfo.Query(groupId: groupId) - - guard let database: Database = try? DI.Container.shared.resolve(), - let info = try? database.fetchGroupInfos(query).first else { - return nil - } - - return info - } -} diff --git a/Sources/LaunchFeature/LaunchViewModel+Errors.swift b/Sources/LaunchFeature/LaunchViewModel+Errors.swift deleted file mode 100644 index 399667657243523475a2857e9838451a0198d587..0000000000000000000000000000000000000000 --- a/Sources/LaunchFeature/LaunchViewModel+Errors.swift +++ /dev/null @@ -1,42 +0,0 @@ -import XXClient -import Foundation - -extension LaunchViewModel { - func updateErrors( - completion: @escaping (Result<Void, Error>) -> Void - ) { - let url = "https://git.xx.network/elixxir/client-error-database/-/raw/main/clientErrors.json" - downloadErrors(from: url) { - switch $0 { - case .success(let string): - do { - try UpdateCommonErrors.live(jsonFile: string) - completion(.success(())) - } catch { - completion(.failure(error)) - } - case .failure(let error): - completion(.failure(error)) - } - } - } - - func downloadErrors( - from urlString: String, - completion: @escaping (Result<String, Error>) -> Void - ) { - URLSession.shared.dataTask(with: URL(string: urlString)!) { data, _, error in - if let error { - completion(.failure(error)) - return - } - guard let data else { - fatalError("No errors or data when downloading \(urlString)") - } - guard let string = String(data: data, encoding: .utf8) else { - fatalError("Impossible to decode error json") - } - completion(.success(string)) - }.resume() - } -} diff --git a/Sources/LaunchFeature/LaunchViewModel+Messenger.swift b/Sources/LaunchFeature/LaunchViewModel+Messenger.swift deleted file mode 100644 index abf90591ceb280b045fb00babcbc65ee111337a9..0000000000000000000000000000000000000000 --- a/Sources/LaunchFeature/LaunchViewModel+Messenger.swift +++ /dev/null @@ -1,431 +0,0 @@ -import Shared -import XXClient -import XXModels -import Foundation -import XXMessengerClient - -extension LaunchViewModel { - func setupBackupCallback() { - backupCallbackCancellable = messenger.registerBackupCallback(.init(handle: { [weak self] in - print(">>> Backup callback from bindings got called") - self?.backupService.updateLocalBackup($0) - })) - } - - func setupMessageCallback() { - messageListenerCallbacksCancellable = messenger.registerMessageListener(.init(handle: { - guard let payload = try? Payload(with: $0.payload) else { - fatalError("Couldn't decode payload: \(String(data: $0.payload, encoding: .utf8) ?? "nil")") - } - - try! self.database.saveMessage(.init( - networkId: $0.id, - senderId: $0.sender, - recipientId: self.messenger.e2e.get()!.getContact().getId(), - groupId: nil, - date: Date.fromTimestamp($0.timestamp), - status: .received, - isUnread: true, - text: payload.text, - replyMessageId: payload.reply?.messageId, - roundURL: $0.roundURL, - fileTransferId: nil - )) - - if var contact = try? self.database.fetchContacts(.init(id: [$0.sender])).first { - contact.isRecent = false - try! self.database.saveContact(contact) - } - })) - } - - func setupAuthCallback() { - authCallbacksCancellable = messenger.registerAuthCallbacks( - AuthCallbacks(handle: { - switch $0 { - case .confirm(contact: let contact, receptionId: _, ephemeralId: _, roundId: _): - self.handleConfirm(from: contact) - case .request(contact: let contact, receptionId: _, ephemeralId: _, roundId: _): - self.handleDirectRequest(from: contact) - case .reset(contact: let contact, receptionId: _, ephemeralId: _, roundId: _): - self.handleReset(from: contact) - } - }) - ) - } - - func handleReset(from user: XXClient.Contact) { - if var contact = try? database.fetchContacts(.init(id: [user.getId()])).first { - contact.authStatus = .friend - _ = try? database.saveContact(contact) - } - } - - func handleConfirm(from contact: XXClient.Contact) { - guard let id = try? contact.getId() else { - fatalError("Couldn't extract ID from contact confirmation arrived.") - } - - guard var existentContact = try? database.fetchContacts(.init(id: [id])).first else { - print(">>> Tried to handle a confirmation from someone that is not a contact yet") - return - } - - existentContact.isRecent = true - existentContact.authStatus = .friend - try! database.saveContact(existentContact) - } - - func setupLogWriter() { - _ = try! SetLogLevel.live(.error) - RegisterLogWriter.live(.init(handle: { - XXLogger.live().debug($0) - })) - } - - func listenToNetworkUpdates() { - networkMonitor.start() - networkCallbacksCancellable = messenger.cMix.get()!.addHealthCallback(.init(handle: { [weak self] in - guard let self else { return } - self.networkMonitor.update($0) - })) - } - - func handleDirectRequest(from contact: XXClient.Contact) { - guard let id = try? contact.getId() else { - fatalError("Couldn't extract ID from contact request arrived.") - } - - if let _ = try? database.fetchContacts(.init(id: [id])).first { - print(">>> Tried to handle request from pre-existing contact.") - return - } - - let facts = try? contact.getFacts() - let email = facts?.first(where: { $0.type == .email })?.value - let phone = facts?.first(where: { $0.type == .phone })?.value - let username = facts?.first(where: { $0.type == .username })?.value - - var model = try! database.saveContact(.init( - id: id, - marshaled: contact.data, - username: username, - email: email, - phone: phone, - nickname: nil, - photo: nil, - authStatus: .verificationInProgress, - isRecent: true, - createdAt: Date() - )) - - do { - let messenger: Messenger = try DI.Container.shared.resolve() - try messenger.waitForNetwork() - - if try messenger.verifyContact(contact) { - print(">>> [messenger.verifyContact \(#file):\(#line)]") - - model.authStatus = .verified - model = try database.saveContact(model) - } else { - print(">>> [messenger.verifyContact \(#file):\(#line)]") - try database.deleteContact(model) - } - } catch { - print(">>> [messenger.verifyContact] thrown an exception: \(error.localizedDescription)") - - model.authStatus = .verificationFailed - model = try! database.saveContact(model) - } - } - - func handleGroupRequest(from group: XXClient.Group, messenger: Messenger) { - if let _ = try? database.fetchGroups(.init(id: [group.getId()])).first { - print(">>> Tried to handle a group request that is already handled") - return - } - - guard var members = try? group.getMembership(), let leader = members.first else { - fatalError("Failed to get group membership/leader") - } - - try! database.saveGroup(.init( - id: group.getId(), - name: String(data: group.getName(), encoding: .utf8)!, - leaderId: leader.id, - createdAt: Date.fromMSTimestamp(group.getCreatedMS()), - authStatus: .pending, - serialized: group.serialize() - )) - - if let initMessageData = group.getInitMessage(), - let initMessage = String(data: initMessageData, encoding: .utf8) { - try! database.saveMessage(.init( - senderId: leader.id, - recipientId: nil, - groupId: group.getId(), - date: Date.fromMSTimestamp(group.getCreatedMS()), - status: .received, - isUnread: true, - text: initMessage - )) - } - - print(">>> All members in the arrived group request:") - members.forEach { print(">>> \($0.id.base64EncodedString().prefix(10))...") } - print(">>> My ud.id is: \(try! messenger.ud.get()!.getContact().getId().base64EncodedString().prefix(10))...") - print(">>> My e2e.id is: \(try! messenger.e2e.get()!.getContact().getId().base64EncodedString().prefix(10))...") - - let friends = try! database.fetchContacts(.init( - id: Set(members.map(\.id)), - authStatus: [ - .friend, - .hidden, - .requesting, - .confirming, - .verificationInProgress, - .verified, - .requested, - .requestFailed, - .verificationFailed, - .confirmationFailed - ] - )) - - print(">>> These people I already know:") - friends.forEach { - print(">>> Username: \($0.username), authStatus: \($0.authStatus.rawValue), id: \($0.id.base64EncodedString().prefix(10))...") - } - - let strangers = Set(members.map(\.id)).subtracting(Set(friends.map(\.id))) - - strangers.forEach { - if let stranger = try? database.fetchContacts(.init(id: [$0])).first { - print(">>> This is a stranger, but I already knew about his/her existance: \(stranger.id.base64EncodedString().prefix(10))...") - } else { - print(">>> This is a complete stranger. Storing on the db: \($0.base64EncodedString().prefix(10))...") - - try! database.saveContact(.init( - id: $0, - marshaled: nil, - username: "Fetching...", - email: nil, - phone: nil, - nickname: nil, - photo: nil, - authStatus: .stranger, - isRecent: false, - isBlocked: false, - isBanned: false, - createdAt: Date.fromMSTimestamp(group.getCreatedMS()) - )) - } - } - - members.forEach { - let model = XXModels.GroupMember(groupId: group.getId(), contactId: $0.id) - _ = try? database.saveGroupMember(model) - } - - print(">>> Performing a multi-lookup for group strangers:") - - do { - let multiLookup = try messenger.lookupContacts(ids: strangers.map { $0 }) - - for user in multiLookup.contacts { - print(">>> Found stranger w/ id: \(try! user.getId().base64EncodedString().prefix(10))...") - - if var foo = try? self.database.fetchContacts(.init(id: [user.getId()])).first, - let username = try? user.getFact(.username)?.value { - foo.username = username - print(">>> Set username: \(username) for \(try! user.getId().base64EncodedString().prefix(10))...") - _ = try? self.database.saveContact(foo) - } - } - - for error in multiLookup.errors { - print(">>> Failure on Multilookup: \(error.localizedDescription)") - } - - for failedId in multiLookup.failedIds { - print(">>> Failed id: \(failedId.base64EncodedString().prefix(10))...") - } - } catch { - print(">>> Exception on multilookup: \(error.localizedDescription)") - } - } - - func generateGroupManager() throws { - let manager = try NewGroupChat.live( - e2eId: messenger.e2e()!.getId(), - groupRequest: .init(handle: { [weak self] group in - guard let self else { return } - self.handleGroupRequest(from: group, messenger: self.messenger) - }), - groupChatProcessor: .init(handle: { result in - switch result { - case .success(let cb): - - print("Incoming GroupMessage:") - print("- groupId: \(cb.decryptedMessage.groupId.base64EncodedString().prefix(10))...") - print("- senderId: \(cb.decryptedMessage.senderId.base64EncodedString().prefix(10))...") - print("- messageId: \(cb.decryptedMessage.messageId.base64EncodedString().prefix(10))...") - - if let payload = try? Payload(with: cb.decryptedMessage.payload) { - print("- payload.text: \(payload.text)") - - if let reply = payload.reply { - print("- payload.reply.senderId: \(reply.senderId.base64EncodedString().prefix(10))...") - print("- payload.reply.messageId: \(reply.messageId.base64EncodedString().prefix(10))...") - } else { - print("- payload.reply: ∅") - } - } - print("") - - guard let payload = try? Payload(with: cb.decryptedMessage.payload) else { - fatalError("Couldn't decode payload: \(String(data: cb.decryptedMessage.payload, encoding: .utf8) ?? "nil")") - } - - let msg = Message( - networkId: cb.decryptedMessage.messageId, - senderId: cb.decryptedMessage.senderId, - recipientId: nil, - groupId: cb.decryptedMessage.groupId, - date: Date.fromTimestamp(Int(cb.decryptedMessage.timestamp)), - status: .received, - isUnread: true, - text: payload.text, - replyMessageId: payload.reply?.messageId, - roundURL: "https://google.com.br", - fileTransferId: nil - ) - - _ = try? self.database.saveMessage(msg) - - case .failure(let error): - break - } - }) - ) - - DI.Container.shared.register(manager) - } - - func generateTrafficManager() throws { - let manager = try NewDummyTrafficManager.live( - cMixId: messenger.e2e()!.getId() - ) - - DI.Container.shared.register(manager) - try! manager.setStatus(dummyTrafficOn) - } - - func setupMessenger() throws { - setupLogWriter() - setupAuthCallback() - setupBackupCallback() - setupMessageCallback() - - if messenger.isLoaded() == false { - if messenger.isCreated() == false { - try messenger.create() - } - - try messenger.load() - } - - try messenger.start() - - if messenger.isConnected() == false { - try messenger.connect() - try messenger.listenForMessages() - } - - try generateGroupManager() - try generateTrafficManager() - listenToNetworkUpdates() - - if messenger.isLoggedIn() == false { - if try messenger.isRegistered() { - try messenger.logIn() - hudController.dismiss() - stateSubject.value.shouldPushChats = true - } else { - try? sftpManager.unlink() - try? dropboxManager.unlink() - hudController.dismiss() - stateSubject.value.shouldPushOnboarding = true - } - } else { - hudController.dismiss() - stateSubject.value.shouldPushChats = true - } - if !messenger.isBackupRunning() { - try? messenger.resumeBackup() - } - - try messenger.trackServices { - print(">>> Error on track services callback: \($0.localizedDescription)") - } - - // TODO: Biometric auth - } -} - - -//func handleIncomingTransfer(_ receivedFile: ReceivedFile) { -// if var model = try? database.saveFileTransfer(.init( -// id: receivedFile.transferId, -// contactId: receivedFile.senderId, -// name: receivedFile.name, -// type: receivedFile.type, -// data: nil, -// progress: 0.0, -// isIncoming: true, -// createdAt: Date() -// )) { -// try! database.saveMessage(.init( -// networkId: nil, -// senderId: receivedFile.senderId, -// recipientId: messenger.e2e.get()!.getContact().getId(), -// groupId: nil, -// date: Date(), -// status: .receiving, -// isUnread: false, -// text: "", -// replyMessageId: nil, -// roundURL: nil, -// fileTransferId: model.id -// )) -// -// if let manager: XXClient.FileTransfer = try? DI.Container.shared.resolve() { -// print(">>> registerReceivedProgressCallback") -// -// try! manager.registerReceivedProgressCallback( -// transferId: receivedFile.transferId, -// period: 1_000, -// callback: .init(handle: { [weak self] in -// guard let self else { return } -// switch $0 { -// case .success(let cb): -// if cb.progress.completed { -// model.progress = 100 -// model.data = try! manager.receive(transferId: receivedFile.transferId) -// } else { -// model.progress = Float(cb.progress.transmitted/cb.progress.total) -// } -// -// model = try! self.database.saveFileTransfer(model) -// -// case .failure(let error): -// print(error.localizedDescription) -// } -// }) -// ) -// } else { -// //print(DI.Container.shared.dependencies) -// } -// } -//} diff --git a/Sources/LaunchFeature/LaunchViewModel.swift b/Sources/LaunchFeature/LaunchViewModel.swift index 3bd27c5e7669f6f9bf98e6c11ab25cddfb870b85..82798860455c662ca9e773e4dc2ea641852a3a08 100644 --- a/Sources/LaunchFeature/LaunchViewModel.swift +++ b/Sources/LaunchFeature/LaunchViewModel.swift @@ -5,15 +5,25 @@ import XXModels import Keychain import XXClient import CloudFiles -import Foundation -import Permissions +import CheckVersion +import AppResources import BackupFeature -import VersionChecking import ReportingFeature -import CombineSchedulers import CloudFilesDropbox import XXMessengerClient +import UpdateErrors +import FetchBannedList +import ProcessBannedList + +import AppCore +import Foundation +import PermissionsFeature +import ComposableArchitecture + +import XXDatabase +import XXLegacyDatabaseMigrator + import class XXClient.Cancellable final class LaunchViewModel { @@ -32,37 +42,43 @@ final class LaunchViewModel { var shouldPushOnboarding = false } - @Dependency var database: Database - @Dependency var messenger: Messenger - @Dependency var versionCheck: VersionCheck - @Dependency var hudController: HUDController - @Dependency var backupService: BackupService - @Dependency var fetchBannedList: FetchBannedList - @Dependency var reportingStatus: ReportingStatus - @Dependency var toastController: ToastController - @Dependency var keychainHandler: KeychainHandling - @Dependency var networkMonitor: NetworkMonitoring - @Dependency var processBannedList: ProcessBannedList - @Dependency var permissionHandler: PermissionHandling + @Dependency(\.app.bgQueue) var bgQueue + @Dependency(\.permissions) var permissions + @Dependency(\.app.messenger) var messenger + @Dependency(\.app.dbManager) var dbManager + @Dependency(\.keychain) var keychainManager + @Dependency(\.updateErrors) var updateErrors + @Dependency(\.groupManager) var groupManager + @Dependency(\.app.hudManager) var hudManager + @Dependency(\.checkVersion) var checkVersion + @Dependency(\.dummyTraffic) var dummyTraffic + @Dependency(\.backupService) var backupService + @Dependency(\.app.toastManager) var toastManager + @Dependency(\.fetchBannedList) var fetchBannedList + @Dependency(\.reportingStatus) var reportingStatus + @Dependency(\.app.networkMonitor) var networkMonitor + @Dependency(\.processBannedList) var processBannedList + + @Dependency(\.app.authHandler) var authHandler + @Dependency(\.app.backupHandler) var backupHandler + @Dependency(\.app.messageListener) var messageListener + @Dependency(\.app.receiveFileHandler) var receiveFileHandler + + var authHandlerCancellable: Cancellable? + var backupHandlerCancellable: Cancellable? + var networkHandlerCancellable: Cancellable? + var receiveFileHandlerCancellable: Cancellable? + var messageListenerHandlerCancellable: Cancellable? @KeyObject(.username, defaultValue: nil) var username: String? @KeyObject(.biometrics, defaultValue: false) var isBiometricsOn: Bool @KeyObject(.acceptedTerms, defaultValue: false) var didAcceptTerms: Bool @KeyObject(.dummyTrafficOn, defaultValue: false) var dummyTrafficOn: Bool - var authCallbacksCancellable: Cancellable? - var backupCallbackCancellable: Cancellable? - var networkCallbacksCancellable: Cancellable? - var messageListenerCallbacksCancellable: Cancellable? - var statePublisher: AnyPublisher<ViewState, Never> { stateSubject.eraseToAnyPublisher() } - private var scheduler: AnySchedulerOf<DispatchQueue> = { - DispatchQueue.global().eraseToAnyScheduler() - }() - let dropboxManager = CloudFilesManager.dropbox( appKey: "ppx0de5f16p9aq2", path: "/backup/backup.xxm" @@ -77,92 +93,209 @@ final class LaunchViewModel { let stateSubject = CurrentValueSubject <ViewState, Never>(.init()) - func viewDidAppear() { - scheduler.schedule(after: .init(.now() + 1)) { [weak self] in - guard let self else { return } - self.startLaunch() - } - } - - private func startLaunch() { + func startLaunch() { if !didAcceptTerms { stateSubject.value.shouldShowTerms = true } - hudController.show() - versionCheck.verify { [weak self] in - guard let self else { return } + hudManager.show() + checkVersion { switch $0 { - case .upToDate: - self.didVerifyVersion() + case .success(let result): + switch result { + case .updated: + self.didVerifyVersion() + case .outdated(let appUrl): + self.hudManager.hide() + + self.stateSubject.value.shouldOfferUpdate = .init( + content: Localized.Launch.Version.Recommended.title, + urlString: appUrl, + positiveActionTitle: Localized.Launch.Version.Recommended.positive, + negativeActionTitle: Localized.Launch.Version.Recommended.negative, + actionStyle: .simplestColoredRed + ) + case .wayTooOld(let appUrl, let minimumVersionMessage): + self.hudManager.hide() + + self.stateSubject.value.shouldOfferUpdate = .init( + content: minimumVersionMessage, + urlString: appUrl, + positiveActionTitle: Localized.Launch.Version.Required.positive, + negativeActionTitle: nil, + actionStyle: .brandColored + ) + } case .failure(let error): - self.hudController.show(.init( + self.hudManager.show(.init( title: Localized.Launch.Version.failed, content: error.localizedDescription )) - case .outdated(let info): - self.hudController.dismiss() - let isRequired = info.isRequired ?? false - - let content = isRequired ? - info.minimumMessage : - Localized.Launch.Version.Recommended.title - - let positiveActionTitle = isRequired ? - Localized.Launch.Version.Required.positive : - Localized.Launch.Version.Recommended.positive - - self.stateSubject.value.shouldOfferUpdate = .init( - content: content, - urlString: info.appUrl, - positiveActionTitle: positiveActionTitle, - negativeActionTitle: isRequired ? nil : Localized.Launch.Version.Recommended.negative, - actionStyle: isRequired ? .brandColored : .simplestColoredRed - ) } } } func didRefuseUpdating() { - hudController.show() + hudManager.show() didVerifyVersion() } private func didVerifyVersion() { - updateBannedList { [weak self] in - guard let self else { return } + updateBannedList { self.updateErrors { switch $0 { case .success: - self.didFinishAsyncWork() + do { + if !self.dbManager.hasDB() { + try self.dbManager.makeDB() + } + try self.setupMessenger() + } catch { + let xxError = CreateUserFriendlyErrorMessage.live(error.localizedDescription) + self.hudManager.show(.init(content: xxError)) + } case .failure(let error): - self.hudController.show(.init(error: error)) + self.hudManager.show(.init(error: error)) } } } } +} + +extension LaunchViewModel { + func setupMessenger() throws { + authHandlerCancellable = authHandler { + print("\($0.localizedDescription)") + } + backupHandlerCancellable = backupHandler { + print("\($0.localizedDescription)") + } + receiveFileHandlerCancellable = receiveFileHandler { + print("\($0.localizedDescription)") + } + messageListenerHandlerCancellable = messageListener { + print("\($0.localizedDescription)") + } + + if messenger.isLoaded() == false { + if messenger.isCreated() == false { + try messenger.create() + } + try messenger.load() + } + try messenger.start() + if messenger.isConnected() == false { + try messenger.connect() + try messenger.listenForMessages() + } + + let dummyTrafficManager = try NewDummyTrafficManager.live( + cMixId: messenger.e2e()!.getId() + ) + dummyTraffic.set(dummyTrafficManager) - private func didFinishAsyncWork() { - do { - try setupDatabase() - try setupMessenger() - } catch { - let xxError = CreateUserFriendlyErrorMessage.live(error.localizedDescription) - hudController.show(.init(content: xxError)) + try dummyTrafficManager.setStatus(dummyTrafficOn) + + if messenger.isLoggedIn() == false { + if try messenger.isRegistered() { + try messenger.logIn() + hudManager.hide() + stateSubject.value.shouldPushChats = true + } else { + try? sftpManager.unlink() + try? dropboxManager.unlink() + hudManager.hide() + stateSubject.value.shouldPushOnboarding = true + } + } else { + hudManager.hide() + stateSubject.value.shouldPushChats = true + } + if !messenger.isBackupRunning() { + try? messenger.resumeBackup() + } + + try generateGroupManager() + + try messenger.trackServices { + print("\($0.localizedDescription)") } + + try messenger.startFileTransfer() + + networkMonitor.start() + networkHandlerCancellable = messenger.cMix.get()!.addHealthCallback( + HealthCallback { + self.networkMonitor.update($0) + } + ) } +} - private func checkBiometrics(completion: @escaping (Result<Bool, Error>) -> Void) { - if permissionHandler.isBiometricsAvailable && isBiometricsOn { - permissionHandler.requestBiometrics { - switch $0 { - case .success(let granted): - completion(.success(granted)) - case .failure(let error): - completion(.failure(error)) +extension LaunchViewModel { + func updateBannedList(completion: @escaping () -> Void) { + fetchBannedList { result in + switch result { + case .failure(_): + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + self.updateBannedList(completion: completion) } + case .success(let data): + self.processBannedList(data, completion: completion) } - } else { - completion(.success(true)) } } + + func processBannedList(_ data: Data, completion: @escaping () -> Void) { + processBannedList( + data: data, + forEach: { result in + switch result { + case .success(let userId): + let query = Contact.Query(id: [userId]) + if var contact = try! dbManager.getDB().fetchContacts(query).first { + if contact.isBanned == false { + contact.isBanned = true + try! dbManager.getDB().saveContact(contact) + enqueueBanWarning(contact: contact) + } + } else { + try! dbManager.getDB().saveContact(.init(id: userId, isBanned: true)) + } + + case .failure(_): + break + } + }, + completion: { result in + switch result { + case .failure(_): + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + self.updateBannedList(completion: completion) + } + case .success(_): + completion() + } + } + ) + } + + func enqueueBanWarning(contact: XXModels.Contact) { + let name = (contact.nickname ?? contact.username) ?? "One of your contacts" + toastManager.enqueue(.init( + title: "\(name) has been banned for offensive content.", + leftImage: Asset.requestSentToaster.image + )) + } + + func getContactWith(userId: Data) -> XXModels.Contact? { + try? dbManager.getDB().fetchContacts(.init( + id: [userId], + isBlocked: reportingStatus.isEnabled() ? false : nil, + isBanned: reportingStatus.isEnabled() ? false : nil + )).first + } + + func getGroupInfoWith(groupId: Data) -> GroupInfo? { + try? dbManager.getDB().fetchGroupInfos(.init(groupId: groupId)).first + } } diff --git a/Sources/LaunchFeature/VersionChecking.swift b/Sources/LaunchFeature/VersionChecking.swift deleted file mode 100644 index 5dce368728baf174cf24c92b88189cc4b2b7ee0f..0000000000000000000000000000000000000000 --- a/Sources/LaunchFeature/VersionChecking.swift +++ /dev/null @@ -1,49 +0,0 @@ -import Combine -import Foundation - -public typealias VersionCompletion = (VersionCheck.Requirement) -> Void - -public struct VersionCheck { - public enum Requirement { - case upToDate - case failure(Error) - case outdated(DappVersionInformation) - } - - public var verify: (@escaping VersionCompletion) -> Void -} - -public extension VersionCheck { - static let unimplemented: Self = .init(verify: { _ in fatalError() }) - - static let mock: Self = .init { $0(.upToDate) } - - static let live: Self = .init { completion in - let request = URLRequest( - url: URL(string: "https://elixxir-bins.s3-us-west-1.amazonaws.com/client/dapps/appdb.json")!, - cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, - timeoutInterval: 5 - ) - URLSession.shared.dataTask(with: request) { data, _, error in - if let error { - completion(.failure(error)) - return - } - guard let data else { - fatalError("No data for version checking") - } - guard var model = try? JSONDecoder().decode(BackendVersionInformation.self, from: data) else { - fatalError() - } - let bundleVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String - if bundleVersion >= model.info.recommended { - completion(.upToDate) - } else { - if bundleVersion < model.info.minimum { - model.info.isRequired = true - } - completion(.outdated(model.info)) - } - }.resume() - } -} diff --git a/Sources/LaunchFeature/ViewModel+GroupManager.swift b/Sources/LaunchFeature/ViewModel+GroupManager.swift new file mode 100644 index 0000000000000000000000000000000000000000..c0bef6c8933501f14705df1a1022aaed2ca643e8 --- /dev/null +++ b/Sources/LaunchFeature/ViewModel+GroupManager.swift @@ -0,0 +1,192 @@ +import Shared +import AppCore +import XXModels +import XXClient +import Foundation +import XXMessengerClient + +extension LaunchViewModel { + func generateGroupManager() throws { + groupManager.set(try NewGroupChat.live( + e2eId: messenger.e2e()!.getId(), + groupRequest: .init(handle: { [weak self] group in + guard let self else { return } + self.handleGroupRequest(from: group, messenger: self.messenger) + }), + groupChatProcessor: .init(handle: { [weak self] result in + guard let self else { return } + + switch result { + case .success(let cb): + do { + let payload = try MessagePayload.decode(cb.decryptedMessage.payload) + + try self.dbManager.getDB().saveMessage( + .init( + networkId: cb.decryptedMessage.messageId, + senderId: cb.decryptedMessage.senderId, + recipientId: nil, + groupId: cb.decryptedMessage.groupId, + date: Date.fromTimestamp(Int(cb.decryptedMessage.timestamp)), + status: .received, + isUnread: true, + text: payload.text, + replyMessageId: payload.replyingTo, + roundURL: cb.roundUrl + ) + ) + } catch { + print(error.localizedDescription) + } + case .failure(let error): + print(error.localizedDescription) + } + }) + )) + } + + func handleGroupRequest(from group: XXClient.Group, messenger: Messenger) { + if let _ = try? dbManager.getDB().fetchGroups(.init(id: [group.getId()])).first { return } + + guard var members = try? group.getMembership(), let leader = members.first else { + fatalError("Failed to get group membership/leader") + } + + try! dbManager.getDB().saveGroup(.init( + id: group.getId(), + name: String(data: group.getName(), encoding: .utf8)!, + leaderId: leader.id, + createdAt: Date.fromMSTimestamp(group.getCreatedMS()), + authStatus: .pending, + serialized: group.serialize() + )) + + if let initMessageData = group.getInitMessage(), + let initMessage = String(data: initMessageData, encoding: .utf8) { + try! dbManager.getDB().saveMessage(.init( + senderId: leader.id, + recipientId: nil, + groupId: group.getId(), + date: Date.fromMSTimestamp(group.getCreatedMS()), + status: .received, + isUnread: true, + text: initMessage + )) + } + + let friends = try! dbManager.getDB().fetchContacts(.init( + id: Set(members.map(\.id)), + authStatus: [ + .friend, + .hidden, + .requesting, + .confirming, + .verificationInProgress, + .verified, + .requested, + .requestFailed, + .verificationFailed, + .confirmationFailed + ] + )) + + let strangers = Set(members.map(\.id)).subtracting(Set(friends.map(\.id))) + + strangers.forEach { + if let stranger = try? dbManager.getDB().fetchContacts(.init(id: [$0])).first { + print(">>> This is a stranger, but I already knew about his/her existance: \(stranger.id.base64EncodedString().prefix(10))...") + } else { + try! dbManager.getDB().saveContact(.init( + id: $0, + marshaled: nil, + username: "Fetching...", + email: nil, + phone: nil, + nickname: nil, + photo: nil, + authStatus: .stranger, + isRecent: false, + isBlocked: false, + isBanned: false, + createdAt: Date.fromMSTimestamp(group.getCreatedMS()) + )) + } + } + + members.forEach { + let model = XXModels.GroupMember(groupId: group.getId(), contactId: $0.id) + _ = try? dbManager.getDB().saveGroupMember(model) + } + + do { + let multiLookup = try messenger.lookupContacts(ids: strangers.map { $0 }) + + for user in multiLookup.contacts { + if var foo = try? self.dbManager.getDB().fetchContacts(.init(id: [user.getId()])).first, + let username = try? user.getFact(.username)?.value { + foo.username = username + _ = try? self.dbManager.getDB().saveContact(foo) + } + } + } catch { + // TODO + } + } +} + + + +//func handleIncomingTransfer(_ receivedFile: ReceivedFile) { +// if var model = try? dbManager.getDB().saveFileTransfer(.init( +// id: receivedFile.transferId, +// contactId: receivedFile.senderId, +// name: receivedFile.name, +// type: receivedFile.type, +// data: nil, +// progress: 0.0, +// isIncoming: true, +// createdAt: Date() +// )) { +// try! dbManager.getDB().saveMessage(.init( +// networkId: nil, +// senderId: receivedFile.senderId, +// recipientId: messenger.e2e.get()!.getContact().getId(), +// groupId: nil, +// date: Date(), +// status: .receiving, +// isUnread: false, +// text: "", +// replyMessageId: nil, +// roundURL: nil, +// fileTransferId: model.id +// )) +// +// if let manager: XXClient.FileTransfer = try? DI.Container.shared.resolve() { +// print(">>> registerReceivedProgressCallback") +// +// try! manager.registerReceivedProgressCallback( +// transferId: receivedFile.transferId, +// period: 1_000, +// callback: .init(handle: { [weak self] in +// guard let self else { return } +// switch $0 { +// case .success(let cb): +// if cb.progress.completed { +// model.progress = 100 +// model.data = try! manager.receive(transferId: receivedFile.transferId) +// } else { +// model.progress = Float(cb.progress.transmitted/cb.progress.total) +// } +// +// model = try! self.dbManager.getDB().saveFileTransfer(model) +// +// case .failure(let error): +// print(error.localizedDescription) +// } +// }) +// ) +// } else { +// //print(DI.Container.shared.dependencies) +// } +// } +//} diff --git a/Sources/MenuFeature/Controllers/MenuController.swift b/Sources/MenuFeature/Controllers/MenuController.swift index 1c7623d54d4f74dd0fb30d7c51c4876c067f2db9..e02c7e62f37001cbb9780905c5fe475070d98f62 100644 --- a/Sources/MenuFeature/Controllers/MenuController.swift +++ b/Sources/MenuFeature/Controllers/MenuController.swift @@ -1,13 +1,15 @@ -import DI import UIKit import Shared import Combine -import Navigation +import AppCore +import Dependencies +import AppResources +import AppNavigation import DrawerFeature public final class MenuController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var barStylist: StatusBarStylist + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = MenuView() @@ -16,8 +18,14 @@ public final class MenuController: UIViewController { private var cancellables = Set<AnyCancellable>() private var drawerCancellables = Set<AnyCancellable>() - public init(_ currentItem: MenuItem) { + private var navController: UINavigationController? + + public init( + _ currentItem: MenuItem, + _ navController: UINavigationController? = nil + ) { self.currentItem = currentItem + self.navController = navController super.init(nibName: nil, bundle: nil) } @@ -57,12 +65,12 @@ public final class MenuController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - barStylist.styleSubject.send(.lightContent) + statusBar.set(.lightContent) } public override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - barStylist.styleSubject.send(.darkContent) + statusBar.set(.darkContent) } private func setupBindings() { @@ -74,7 +82,7 @@ public final class MenuController: UIViewController { .sink { [unowned self] in navigator.perform(DismissModal(from: self)) { [weak self] in guard let self, self.currentItem != .scan else { return } - self.navigator.perform(PresentScan(on: self.navigationController!)) + self.navigator.perform(PresentScan(on: self.navController!)) } }.store(in: &cancellables) @@ -86,7 +94,7 @@ public final class MenuController: UIViewController { .sink { [unowned self] in navigator.perform(DismissModal(from: self)) { [weak self] in guard let self, self.currentItem != .profile else { return } - self.navigator.perform(PresentProfile(on: self.navigationController!)) + self.navigator.perform(PresentProfile(on: self.navController!)) } }.store(in: &cancellables) @@ -97,7 +105,7 @@ public final class MenuController: UIViewController { .sink { [unowned self] in navigator.perform(DismissModal(from: self)) { [weak self] in guard let self, self.currentItem != .scan else { return } - self.navigator.perform(PresentScan(on: self.navigationController!)) + self.navigator.perform(PresentScan(on: self.navController!)) } }.store(in: &cancellables) @@ -108,7 +116,7 @@ public final class MenuController: UIViewController { .sink { [unowned self] in navigator.perform(DismissModal(from: self)) { [weak self] in guard let self, self.currentItem != .chats else { return } - self.navigator.perform(PresentChatList(on: self.navigationController!)) + self.navigator.perform(PresentChatList(on: self.navController!)) } }.store(in: &cancellables) @@ -119,7 +127,7 @@ public final class MenuController: UIViewController { .sink { [unowned self] in navigator.perform(DismissModal(from: self)) { [weak self] in guard let self, self.currentItem != .contacts else { return } - self.navigator.perform(PresentContactList(on: self.navigationController!)) + self.navigator.perform(PresentContactList(on: self.navController!)) } }.store(in: &cancellables) @@ -130,7 +138,7 @@ public final class MenuController: UIViewController { .sink { [unowned self] in navigator.perform(DismissModal(from: self)) { [weak self] in guard let self, self.currentItem != .settings else { return } - self.navigator.perform(PresentSettings(on: self.navigationController!)) + self.navigator.perform(PresentSettings(on: self.navController!)) } }.store(in: &cancellables) @@ -158,7 +166,7 @@ public final class MenuController: UIViewController { .sink { [unowned self] in navigator.perform(DismissModal(from: self)) { [weak self] in guard let self, self.currentItem != .requests else { return } - self.navigator.perform(PresentRequests(on: self.navigationController!)) + self.navigator.perform(PresentRequests(on: self.navController!)) } }.store(in: &cancellables) diff --git a/Sources/MenuFeature/ViewModels/MenuViewModel.swift b/Sources/MenuFeature/ViewModels/MenuViewModel.swift index 322aae91c2aea30dd86ce5c29fb277ba169f7155..3005d8e2a7b22b375f44dfdac8635fd31f8c47d3 100644 --- a/Sources/MenuFeature/ViewModels/MenuViewModel.swift +++ b/Sources/MenuFeature/ViewModels/MenuViewModel.swift @@ -1,60 +1,61 @@ import Combine +import AppCore import XXModels import XXClient import Defaults import Foundation import ReportingFeature -import DI +import ComposableArchitecture final class MenuViewModel { - @Dependency var database: Database - @Dependency var reportingStatus: ReportingStatus - - @KeyObject(.avatar, defaultValue: nil) var avatar: Data? - @KeyObject(.username, defaultValue: "") var username: String - - var requestCount: AnyPublisher<Int, Never> { - let groupQuery = Group.Query( - authStatus: [.pending], - isLeaderBlocked: reportingStatus.isEnabled() ? false : nil, - isLeaderBanned: reportingStatus.isEnabled() ? false : nil - ) - - let contactsQuery = Contact.Query( - authStatus: [ - .verified, - .confirming, - .confirmationFailed, - .verificationFailed, - .verificationInProgress - ], - isBlocked: reportingStatus.isEnabled() ? false : nil, - isBanned: reportingStatus.isEnabled() ? false : nil - ) - - return Publishers.CombineLatest( - database.fetchContactsPublisher(contactsQuery) - .replaceError(with: []), - database.fetchGroupsPublisher(groupQuery) - .replaceError(with: []) - ) - .map { $0.0.count + $0.1.count } - .eraseToAnyPublisher() - } - - var xxdk: String { - GetVersion.live() - } - - var build: String { - Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "" - } - - var version: String { - Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "" - } - - var referralDeeplink: String { - "https://elixxir.io/connect?username=\(username)" - } + @Dependency(\.app.dbManager) var dbManager: DBManager + @Dependency(\.reportingStatus) var reportingStatus: ReportingStatus + + @KeyObject(.avatar, defaultValue: nil) var avatar: Data? + @KeyObject(.username, defaultValue: "") var username: String + + var requestCount: AnyPublisher<Int, Never> { + let groupQuery = Group.Query( + authStatus: [.pending], + isLeaderBlocked: reportingStatus.isEnabled() ? false : nil, + isLeaderBanned: reportingStatus.isEnabled() ? false : nil + ) + + let contactsQuery = Contact.Query( + authStatus: [ + .verified, + .confirming, + .confirmationFailed, + .verificationFailed, + .verificationInProgress + ], + isBlocked: reportingStatus.isEnabled() ? false : nil, + isBanned: reportingStatus.isEnabled() ? false : nil + ) + + return Publishers.CombineLatest( + try! dbManager.getDB().fetchContactsPublisher(contactsQuery) + .replaceError(with: []), + try! dbManager.getDB().fetchGroupsPublisher(groupQuery) + .replaceError(with: []) + ) + .map { $0.0.count + $0.1.count } + .eraseToAnyPublisher() + } + + var xxdk: String { + GetVersion.live() + } + + var build: String { + Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "" + } + + var version: String { + Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "" + } + + var referralDeeplink: String { + "https://elixxir.io/connect?username=\(username)" + } } diff --git a/Sources/MenuFeature/Views/MenuHeaderView.swift b/Sources/MenuFeature/Views/MenuHeaderView.swift index 2f925646b3b03c3aec498ccb67578a1b4f40fb89..eaea1321b5ea8f1eaaf7a7ebbda4c0190433979d 100644 --- a/Sources/MenuFeature/Views/MenuHeaderView.swift +++ b/Sources/MenuFeature/Views/MenuHeaderView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class MenuHeaderView: UIView { let nameButton = UIButton() diff --git a/Sources/MenuFeature/Views/MenuSectionButton.swift b/Sources/MenuFeature/Views/MenuSectionButton.swift index c5f6ea371a10f0ff3fc087b31f6dc744e11fd4a9..2432cdf6b8cb86833e407c9f5e22deeed2def9f5 100644 --- a/Sources/MenuFeature/Views/MenuSectionButton.swift +++ b/Sources/MenuFeature/Views/MenuSectionButton.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class MenuSectionButton: UIControl { let titleLabel = UILabel() diff --git a/Sources/MenuFeature/Views/MenuView.swift b/Sources/MenuFeature/Views/MenuView.swift index 8e83eacb17ce598f2a72ad0d97a1db064c6f1904..e62deae1b0ef07fdb6938845aec4048a824d2386 100644 --- a/Sources/MenuFeature/Views/MenuView.swift +++ b/Sources/MenuFeature/Views/MenuView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class MenuView: UIView { let buildLabel = UILabel() diff --git a/Sources/OnboardingFeature/Controllers/OnboardingCodeController.swift b/Sources/OnboardingFeature/Controllers/OnboardingCodeController.swift index 05f0be5bbc98ea56a1e57ada2328382b754cb712..6fa2993c058cd37554b92efd4dea45209a490cfc 100644 --- a/Sources/OnboardingFeature/Controllers/OnboardingCodeController.swift +++ b/Sources/OnboardingFeature/Controllers/OnboardingCodeController.swift @@ -1,16 +1,16 @@ import UIKit import Shared import Combine -import Navigation +import AppCore import AppResources +import Dependencies +import AppNavigation import DrawerFeature -import StatusBarFeature import ScrollViewController -import ComposableArchitecture public final class OnboardingCodeController: UIViewController { @Dependency(\.navigator) var navigator: Navigator - @Dependency(\.statusBar) var statusBar: StatusBarStyleManager + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = OnboardingCodeView() private lazy var scrollViewController = ScrollViewController() @@ -41,7 +41,7 @@ public final class OnboardingCodeController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationItem.backButtonTitle = "" - statusBar.update(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar.customize(translucent: true) } diff --git a/Sources/OnboardingFeature/Controllers/OnboardingEmailController.swift b/Sources/OnboardingFeature/Controllers/OnboardingEmailController.swift index df1fc7bfebd56330f8e76f518d9ffde6769f74e7..d15c6594393c5cb7942d78abd3173378dc4756b4 100644 --- a/Sources/OnboardingFeature/Controllers/OnboardingEmailController.swift +++ b/Sources/OnboardingFeature/Controllers/OnboardingEmailController.swift @@ -1,16 +1,16 @@ import UIKit import Shared import Combine -import Navigation +import AppCore import AppResources +import Dependencies +import AppNavigation import DrawerFeature -import StatusBarFeature import ScrollViewController -import ComposableArchitecture public final class OnboardingEmailController: UIViewController { @Dependency(\.navigator) var navigator: Navigator - @Dependency(\.statusBar) var statusBar: StatusBarStyleManager + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = OnboardingEmailView() private lazy var scrollViewController = ScrollViewController() @@ -22,7 +22,7 @@ public final class OnboardingEmailController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationItem.backButtonTitle = " " - statusBar.update(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar.customize(translucent: true) } diff --git a/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift b/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift index 0f4eebf87044c777e9af241523077fd1634f1d82..466a45994a90979bdcbf09e427efad88cef93b24 100644 --- a/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift +++ b/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift @@ -1,16 +1,16 @@ import UIKit import Shared import Combine -import Navigation +import AppCore import AppResources +import Dependencies +import AppNavigation import DrawerFeature -import StatusBarFeature import ScrollViewController -import ComposableArchitecture public final class OnboardingPhoneController: UIViewController { @Dependency(\.navigator) var navigator: Navigator - @Dependency(\.statusBar) var statusBar: StatusBarStyleManager + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = OnboardingPhoneView() private lazy var scrollViewController = ScrollViewController() @@ -22,7 +22,7 @@ public final class OnboardingPhoneController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationItem.backButtonTitle = "" - statusBar.update(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar.customize(translucent: true) } diff --git a/Sources/OnboardingFeature/Controllers/OnboardingStartController.swift b/Sources/OnboardingFeature/Controllers/OnboardingStartController.swift index 4e09d6b6609e27454822912e2b844bf8b3e4aa1b..0fd4158db96cda5d8f7cea46388de3b6890a7864 100644 --- a/Sources/OnboardingFeature/Controllers/OnboardingStartController.swift +++ b/Sources/OnboardingFeature/Controllers/OnboardingStartController.swift @@ -1,6 +1,6 @@ import UIKit import Combine -import Navigation +import AppNavigation import ComposableArchitecture public final class OnboardingStartController: UIViewController { diff --git a/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift b/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift index 42f4f18981fabb12a44713d22f2904a4e831af54..619a983b7149929bf7ca1232aff937bcaf34baa8 100644 --- a/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift +++ b/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift @@ -1,16 +1,16 @@ import UIKit import Shared import Combine -import Navigation +import AppCore import AppResources +import Dependencies +import AppNavigation import DrawerFeature -import StatusBarFeature import ScrollViewController -import ComposableArchitecture public final class OnboardingUsernameController: UIViewController { @Dependency(\.navigator) var navigator: Navigator - @Dependency(\.statusBar) var statusBar: StatusBarStyleManager + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = OnboardingUsernameView() private lazy var scrollViewController = ScrollViewController() @@ -22,7 +22,7 @@ public final class OnboardingUsernameController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationItem.backButtonTitle = "" - statusBar.update(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar.customize(translucent: true) } diff --git a/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift b/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift index 09a9221ba5765e85d92a9626b41377566a0977c2..f3bc2af0248b805fae258212d7573d89c9586dc5 100644 --- a/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift +++ b/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift @@ -2,15 +2,15 @@ import UIKit import Shared import Combine import Defaults -import Navigation +import AppCore +import Dependencies import AppResources +import AppNavigation import DrawerFeature -import StatusBarFeature -import ComposableArchitecture public final class OnboardingWelcomeController: UIViewController { @Dependency(\.navigator) var navigator: Navigator - @Dependency(\.statusBar) var statusBar: StatusBarStyleManager + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist @KeyObject(.username, defaultValue: "") var username: String @@ -25,7 +25,7 @@ public final class OnboardingWelcomeController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - statusBar.update(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar.customize(translucent: true) } diff --git a/Sources/OnboardingFeature/OnboardingDependencies.swift b/Sources/OnboardingFeature/OnboardingDependencies.swift deleted file mode 100644 index fe3dce7f61997d7fc4b4d80d2d4b20caf358bcf4..0000000000000000000000000000000000000000 --- a/Sources/OnboardingFeature/OnboardingDependencies.swift +++ /dev/null @@ -1,91 +0,0 @@ -import Navigation -import Dependencies - -private enum NavigatorKey: DependencyKey { - static let liveValue: Navigator = CombinedNavigator.core - static let testValue: Navigator = UnimplementedNavigator() -} - -extension DependencyValues { - var navigator: Navigator { - get { self[NavigatorKey.self] } - set { self[NavigatorKey.self] = newValue } - } -} - -import UIKit -import XCTestDynamicOverlay -import ComposableArchitecture - -public struct PresentStep: Navigation.Action, Equatable { - public init(viewController: UIViewController, from: UIViewController) { - self.viewController = viewController - self.from = from - } - - public var viewController: UIViewController - public var from: UIViewController -} - -struct PresentStepNavigator: Navigation.TypedNavigator { - @Dependency(\.navigator) var navigator - - func perform(_ action: PresentStep, completion: @escaping () -> Void) { - guard let navigationController = action.from.navigationController else { - completion() - return - } - navigator.perform( - SetStack( - navigationController.viewControllers + [action.viewController], - on: navigationController - ), - completion: completion - ) - } -} - -public struct DismissToStep: Navigation.Action, Equatable { - public init(viewController: UIViewController) { - self.viewController = viewController - } - - public var viewController: UIViewController -} - -struct DismissToStepNavigator: Navigation.TypedNavigator { - @Dependency(\.navigator) var navigator - - func perform(_ action: DismissToStep, completion: @escaping () -> Void) { - guard let navigationController = action.viewController.navigationController else { - completion() - return - } - navigator.perform( - PopTo(action.viewController, on: navigationController), - completion: completion - ) - } -} - -extension CombinedNavigator { - public static let core = CombinedNavigator( - SetStackNavigator(), - PopToNavigator(), - PresentStepNavigator(), - DismissToStepNavigator() - ) -} - -public struct UnimplementedNavigator: Navigator { - public init() {} - - public func perform(_ action: Navigation.Action, completion: @escaping () -> Void) { - XCTestDynamicOverlay.XCTFail("UnimplementedNavigator.perform not implemented") - } - - public func canPerform(_ action: Action) -> Bool { - XCTestDynamicOverlay.XCTFail("UnimplementedNavigator.canPerform not implemented") - return false - } -} diff --git a/Sources/PermissionsFeature/Dependency.swift b/Sources/PermissionsFeature/Dependency.swift new file mode 100644 index 0000000000000000000000000000000000000000..766f239ff492ca00b4ed0fc9a8afaa879818812b --- /dev/null +++ b/Sources/PermissionsFeature/Dependency.swift @@ -0,0 +1,13 @@ +import Dependencies + +private enum PermissionsDependencyKey: DependencyKey { + static let liveValue: PermissionsManager = .live + static let testValue: PermissionsManager = .unimplemented +} + +extension DependencyValues { + public var permissions: PermissionsManager { + get { self[PermissionsDependencyKey.self] } + set { self[PermissionsDependencyKey.self] = newValue } + } +} diff --git a/Sources/PermissionsFeature/PermissionPush.swift b/Sources/PermissionsFeature/PermissionPush.swift new file mode 100644 index 0000000000000000000000000000000000000000..a4be4209b7656f59615ca95b6a89ce187ccf5515 --- /dev/null +++ b/Sources/PermissionsFeature/PermissionPush.swift @@ -0,0 +1,66 @@ +import UserNotifications +import XCTestDynamicOverlay + +public struct PermissionPush { + public var status: PermissionPushStatus + public var request: PermissionPushRequest + + public static let live = PermissionPush( + status: .live, + request: .live + ) + public static let unimplemented = PermissionPush( + status: .unimplemented, + request: .unimplemented + ) +} + +public struct PermissionPushRequest { + public var run: (@escaping (Bool) -> Void) -> Void + + public func callAsFunction(_ completion: @escaping (Bool) -> Void) -> Void { + run(completion) + } +} + +extension PermissionPushRequest { + public static let live = PermissionPushRequest { completion in + let current = UNUserNotificationCenter.current() + current.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in + if error != nil { + completion(false) + return + } + completion(granted) + } + } +} + +extension PermissionPushRequest { + public static let unimplemented = PermissionPushRequest( + run: XCTUnimplemented("\(Self.self)") + ) +} + +public struct PermissionPushStatus { + public var run: (@escaping (Bool) -> Void) -> Void + + public func callAsFunction(_ completion: @escaping (Bool) -> Void) -> Void { + run(completion) + } +} + +extension PermissionPushStatus { + public static let live = PermissionPushStatus { completion in + let current = UNUserNotificationCenter.current() + current.getNotificationSettings { + completion($0.authorizationStatus == .authorized) + } + } +} + +extension PermissionPushStatus { + public static let unimplemented = PermissionPushStatus( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/PermissionsFeature/PermissionType.swift b/Sources/PermissionsFeature/PermissionType.swift deleted file mode 100644 index 93b8ba3906708b240dc45fb39bbca98173ae0495..0000000000000000000000000000000000000000 --- a/Sources/PermissionsFeature/PermissionType.swift +++ /dev/null @@ -1,5 +0,0 @@ -public enum PermissionType: Int { - case camera - case library - case microphone -} diff --git a/Sources/PermissionsFeature/PermissionsManager.swift b/Sources/PermissionsFeature/PermissionsManager.swift index 89fd4b377e122725bd112bc4a1fdc3c760688a61..33373e6e3fbf9faf738cbd80f89a24c2ee13f7eb 100644 --- a/Sources/PermissionsFeature/PermissionsManager.swift +++ b/Sources/PermissionsFeature/PermissionsManager.swift @@ -1,33 +1,22 @@ public struct PermissionsManager { + public var push: PermissionPush public var camera: PermissionCamera public var library: PermissionLibrary public var microphone: PermissionMicrophone public var biometrics: PermissionBiometrics public static let live = PermissionsManager( + push: .live, camera: .live, library: .live, microphone: .live, biometrics: .live ) public static let unimplemented = PermissionsManager( + push: .unimplemented, camera: .unimplemented, library: .unimplemented, microphone: .unimplemented, biometrics: .unimplemented ) } - -import Dependencies - -private enum PermissionsDependencyKey: DependencyKey { - static let liveValue: PermissionsManager = .live - static let testValue: PermissionsManager = .unimplemented -} - -extension DependencyValues { - public var permissions: PermissionsManager { - get { self[PermissionsDependencyKey.self] } - set { self[PermissionsDependencyKey.self] = newValue } - } -} diff --git a/Sources/ProcessBannedList/Dependency.swift b/Sources/ProcessBannedList/Dependency.swift new file mode 100644 index 0000000000000000000000000000000000000000..1f02756a5541c2907e9cb6f23fe45510c43afa16 --- /dev/null +++ b/Sources/ProcessBannedList/Dependency.swift @@ -0,0 +1,13 @@ +import Dependencies + +private enum ProcessBannedListDependencyKey: DependencyKey { + static let liveValue: ProcessBannedList = .live + static let testValue: ProcessBannedList = .unimplemented +} + +extension DependencyValues { + public var processBannedList: ProcessBannedList { + get { self[ProcessBannedListDependencyKey.self] } + set { self[ProcessBannedListDependencyKey.self] = newValue } + } +} diff --git a/Sources/ProcessBannedList/ProcessBannedList.swift b/Sources/ProcessBannedList/ProcessBannedList.swift new file mode 100644 index 0000000000000000000000000000000000000000..900738b323abf85587789fa040e891563cd9f85a --- /dev/null +++ b/Sources/ProcessBannedList/ProcessBannedList.swift @@ -0,0 +1,64 @@ +import Foundation +import SwiftCSV +import XCTestDynamicOverlay + +public struct ProcessBannedList { + public enum ElementError: Swift.Error { + case missingUserId + case invalidUserId(String) + } + + public enum Error: Swift.Error { + case invalidData + case csv(Swift.Error) + } + + public typealias ForEach = (Result<Data, ElementError>) -> Void + public typealias Completion = (Result<Void, Error>) -> Void + + public var run: (Data, ForEach, Completion) -> Void + + public func callAsFunction( + data: Data, + forEach: ForEach, + completion: Completion + ) { + run(data, forEach, completion) + } +} + +extension ProcessBannedList { + public static let live = ProcessBannedList { data, forEach, completion in + guard let csvString = String(data: data, encoding: .utf8) else { + completion(.failure(.invalidData)) + return + } + let csv: EnumeratedCSV + do { + csv = try EnumeratedCSV(string: csvString) + } + catch { + completion(.failure(.csv(error))) + return + } + csv.rows.forEach { row in + guard let userIdString = row.first else { + forEach(.failure(.missingUserId)) + return + } + guard let userId = Data(base64Encoded: userIdString) else { + forEach(.failure(.invalidUserId(userIdString))) + return + } + forEach(.success(userId)) + } + completion(.success(())) + } +} + +extension ProcessBannedList { + public static let unimplemented = ProcessBannedList { _, _, _ in + let run: () -> Void = XCTUnimplemented("\(Self.self)") + run() + } +} diff --git a/Sources/ProfileFeature/Controllers/ProfileCodeController.swift b/Sources/ProfileFeature/Controllers/ProfileCodeController.swift index 8f6ff16f9df23b36fee5b4ff316a4044f40abe32..d5d640db0263cf29abda90a9b1ec962e89e9081c 100644 --- a/Sources/ProfileFeature/Controllers/ProfileCodeController.swift +++ b/Sources/ProfileFeature/Controllers/ProfileCodeController.swift @@ -1,13 +1,15 @@ import UIKit import Shared import Combine -import Navigation -import DI +import AppCore +import AppResources +import Dependencies +import AppNavigation import ScrollViewController public final class ProfileCodeController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var barStylist: StatusBarStylist + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = ProfileCodeView() private lazy var scrollViewController = ScrollViewController() diff --git a/Sources/ProfileFeature/Controllers/ProfileController.swift b/Sources/ProfileFeature/Controllers/ProfileController.swift index 3662169233784d0c2f6420e0e5d1c512a414334c..23dca61bf795b7ede333f78d8ed0c2d7fdfa2588 100644 --- a/Sources/ProfileFeature/Controllers/ProfileController.swift +++ b/Sources/ProfileFeature/Controllers/ProfileController.swift @@ -1,13 +1,15 @@ import UIKit import Shared import Combine -import Navigation +import AppCore +import AppResources +import AppNavigation import DrawerFeature -import DI +import ComposableArchitecture public final class ProfileController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var barStylist: StatusBarStylist + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = ProfileView() @@ -21,7 +23,7 @@ public final class ProfileController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - barStylist.styleSubject.send(.lightContent) + statusBar.set(.lightContent) navigationController?.navigationBar .customize(backgroundColor: Asset.neutralBody.color) viewModel.refresh() diff --git a/Sources/ProfileFeature/Controllers/ProfileEmailController.swift b/Sources/ProfileFeature/Controllers/ProfileEmailController.swift index 1bfc1c789b7f4de906f56aaad09c94f08a8fb7ba..ef9ef2f8f501ae6160d96cd72376d33222d9cdac 100644 --- a/Sources/ProfileFeature/Controllers/ProfileEmailController.swift +++ b/Sources/ProfileFeature/Controllers/ProfileEmailController.swift @@ -1,13 +1,15 @@ -import DI import UIKit import Shared import Combine -import Navigation +import AppCore +import AppResources +import AppNavigation import ScrollViewController +import ComposableArchitecture public final class ProfileEmailController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var barStylist: StatusBarStylist + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = ProfileEmailView() private lazy var scrollViewController = ScrollViewController() @@ -18,7 +20,7 @@ public final class ProfileEmailController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationItem.backButtonTitle = "" - barStylist.styleSubject.send(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar.customize(backgroundColor: Asset.neutralWhite.color) } diff --git a/Sources/ProfileFeature/Controllers/ProfilePhoneController.swift b/Sources/ProfileFeature/Controllers/ProfilePhoneController.swift index bd9689a44bcc96f648bb1c6427de7b15148957a7..99ac70b864328dd362fe88a2487fc3ba8fec2ce2 100644 --- a/Sources/ProfileFeature/Controllers/ProfilePhoneController.swift +++ b/Sources/ProfileFeature/Controllers/ProfilePhoneController.swift @@ -1,13 +1,15 @@ -import DI import UIKit import Shared import Combine -import Navigation +import AppCore +import AppResources +import Dependencies +import AppNavigation import ScrollViewController public final class ProfilePhoneController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var barStylist: StatusBarStylist + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = ProfilePhoneView() private lazy var scrollViewController = ScrollViewController() @@ -18,7 +20,7 @@ public final class ProfilePhoneController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationItem.backButtonTitle = "" - barStylist.styleSubject.send(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar .customize(backgroundColor: Asset.neutralWhite.color) } diff --git a/Sources/ProfileFeature/ViewModels/ProfileCodeViewModel.swift b/Sources/ProfileFeature/ViewModels/ProfileCodeViewModel.swift index 9a1638ec536cdc728a5eb1b55c81dd93faecb24a..a018da77f237fc1c4b604ea791aa0289452638d0 100644 --- a/Sources/ProfileFeature/ViewModels/ProfileCodeViewModel.swift +++ b/Sources/ProfileFeature/ViewModels/ProfileCodeViewModel.swift @@ -1,12 +1,12 @@ import Shared import Combine +import AppCore import Defaults import XXClient import InputField import Foundation -import CombineSchedulers import XXMessengerClient -import DI +import ComposableArchitecture final class ProfileCodeViewModel { struct ViewState: Equatable { @@ -20,8 +20,10 @@ final class ProfileCodeViewModel { stateSubject.eraseToAnyPublisher() } - @Dependency var messenger: Messenger - @Dependency var hudController: HUDController + @Dependency(\.app.messenger) var messenger: Messenger + @Dependency(\.app.hudManager) var hudManager: HUDManager + @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue> + @KeyObject(.email, defaultValue: nil) var email: String? @KeyObject(.phone, defaultValue: nil) var phone: String? @@ -30,7 +32,6 @@ final class ProfileCodeViewModel { private let content: String private let confirmationId: String private let stateSubject = CurrentValueSubject<ViewState, Never>(.init()) - private var scheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler() init( isEmail: Bool, @@ -61,8 +62,8 @@ final class ProfileCodeViewModel { } func didTapNext() { - hudController.show() - scheduler.schedule { [weak self] in + hudManager.show() + bgQueue.schedule { [weak self] in guard let self else { return } do { try self.messenger.ud.get()!.confirmFact( @@ -75,11 +76,11 @@ final class ProfileCodeViewModel { self.phone = self.content } self.timer?.invalidate() - self.hudController.dismiss() + self.hudManager.hide() self.stateSubject.value.didConfirm = true } catch { let xxError = CreateUserFriendlyErrorMessage.live(error.localizedDescription) - self.hudController.show(.init(content: xxError)) + self.hudManager.show(.init(content: xxError)) } } } diff --git a/Sources/ProfileFeature/ViewModels/ProfileEmailViewModel.swift b/Sources/ProfileFeature/ViewModels/ProfileEmailViewModel.swift index fd27e4f0081cd1bda51e10fa569153943c1916c9..96210b7dc00867639c7861d377d868df11bb8b62 100644 --- a/Sources/ProfileFeature/ViewModels/ProfileEmailViewModel.swift +++ b/Sources/ProfileFeature/ViewModels/ProfileEmailViewModel.swift @@ -1,11 +1,12 @@ import Shared import Combine +import AppCore import XXClient import Foundation import InputField import CombineSchedulers import XXMessengerClient -import DI +import ComposableArchitecture final class ProfileEmailViewModel { struct ViewState: Equatable { @@ -13,16 +14,16 @@ final class ProfileEmailViewModel { var confirmationId: String? var status: InputField.ValidationStatus = .unknown(nil) } - - @Dependency var messenger: Messenger - @Dependency var hudController: HUDController + + @Dependency(\.app.messenger) var messenger: Messenger + @Dependency(\.app.hudManager) var hudManager: HUDManager + @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue> var statePublisher: AnyPublisher<ViewState, Never> { stateSubject.eraseToAnyPublisher() } private let stateSubject = CurrentValueSubject<ViewState, Never>(.init()) - private var scheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler() func clearUp() { stateSubject.value.confirmationId = nil @@ -34,17 +35,17 @@ final class ProfileEmailViewModel { } func didTapNext() { - hudController.show() - scheduler.schedule { [weak self] in + hudManager.show() + bgQueue.schedule { [weak self] in guard let self else { return } do { let confirmationId = try self.messenger.ud.get()!.sendRegisterFact( .init(type: .email, value: self.stateSubject.value.input) ) - self.hudController.dismiss() + self.hudManager.hide() self.stateSubject.value.confirmationId = confirmationId } catch { - self.hudController.dismiss() + self.hudManager.hide() let xxError = CreateUserFriendlyErrorMessage.live(error.localizedDescription) self.stateSubject.value.status = .invalid(xxError) } diff --git a/Sources/ProfileFeature/ViewModels/ProfilePhoneViewModel.swift b/Sources/ProfileFeature/ViewModels/ProfilePhoneViewModel.swift index 2eadd36da44361a596cc1ddaa81dbbefb60c5d0b..90387a69dde095491775b47cd4e138ae6786f483 100644 --- a/Sources/ProfileFeature/ViewModels/ProfilePhoneViewModel.swift +++ b/Sources/ProfileFeature/ViewModels/ProfilePhoneViewModel.swift @@ -1,12 +1,13 @@ import Shared import Combine +import AppCore import XXClient import InputField import Foundation +import Dependencies import CombineSchedulers import XXMessengerClient import CountryListFeature -import DI final class ProfilePhoneViewModel { struct ViewState: Equatable { @@ -17,15 +18,15 @@ final class ProfilePhoneViewModel { var country: Country = .fromMyPhone() } - @Dependency var messenger: Messenger - @Dependency var hudController: HUDController + @Dependency(\.app.messenger) var messenger: Messenger + @Dependency(\.app.hudManager) var hudManager: HUDManager + @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue> var statePublisher: AnyPublisher<ViewState, Never> { stateSubject.eraseToAnyPublisher() } private let stateSubject = CurrentValueSubject<ViewState, Never>(.init()) - private var scheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler() func didInput(_ string: String) { stateSubject.value.input = string @@ -42,8 +43,8 @@ final class ProfilePhoneViewModel { } func didTapNext() { - hudController.show() - scheduler.schedule { [weak self] in + hudManager.show() + bgQueue.schedule { [weak self] in guard let self else { return } let content = "\(self.stateSubject.value.input)\(self.stateSubject.value.country.code)" do { @@ -51,12 +52,12 @@ final class ProfilePhoneViewModel { .init(type: .phone, value: content) ) - self.hudController.dismiss() + self.hudManager.hide() self.stateSubject.value.content = content self.stateSubject.value.confirmationId = confirmationId } catch { let xxError = CreateUserFriendlyErrorMessage.live(error.localizedDescription) - self.hudController.show(.init(content: xxError)) + self.hudManager.show(.init(content: xxError)) } } } diff --git a/Sources/ProfileFeature/ViewModels/ProfileViewModel.swift b/Sources/ProfileFeature/ViewModels/ProfileViewModel.swift index 17daebf9d9bbbba24b545c59f7dca5c8a0e88660..07ab592ebd904e0a1dc91314e7b616a5cea7a646 100644 --- a/Sources/ProfileFeature/ViewModels/ProfileViewModel.swift +++ b/Sources/ProfileFeature/ViewModels/ProfileViewModel.swift @@ -1,15 +1,15 @@ import UIKit import Shared +import AppCore import Combine import Defaults import XXClient -import Foundation -import Permissions import BackupFeature import XXMessengerClient import CombineSchedulers import CountryListFeature -import DI +import PermissionsFeature +import ComposableArchitecture enum ProfileNavigationRoutes { case none @@ -31,21 +31,24 @@ final class ProfileViewModel { @KeyObject(.sharingEmail, defaultValue: false) var isEmailSharing: Bool @KeyObject(.sharingPhone, defaultValue: false) var isPhoneSharing: Bool - @Dependency var messenger: Messenger - @Dependency var backupService: BackupService - @Dependency var hudController: HUDController - @Dependency var permissions: PermissionHandling + @Dependency(\.app.messenger) var messenger: Messenger + @Dependency(\.app.hudManager) var hudManager: HUDManager + @Dependency(\.backupService) var backupService: BackupService + @Dependency(\.permissions) var permissions: PermissionsManager + @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue> var name: String { username! } - var state: AnyPublisher<ProfileViewState, Never> { stateRelay.eraseToAnyPublisher() } + var state: AnyPublisher<ProfileViewState, Never> { + stateRelay.eraseToAnyPublisher() + } private let stateRelay = CurrentValueSubject<ProfileViewState, Never>(.init()) - var navigation: AnyPublisher<ProfileNavigationRoutes, Never> { navigationRoutes.eraseToAnyPublisher() } + var navigation: AnyPublisher<ProfileNavigationRoutes, Never> { + navigationRoutes.eraseToAnyPublisher() + } private let navigationRoutes = PassthroughSubject<ProfileNavigationRoutes, Never>() - var backgroundScheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler() - init() { refresh() } @@ -66,7 +69,7 @@ final class ProfileViewModel { } func didRequestLibraryAccess() { - if permissions.isPhotosAllowed { + if permissions.library.status() { navigationRoutes.send(.library) } else { navigationRoutes.send(.libraryPermission) @@ -83,11 +86,10 @@ final class ProfileViewModel { } func didTapDelete(isEmail: Bool) { - hudController.show() + hudManager.show() - backgroundScheduler.schedule { [weak self] in + bgQueue.schedule { [weak self] in guard let self else { return } - do { try self.messenger.ud.get()!.removeFact( .init( @@ -95,7 +97,6 @@ final class ProfileViewModel { value: isEmail ? self.emailStored! : self.phoneStored! ) ) - if isEmail { self.emailStored = nil self.isEmailSharing = false @@ -103,13 +104,12 @@ final class ProfileViewModel { self.phoneStored = nil self.isPhoneSharing = false } - self.backupService.didUpdateFacts() - self.hudController.dismiss() + self.hudManager.hide() self.refresh() } catch { let xxError = CreateUserFriendlyErrorMessage.live(error.localizedDescription) - self.hudController.show(.init(content: xxError)) + self.hudManager.show(.init(content: xxError)) } } } diff --git a/Sources/ProfileFeature/Views/ProfileCodeView.swift b/Sources/ProfileFeature/Views/ProfileCodeView.swift index f8d7199bb22c5b25af077cacd7315774b83ff5fe..dc4fb0310ff4bcbdf5736878b5c4e984a7472686 100644 --- a/Sources/ProfileFeature/Views/ProfileCodeView.swift +++ b/Sources/ProfileFeature/Views/ProfileCodeView.swift @@ -1,6 +1,7 @@ import UIKit import Shared import InputField +import AppResources final class ProfileCodeView: UIView { let titleLabel = UILabel() diff --git a/Sources/ProfileFeature/Views/ProfileEmailView.swift b/Sources/ProfileFeature/Views/ProfileEmailView.swift index 185c0ef797ec8574a31e722dc3478bac3d12fe5c..6c689f68169a7b55261c7444f6c7e3a4e5fe2026 100644 --- a/Sources/ProfileFeature/Views/ProfileEmailView.swift +++ b/Sources/ProfileFeature/Views/ProfileEmailView.swift @@ -1,74 +1,75 @@ import UIKit import Shared import InputField +import AppResources final class ProfileEmailView: UIView { - let titleLabel = UILabel() - let imageView = UIImageView() - let inputField = InputField() - let saveButton = CapsuleButton() + let titleLabel = UILabel() + let imageView = UIImageView() + let inputField = InputField() + let saveButton = CapsuleButton() - init() { - super.init(frame: .zero) + init() { + super.init(frame: .zero) - titleLabel.text = Localized.Profile.EmailScreen.title - titleLabel.textAlignment = .center - titleLabel.textColor = Asset.neutralActive.color - titleLabel.font = Fonts.Mulish.bold.font(size: 32.0) - imageView.contentMode = .center - imageView.image = Asset.profileEmail.image - saveButton.setStyle(.brandColored) - saveButton.setTitle(Localized.Profile.EmailScreen.action, for: .normal) + titleLabel.text = Localized.Profile.EmailScreen.title + titleLabel.textAlignment = .center + titleLabel.textColor = Asset.neutralActive.color + titleLabel.font = Fonts.Mulish.bold.font(size: 32.0) + imageView.contentMode = .center + imageView.image = Asset.profileEmail.image + saveButton.setStyle(.brandColored) + saveButton.setTitle(Localized.Profile.EmailScreen.action, for: .normal) - inputField.setup( - title: Localized.Profile.EmailScreen.input, - placeholder: Localized.Profile.EmailScreen.input, - subtitleColor: Asset.neutralWeak.color, - allowsEmptySpace: false, - keyboardType: .emailAddress, - autocapitalization: .none, - contentType: .emailAddress - ) + inputField.setup( + title: Localized.Profile.EmailScreen.input, + placeholder: Localized.Profile.EmailScreen.input, + subtitleColor: Asset.neutralWeak.color, + allowsEmptySpace: false, + keyboardType: .emailAddress, + autocapitalization: .none, + contentType: .emailAddress + ) - addSubview(imageView) - addSubview(titleLabel) - addSubview(inputField) - addSubview(saveButton) + addSubview(imageView) + addSubview(titleLabel) + addSubview(inputField) + addSubview(saveButton) - imageView.snp.makeConstraints { make in - make.top.equalToSuperview().offset(60) - make.centerX.equalToSuperview() - } + imageView.snp.makeConstraints { make in + make.top.equalToSuperview().offset(60) + make.centerX.equalToSuperview() + } - titleLabel.snp.makeConstraints { make in - make.top.equalTo(imageView.snp.bottom).offset(39) - make.centerX.equalToSuperview() - } + titleLabel.snp.makeConstraints { make in + make.top.equalTo(imageView.snp.bottom).offset(39) + make.centerX.equalToSuperview() + } - inputField.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(35) - make.left.equalToSuperview().offset(24) - make.right.equalToSuperview().offset(-24) - } + inputField.snp.makeConstraints { make in + make.top.equalTo(titleLabel.snp.bottom).offset(35) + make.left.equalToSuperview().offset(24) + make.right.equalToSuperview().offset(-24) + } - saveButton.snp.makeConstraints { make in - make.top.greaterThanOrEqualTo(inputField.snp.bottom).offset(40) - make.left.equalToSuperview().offset(24) - make.right.equalToSuperview().offset(-24) - make.bottom.equalTo(safeAreaLayoutGuide).offset(-40) - } + saveButton.snp.makeConstraints { make in + make.top.greaterThanOrEqualTo(inputField.snp.bottom).offset(40) + make.left.equalToSuperview().offset(24) + make.right.equalToSuperview().offset(-24) + make.bottom.equalTo(safeAreaLayoutGuide).offset(-40) } + } - required init?(coder: NSCoder) { nil } + required init?(coder: NSCoder) { nil } - func update(status: InputField.ValidationStatus) { - inputField.update(status: status) + func update(status: InputField.ValidationStatus) { + inputField.update(status: status) - switch status { - case .valid: - saveButton.isEnabled = true - case .invalid, .unknown: - saveButton.isEnabled = false - } + switch status { + case .valid: + saveButton.isEnabled = true + case .invalid, .unknown: + saveButton.isEnabled = false } + } } diff --git a/Sources/ProfileFeature/Views/ProfilePhoneView.swift b/Sources/ProfileFeature/Views/ProfilePhoneView.swift index 12f5062fd269d5092a036685943d260304b5b4a0..1dccecfa1276e2983520b01e792ff83d762d527c 100644 --- a/Sources/ProfileFeature/Views/ProfilePhoneView.swift +++ b/Sources/ProfileFeature/Views/ProfilePhoneView.swift @@ -1,73 +1,74 @@ import UIKit import Shared import InputField +import AppResources final class ProfilePhoneView: UIView { - let titleLabel = UILabel() - let imageView = UIImageView() - let inputField = InputField() - let saveButton = CapsuleButton() - - init() { - super.init(frame: .zero) - - titleLabel.text = "Add Phone" - titleLabel.textAlignment = .center - titleLabel.textColor = Asset.neutralActive.color - titleLabel.font = Fonts.Mulish.bold.font(size: 32.0) - imageView.contentMode = .center - imageView.image = Asset.profilePhone.image - saveButton.setStyle(.brandColored) - saveButton.setTitle(Localized.Profile.PhoneScreen.action, for: .normal) - - inputField.setup( - style: .phone, - title: Localized.Profile.PhoneScreen.input, - placeholder: "10651613216", - subtitleColor: Asset.neutralWeak.color, - keyboardType: .phonePad, - contentType: .telephoneNumber - ) - - addSubview(imageView) - addSubview(titleLabel) - addSubview(inputField) - addSubview(saveButton) - - imageView.snp.makeConstraints { make in - make.top.equalToSuperview().offset(60) - make.centerX.equalToSuperview() - } - - titleLabel.snp.makeConstraints { make in - make.top.equalTo(imageView.snp.bottom).offset(39) - make.centerX.equalToSuperview() - } - - inputField.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(35) - make.left.equalToSuperview().offset(24) - make.right.equalToSuperview().offset(-24) - } - - saveButton.snp.makeConstraints { make in - make.top.greaterThanOrEqualTo(inputField.snp.bottom).offset(40) - make.left.equalToSuperview().offset(24) - make.right.equalToSuperview().offset(-24) - make.bottom.equalTo(safeAreaLayoutGuide).offset(-40) - } + let titleLabel = UILabel() + let imageView = UIImageView() + let inputField = InputField() + let saveButton = CapsuleButton() + + init() { + super.init(frame: .zero) + + titleLabel.text = "Add Phone" + titleLabel.textAlignment = .center + titleLabel.textColor = Asset.neutralActive.color + titleLabel.font = Fonts.Mulish.bold.font(size: 32.0) + imageView.contentMode = .center + imageView.image = Asset.profilePhone.image + saveButton.setStyle(.brandColored) + saveButton.setTitle(Localized.Profile.PhoneScreen.action, for: .normal) + + inputField.setup( + style: .phone, + title: Localized.Profile.PhoneScreen.input, + placeholder: "10651613216", + subtitleColor: Asset.neutralWeak.color, + keyboardType: .phonePad, + contentType: .telephoneNumber + ) + + addSubview(imageView) + addSubview(titleLabel) + addSubview(inputField) + addSubview(saveButton) + + imageView.snp.makeConstraints { make in + make.top.equalToSuperview().offset(60) + make.centerX.equalToSuperview() + } + + titleLabel.snp.makeConstraints { make in + make.top.equalTo(imageView.snp.bottom).offset(39) + make.centerX.equalToSuperview() } - - required init?(coder: NSCoder) { nil } - - func update(status: InputField.ValidationStatus) { - inputField.update(status: status) - - switch status { - case .valid: - saveButton.isEnabled = true - case .invalid, .unknown: - saveButton.isEnabled = false - } + + inputField.snp.makeConstraints { make in + make.top.equalTo(titleLabel.snp.bottom).offset(35) + make.left.equalToSuperview().offset(24) + make.right.equalToSuperview().offset(-24) + } + + saveButton.snp.makeConstraints { make in + make.top.greaterThanOrEqualTo(inputField.snp.bottom).offset(40) + make.left.equalToSuperview().offset(24) + make.right.equalToSuperview().offset(-24) + make.bottom.equalTo(safeAreaLayoutGuide).offset(-40) + } + } + + required init?(coder: NSCoder) { nil } + + func update(status: InputField.ValidationStatus) { + inputField.update(status: status) + + switch status { + case .valid: + saveButton.isEnabled = true + case .invalid, .unknown: + saveButton.isEnabled = false } + } } diff --git a/Sources/ProfileFeature/Views/ProfileView.swift b/Sources/ProfileFeature/Views/ProfileView.swift index e95e3e63c08f97b705a4da31c9e3264d8c1ba1f4..55081d330c759c7bcc6fc798c9bc92a33969990d 100644 --- a/Sources/ProfileFeature/Views/ProfileView.swift +++ b/Sources/ProfileFeature/Views/ProfileView.swift @@ -1,43 +1,44 @@ import UIKit import Shared +import AppResources final class ProfileView: UIView { - let stackView = UIStackView() - let cardComponent = AvatarCardComponent() - let emailView = AttributeComponent() - let phoneView = AttributeComponent() - - init() { - super.init(frame: .zero) - backgroundColor = Asset.neutralWhite.color - - let emailTitle = Localized.Profile.Email.title - let phoneTitle = Localized.Profile.Phone.title - - emailView.set(title: emailTitle, style: .interactive) - phoneView.set(title: phoneTitle, style: .interactive) - - stackView.spacing = 41 - stackView.axis = .vertical - stackView.addArrangedSubview(emailView) - stackView.addArrangedSubview(phoneView) - - addSubview(stackView) - addSubview(cardComponent) - - cardComponent.snp.makeConstraints { make in - make.top.equalToSuperview() - make.left.equalToSuperview() - make.right.equalToSuperview() - } - - stackView.snp.makeConstraints { make in - make.top.equalTo(cardComponent.snp.bottom).offset(24) - make.left.equalToSuperview().offset(24) - make.right.equalToSuperview().offset(-26) - make.bottom.lessThanOrEqualToSuperview() - } + let stackView = UIStackView() + let cardComponent = AvatarCardComponent() + let emailView = AttributeComponent() + let phoneView = AttributeComponent() + + init() { + super.init(frame: .zero) + backgroundColor = Asset.neutralWhite.color + + let emailTitle = Localized.Profile.Email.title + let phoneTitle = Localized.Profile.Phone.title + + emailView.set(title: emailTitle, style: .interactive) + phoneView.set(title: phoneTitle, style: .interactive) + + stackView.spacing = 41 + stackView.axis = .vertical + stackView.addArrangedSubview(emailView) + stackView.addArrangedSubview(phoneView) + + addSubview(stackView) + addSubview(cardComponent) + + cardComponent.snp.makeConstraints { + $0.top.equalToSuperview() + $0.left.equalToSuperview() + $0.right.equalToSuperview() + } + + stackView.snp.makeConstraints { + $0.top.equalTo(cardComponent.snp.bottom).offset(24) + $0.left.equalToSuperview().offset(24) + $0.right.equalToSuperview().offset(-26) + $0.bottom.lessThanOrEqualToSuperview() } + } - required init?(coder: NSCoder) { nil } + required init?(coder: NSCoder) { nil } } diff --git a/Sources/PushFeature/MockPushHandler.swift b/Sources/PushFeature/MockPushHandler.swift index 41aced17ade52d9c4b02ec092feec29276fc21fb..385175c34179ad084651fd87a6d873612d758cff 100644 --- a/Sources/PushFeature/MockPushHandler.swift +++ b/Sources/PushFeature/MockPushHandler.swift @@ -1,39 +1,39 @@ import UIKit public struct MockPushHandler: PushHandling { - public init() {} - - public func registerToken(_ token: Data) { - // TODO - } - - public func requestAuthorization( - _ completion: @escaping (Result<Bool, Error>) -> Void - ) { - completion(.success(true)) - } - - public func handlePush( - _ notification: [AnyHashable : Any], - _ completion: @escaping (UIBackgroundFetchResult) -> Void - ) { - completion(.noData) - } - - public func handlePush( - _ request: UNNotificationRequest, - _ completion: @escaping (UNNotificationContent) -> Void - ) { - let content = UNMutableNotificationContent() - content.title = String(describing: Self.self) - completion(content) - } - - public func handleAction( - _ router: PushRouter, - _ userInfo: [AnyHashable : Any], - _ completion: @escaping () -> Void - ) { - completion() - } + public init() {} + + public func registerToken(_ token: Data) { + // TODO + } + + public func requestAuthorization( + _ completion: @escaping (Result<Bool, Error>) -> Void + ) { + completion(.success(true)) + } + + public func handlePush( + _ notification: [AnyHashable : Any], + _ completion: @escaping (UIBackgroundFetchResult) -> Void + ) { + completion(.noData) + } + + public func handlePush( + _ request: UNNotificationRequest, + _ completion: @escaping (UNNotificationContent) -> Void + ) { + let content = UNMutableNotificationContent() + content.title = String(describing: Self.self) + completion(content) + } + + public func handleAction( + _ router: PushRouter, + _ userInfo: [AnyHashable : Any], + _ completion: @escaping () -> Void + ) { + completion() + } } diff --git a/Sources/PushFeature/PushExtractor.swift b/Sources/PushFeature/PushExtractor.swift index 75c6a69070d0d301498122896ac90ab2ab48c987..ee584586e407307ac3bf7bc8e71f597e19df5485 100644 --- a/Sources/PushFeature/PushExtractor.swift +++ b/Sources/PushFeature/PushExtractor.swift @@ -2,7 +2,6 @@ import XXModels import XXClient import Foundation import XXMessengerClient -import DI public struct PushExtractor { enum Constants { diff --git a/Sources/PushFeature/PushHandler.swift b/Sources/PushFeature/PushHandler.swift index 1be9e28d179954a659b615f236825a753bd468bf..86bb7a29a27d702e639f1c8813340b43f37e7907 100644 --- a/Sources/PushFeature/PushHandler.swift +++ b/Sources/PushFeature/PushHandler.swift @@ -1,11 +1,12 @@ import UIKit +import AppCore import Defaults import XXClient import XXModels import XXDatabase -import XXMessengerClient import ReportingFeature -import DI +import XXMessengerClient +import ComposableArchitecture public final class PushHandler: PushHandling { private enum Constants { @@ -13,7 +14,7 @@ public final class PushHandler: PushHandling { static let usernamesSetting = "isShowingUsernames" } - @Dependency var messenger: Messenger + @Dependency(\.app.messenger) var messenger: Messenger @KeyObject(.pushNotifications, defaultValue: false) var isPushEnabled: Bool diff --git a/Sources/PushFeature/PushHandling.swift b/Sources/PushFeature/PushHandling.swift index c17c7e600d9c8f3ed222f3e7b8e1d6db035d85ce..1fe3a41bbcf099f3676d821248cf5e4922622f09 100644 --- a/Sources/PushFeature/PushHandling.swift +++ b/Sources/PushFeature/PushHandling.swift @@ -1,70 +1,69 @@ import UIKit public protocol PushHandling { + /// Submits the APNS token to a 3rd-party service. + /// This should be called whenever the user accepts + /// receiving remote push notifications. + /// + /// - Parameters: + /// - token: The APNS provided token + /// + func registerToken( + _ token: Data + ) - /// Submits the APNS token to a 3rd-party service. - /// This should be called whenever the user accepts - /// receiving remote push notifications. - /// - /// - Parameters: - /// - token: The APNS provided token - /// - func registerToken( - _ token: Data - ) + /// Prompts a system alert to the user requesting + /// permission for receiving remote push notifications + /// + /// - Parameters: + /// - completion: Async result closure containing the user reponse + /// + func requestAuthorization( + _ completion: @escaping (Result<Bool, Error>) -> Void + ) - /// Prompts a system alert to the user requesting - /// permission for receiving remote push notifications - /// - /// - Parameters: - /// - completion: Async result closure containing the user reponse - /// - func requestAuthorization( - _ completion: @escaping (Result<Bool, Error>) -> Void - ) + /// Evaluates if the notification should be displayed or not + /// and if yes, how should it look like. + /// + /// - Note: This function should be called by the main app target + /// - Warning: The notifications should only appear if the app is in background + /// + /// - Parameters: + /// - userInfo: Dictionary contaning the payload of the remote push + /// - completion: Async closure containing the operation chosed + /// + func handlePush( + _ userInfo: [AnyHashable: Any], + _ completion: @escaping (UIBackgroundFetchResult) -> Void + ) - /// Evaluates if the notification should be displayed or not - /// and if yes, how should it look like. - /// - /// - Note: This function should be called by the main app target - /// - Warning: The notifications should only appear if the app is in background - /// - /// - Parameters: - /// - userInfo: Dictionary contaning the payload of the remote push - /// - completion: Async closure containing the operation chosed - /// - func handlePush( - _ userInfo: [AnyHashable: Any], - _ completion: @escaping (UIBackgroundFetchResult) -> Void - ) + /// Evaluates if the notification should be displayed or not + /// and if yes, how it should look like and who is it from + /// + /// - Note: This function should be called by the `NotificationExtension` + /// + /// - Parameters: + /// - request: The notification request that arrived for the `NotificationExtension` + /// - completion: Async closure containing the operation chosed + /// + func handlePush( + _ request: UNNotificationRequest, + _ completion: @escaping (UNNotificationContent) -> Void + ) - /// Evaluates if the notification should be displayed or not - /// and if yes, how it should look like and who is it from - /// - /// - Note: This function should be called by the `NotificationExtension` - /// - /// - Parameters: - /// - request: The notification request that arrived for the `NotificationExtension` - /// - completion: Async closure containing the operation chosed - /// - func handlePush( - _ request: UNNotificationRequest, - _ completion: @escaping (UNNotificationContent) -> Void - ) - - /// Deeplinks to any UI flow set within the notification. - /// It can get called either when the user starts the app - /// from a notification or when the user has the app in - /// background and resumes the app by tapping on a push - /// - /// - Parameters: - /// - router: Router instance that will decide the correct UI flow - /// - userInfo: Dictionary contaning the payload of the notification - /// - completion: Async empty closure - /// - func handleAction( - _ router: PushRouter, - _ userInfo: [AnyHashable: Any], - _ completion: @escaping () -> Void - ) + /// Deeplinks to any UI flow set within the notification. + /// It can get called either when the user starts the app + /// from a notification or when the user has the app in + /// background and resumes the app by tapping on a push + /// + /// - Parameters: + /// - router: Router instance that will decide the correct UI flow + /// - userInfo: Dictionary contaning the payload of the notification + /// - completion: Async empty closure + /// + func handleAction( + _ router: PushRouter, + _ userInfo: [AnyHashable: Any], + _ completion: @escaping () -> Void + ) } diff --git a/Sources/PushFeature/PushRouter.swift b/Sources/PushFeature/PushRouter.swift index 05885d5196b6025f24d18bd47f5922f1164d1f43..942f747ec8f4df9559d59f12e9a8c3334dacf5ea 100644 --- a/Sources/PushFeature/PushRouter.swift +++ b/Sources/PushFeature/PushRouter.swift @@ -1,23 +1,23 @@ import Foundation public struct PushRouter { - public typealias NavigateTo = (Route, @escaping () -> Void) -> Void + public typealias NavigateTo = (Route, @escaping () -> Void) -> Void - public enum Route { - case requests - case groupChat(id: Data) - case contactChat(id: Data) - case search(username: String) - } + public enum Route { + case requests + case groupChat(id: Data) + case contactChat(id: Data) + case search(username: String) + } - public var navigateTo: NavigateTo + public var navigateTo: NavigateTo - public init(navigateTo: @escaping NavigateTo) { - self.navigateTo = navigateTo - } + public init(navigateTo: @escaping NavigateTo) { + self.navigateTo = navigateTo + } } public extension PushRouter { - static let noop = PushRouter { _, _ in } + static let noop = PushRouter { _, _ in } } diff --git a/Sources/ReportingFeature/Dependency.swift b/Sources/ReportingFeature/Dependency.swift new file mode 100644 index 0000000000000000000000000000000000000000..33e19ae1c98c65fba2b084c9dc0bbf5e10c03c33 --- /dev/null +++ b/Sources/ReportingFeature/Dependency.swift @@ -0,0 +1,25 @@ +import Dependencies + +private enum ReportingStatusDependencyKey: DependencyKey { + static let liveValue: ReportingStatus = .live() + static let testValue: ReportingStatus = .unimplemented +} + +extension DependencyValues { + public var reportingStatus: ReportingStatus { + get { self[ReportingStatusDependencyKey.self] } + set { self[ReportingStatusDependencyKey.self] = newValue } + } +} + +private enum SendReportDependencyKey: DependencyKey { + static let liveValue: SendReport = .live + static let testValue: SendReport = .unimplemented +} + +extension DependencyValues { + public var sendReport: SendReport { + get { self[SendReportDependencyKey.self] } + set { self[SendReportDependencyKey.self] = newValue } + } +} diff --git a/Sources/ReportingFeature/FetchBannedList.swift b/Sources/ReportingFeature/FetchBannedList.swift deleted file mode 100644 index 2620b15c84e5d31316f663a363ff6a4526d1880d..0000000000000000000000000000000000000000 --- a/Sources/ReportingFeature/FetchBannedList.swift +++ /dev/null @@ -1,46 +0,0 @@ -import Foundation -import XCTestDynamicOverlay - -public struct FetchBannedList { - public enum Error: Swift.Error, Equatable { - case network(URLError) - case invalidResponse - } - - public typealias Completion = (Result<Data, Error>) -> Void - - public var run: (@escaping Completion) -> Void - - public func callAsFunction(completion: @escaping Completion) { - run(completion) - } -} - -extension FetchBannedList { - public static let live = FetchBannedList { completion in - let url = URL(string: "https://elixxir-bins.s3.us-west-1.amazonaws.com/client/bannedUsers/banned.csv")! - let session = URLSession.shared - let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData) - let task = session.dataTask(with: request) { data, response, error in - if let error = error { - completion(.failure(.network(error as! URLError))) - return - } - guard let response = response as? HTTPURLResponse, - (200..<300).contains(response.statusCode), - let data = data - else { - completion(.failure(.invalidResponse)) - return - } - completion(.success(data)) - } - task.resume() - } -} - -extension FetchBannedList { - public static let unimplemented = FetchBannedList( - run: XCTUnimplemented("\(Self.self)") - ) -} diff --git a/Sources/ReportingFeature/MakeAppScreenshot.swift b/Sources/ReportingFeature/MakeAppScreenshot.swift index 7d44af879f35d792685f3e278cc6ce9c044d8a07..75a1fc31e6139e9a3fc2510eb811f00ffe37a628 100644 --- a/Sources/ReportingFeature/MakeAppScreenshot.swift +++ b/Sources/ReportingFeature/MakeAppScreenshot.swift @@ -3,51 +3,51 @@ import UIKit import XCTestDynamicOverlay public struct MakeAppScreenshot { - public enum Error: Swift.Error, Equatable { - case unableToGetForegroundWindowScene - case unableToGetKeyWindow - } - - public var run: () throws -> UIImage - - public func callAsFunction() throws -> UIImage { - try run() - } + public enum Error: Swift.Error, Equatable { + case unableToGetForegroundWindowScene + case unableToGetKeyWindow + } + + public var run: () throws -> UIImage + + public func callAsFunction() throws -> UIImage { + try run() + } } extension MakeAppScreenshot { - public static let live = MakeAppScreenshot { - let scene: UIWindowScene? = UIApplication.shared.connectedScenes - .filter { $0.activationState == .foregroundActive } - .compactMap { $0 as? UIWindowScene } - .first - - guard let scene = scene else { - throw Error.unableToGetForegroundWindowScene - } - - let window: UIWindow? = scene.windows.first(where: \.isKeyWindow) - - guard let keyWindow = window else { - throw Error.unableToGetKeyWindow - } - - let rendererFormat = UIGraphicsImageRendererFormat() - rendererFormat.scale = scene.screen.scale - - let renderer = UIGraphicsImageRenderer( - bounds: keyWindow.bounds, - format: rendererFormat - ) - - return renderer.image { ctx in - keyWindow.layer.render(in: ctx.cgContext) - } + public static let live = MakeAppScreenshot { + let scene: UIWindowScene? = UIApplication.shared.connectedScenes + .filter { $0.activationState == .foregroundActive } + .compactMap { $0 as? UIWindowScene } + .first + + guard let scene = scene else { + throw Error.unableToGetForegroundWindowScene + } + + let window: UIWindow? = scene.windows.first(where: \.isKeyWindow) + + guard let keyWindow = window else { + throw Error.unableToGetKeyWindow } + + let rendererFormat = UIGraphicsImageRendererFormat() + rendererFormat.scale = scene.screen.scale + + let renderer = UIGraphicsImageRenderer( + bounds: keyWindow.bounds, + format: rendererFormat + ) + + return renderer.image { ctx in + keyWindow.layer.render(in: ctx.cgContext) + } + } } extension MakeAppScreenshot { - public static let unimplemented = MakeAppScreenshot( - run: XCTUnimplemented("\(Self.self)") - ) + public static let unimplemented = MakeAppScreenshot( + run: XCTUnimplemented("\(Self.self)") + ) } diff --git a/Sources/ReportingFeature/ProcessBannedList.swift b/Sources/ReportingFeature/ProcessBannedList.swift deleted file mode 100644 index 3399a34ee3614bc41df393251d1e21a9309cdf52..0000000000000000000000000000000000000000 --- a/Sources/ReportingFeature/ProcessBannedList.swift +++ /dev/null @@ -1,64 +0,0 @@ -import Foundation -import SwiftCSV -import XCTestDynamicOverlay - -public struct ProcessBannedList { - public enum ElementError: Swift.Error { - case missingUserId - case invalidUserId(String) - } - - public enum Error: Swift.Error { - case invalidData - case csv(Swift.Error) - } - - public typealias ForEach = (Result<Data, ElementError>) -> Void - public typealias Completion = (Result<Void, Error>) -> Void - - public var run: (Data, ForEach, Completion) -> Void - - public func callAsFunction( - data: Data, - forEach: ForEach, - completion: Completion - ) { - run(data, forEach, completion) - } -} - -extension ProcessBannedList { - public static let live = ProcessBannedList { data, forEach, completion in - guard let csvString = String(data: data, encoding: .utf8) else { - completion(.failure(.invalidData)) - return - } - let csv: EnumeratedCSV - do { - csv = try EnumeratedCSV(string: csvString) - } - catch { - completion(.failure(.csv(error))) - return - } - csv.rows.forEach { row in - guard let userIdString = row.first else { - forEach(.failure(.missingUserId)) - return - } - guard let userId = Data(base64Encoded: userIdString) else { - forEach(.failure(.invalidUserId(userIdString))) - return - } - forEach(.success(userId)) - } - completion(.success(())) - } -} - -extension ProcessBannedList { - public static let unimplemented = ProcessBannedList { _, _, _ in - let run: () -> Void = XCTUnimplemented("\(Self.self)") - run() - } -} diff --git a/Sources/ReportingFeature/Report.swift b/Sources/ReportingFeature/Report.swift index c2032b6a6b87d096f8f6883085d4c6e887630600..56ff6192eeee64004e4cb5a3b5d978017d719998 100644 --- a/Sources/ReportingFeature/Report.swift +++ b/Sources/ReportingFeature/Report.swift @@ -1,52 +1,52 @@ import Foundation public struct Report: Encodable { - public init( - sender: ReportUser, - recipient: ReportUser, - type: ReportType, - screenshot: Data, - partyName: String? = nil, - partyBlob: String? = nil, - partyMembers: [ReportUser]? = nil - ) { - self.sender = sender - self.recipient = recipient - self.type = type - self.screenshot = screenshot - self.partyName = partyName - self.partyBlob = partyBlob - self.partyMembers = partyMembers - } - - public var sender: ReportUser - public var recipient: ReportUser - public var type: ReportType - public var screenshot: Data - public var partyName: String? - public var partyBlob: String? - public var partyMembers: [ReportUser]? + public init( + sender: ReportUser, + recipient: ReportUser, + type: ReportType, + screenshot: Data, + partyName: String? = nil, + partyBlob: String? = nil, + partyMembers: [ReportUser]? = nil + ) { + self.sender = sender + self.recipient = recipient + self.type = type + self.screenshot = screenshot + self.partyName = partyName + self.partyBlob = partyBlob + self.partyMembers = partyMembers + } + + public var sender: ReportUser + public var recipient: ReportUser + public var type: ReportType + public var screenshot: Data + public var partyName: String? + public var partyBlob: String? + public var partyMembers: [ReportUser]? } extension Report { - public struct ReportUser: Encodable { - public init( - userId: String, - username: String - ) { - self.userId = userId - self.username = username - } - - public var userId: String - public var username: String + public struct ReportUser: Encodable { + public init( + userId: String, + username: String + ) { + self.userId = userId + self.username = username } + + public var userId: String + public var username: String + } } extension Report { - public enum ReportType: String, Encodable { - case dm - case group - case channel - } + public enum ReportType: String, Encodable { + case dm + case group + case channel + } } diff --git a/Sources/ReportingFeature/ReportingStatus.swift b/Sources/ReportingFeature/ReportingStatus.swift index f6346ce1350caa9bca8ead86515724a5edf7171d..2da0592e647cc059be8d2152e72a4e71c81d5689 100644 --- a/Sources/ReportingFeature/ReportingStatus.swift +++ b/Sources/ReportingFeature/ReportingStatus.swift @@ -20,14 +20,14 @@ extension ReportingStatus { if isOptional.get() == false { return true } - + return isEnabled.get() }, isEnabledPublisher: { if isOptional.get() == false { return Just(true).eraseToAnyPublisher() } - + return isEnabled.publisher() }, enable: { enabled in @@ -35,7 +35,7 @@ extension ReportingStatus { } ) } - + public static func mock( isEnabled: Bool = false, isOptional: Bool = true @@ -48,4 +48,11 @@ extension ReportingStatus { enable: { isEnabledSubject.send($0) } ) } + + public static let unimplemented = ReportingStatus( + isOptional: { fatalError() }, + isEnabled: { fatalError() }, + isEnabledPublisher: { fatalError() }, + enable: { _ in } + ) } diff --git a/Sources/ReportingFeature/ReportingStatusIsEnabled.swift b/Sources/ReportingFeature/ReportingStatusIsEnabled.swift index 32f23fcb78a3b5e8fe7f6dc15a95a1b27e278bfb..ecf02aba50fff9f2334ad50dee4a6d2b8aa2dbb8 100644 --- a/Sources/ReportingFeature/ReportingStatusIsEnabled.swift +++ b/Sources/ReportingFeature/ReportingStatusIsEnabled.swift @@ -2,37 +2,37 @@ import Combine import Foundation public struct ReportingStatusIsEnabled { - public var get: () -> Bool - public var set: (Bool) -> Void - public var publisher: () -> AnyPublisher<Bool, Never> + public var get: () -> Bool + public var set: (Bool) -> Void + public var publisher: () -> AnyPublisher<Bool, Never> } extension ReportingStatusIsEnabled { - public static func live( - userDefaults: UserDefaults = .standard - ) -> ReportingStatusIsEnabled { - ReportingStatusIsEnabled( - get: { - userDefaults.isReportingEnabled - }, - set: { enabled in - userDefaults.isReportingEnabled = enabled - }, - publisher: { - userDefaults.publisher(for: \.isReportingEnabled).eraseToAnyPublisher() - } - ) - } + public static func live( + userDefaults: UserDefaults = .standard + ) -> ReportingStatusIsEnabled { + ReportingStatusIsEnabled( + get: { + userDefaults.isReportingEnabled + }, + set: { enabled in + userDefaults.isReportingEnabled = enabled + }, + publisher: { + userDefaults.publisher(for: \.isReportingEnabled).eraseToAnyPublisher() + } + ) + } } private extension UserDefaults { - static let isReportingEnabledKey = "isReportingEnabled" - - @objc var isReportingEnabled: Bool { - get { - bool(forKey: Self.isReportingEnabledKey) - } set { - set(newValue, forKey: Self.isReportingEnabledKey) - } + static let isReportingEnabledKey = "isReportingEnabled" + + @objc var isReportingEnabled: Bool { + get { + bool(forKey: Self.isReportingEnabledKey) + } set { + set(newValue, forKey: Self.isReportingEnabledKey) } + } } diff --git a/Sources/ReportingFeature/ReportingStatusIsOptional.swift b/Sources/ReportingFeature/ReportingStatusIsOptional.swift index e0cc6591bf1d13c68d022a2f8587cc495403f89d..ca5ad5654ba72678496581b7ac3aae56626bc577 100644 --- a/Sources/ReportingFeature/ReportingStatusIsOptional.swift +++ b/Sources/ReportingFeature/ReportingStatusIsOptional.swift @@ -1,24 +1,24 @@ import Foundation public struct ReportingStatusIsOptional { - public var get: () -> Bool + public var get: () -> Bool } extension ReportingStatusIsOptional { - public static func live( - plist url: URL = Bundle.main.url(forResource: "Info", withExtension: "plist")! - ) -> ReportingStatusIsOptional { - ReportingStatusIsOptional { - struct Plist: Decodable { - let isReportingOptional: Bool - } - - guard let data = try? Data(contentsOf: url), - let infoPlist = try? PropertyListDecoder().decode(Plist.self, from: data) else { - return true - } - - return infoPlist.isReportingOptional - } + public static func live( + plist url: URL = Bundle.main.url(forResource: "Info", withExtension: "plist")! + ) -> ReportingStatusIsOptional { + ReportingStatusIsOptional { + struct Plist: Decodable { + let isReportingOptional: Bool + } + + guard let data = try? Data(contentsOf: url), + let infoPlist = try? PropertyListDecoder().decode(Plist.self, from: data) else { + return true + } + + return infoPlist.isReportingOptional } + } } diff --git a/Sources/ReportingFeature/SendReport.swift b/Sources/ReportingFeature/SendReport.swift index 4793d998d49c43426012ac9760cd28e39e8eb8e6..678bfbc3d1ce2a8339975e74b56330591b6ff06d 100644 --- a/Sources/ReportingFeature/SendReport.swift +++ b/Sources/ReportingFeature/SendReport.swift @@ -2,93 +2,93 @@ import Foundation import XCTestDynamicOverlay public struct SendReport { - public typealias Completion = (Result<Void, Error>) -> Void + public typealias Completion = (Result<Void, Error>) -> Void - public var run: (Report, @escaping Completion) -> Void + public var run: (Report, @escaping Completion) -> Void - public func callAsFunction(_ report: Report, completion: @escaping Completion) { - run(report, completion) - } + public func callAsFunction(_ report: Report, completion: @escaping Completion) { + run(report, completion) + } } extension SendReport { - public static let live = SendReport { report, completion in - let url = URL(string: "https://3.74.237.181:11420/report")! - var request = URLRequest(url: url) - request.httpMethod = "POST" - do { - request.httpBody = try JSONEncoder().encode(report) - } catch { - completion(.failure(error)) - return - } - let session = URLSession( - configuration: .default, - delegate: SessionDelegate(), - delegateQueue: nil - ) - let task = session.dataTask(with: request) { _, _, error in - defer { session.invalidateAndCancel() } - if let error = error { - completion(.failure(error)) - return - } - completion(.success(())) - } - task.resume() + public static let live = SendReport { report, completion in + let url = URL(string: "https://3.74.237.181:11420/report")! + var request = URLRequest(url: url) + request.httpMethod = "POST" + do { + request.httpBody = try JSONEncoder().encode(report) + } catch { + completion(.failure(error)) + return + } + let session = URLSession( + configuration: .default, + delegate: SessionDelegate(), + delegateQueue: nil + ) + let task = session.dataTask(with: request) { _, _, error in + defer { session.invalidateAndCancel() } + if let error = error { + completion(.failure(error)) + return + } + completion(.success(())) } + task.resume() + } - public static func mock( - result: Result<Void, Error> = .success(()) - ) -> SendReport { - SendReport { report, completion in - print("[SendReport.mock] Sending report: \(report)") - DispatchQueue.main.asyncAfter(deadline: .now() + 3) { - print("[SendReport.mock] Sending report finished") - completion(result) - } - } + public static func mock( + result: Result<Void, Error> = .success(()) + ) -> SendReport { + SendReport { report, completion in + print("[SendReport.mock] Sending report: \(report)") + DispatchQueue.main.asyncAfter(deadline: .now() + 3) { + print("[SendReport.mock] Sending report finished") + completion(result) + } } + } } extension SendReport { - public static let unimplemented = SendReport( - run: XCTUnimplemented("\(Self.self)") - ) + public static let unimplemented = SendReport( + run: XCTUnimplemented("\(Self.self)") + ) } private final class SessionDelegate: NSObject, URLSessionDelegate { - func urlSession( - _ session: URLSession, - didReceive challenge: URLAuthenticationChallenge, - completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void - ) { - let authMethod = challenge.protectionSpace.authenticationMethod - guard authMethod == NSURLAuthenticationMethodServerTrust else { - return completionHandler(.cancelAuthenticationChallenge, nil) - } - - guard let serverTrust = challenge.protectionSpace.serverTrust else { - return completionHandler(.cancelAuthenticationChallenge, nil) - } + func urlSession( + _ session: URLSession, + didReceive challenge: URLAuthenticationChallenge, + completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void + ) { + let authMethod = challenge.protectionSpace.authenticationMethod + guard authMethod == NSURLAuthenticationMethodServerTrust else { + return completionHandler(.cancelAuthenticationChallenge, nil) + } - guard let serverCert = SecTrustGetCertificateAtIndex(serverTrust, 0) else { - return completionHandler(.cancelAuthenticationChallenge, nil) - } + guard let serverTrust = challenge.protectionSpace.serverTrust else { + return completionHandler(.cancelAuthenticationChallenge, nil) + } - let serverCertCFData = SecCertificateCopyData(serverCert) - let serverCertData = Data( - bytes: CFDataGetBytePtr(serverCertCFData), - count: CFDataGetLength(serverCertCFData) - ) + guard let serverCert = SecTrustGetCertificateAtIndex(serverTrust, 0) else { + return completionHandler(.cancelAuthenticationChallenge, nil) + } - let localCertURL = Bundle.module.url(forResource: "report_cert", withExtension: "der")! - let localCertData = try! Data(contentsOf: localCertURL) + let serverCertCFData = SecCertificateCopyData(serverCert) + let serverCertData = Data( + bytes: CFDataGetBytePtr(serverCertCFData), + count: CFDataGetLength(serverCertCFData) + ) - guard serverCertData == localCertData else { - return completionHandler(.cancelAuthenticationChallenge, nil) - } + let localCertURL = Bundle.module.url(forResource: "report_cert", withExtension: "der")! + let localCertData = try! Data(contentsOf: localCertURL) - completionHandler(.useCredential, URLCredential(trust: serverTrust)) + guard serverCertData == localCertData else { + return completionHandler(.cancelAuthenticationChallenge, nil) } + + completionHandler(.useCredential, URLCredential(trust: serverTrust)) + } } diff --git a/Sources/RequestPermissionFeature/RequestPermissionController.swift b/Sources/RequestPermissionFeature/RequestPermissionController.swift index 2a8e364d2315742f0e0d674549830ac9a9778744..9c12752c92e20763c81647fe501e749d014eca3e 100644 --- a/Sources/RequestPermissionFeature/RequestPermissionController.swift +++ b/Sources/RequestPermissionFeature/RequestPermissionController.swift @@ -1,13 +1,14 @@ import UIKit import Shared import Combine +import AppCore +import Dependencies import AppResources -import StatusBarFeature +import AppNavigation import PermissionsFeature -import ComposableArchitecture public final class RequestPermissionController: UIViewController { - @Dependency(\.statusBar) var statusBar: StatusBarStyleManager + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist @Dependency(\.permissions) var permissions: PermissionsManager private lazy var screenView = RequestPermissionView() @@ -28,7 +29,7 @@ public final class RequestPermissionController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - statusBar.update(.darkContent) + statusBar.set(.darkContent) } public override func viewDidLoad() { diff --git a/Sources/RequestsFeature/Controllers/RequestsContainerController.swift b/Sources/RequestsFeature/Controllers/RequestsContainerController.swift index 97615508af52356b838b25614e59dc15d29da2b6..11a4270c77ca96b452a2bbbcc1fcb2d3f9df560f 100644 --- a/Sources/RequestsFeature/Controllers/RequestsContainerController.swift +++ b/Sources/RequestsFeature/Controllers/RequestsContainerController.swift @@ -1,13 +1,15 @@ import UIKit import Shared import Combine -import Navigation +import AppCore +import AppResources +import Dependencies +import AppNavigation import ContactFeature -import DI public final class RequestsContainerController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var barStylist: StatusBarStylist + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = RequestsContainerView() private var cancellables = Set<AnyCancellable>() @@ -29,7 +31,7 @@ public final class RequestsContainerController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - barStylist.styleSubject.send(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar .customize(backgroundColor: Asset.neutralWhite.color) diff --git a/Sources/RequestsFeature/Controllers/RequestsFailedController.swift b/Sources/RequestsFeature/Controllers/RequestsFailedController.swift index 7f3f63fa70018e906325f7129a92711331bb0a09..a7e65e87d510deaac71d9a0b3a2024c8d58f99b3 100644 --- a/Sources/RequestsFeature/Controllers/RequestsFailedController.swift +++ b/Sources/RequestsFeature/Controllers/RequestsFailedController.swift @@ -1,6 +1,5 @@ import UIKit import Combine -import DI final class RequestsFailedController: UIViewController { private lazy var screenView = RequestsFailedView() diff --git a/Sources/RequestsFeature/Controllers/RequestsReceivedController.swift b/Sources/RequestsFeature/Controllers/RequestsReceivedController.swift index 674e3bd10232c2d4e615200526e4c5ebe1c2264b..3ee84bb9e1b29b539448cb11e222fbcf3c452ce8 100644 --- a/Sources/RequestsFeature/Controllers/RequestsReceivedController.swift +++ b/Sources/RequestsFeature/Controllers/RequestsReceivedController.swift @@ -2,14 +2,16 @@ import UIKit import Shared import Combine import XXModels -import Navigation +import AppCore +import AppResources +import Dependencies +import AppNavigation import DrawerFeature import CountryListFeature -import DI final class RequestsReceivedController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var toaster: ToastController + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.toastManager) var toaster: ToastManager private lazy var screenView = RequestsReceivedView() private var cancellables = Set<AnyCancellable>() @@ -265,7 +267,7 @@ extension RequestsReceivedController { navigator.perform(DismissModal(from: self)) { [weak self] in guard let self else { return } self.drawerCancellables.removeAll() - self.navigator.perform(PresentChat(contact: contact, on: navigationController!)) + self.navigator.perform(PresentChat(contact: contact, on: self.navigationController!)) } }.store(in: &drawerCancellables) diff --git a/Sources/RequestsFeature/Controllers/RequestsSentController.swift b/Sources/RequestsFeature/Controllers/RequestsSentController.swift index 25a35aae40e1a0d673827ac39876192c79e18329..a2000f74cd532e7c90f404c79e1f4225ea919a7f 100644 --- a/Sources/RequestsFeature/Controllers/RequestsSentController.swift +++ b/Sources/RequestsFeature/Controllers/RequestsSentController.swift @@ -1,6 +1,5 @@ import UIKit import Combine -import DI final class RequestsSentController: UIViewController { var connectionsPublisher: AnyPublisher<Void, Never> { diff --git a/Sources/RequestsFeature/ViewModels/RequestsFailedViewModel.swift b/Sources/RequestsFeature/ViewModels/RequestsFailedViewModel.swift index 5354509e3df44e0b63b05211338751535ae8c15b..6481b67c4a5c25a148980b6a3ed2d454d61f7822 100644 --- a/Sources/RequestsFeature/ViewModels/RequestsFailedViewModel.swift +++ b/Sources/RequestsFeature/ViewModels/RequestsFailedViewModel.swift @@ -1,17 +1,18 @@ import UIKit import Shared +import AppCore import Combine import XXModels import Defaults import XXClient +import Dependencies import CombineSchedulers -import DI import XXMessengerClient final class RequestsFailedViewModel { - @Dependency var database: Database - @Dependency var messenger: Messenger - @Dependency var hudController: HUDController + @Dependency(\.app.dbManager) var dbManager: DBManager + @Dependency(\.app.messenger) var messenger: Messenger + @Dependency(\.app.hudManager) var hudManager: HUDManager @KeyObject(.username, defaultValue: nil) var username: String? @KeyObject(.sharingEmail, defaultValue: false) var sharingEmail: Bool @@ -27,7 +28,7 @@ final class RequestsFailedViewModel { var backgroundScheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler() init() { - database.fetchContactsPublisher(.init(authStatus: [.requestFailed, .confirmationFailed])) + try! dbManager.getDB().fetchContactsPublisher(.init(authStatus: [.requestFailed, .confirmationFailed])) .replaceError(with: []) .map { data -> NSDiffableDataSourceSnapshot<Section, Request> in var snapshot = NSDiffableDataSourceSnapshot<Section, Request>() @@ -45,7 +46,7 @@ final class RequestsFailedViewModel { return } - hudController.show() + hudManager.show() backgroundScheduler.schedule { [weak self] in guard let self else { return } @@ -81,11 +82,11 @@ final class RequestsFailedViewModel { contact.authStatus = .friend } - try self.database.saveContact(contact) - self.hudController.dismiss() + try self.dbManager.getDB().saveContact(contact) + self.hudManager.hide() } catch { let xxError = CreateUserFriendlyErrorMessage.live(error.localizedDescription) - self.hudController.show(.init(content: xxError)) + self.hudManager.show(.init(content: xxError)) } } } diff --git a/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift b/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift index 466050340082d44c5bb768c3cd02e7011c6671ca..eb8469c67044a3cb962927a389da2831eb56ee4b 100644 --- a/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift +++ b/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift @@ -1,13 +1,14 @@ import UIKit import Shared +import AppCore import Combine import Defaults import XXModels import XXClient +import Dependencies import DrawerFeature import ReportingFeature import CombineSchedulers -import DI import XXMessengerClient import struct XXModels.Group @@ -19,11 +20,12 @@ struct RequestReceived: Hashable, Equatable { } final class RequestsReceivedViewModel { - @Dependency var database: Database - @Dependency var messenger: Messenger - @Dependency var groupManager: GroupChat - @Dependency var hudController: HUDController - @Dependency var reportingStatus: ReportingStatus + @Dependency(\.app.dbManager) var dbManager: DBManager + @Dependency(\.app.messenger) var messenger: Messenger + @Dependency(\.app.hudManager) var hudManager: HUDManager + @Dependency(\.reportingStatus) var reportingStatus: ReportingStatus + + //@Dependency var groupManager: GroupChat @KeyObject(.isShowingHiddenRequests, defaultValue: false) var isShowingHiddenRequests: Bool @@ -74,11 +76,11 @@ final class RequestsReceivedViewModel { isBanned: reportingStatus.isEnabled() ? false : nil ) - let groupStream = database + let groupStream = try! dbManager.getDB() .fetchGroupsPublisher(groupsQuery) .replaceError(with: []) - let contactsStream = database + let contactsStream = try! dbManager.getDB() .fetchContactsPublisher(contactsQuery) .replaceError(with: []) @@ -151,7 +153,7 @@ final class RequestsReceivedViewModel { do { contact.authStatus = .verificationInProgress - try self.database.saveContact(contact) + try self.dbManager.getDB().saveContact(contact) print(">>> [messenger.verifyContact] will start") @@ -159,17 +161,17 @@ final class RequestsReceivedViewModel { print(">>> [messenger.verifyContact] verified") contact.authStatus = .verified - contact = try self.database.saveContact(contact) + contact = try self.dbManager.getDB().saveContact(contact) } else { print(">>> [messenger.verifyContact] is fake") - try self.database.deleteContact(contact) + try self.dbManager.getDB().deleteContact(contact) } } catch { print(">>> [messenger.verifyContact] thrown an exception: \(error.localizedDescription)") contact.authStatus = .verificationFailed - _ = try? self.database.saveContact(contact) + _ = try? self.dbManager.getDB().saveContact(contact) } } } else if request.status == .verifying { @@ -178,29 +180,29 @@ final class RequestsReceivedViewModel { } func didRequestHide(group: Group) { - if var group = try? database.fetchGroups(.init(id: [group.id])).first { + if var group = try? dbManager.getDB().fetchGroups(.init(id: [group.id])).first { group.authStatus = .hidden - _ = try? database.saveGroup(group) + _ = try? dbManager.getDB().saveGroup(group) } } func didRequestAccept(group: Group) { - hudController.show() + hudManager.show() backgroundScheduler.schedule { [weak self] in guard let self else { return } do { - try self.groupManager.joinGroup(serializedGroupData: group.serialized) + //try self.groupManager.joinGroup(serializedGroupData: group.serialized) var group = group group.authStatus = .participating - try self.database.saveGroup(group) + try self.dbManager.getDB().saveGroup(group) - self.hudController.dismiss() + self.hudManager.hide() self.groupConfirmationSubject.send(group) } catch { - self.hudController.show(.init(error: error)) + self.hudManager.show(.init(error: error)) } } } @@ -209,8 +211,8 @@ final class RequestsReceivedViewModel { _ group: Group, _ completion: @escaping (Result<[DrawerTableCellModel], Error>) -> Void ) { - if let info = try? database.fetchGroupInfos(.init(groupId: group.id)).first { - database.fetchContactsPublisher(.init(id: Set(info.members.map(\.id)))) + if let info = try? dbManager.getDB().fetchGroupInfos(.init(groupId: group.id)).first { + try! dbManager.getDB().fetchContactsPublisher(.init(id: Set(info.members.map(\.id)))) .replaceError(with: []) .sink { members in let withUsername = members @@ -243,14 +245,14 @@ final class RequestsReceivedViewModel { } func didRequestHide(contact: XXModels.Contact) { - if var contact = try? database.fetchContacts(.init(id: [contact.id])).first { + if var contact = try? dbManager.getDB().fetchContacts(.init(id: [contact.id])).first { contact.authStatus = .hidden - _ = try? database.saveContact(contact) + _ = try? dbManager.getDB().saveContact(contact) } } func didRequestAccept(contact: XXModels.Contact, nickname: String? = nil) { - hudController.show() + hudManager.show() var contact = contact contact.authStatus = .confirming @@ -260,24 +262,24 @@ final class RequestsReceivedViewModel { guard let self else { return } do { - try self.database.saveContact(contact) + try self.dbManager.getDB().saveContact(contact) let _ = try self.messenger.e2e.get()!.confirmReceivedRequest(partner: .live(contact.marshaled!)) contact.authStatus = .friend - try self.database.saveContact(contact) + try self.dbManager.getDB().saveContact(contact) - self.hudController.dismiss() + self.hudManager.hide() self.contactConfirmationSubject.send(contact) } catch { contact.authStatus = .confirmationFailed - _ = try? self.database.saveContact(contact) - self.hudController.show(.init(error: error)) + _ = try? self.dbManager.getDB().saveContact(contact) + self.hudManager.show(.init(error: error)) } } } func groupChatWith(group: Group) -> GroupInfo { - guard let info = try? database.fetchGroupInfos(.init(groupId: group.id)).first else { + guard let info = try? dbManager.getDB().fetchGroupInfos(.init(groupId: group.id)).first else { fatalError() } diff --git a/Sources/RequestsFeature/ViewModels/RequestsSentViewModel.swift b/Sources/RequestsFeature/ViewModels/RequestsSentViewModel.swift index c527b0fbad997b09309e458ffe0df638b049bf65..abe6ba7b6c2e20c14115ce6f54535f841c693930 100644 --- a/Sources/RequestsFeature/ViewModels/RequestsSentViewModel.swift +++ b/Sources/RequestsFeature/ViewModels/RequestsSentViewModel.swift @@ -1,13 +1,15 @@ import UIKit import Shared +import AppCore import Combine import Defaults import XXModels import Defaults import XXClient +import AppResources +import Dependencies import ReportingFeature import CombineSchedulers -import DI import XXMessengerClient struct RequestSent: Hashable, Equatable { @@ -16,11 +18,11 @@ struct RequestSent: Hashable, Equatable { } final class RequestsSentViewModel { - @Dependency var database: Database - @Dependency var messenger: Messenger - @Dependency var hudController: HUDController - @Dependency var reportingStatus: ReportingStatus - @Dependency var toastController: ToastController + @Dependency(\.app.dbManager) var dbManager: DBManager + @Dependency(\.app.messenger) var messenger: Messenger + @Dependency(\.app.hudManager) var hudManager: HUDManager + @Dependency(\.app.toastManager) var toastManager: ToastManager + @Dependency(\.reportingStatus) var reportingStatus: ReportingStatus @KeyObject(.username, defaultValue: nil) var username: String? @KeyObject(.sharingEmail, defaultValue: false) var sharingEmail: Bool @@ -45,7 +47,7 @@ final class RequestsSentViewModel { isBanned: reportingStatus.isEnabled() ? false : nil ) - database.fetchContactsPublisher(query) + try! dbManager.getDB().fetchContactsPublisher(query) .replaceError(with: []) .removeDuplicates() .map { data -> NSDiffableDataSourceSnapshot<Section, RequestSent> in @@ -67,7 +69,7 @@ final class RequestsSentViewModel { let name = (contact.nickname ?? contact.username) ?? "" - hudController.show() + hudManager.show() backgroundScheduler.schedule { [weak self] in guard let self else { return } @@ -92,7 +94,7 @@ final class RequestsSentViewModel { myFacts: includedFacts ) - self.hudController.dismiss() + self.hudManager.hide() var item = item var allRequests = self.itemsSubject.value.itemIdentifiers @@ -104,7 +106,7 @@ final class RequestsSentViewModel { item.isResent = true allRequests.append(item) - self.toastController.enqueueToast(model: .init( + self.toastManager.enqueue(.init( title: Localized.Requests.Sent.Toast.resent(name), leftImage: Asset.requestSentToaster.image )) @@ -114,13 +116,13 @@ final class RequestsSentViewModel { snapshot.appendItems(allRequests, toSection: .appearing) self.itemsSubject.send(snapshot) } catch { - self.toastController.enqueueToast(model: .init( + self.toastManager.enqueue(.init( title: Localized.Requests.Sent.Toast.resentFailed(name), leftImage: Asset.requestFailedToaster.image )) let xxError = CreateUserFriendlyErrorMessage.live(error.localizedDescription) - self.hudController.show(.init(content: xxError)) + self.hudManager.show(.init(content: xxError)) } } } diff --git a/Sources/RequestsFeature/Views/RequestCell.swift b/Sources/RequestsFeature/Views/RequestCell.swift index c1c81d28222654804ad1b0332608ce807c080c9d..217e969783cff7cfe659711861ebee6a5ef51d99 100644 --- a/Sources/RequestsFeature/Views/RequestCell.swift +++ b/Sources/RequestsFeature/Views/RequestCell.swift @@ -1,6 +1,7 @@ import UIKit import Shared import Combine +import AppResources import CountryListFeature final class RequestCell: UICollectionViewCell { diff --git a/Sources/RequestsFeature/Views/RequestCellButton.swift b/Sources/RequestsFeature/Views/RequestCellButton.swift index b876ba691252c0a938ea40e162856a4102fb0ae2..005197063e8220863d70edec1cfc1c9a5e93bf5c 100644 --- a/Sources/RequestsFeature/Views/RequestCellButton.swift +++ b/Sources/RequestsFeature/Views/RequestCellButton.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class RequestCellButton: UIControl { let titleLabel = UILabel() diff --git a/Sources/RequestsFeature/Views/RequestReceivedEmptyCell.swift b/Sources/RequestsFeature/Views/RequestReceivedEmptyCell.swift index 574d63b624404285a5c1310706453bbd2f0b8d63..4239feca2ff52a7db5573e39b3dca1bff438bbe1 100644 --- a/Sources/RequestsFeature/Views/RequestReceivedEmptyCell.swift +++ b/Sources/RequestsFeature/Views/RequestReceivedEmptyCell.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class RequestReceivedEmptyCell: UICollectionViewCell { private let titleLabel = UILabel() diff --git a/Sources/RequestsFeature/Views/RequestSegmentedButton.swift b/Sources/RequestsFeature/Views/RequestSegmentedButton.swift index 36077491610b6f25bddebd8bc5801058c4d634ca..85f49138de97d52b9551ed2f8a659ddf562a9565 100644 --- a/Sources/RequestsFeature/Views/RequestSegmentedButton.swift +++ b/Sources/RequestsFeature/Views/RequestSegmentedButton.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class RequestSegmentedButton: UIControl { let titleLabel = UILabel() diff --git a/Sources/RequestsFeature/Views/RequestsContainerView.swift b/Sources/RequestsFeature/Views/RequestsContainerView.swift index a5188d080d1773a256ffb06cc6b8f7376c243f2b..222a9eb74e6ac3075d63fe34d6917db8bb6cc4d3 100644 --- a/Sources/RequestsFeature/Views/RequestsContainerView.swift +++ b/Sources/RequestsFeature/Views/RequestsContainerView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class RequestsContainerView: UIView { let scrollView = UIScrollView() diff --git a/Sources/RequestsFeature/Views/RequestsFailedView.swift b/Sources/RequestsFeature/Views/RequestsFailedView.swift index 86d76dc764f89478da0c6b4df19b02c95b02ebd4..b3ded76b653c0d8053e5476f42f0c1bfb62b9c08 100644 --- a/Sources/RequestsFeature/Views/RequestsFailedView.swift +++ b/Sources/RequestsFeature/Views/RequestsFailedView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class RequestsFailedView: UIView { let titleLabel = UILabel() diff --git a/Sources/RequestsFeature/Views/RequestsHiddenSectionHeader.swift b/Sources/RequestsFeature/Views/RequestsHiddenSectionHeader.swift index 7a8aed798d0c459ce1dd48c028ff12a23dab195f..9442a7a55097f411933b048522893da51afb0763 100644 --- a/Sources/RequestsFeature/Views/RequestsHiddenSectionHeader.swift +++ b/Sources/RequestsFeature/Views/RequestsHiddenSectionHeader.swift @@ -1,6 +1,7 @@ import UIKit import Shared import Combine +import AppResources final class RequestsHiddenSectionHeader: UICollectionReusableView { let titleLabel = UILabel() diff --git a/Sources/RequestsFeature/Views/RequestsReceivedView.swift b/Sources/RequestsFeature/Views/RequestsReceivedView.swift index f8c55b664deabb2c5bc0cff54e2246d048907f6b..5e60e04ac94f513c3e96018e2dd3762fedb8ca56 100644 --- a/Sources/RequestsFeature/Views/RequestsReceivedView.swift +++ b/Sources/RequestsFeature/Views/RequestsReceivedView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class RequestsReceivedView: UIView { lazy var collectionView: UICollectionView = { diff --git a/Sources/RequestsFeature/Views/RequestsSegmentedControl.swift b/Sources/RequestsFeature/Views/RequestsSegmentedControl.swift index d58fbf874ba461f7ee112732fd88c8fed8075a30..6467704207b98661ee3158a471264bb073b1aa01 100644 --- a/Sources/RequestsFeature/Views/RequestsSegmentedControl.swift +++ b/Sources/RequestsFeature/Views/RequestsSegmentedControl.swift @@ -1,6 +1,7 @@ import UIKit import Shared import SnapKit +import AppResources final class RequestsSegmentedControl: UIView { private let trackView = UIView() diff --git a/Sources/RequestsFeature/Views/RequestsSentView.swift b/Sources/RequestsFeature/Views/RequestsSentView.swift index 6c65a92b91b99464a4a1d7e3210ff655465987ba..1035850ca0165d44893bef7fc3750c0679ee6bcf 100644 --- a/Sources/RequestsFeature/Views/RequestsSentView.swift +++ b/Sources/RequestsFeature/Views/RequestsSentView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class RequestsSentView: UIView { let titleLabel = UILabel() diff --git a/Sources/RestoreFeature/Controllers/RestoreController.swift b/Sources/RestoreFeature/Controllers/RestoreController.swift index a58bdd1f8200c759660ec8398337dcf50c6a42fb..ac6f6ae1b3778411c35f750734fe2498371e3f90 100644 --- a/Sources/RestoreFeature/Controllers/RestoreController.swift +++ b/Sources/RestoreFeature/Controllers/RestoreController.swift @@ -1,13 +1,13 @@ -import DI import UIKit import Shared import Combine -import Navigation import AppResources +import AppNavigation import DrawerFeature +import ComposableArchitecture public final class RestoreController: UIViewController { - @Dependency var navigator: Navigator + @Dependency(\.navigator) var navigator: Navigator private lazy var screenView = RestoreView() @@ -56,8 +56,9 @@ public final class RestoreController: UIViewController { .sink { [unowned self] in screenView.updateFor(step: $0) if $0 == .wrongPass { - navigator.perform(PresentPassphrase(onCancel: { - navigator.perform(DismissModal(from: self)) + navigator.perform(PresentPassphrase(onCancel: { [weak self] in + guard let self else { return } + self.navigator.perform(DismissModal(from: self)) }, onPassphrase: { [weak self] passphrase in guard let self else { return } self.viewModel.retryWith(passphrase: passphrase) @@ -87,8 +88,9 @@ public final class RestoreController: UIViewController { .restoreButton .publisher(for: .touchUpInside) .sink { [unowned self] in - navigator.perform(PresentPassphrase(onCancel: { - navigator.perform(DismissModal(from: self)) + navigator.perform(PresentPassphrase(onCancel: { [weak self] in + guard let self else { return } + self.navigator.perform(DismissModal(from: self)) }, onPassphrase: { [weak self] passphrase in guard let self else { return } self.viewModel.didTapRestore(passphrase: passphrase) diff --git a/Sources/RestoreFeature/Controllers/RestoreListController.swift b/Sources/RestoreFeature/Controllers/RestoreListController.swift index 14fdc78eb0ec8ba4dc1787ac002a3c799f38ba99..cbce48e53d9b5943a84495906dfd64cc38bfd484 100644 --- a/Sources/RestoreFeature/Controllers/RestoreListController.swift +++ b/Sources/RestoreFeature/Controllers/RestoreListController.swift @@ -1,12 +1,13 @@ import UIKit import Shared import Combine -import Navigation +import AppResources +import AppNavigation import DrawerFeature -import DI +import ComposableArchitecture public final class RestoreListController: UIViewController { - @Dependency var navigator: Navigator + @Dependency(\.navigator) var navigator: Navigator private lazy var screenView = RestoreListView() diff --git a/Sources/RestoreFeature/Controllers/RestorePassphraseController.swift b/Sources/RestoreFeature/Controllers/RestorePassphraseController.swift index 45bd32d7d3c8f6a5c252c2f9c5ae54767eb5ee27..660287c6ca004e36beb04bfeb60a4ad839f55e1f 100644 --- a/Sources/RestoreFeature/Controllers/RestorePassphraseController.swift +++ b/Sources/RestoreFeature/Controllers/RestorePassphraseController.swift @@ -17,13 +17,13 @@ public final class RestorePassphraseController: UIViewController { } } - private let cancelClosure: EmptyClosure - private let stringClosure: StringClosure + private let cancelClosure: () -> Void + private let stringClosure: (String) -> Void private var cancellables = Set<AnyCancellable>() public init( - _ cancelClosure: @escaping EmptyClosure, - _ stringClosure: @escaping StringClosure + _ cancelClosure: @escaping () -> Void, + _ stringClosure: @escaping (String) -> Void ) { self.stringClosure = stringClosure self.cancelClosure = cancelClosure diff --git a/Sources/RestoreFeature/Controllers/RestoreSFTPController.swift b/Sources/RestoreFeature/Controllers/RestoreSFTPController.swift index ea56e1d63f504466c6104e66af735e8778a91aa1..1cfe3d251c7826bcad639009778dc9b26837f4a1 100644 --- a/Sources/RestoreFeature/Controllers/RestoreSFTPController.swift +++ b/Sources/RestoreFeature/Controllers/RestoreSFTPController.swift @@ -1,6 +1,5 @@ import UIKit import Combine -import DI import ScrollViewController public final class RestoreSFTPController: UIViewController { diff --git a/Sources/RestoreFeature/Controllers/RestoreSuccessController.swift b/Sources/RestoreFeature/Controllers/RestoreSuccessController.swift index c413c18a7cca79e3a144310f8923b669137d18e9..6fca825a4df49a270c800ca78b3f5d7757659a66 100644 --- a/Sources/RestoreFeature/Controllers/RestoreSuccessController.swift +++ b/Sources/RestoreFeature/Controllers/RestoreSuccessController.swift @@ -1,12 +1,13 @@ import UIKit import Shared import Combine -import Navigation -import DI +import AppCore +import Dependencies +import AppNavigation public final class RestoreSuccessController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var barStylist: StatusBarStylist + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = RestoreSuccessView() private var cancellables = Set<AnyCancellable>() @@ -17,7 +18,7 @@ public final class RestoreSuccessController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - barStylist.styleSubject.send(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar.customize(translucent: true) } diff --git a/Sources/RestoreFeature/ViewModels/RestoreListViewModel.swift b/Sources/RestoreFeature/ViewModels/RestoreListViewModel.swift index 0e30679443ab906f6ea0744e2360bc3c4a5f680a..40428ff3d9c49c175afb5d48d5cf8c18af28320b 100644 --- a/Sources/RestoreFeature/ViewModels/RestoreListViewModel.swift +++ b/Sources/RestoreFeature/ViewModels/RestoreListViewModel.swift @@ -1,9 +1,10 @@ import UIKit import Shared +import AppCore import Combine import CloudFiles import CloudFilesSFTP -import DI +import ComposableArchitecture public struct RestorationDetails { var provider: CloudService @@ -11,7 +12,7 @@ public struct RestorationDetails { } final class RestoreListViewModel { - @Dependency var hudController: HUDController + @Dependency(\.app.hudManager) var hudManager: HUDManager var sftpPublisher: AnyPublisher<Void, Never> { sftpSubject.eraseToAnyPublisher() @@ -51,33 +52,33 @@ final class RestoreListViewModel { case .success: onSuccess() case .failure(let error): - self.hudController.show(.init(error: error)) + self.hudManager.show(.init(error: error)) } } } catch { - hudController.show(.init(error: error)) + hudManager.show(.init(error: error)) } } func fetch(provider: CloudService) { - hudController.show() + hudManager.show() do { try CloudFilesManager.all[provider]!.fetch { [weak self] in guard let self else { return } switch $0 { case .success(let metadata): - self.hudController.dismiss() + self.hudManager.hide() self.detailsSubject.send(.init( provider: provider, metadata: metadata )) case .failure(let error): - self.hudController.show(.init(error: error)) + self.hudManager.show(.init(error: error)) } } } catch { - hudController.show(.init(error: error)) + hudManager.show(.init(error: error)) } } } diff --git a/Sources/RestoreFeature/ViewModels/RestoreSFTPViewModel.swift b/Sources/RestoreFeature/ViewModels/RestoreSFTPViewModel.swift index 7b5eeaa3e703b8fac56874f576b100818da96124..d18036fc37123a30f56b7126b4e595e4e9a1a89b 100644 --- a/Sources/RestoreFeature/ViewModels/RestoreSFTPViewModel.swift +++ b/Sources/RestoreFeature/ViewModels/RestoreSFTPViewModel.swift @@ -4,7 +4,9 @@ import Combine import Foundation import CloudFiles import CloudFilesSFTP -import DI + +import AppCore +import ComposableArchitecture struct SFTPViewState { var host: String = "" @@ -14,7 +16,7 @@ struct SFTPViewState { } final class RestoreSFTPViewModel { - @Dependency var hudController: HUDController + @Dependency(\.app.hudManager) var hudManager: HUDManager var statePublisher: AnyPublisher<SFTPViewState, Never> { stateSubject.eraseToAnyPublisher() @@ -43,7 +45,7 @@ final class RestoreSFTPViewModel { } func didTapLogin() { - hudController.show() + hudManager.show() let host = stateSubject.value.host let username = stateSubject.value.username @@ -62,14 +64,14 @@ final class RestoreSFTPViewModel { ).link(anyController) { switch $0 { case .success: - self.hudController.dismiss() + self.hudManager.hide() self.authSubject.send((host, username, password)) case .failure(let error): - self.hudController.show(.init(error: error)) + self.hudManager.show(.init(error: error)) } } } catch { - self.hudController.show(.init(error: error)) + self.hudManager.show(.init(error: error)) } } } diff --git a/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift b/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift index 8555ff7de15b53fa96671df6cb2347a7608d06a1..8c0c4f2945ab51cc42ebead30c683f64efe67796 100644 --- a/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift +++ b/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift @@ -3,13 +3,15 @@ import Shared import Combine import Defaults import CloudFiles -import DI import XXClient import XXModels import XXDatabase import XXMessengerClient +import AppCore +import ComposableArchitecture + enum Step { case done case wrongPass @@ -36,8 +38,8 @@ extension Step: Equatable { } final class RestoreViewModel { - @Dependency var database: Database - @Dependency var messenger: Messenger + @Dependency(\.app.dbManager) var dbManager: DBManager + @Dependency(\.app.messenger) var messenger: Messenger @KeyObject(.phone, defaultValue: nil) var phone: String? @KeyObject(.email, defaultValue: nil) var email: String? @@ -125,7 +127,7 @@ final class RestoreViewModel { onProgress: { print(">>> \($0)") } ) - try self.database.saveContact(.init( + try self.dbManager.getDB().saveContact(.init( id: self.messenger.e2e.get()!.getContact().getId(), marshaled: self.messenger.e2e.get()!.getContact().data, username: self.username!, @@ -146,7 +148,7 @@ final class RestoreViewModel { multilookup.contacts.forEach { print(">>> Found \(try! $0.getFact(.username)?.value)") - try! self.database.saveContact(.init( + try! self.dbManager.getDB().saveContact(.init( id: try $0.getId(), marshaled: $0.data, username: try? $0.getFact(.username)?.value, diff --git a/Sources/RestoreFeature/Views/RestoreDetailsView.swift b/Sources/RestoreFeature/Views/RestoreDetailsView.swift index 55f18c4d2befcd5dcfd9fa296fe7f6c888fd1aee..b7fad9fbd8c71b50306c9efd66fbcc51b3904e6a 100644 --- a/Sources/RestoreFeature/Views/RestoreDetailsView.swift +++ b/Sources/RestoreFeature/Views/RestoreDetailsView.swift @@ -1,56 +1,57 @@ import UIKit import Shared +import AppResources final class RestoreDetailsView: UIView { - let separatorView = UIView() - let imageView = UIImageView() - let titleLabel = UILabel() - - let stackView = UIStackView() - let dateView = DetailRowButton() - let sizeView = DetailRowButton() - - init() { - super.init(frame: .zero) - separatorView.backgroundColor = Asset.neutralLine.color - - titleLabel.font = Fonts.Mulish.semiBold.font(size: 16.0) - titleLabel.textColor = Asset.neutralActive.color - - stackView.axis = .vertical - stackView.spacing = 22 - stackView.addArrangedSubview(dateView) - stackView.addArrangedSubview(sizeView) - - addSubview(separatorView) - addSubview(imageView) - addSubview(titleLabel) - addSubview(stackView) - - separatorView.snp.makeConstraints { make in - make.top.equalToSuperview() - make.left.equalToSuperview().offset(25) - make.right.equalToSuperview().offset(-25) - make.height.equalTo(1) - } - - imageView.snp.makeConstraints { make in - make.top.equalTo(separatorView.snp.bottom).offset(40) - make.left.equalToSuperview().offset(24) - } - - titleLabel.snp.makeConstraints { make in - make.centerY.equalTo(imageView) - make.left.equalToSuperview().offset(92) - } - - stackView.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(20) - make.left.equalTo(titleLabel) - make.right.equalToSuperview().offset(-40) - make.bottom.lessThanOrEqualToSuperview().offset(-20) - } + let separatorView = UIView() + let imageView = UIImageView() + let titleLabel = UILabel() + + let stackView = UIStackView() + let dateView = DetailRowButton() + let sizeView = DetailRowButton() + + init() { + super.init(frame: .zero) + separatorView.backgroundColor = Asset.neutralLine.color + + titleLabel.font = Fonts.Mulish.semiBold.font(size: 16.0) + titleLabel.textColor = Asset.neutralActive.color + + stackView.axis = .vertical + stackView.spacing = 22 + stackView.addArrangedSubview(dateView) + stackView.addArrangedSubview(sizeView) + + addSubview(separatorView) + addSubview(imageView) + addSubview(titleLabel) + addSubview(stackView) + + separatorView.snp.makeConstraints { make in + make.top.equalToSuperview() + make.left.equalToSuperview().offset(25) + make.right.equalToSuperview().offset(-25) + make.height.equalTo(1) } - required init?(coder: NSCoder) { nil } + imageView.snp.makeConstraints { make in + make.top.equalTo(separatorView.snp.bottom).offset(40) + make.left.equalToSuperview().offset(24) + } + + titleLabel.snp.makeConstraints { make in + make.centerY.equalTo(imageView) + make.left.equalToSuperview().offset(92) + } + + stackView.snp.makeConstraints { make in + make.top.equalTo(titleLabel.snp.bottom).offset(20) + make.left.equalTo(titleLabel) + make.right.equalToSuperview().offset(-40) + make.bottom.lessThanOrEqualToSuperview().offset(-20) + } + } + + required init?(coder: NSCoder) { nil } } diff --git a/Sources/RestoreFeature/Views/RestoreListView.swift b/Sources/RestoreFeature/Views/RestoreListView.swift index a955173a742b7c0b94f95601f90634353ada327f..bb8f4e5a0767c2c6a81002e36b3ddb491ce4569f 100644 --- a/Sources/RestoreFeature/Views/RestoreListView.swift +++ b/Sources/RestoreFeature/Views/RestoreListView.swift @@ -1,127 +1,128 @@ import UIKit import Shared +import AppResources final class RestoreListView: UIView { - let titleLabel = UILabel() - let stackView = UIStackView() - let firstSubtitleLabel = UILabel() - let secondSubtitleLabel = UILabel() - let sftpButton = RowButton() - let driveButton = RowButton() - let icloudButton = RowButton() - let dropboxButton = RowButton() - let cancelButton = CapsuleButton() - - init() { - super.init(frame: .zero) - backgroundColor = Asset.neutralWhite.color - - setupTitle(Localized.AccountRestore.List.title) - setupSubtitle(Localized.AccountRestore.List.firstSubtitle) - - let paragraph = NSMutableParagraphStyle() - paragraph.alignment = .left - paragraph.lineHeightMultiple = 1.15 - - let attrString = NSMutableAttributedString( - string: Localized.AccountRestore.List.secondSubtitle, - attributes: [ - .foregroundColor: Asset.neutralBody.color, - .font: Fonts.Mulish.regular.font(size: 16.0) as Any, - .paragraphStyle: paragraph - ] - ) - - secondSubtitleLabel.numberOfLines = 0 - secondSubtitleLabel.attributedText = attrString - - sftpButton.setup(title: Localized.Backup.sftp, icon: Asset.restoreSFTP.image) - icloudButton.setup(title: Localized.Backup.iCloud, icon: Asset.restoreIcloud.image) - dropboxButton.setup(title: Localized.Backup.dropbox, icon: Asset.restoreDropbox.image) - driveButton.setup(title: Localized.Backup.googleDrive, icon: Asset.restoreDrive.image) - - cancelButton.set(style: .seeThrough, title: Localized.AccountRestore.List.cancel) - - stackView.axis = .vertical - stackView.distribution = .fillEqually - stackView.addArrangedSubview(driveButton) - stackView.addArrangedSubview(icloudButton) - stackView.addArrangedSubview(dropboxButton) - stackView.addArrangedSubview(sftpButton) - - addSubview(titleLabel) - addSubview(firstSubtitleLabel) - addSubview(secondSubtitleLabel) - addSubview(stackView) - addSubview(cancelButton) - - titleLabel.snp.makeConstraints { - $0.top.equalTo(safeAreaLayoutGuide).offset(15) - $0.left.equalToSuperview().offset(38) - $0.right.equalToSuperview().offset(-41) - } - - firstSubtitleLabel.snp.makeConstraints { - $0.top.equalTo(titleLabel.snp.bottom).offset(8) - $0.left.equalToSuperview().offset(38) - $0.right.equalToSuperview().offset(-41) - } - - secondSubtitleLabel.snp.makeConstraints { - $0.top.equalTo(firstSubtitleLabel.snp.bottom).offset(8) - $0.left.equalToSuperview().offset(38) - $0.right.equalToSuperview().offset(-41) - } - - stackView.snp.makeConstraints { - $0.top.equalTo(secondSubtitleLabel.snp.bottom).offset(28) - $0.left.equalToSuperview().offset(24) - $0.right.equalToSuperview().offset(-24) - } - - cancelButton.snp.makeConstraints { - $0.top.greaterThanOrEqualTo(stackView.snp.bottom).offset(20) - $0.left.equalToSuperview().offset(40) - $0.right.equalToSuperview().offset(-40) - $0.bottom.equalTo(safeAreaLayoutGuide).offset(-50) - } + let titleLabel = UILabel() + let stackView = UIStackView() + let firstSubtitleLabel = UILabel() + let secondSubtitleLabel = UILabel() + let sftpButton = RowButton() + let driveButton = RowButton() + let icloudButton = RowButton() + let dropboxButton = RowButton() + let cancelButton = CapsuleButton() + + init() { + super.init(frame: .zero) + backgroundColor = Asset.neutralWhite.color + + setupTitle(Localized.AccountRestore.List.title) + setupSubtitle(Localized.AccountRestore.List.firstSubtitle) + + let paragraph = NSMutableParagraphStyle() + paragraph.alignment = .left + paragraph.lineHeightMultiple = 1.15 + + let attrString = NSMutableAttributedString( + string: Localized.AccountRestore.List.secondSubtitle, + attributes: [ + .foregroundColor: Asset.neutralBody.color, + .font: Fonts.Mulish.regular.font(size: 16.0) as Any, + .paragraphStyle: paragraph + ] + ) + + secondSubtitleLabel.numberOfLines = 0 + secondSubtitleLabel.attributedText = attrString + + sftpButton.setup(title: Localized.Backup.sftp, icon: Asset.restoreSFTP.image) + icloudButton.setup(title: Localized.Backup.iCloud, icon: Asset.restoreIcloud.image) + dropboxButton.setup(title: Localized.Backup.dropbox, icon: Asset.restoreDropbox.image) + driveButton.setup(title: Localized.Backup.googleDrive, icon: Asset.restoreDrive.image) + + cancelButton.set(style: .seeThrough, title: Localized.AccountRestore.List.cancel) + + stackView.axis = .vertical + stackView.distribution = .fillEqually + stackView.addArrangedSubview(driveButton) + stackView.addArrangedSubview(icloudButton) + stackView.addArrangedSubview(dropboxButton) + stackView.addArrangedSubview(sftpButton) + + addSubview(titleLabel) + addSubview(firstSubtitleLabel) + addSubview(secondSubtitleLabel) + addSubview(stackView) + addSubview(cancelButton) + + titleLabel.snp.makeConstraints { + $0.top.equalTo(safeAreaLayoutGuide).offset(15) + $0.left.equalToSuperview().offset(38) + $0.right.equalToSuperview().offset(-41) } - required init?(coder: NSCoder) { nil } - - private func setupTitle(_ title: String) { - let attString = NSMutableAttributedString(string: title) - let paragraph = NSMutableParagraphStyle() - paragraph.alignment = .left - paragraph.lineHeightMultiple = 1 - - attString.addAttribute(.paragraphStyle, value: paragraph) - attString.addAttribute(.foregroundColor, value: Asset.neutralActive.color) - attString.addAttribute(.font, value: Fonts.Mulish.bold.font(size: 34.0) as Any) + firstSubtitleLabel.snp.makeConstraints { + $0.top.equalTo(titleLabel.snp.bottom).offset(8) + $0.left.equalToSuperview().offset(38) + $0.right.equalToSuperview().offset(-41) + } - attString.addAttributes(attributes: [ - .font: Fonts.Mulish.bold.font(size: 34.0) as Any, - .foregroundColor: Asset.brandPrimary.color - ], betweenCharacters: "#") + secondSubtitleLabel.snp.makeConstraints { + $0.top.equalTo(firstSubtitleLabel.snp.bottom).offset(8) + $0.left.equalToSuperview().offset(38) + $0.right.equalToSuperview().offset(-41) + } - titleLabel.numberOfLines = 0 - titleLabel.attributedText = attString + stackView.snp.makeConstraints { + $0.top.equalTo(secondSubtitleLabel.snp.bottom).offset(28) + $0.left.equalToSuperview().offset(24) + $0.right.equalToSuperview().offset(-24) } - private func setupSubtitle(_ subtitle: String) { - let paragraph = NSMutableParagraphStyle() - paragraph.alignment = .left - paragraph.lineHeightMultiple = 1.15 - - let attString = NSAttributedString( - string: subtitle, - attributes: [ - .foregroundColor: Asset.neutralBody.color, - .font: Fonts.Mulish.regular.font(size: 16.0) as Any, - .paragraphStyle: paragraph - ]) - - firstSubtitleLabel.numberOfLines = 0 - firstSubtitleLabel.attributedText = attString + cancelButton.snp.makeConstraints { + $0.top.greaterThanOrEqualTo(stackView.snp.bottom).offset(20) + $0.left.equalToSuperview().offset(40) + $0.right.equalToSuperview().offset(-40) + $0.bottom.equalTo(safeAreaLayoutGuide).offset(-50) } + } + + required init?(coder: NSCoder) { nil } + + private func setupTitle(_ title: String) { + let attString = NSMutableAttributedString(string: title) + let paragraph = NSMutableParagraphStyle() + paragraph.alignment = .left + paragraph.lineHeightMultiple = 1 + + attString.addAttribute(.paragraphStyle, value: paragraph) + attString.addAttribute(.foregroundColor, value: Asset.neutralActive.color) + attString.addAttribute(.font, value: Fonts.Mulish.bold.font(size: 34.0) as Any) + + attString.addAttributes(attributes: [ + .font: Fonts.Mulish.bold.font(size: 34.0) as Any, + .foregroundColor: Asset.brandPrimary.color + ], betweenCharacters: "#") + + titleLabel.numberOfLines = 0 + titleLabel.attributedText = attString + } + + private func setupSubtitle(_ subtitle: String) { + let paragraph = NSMutableParagraphStyle() + paragraph.alignment = .left + paragraph.lineHeightMultiple = 1.15 + + let attString = NSAttributedString( + string: subtitle, + attributes: [ + .foregroundColor: Asset.neutralBody.color, + .font: Fonts.Mulish.regular.font(size: 16.0) as Any, + .paragraphStyle: paragraph + ]) + + firstSubtitleLabel.numberOfLines = 0 + firstSubtitleLabel.attributedText = attString + } } diff --git a/Sources/RestoreFeature/Views/RestorePassphraseView.swift b/Sources/RestoreFeature/Views/RestorePassphraseView.swift index 6dfaa4d716ae2a354ab9afa4a0d9563df97cc3f8..9eecc341c17383fd45c1a3eea48d810701e91222 100644 --- a/Sources/RestoreFeature/Views/RestorePassphraseView.swift +++ b/Sources/RestoreFeature/Views/RestorePassphraseView.swift @@ -1,6 +1,7 @@ import UIKit import Shared import InputField +import AppResources final class RestorePassphraseView: UIView { let titleLabel = UILabel() diff --git a/Sources/RestoreFeature/Views/RestoreProgressView.swift b/Sources/RestoreFeature/Views/RestoreProgressView.swift index 95a471f02bf53bf936ca1cd30d98217bc3afde7c..bde3b25657b8ff55cbdc6288e37f1bed7c0e695e 100644 --- a/Sources/RestoreFeature/Views/RestoreProgressView.swift +++ b/Sources/RestoreFeature/Views/RestoreProgressView.swift @@ -1,87 +1,88 @@ import UIKit import Shared +import AppResources final class RestoreProgressView: UIView { - let progressBarFull = UIView() - let progressBarFiller = UIView() - let progressLabel = UILabel() - let warningLabel = UILabel() - let descriptiveProgressLabel = UILabel() - - init() { - super.init(frame: .zero) - warningLabel.textColor = Asset.neutralDisabled.color - progressLabel.textColor = Asset.neutralDisabled.color - descriptiveProgressLabel.textColor = Asset.neutralDisabled.color - - warningLabel.font = Fonts.Mulish.regular.font(size: 14.0) - progressLabel.font = Fonts.Mulish.regular.font(size: 14.0) - descriptiveProgressLabel.font = Fonts.Mulish.regular.font(size: 14.0) - - descriptiveProgressLabel.textAlignment = .center - - progressBarFull.backgroundColor = Asset.neutralLine.color - progressBarFiller.backgroundColor = Asset.brandPrimary.color - progressBarFull.layer.masksToBounds = true - progressBarFull.layer.cornerRadius = 4 - - warningLabel.numberOfLines = 0 - descriptiveProgressLabel.numberOfLines = 0 - warningLabel.text = "This may take up to 5 mins, please don’t close the app and don’t put in background and don’t close your phone screen" - - addSubview(progressBarFull) - addSubview(progressLabel) - addSubview(warningLabel) - addSubview(descriptiveProgressLabel) - progressBarFull.addSubview(progressBarFiller) - - descriptiveProgressLabel.snp.makeConstraints { make in - make.top.greaterThanOrEqualToSuperview() - make.left.equalToSuperview().offset(42) - make.right.equalToSuperview().offset(-42) - make.bottom.equalTo(progressBarFull.snp.top).offset(-15) - } - - progressBarFull.snp.makeConstraints { make in - make.top.greaterThanOrEqualToSuperview() - make.left.equalToSuperview().offset(42) - make.right.equalToSuperview().offset(-42) - make.centerY.equalToSuperview() - make.height.equalTo(8) - } - - progressBarFiller.snp.makeConstraints { make in - make.top.equalToSuperview() - make.left.equalToSuperview() - make.width.equalTo(0) - make.bottom.equalToSuperview() - } - - progressLabel.snp.makeConstraints { make in - make.top.equalTo(progressBarFull.snp.bottom).offset(15) - make.left.equalToSuperview().offset(42) - make.right.equalToSuperview().offset(-42) - } - - warningLabel.snp.makeConstraints { make in - make.top.equalTo(progressLabel.snp.bottom).offset(15) - make.left.equalToSuperview().offset(42) - make.right.equalToSuperview().offset(-42) - make.bottom.lessThanOrEqualToSuperview() - } + let progressBarFull = UIView() + let progressBarFiller = UIView() + let progressLabel = UILabel() + let warningLabel = UILabel() + let descriptiveProgressLabel = UILabel() + + init() { + super.init(frame: .zero) + warningLabel.textColor = Asset.neutralDisabled.color + progressLabel.textColor = Asset.neutralDisabled.color + descriptiveProgressLabel.textColor = Asset.neutralDisabled.color + + warningLabel.font = Fonts.Mulish.regular.font(size: 14.0) + progressLabel.font = Fonts.Mulish.regular.font(size: 14.0) + descriptiveProgressLabel.font = Fonts.Mulish.regular.font(size: 14.0) + + descriptiveProgressLabel.textAlignment = .center + + progressBarFull.backgroundColor = Asset.neutralLine.color + progressBarFiller.backgroundColor = Asset.brandPrimary.color + progressBarFull.layer.masksToBounds = true + progressBarFull.layer.cornerRadius = 4 + + warningLabel.numberOfLines = 0 + descriptiveProgressLabel.numberOfLines = 0 + warningLabel.text = "This may take up to 5 mins, please don’t close the app and don’t put in background and don’t close your phone screen" + + addSubview(progressBarFull) + addSubview(progressLabel) + addSubview(warningLabel) + addSubview(descriptiveProgressLabel) + progressBarFull.addSubview(progressBarFiller) + + descriptiveProgressLabel.snp.makeConstraints { make in + make.top.greaterThanOrEqualToSuperview() + make.left.equalToSuperview().offset(42) + make.right.equalToSuperview().offset(-42) + make.bottom.equalTo(progressBarFull.snp.top).offset(-15) } - required init?(coder: NSCoder) { nil } + progressBarFull.snp.makeConstraints { make in + make.top.greaterThanOrEqualToSuperview() + make.left.equalToSuperview().offset(42) + make.right.equalToSuperview().offset(-42) + make.centerY.equalToSuperview() + make.height.equalTo(8) + } + + progressBarFiller.snp.makeConstraints { make in + make.top.equalToSuperview() + make.left.equalToSuperview() + make.width.equalTo(0) + make.bottom.equalToSuperview() + } + + progressLabel.snp.makeConstraints { make in + make.top.equalTo(progressBarFull.snp.bottom).offset(15) + make.left.equalToSuperview().offset(42) + make.right.equalToSuperview().offset(-42) + } + + warningLabel.snp.makeConstraints { make in + make.top.equalTo(progressLabel.snp.bottom).offset(15) + make.left.equalToSuperview().offset(42) + make.right.equalToSuperview().offset(-42) + make.bottom.lessThanOrEqualToSuperview() + } + } + + required init?(coder: NSCoder) { nil } - func update(downloaded: Float, total: Float) { - let totalkb = String(format: "%.1f kb", total/1000) - let downloadedKb = String(format: "%.1f kb", downloaded/1000) - let percent = String(format: "%.0f", downloaded/total * 100) + func update(downloaded: Float, total: Float) { + let totalkb = String(format: "%.1f kb", total/1000) + let downloadedKb = String(format: "%.1f kb", downloaded/1000) + let percent = String(format: "%.0f", downloaded/total * 100) - progressLabel.text = "Downloaded \(downloadedKb) of \(totalkb) (\(percent)%)" + progressLabel.text = "Downloaded \(downloadedKb) of \(totalkb) (\(percent)%)" - progressBarFiller.snp.updateConstraints { make in - make.width.equalTo(CGFloat(downloaded/total) * progressBarFull.frame.size.width) - } + progressBarFiller.snp.updateConstraints { make in + make.width.equalTo(CGFloat(downloaded/total) * progressBarFull.frame.size.width) } + } } diff --git a/Sources/RestoreFeature/Views/RestoreSFTPView.swift b/Sources/RestoreFeature/Views/RestoreSFTPView.swift index 5c12026bf8eda8865a226df8886c11a05108740a..e6595e9f9cd31fe6dcb85447049eb32f3dafdb76 100644 --- a/Sources/RestoreFeature/Views/RestoreSFTPView.swift +++ b/Sources/RestoreFeature/Views/RestoreSFTPView.swift @@ -1,6 +1,7 @@ import UIKit import Shared import InputField +import AppResources final class RestoreSFTPView: UIView { let titleLabel = UILabel() diff --git a/Sources/RestoreFeature/Views/RestoreSuccessView.swift b/Sources/RestoreFeature/Views/RestoreSuccessView.swift index 35bdd4c04fc356e32b54a729b642514e504f8c38..ac6b3dea1ee72ec79ccc0d39202c6ab1c25bea1c 100644 --- a/Sources/RestoreFeature/Views/RestoreSuccessView.swift +++ b/Sources/RestoreFeature/Views/RestoreSuccessView.swift @@ -1,78 +1,79 @@ import UIKit import Shared +import AppResources final class RestoreSuccessView: UIView { - let iconImageView = UIImageView() - let titleLabel = UILabel() - let subtitleLabel = UILabel() - let nextButton = CapsuleButton() - - init() { - super.init(frame: .zero) - - iconImageView.contentMode = .center - iconImageView.image = Asset.onboardingSuccess.image - nextButton.set(style: .white, title: Localized.Onboarding.Success.action) - - subtitleLabel.numberOfLines = 0 - subtitleLabel.textColor = Asset.neutralWhite.color - subtitleLabel.font = Fonts.Mulish.regular.font(size: 16.0) - - addSubview(iconImageView) - addSubview(titleLabel) - addSubview(subtitleLabel) - addSubview(nextButton) - - iconImageView.snp.makeConstraints { make in - make.top.equalTo(safeAreaLayoutGuide).offset(40) - make.left.equalToSuperview().offset(40) - } - - titleLabel.snp.makeConstraints { make in - make.top.equalTo(iconImageView.snp.bottom).offset(40) - make.left.equalToSuperview().offset(40) - make.right.equalToSuperview().offset(-90) - } - - subtitleLabel.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(30) - make.left.equalToSuperview().offset(40) - make.right.equalToSuperview().offset(-90) - } - - nextButton.snp.makeConstraints { make in - make.left.equalToSuperview().offset(24) - make.right.equalToSuperview().offset(-24) - make.bottom.equalToSuperview().offset(-60) - } - - setTitle(Localized.AccountRestore.Success.title) - setSubtitle(Localized.AccountRestore.Success.subtitle) + let iconImageView = UIImageView() + let titleLabel = UILabel() + let subtitleLabel = UILabel() + let nextButton = CapsuleButton() + + init() { + super.init(frame: .zero) + + iconImageView.contentMode = .center + iconImageView.image = Asset.onboardingSuccess.image + nextButton.set(style: .white, title: Localized.Onboarding.Success.action) + + subtitleLabel.numberOfLines = 0 + subtitleLabel.textColor = Asset.neutralWhite.color + subtitleLabel.font = Fonts.Mulish.regular.font(size: 16.0) + + addSubview(iconImageView) + addSubview(titleLabel) + addSubview(subtitleLabel) + addSubview(nextButton) + + iconImageView.snp.makeConstraints { make in + make.top.equalTo(safeAreaLayoutGuide).offset(40) + make.left.equalToSuperview().offset(40) } - required init?(coder: NSCoder) { nil } + titleLabel.snp.makeConstraints { make in + make.top.equalTo(iconImageView.snp.bottom).offset(40) + make.left.equalToSuperview().offset(40) + make.right.equalToSuperview().offset(-90) + } - private func setTitle(_ title: String) { - let paragraph = NSMutableParagraphStyle() - paragraph.alignment = .left - paragraph.lineHeightMultiple = 1.1 + subtitleLabel.snp.makeConstraints { make in + make.top.equalTo(titleLabel.snp.bottom).offset(30) + make.left.equalToSuperview().offset(40) + make.right.equalToSuperview().offset(-90) + } - let attrString = NSMutableAttributedString(string: title) + nextButton.snp.makeConstraints { make in + make.left.equalToSuperview().offset(24) + make.right.equalToSuperview().offset(-24) + make.bottom.equalToSuperview().offset(-60) + } - attrString.addAttribute(.font, value: Fonts.Mulish.bold.font(size: 39.0)) - attrString.addAttribute(.foregroundColor, value: Asset.neutralWhite.color) + setTitle(Localized.AccountRestore.Success.title) + setSubtitle(Localized.AccountRestore.Success.subtitle) + } - attrString.addAttribute( - name: .foregroundColor, - value: Asset.neutralBody.color, - betweenCharacters: "#" - ) + required init?(coder: NSCoder) { nil } - titleLabel.numberOfLines = 0 - titleLabel.attributedText = attrString - } + private func setTitle(_ title: String) { + let paragraph = NSMutableParagraphStyle() + paragraph.alignment = .left + paragraph.lineHeightMultiple = 1.1 - private func setSubtitle(_ subtitle: String?) { - subtitleLabel.text = subtitle - } + let attrString = NSMutableAttributedString(string: title) + + attrString.addAttribute(.font, value: Fonts.Mulish.bold.font(size: 39.0)) + attrString.addAttribute(.foregroundColor, value: Asset.neutralWhite.color) + + attrString.addAttribute( + name: .foregroundColor, + value: Asset.neutralBody.color, + betweenCharacters: "#" + ) + + titleLabel.numberOfLines = 0 + titleLabel.attributedText = attrString + } + + private func setSubtitle(_ subtitle: String?) { + subtitleLabel.text = subtitle + } } diff --git a/Sources/RestoreFeature/Views/RestoreView.swift b/Sources/RestoreFeature/Views/RestoreView.swift index 720128d33fa4bed8bd11e1ffed0873f9452682eb..d4bd3aeb238f38a894755497e1ff7a72bcc49c46 100644 --- a/Sources/RestoreFeature/Views/RestoreView.swift +++ b/Sources/RestoreFeature/Views/RestoreView.swift @@ -1,6 +1,7 @@ import UIKit import Shared import CloudFiles +import AppResources final class RestoreView: UIView { let titleLabel = UILabel() diff --git a/Sources/ScanFeature/Controllers/ScanContainerController.swift b/Sources/ScanFeature/Controllers/ScanContainerController.swift index 68f78d7a6b6e0b64340ded20b05ae4f2dd06f197..435710b65d50319e33f4ab0bf1de67f538692f36 100644 --- a/Sources/ScanFeature/Controllers/ScanContainerController.swift +++ b/Sources/ScanFeature/Controllers/ScanContainerController.swift @@ -1,13 +1,15 @@ import UIKit import Shared import Combine -import Navigation +import AppCore +import Dependencies +import AppResources +import AppNavigation import DrawerFeature -import DI public final class ScanContainerController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var barStylist: StatusBarStylist + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = ScanContainerView() @@ -40,7 +42,7 @@ public final class ScanContainerController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - barStylist.styleSubject.send(.lightContent) + statusBar.set(.lightContent) navigationController?.navigationBar.customize(translucent: true) } diff --git a/Sources/ScanFeature/Controllers/ScanController.swift b/Sources/ScanFeature/Controllers/ScanController.swift index bf8875099a7ba127269d1b03e693015542c093bf..e21b9d035bf0d664a06d7f2873244747614f2bf9 100644 --- a/Sources/ScanFeature/Controllers/ScanController.swift +++ b/Sources/ScanFeature/Controllers/ScanController.swift @@ -1,19 +1,19 @@ import UIKit import Shared import Combine -import Permissions -import Navigation +import AppCore +import AppNavigation import CombineSchedulers -import DI +import PermissionsFeature +import ComposableArchitecture final class ScanController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var permissions: PermissionHandling - + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.permissions) var permissions: PermissionsManager + @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue> + private lazy var screenView = ScanView() - - var backgroundScheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler() - + private var status: ScanStatus? private let camera: CameraType private let viewModel = ScanViewModel() @@ -54,18 +54,18 @@ final class ScanController: UIViewController { override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - backgroundScheduler.schedule { [weak self] in + bgQueue.schedule { [weak self] in guard let self else { return } self.camera.stop() } } private func startCamera() { - permissions.requestCamera { [weak self] granted in + permissions.camera.request { [weak self] granted in guard let self else { return } if granted { - self.backgroundScheduler.schedule { + self.bgQueue.schedule { self.camera.start() } } else { diff --git a/Sources/ScanFeature/ViewModels/ScanDisplayViewModel.swift b/Sources/ScanFeature/ViewModels/ScanDisplayViewModel.swift index 5c20f1e3f9111ed699790503ffce76774f02cfe4..75410119075162b70a45a95e8622b36083bb637e 100644 --- a/Sources/ScanFeature/ViewModels/ScanDisplayViewModel.swift +++ b/Sources/ScanFeature/ViewModels/ScanDisplayViewModel.swift @@ -3,7 +3,8 @@ import Shared import Combine import Defaults import XXClient -import DI +import AppCore +import Dependencies import XXMessengerClient struct ScanDisplayViewState: Equatable { @@ -15,7 +16,7 @@ struct ScanDisplayViewState: Equatable { } final class ScanDisplayViewModel { - @Dependency var messenger: Messenger + @Dependency(\.app.messenger) var messenger: Messenger @KeyObject(.email, defaultValue: nil) var email: String? @KeyObject(.phone, defaultValue: nil) var phone: String? diff --git a/Sources/ScanFeature/ViewModels/ScanViewModel.swift b/Sources/ScanFeature/ViewModels/ScanViewModel.swift index 1e4a79a99a43c192211aaf4a77d14ce889d6c9e2..d69cbd692028a4485b276e75e7209d85301b087b 100644 --- a/Sources/ScanFeature/ViewModels/ScanViewModel.swift +++ b/Sources/ScanFeature/ViewModels/ScanViewModel.swift @@ -1,96 +1,98 @@ import Shared +import AppCore import Combine import XXModels import XXClient import Foundation +import AppResources +import Dependencies import ReportingFeature -import DI enum ScanStatus: Equatable { - case reading - case processing - case success - case failed(ScanError) + case reading + case processing + case success + case failed(ScanError) } enum ScanError: Equatable { - case requestOpened - case unknown(String) - case cameraPermission - case alreadyFriends(String) + case requestOpened + case unknown(String) + case cameraPermission + case alreadyFriends(String) } final class ScanViewModel { - @Dependency var database: Database - @Dependency var reportingStatus: ReportingStatus + @Dependency(\.app.dbManager) var dbManager: DBManager + @Dependency(\.reportingStatus) var reportingStatus: ReportingStatus - var contactPublisher: AnyPublisher<XXModels.Contact, Never> { - contactSubject.eraseToAnyPublisher() - } + var contactPublisher: AnyPublisher<XXModels.Contact, Never> { + contactSubject.eraseToAnyPublisher() + } - var statePublisher: AnyPublisher<ScanStatus, Never> { - stateSubject.eraseToAnyPublisher() - } + var statePublisher: AnyPublisher<ScanStatus, Never> { + stateSubject.eraseToAnyPublisher() + } + + private let contactSubject = PassthroughSubject<XXModels.Contact, Never>() + private let stateSubject = CurrentValueSubject<ScanStatus, Never>(.reading) - private let contactSubject = PassthroughSubject<XXModels.Contact, Never>() - private let stateSubject = CurrentValueSubject<ScanStatus, Never>(.reading) + func resetScanner() { + stateSubject.send(.reading) + } - func resetScanner() { - stateSubject.send(.reading) + func didScanData(_ data: Data) { + guard stateSubject.value == .reading else { return } + stateSubject.send(.processing) + + let user = XXClient.Contact.live(data) + + guard let uid = try? user.getId(), + let facts = try? user.getFacts(), + let username = facts.first(where: { $0.type == .username })?.value else { + let errorTitle = Localized.Scan.Error.invalid + stateSubject.send(.failed(.unknown(errorTitle))) + return } - func didScanData(_ data: Data) { - guard stateSubject.value == .reading else { return } - stateSubject.send(.processing) - - let user = XXClient.Contact.live(data) - - guard let uid = try? user.getId(), - let facts = try? user.getFacts(), - let username = facts.first(where: { $0.type == .username })?.value else { - let errorTitle = Localized.Scan.Error.invalid - stateSubject.send(.failed(.unknown(errorTitle))) - return - } - - let email = facts.first { $0.type == .email }?.value - let phone = facts.first { $0.type == .phone }?.value - - if let alreadyContact = try? database.fetchContacts(.init(id: [uid])).first { - if alreadyContact.isBlocked, reportingStatus.isEnabled() { - stateSubject.send(.failed(.unknown("You previously blocked this user."))) - return - } - - if alreadyContact.isBanned, reportingStatus.isEnabled() { - stateSubject.send(.failed(.unknown("This user was banned."))) - return - } - - if alreadyContact.authStatus == .friend { - stateSubject.send(.failed(.alreadyFriends(username))) - } else if [.requested, .verified].contains(alreadyContact.authStatus) { - stateSubject.send(.failed(.requestOpened)) - } else { - let generalErrorTitle = Localized.Scan.Error.general - stateSubject.send(.failed(.unknown(generalErrorTitle))) - } - - return - } - - stateSubject.send(.success) - contactSubject.send(.init( - id: uid, - marshaled: data, - username: username, - email: email, - phone: phone, - nickname: nil, - photo: nil, - authStatus: .stranger, - isRecent: false, - createdAt: Date() - )) + let email = facts.first { $0.type == .email }?.value + let phone = facts.first { $0.type == .phone }?.value + + if let alreadyContact = try? dbManager.getDB().fetchContacts(.init(id: [uid])).first { + if alreadyContact.isBlocked, reportingStatus.isEnabled() { + stateSubject.send(.failed(.unknown("You previously blocked this user."))) + return + } + + if alreadyContact.isBanned, reportingStatus.isEnabled() { + stateSubject.send(.failed(.unknown("This user was banned."))) + return + } + + if alreadyContact.authStatus == .friend { + stateSubject.send(.failed(.alreadyFriends(username))) + } else if [.requested, .verified].contains(alreadyContact.authStatus) { + stateSubject.send(.failed(.requestOpened)) + } else { + let generalErrorTitle = Localized.Scan.Error.general + stateSubject.send(.failed(.unknown(generalErrorTitle))) + } + + return } + + stateSubject.send(.success) + contactSubject.send(.init( + id: uid, + marshaled: data, + username: username, + email: email, + phone: phone, + nickname: nil, + photo: nil, + authStatus: .stranger, + isRecent: false, + createdAt: Date() + )) + } } diff --git a/Sources/ScanFeature/Views/AttributeSwitcher.swift b/Sources/ScanFeature/Views/AttributeSwitcher.swift index 4bc957c6816dfc1c36b50ed5c47e46d6cf0180f5..464979a68754855cdd8b1c1f225acd2d66c4cb7e 100644 --- a/Sources/ScanFeature/Views/AttributeSwitcher.swift +++ b/Sources/ScanFeature/Views/AttributeSwitcher.swift @@ -1,102 +1,103 @@ import UIKit import Shared +import AppResources final class AttributeSwitcher: UIView { - struct State { - var content: String - var isVisible: Bool + struct State { + var content: String + var isVisible: Bool + } + + private let titleLabel = UILabel() + private let contentLabel = UILabel() + private let stackView = UIStackView() + private(set) var switcherView = UISwitch() + private let verticalStackView = UIStackView() + + private(set) var addButton: UIControl = { + let label = UILabel() + let icon = UIImageView() + let control = UIControl() + + icon.image = Asset.scanAdd.image + label.text = Localized.Scan.Display.Share.add + label.textColor = Asset.brandPrimary.color + + control.addSubview(icon) + control.addSubview(label) + + icon.snp.makeConstraints { + $0.left.equalToSuperview() + $0.top.equalToSuperview() + $0.bottom.equalToSuperview() + $0.width.equalTo(icon.snp.height) } - private let titleLabel = UILabel() - private let contentLabel = UILabel() - private let stackView = UIStackView() - private(set) var switcherView = UISwitch() - private let verticalStackView = UIStackView() - - private(set) var addButton: UIControl = { - let label = UILabel() - let icon = UIImageView() - let control = UIControl() - - icon.image = Asset.scanAdd.image - label.text = Localized.Scan.Display.Share.add - label.textColor = Asset.brandPrimary.color - - control.addSubview(icon) - control.addSubview(label) - - icon.snp.makeConstraints { - $0.left.equalToSuperview() - $0.top.equalToSuperview() - $0.bottom.equalToSuperview() - $0.width.equalTo(icon.snp.height) - } - - label.snp.makeConstraints { - $0.left.equalTo(icon.snp.right).offset(5) - $0.top.equalToSuperview() - $0.right.equalToSuperview() - $0.bottom.equalToSuperview() - } + label.snp.makeConstraints { + $0.left.equalTo(icon.snp.right).offset(5) + $0.top.equalToSuperview() + $0.right.equalToSuperview() + $0.bottom.equalToSuperview() + } - return control - }() + return control + }() - public init() { - super.init(frame: .zero) + public init() { + super.init(frame: .zero) - contentLabel.textColor = Asset.neutralActive.color - titleLabel.textColor = Asset.neutralWeak.color - switcherView.onTintColor = Asset.brandPrimary.color + contentLabel.textColor = Asset.neutralActive.color + titleLabel.textColor = Asset.neutralWeak.color + switcherView.onTintColor = Asset.brandPrimary.color - contentLabel.numberOfLines = 0 - contentLabel.font = Fonts.Mulish.regular.font(size: 16.0) - titleLabel.font = Fonts.Mulish.bold.font(size: 12.0) + contentLabel.numberOfLines = 0 + contentLabel.font = Fonts.Mulish.regular.font(size: 16.0) + titleLabel.font = Fonts.Mulish.bold.font(size: 12.0) - addSubview(stackView) + addSubview(stackView) - verticalStackView.spacing = 5 - verticalStackView.axis = .vertical - verticalStackView.addArrangedSubview(titleLabel) - verticalStackView.addArrangedSubview(contentLabel) + verticalStackView.spacing = 5 + verticalStackView.axis = .vertical + verticalStackView.addArrangedSubview(titleLabel) + verticalStackView.addArrangedSubview(contentLabel) - switcherView.setContentCompressionResistancePriority(.required, for: .vertical) - switcherView.setContentCompressionResistancePriority(.required, for: .horizontal) + switcherView.setContentCompressionResistancePriority(.required, for: .vertical) + switcherView.setContentCompressionResistancePriority(.required, for: .horizontal) - stackView.addArrangedSubview(verticalStackView) - stackView.addArrangedSubview(FlexibleSpace()) + stackView.addArrangedSubview(verticalStackView) + stackView.addArrangedSubview(FlexibleSpace()) - let otherHStack = UIStackView() - otherHStack.addArrangedSubview(addButton) - otherHStack.addArrangedSubview(switcherView) + let otherHStack = UIStackView() + otherHStack.addArrangedSubview(addButton) + otherHStack.addArrangedSubview(switcherView) - let otherVStack = UIStackView() - otherVStack.axis = .vertical - otherVStack.addArrangedSubview(otherHStack) - otherVStack.addArrangedSubview(FlexibleSpace()) + let otherVStack = UIStackView() + otherVStack.axis = .vertical + otherVStack.addArrangedSubview(otherHStack) + otherVStack.addArrangedSubview(FlexibleSpace()) - stackView.addArrangedSubview(otherVStack) + stackView.addArrangedSubview(otherVStack) - stackView.snp.makeConstraints { - $0.edges.equalToSuperview() - } + stackView.snp.makeConstraints { + $0.edges.equalToSuperview() } + } - required init?(coder: NSCoder) { nil } + required init?(coder: NSCoder) { nil } - func setup(state: State?, title: String) { - titleLabel.text = title + func setup(state: State?, title: String) { + titleLabel.text = title - guard let state = state else { - addButton.isHidden = false - switcherView.isHidden = true - contentLabel.text = Localized.Scan.Display.Share.notAdded - return - } - - addButton.isHidden = true - switcherView.isHidden = false - switcherView.isOn = state.isVisible - contentLabel.text = state.isVisible ? state.content : Localized.Scan.Display.Share.hidden + guard let state = state else { + addButton.isHidden = false + switcherView.isHidden = true + contentLabel.text = Localized.Scan.Display.Share.notAdded + return } + + addButton.isHidden = true + switcherView.isHidden = false + switcherView.isOn = state.isVisible + contentLabel.text = state.isVisible ? state.content : Localized.Scan.Display.Share.hidden + } } diff --git a/Sources/ScanFeature/Views/ScanContainerView.swift b/Sources/ScanFeature/Views/ScanContainerView.swift index 5b9717598d93484ce74eb58e37b1ed465afc9230..78432064b4d1b02a22629b81e462e4239a14b322 100644 --- a/Sources/ScanFeature/Views/ScanContainerView.swift +++ b/Sources/ScanFeature/Views/ScanContainerView.swift @@ -1,36 +1,37 @@ import UIKit import Shared +import AppResources final class ScanContainerView: UIView { - let stackView = UIStackView() - let leftButton = ScanSegmentedControlButton() - let rightButton = ScanSegmentedControlButton() - - init() { - super.init(frame: .zero) - - backgroundColor = Asset.neutralDark.color - - leftButton.set(selected: true) - rightButton.set(selected: false) - leftButton.imageView.image = Asset.scanScan.image - rightButton.imageView.image = Asset.scanQr.image - leftButton.titleLabel.text = Localized.Scan.SegmentedControl.left - rightButton.titleLabel.text = Localized.Scan.SegmentedControl.right - - stackView.distribution = .fillEqually - stackView.addArrangedSubview(leftButton) - stackView.addArrangedSubview(rightButton) - - addSubview(stackView) - - stackView.snp.makeConstraints { - $0.top.equalTo(safeAreaLayoutGuide).offset(10) - $0.left.equalToSuperview().offset(50) - $0.right.equalToSuperview().offset(-50) - $0.height.equalTo(60) - } + let stackView = UIStackView() + let leftButton = ScanSegmentedControlButton() + let rightButton = ScanSegmentedControlButton() + + init() { + super.init(frame: .zero) + + backgroundColor = Asset.neutralDark.color + + leftButton.set(selected: true) + rightButton.set(selected: false) + leftButton.imageView.image = Asset.scanScan.image + rightButton.imageView.image = Asset.scanQr.image + leftButton.titleLabel.text = Localized.Scan.SegmentedControl.left + rightButton.titleLabel.text = Localized.Scan.SegmentedControl.right + + stackView.distribution = .fillEqually + stackView.addArrangedSubview(leftButton) + stackView.addArrangedSubview(rightButton) + + addSubview(stackView) + + stackView.snp.makeConstraints { + $0.top.equalTo(safeAreaLayoutGuide).offset(10) + $0.left.equalToSuperview().offset(50) + $0.right.equalToSuperview().offset(-50) + $0.height.equalTo(60) } + } - required init?(coder: NSCoder) { nil } + required init?(coder: NSCoder) { nil } } diff --git a/Sources/ScanFeature/Views/ScanDisplayShareView.swift b/Sources/ScanFeature/Views/ScanDisplayShareView.swift index acad98f7cab2ffd36c0b210737c17cd26dc21904..87beade8fdb98b3d6e07040d71ea072f27588234 100644 --- a/Sources/ScanFeature/Views/ScanDisplayShareView.swift +++ b/Sources/ScanFeature/Views/ScanDisplayShareView.swift @@ -2,197 +2,198 @@ import UIKit import Shared import SnapKit import Combine +import AppResources final class ScanDisplayShareView: UIView { - enum Action { - case info - case addEmail - case addPhone - case toggleEmail - case togglePhone + enum Action { + case info + case addEmail + case addPhone + case toggleEmail + case togglePhone + } + + private var isExpanded = false { + didSet { updateBottomConstraint() } + } + + private let upperView = UIView() + private let lowerView = UIView() + private var bottomConstraint: Constraint? + + private let imageView = UIImageView() + private let titleView = TextWithInfoView() + private let emailView = AttributeSwitcher() + private let phoneView = AttributeSwitcher() + private var cancellables = Set<AnyCancellable>() + + private var currentConstraintConstant: CGFloat = 0.0 { + didSet { bottomConstraint?.update(offset: currentConstraintConstant) } + } + + private var bottomConstraintExpanded: CGFloat { + -lowerView.frame.height + } + + private var bottomConstraintNotExpanded: CGFloat { + 0 + } + + var actionPublisher: AnyPublisher<Action, Never> { + actionSubject.eraseToAnyPublisher() + } + + private let actionSubject = PassthroughSubject<Action, Never>() + + init() { + super.init(frame: .zero) + + upperView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(didPan(_:)))) + lowerView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(didPan(_:)))) + + layer.cornerRadius = 30 + imageView.image = Asset.scanDropdown.image + backgroundColor = Asset.neutralWhite.color + clipsToBounds = true + layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + + addSubview(upperView) + addSubview(lowerView) + + upperView.addSubview(imageView) + upperView.addSubview(titleView) + lowerView.addSubview(emailView) + lowerView.addSubview(phoneView) + + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineBreakMode = .byWordWrapping + + titleView.setup( + text: Localized.Scan.Display.Share.title, + attributes: [ + .foregroundColor: Asset.neutralBody.color, + .font: Fonts.Mulish.regular.font(size: 16.0) as Any, + .paragraphStyle: paragraphStyle + ], + didTapInfo: { [weak self] in self?.actionSubject.send(.info) } + ) + + emailView.switcherView + .publisher(for: .valueChanged) + .sink { [unowned self] in actionSubject.send(.toggleEmail) } + .store(in: &cancellables) + + phoneView.switcherView + .publisher(for: .valueChanged) + .sink { [unowned self] in actionSubject.send(.togglePhone) } + .store(in: &cancellables) + + emailView.addButton + .publisher(for: .touchUpInside) + .sink { [unowned self] in actionSubject.send(.addEmail) } + .store(in: &cancellables) + + phoneView.addButton + .publisher(for: .touchUpInside) + .sink { [unowned self] in actionSubject.send(.addPhone) } + .store(in: &cancellables) + + emailView.setup(state: nil, title: Localized.Scan.Display.Share.email) + phoneView.setup(state: nil, title: Localized.Scan.Display.Share.phone) + emailView.alpha = 0.0 + phoneView.alpha = 0.0 + + imageView.snp.makeConstraints { + $0.top.equalToSuperview().offset(15) + $0.centerX.equalToSuperview() } - - private var isExpanded = false { - didSet { updateBottomConstraint() } - } - - private let upperView = UIView() - private let lowerView = UIView() - private var bottomConstraint: Constraint? - - private let imageView = UIImageView() - private let titleView = TextWithInfoView() - private let emailView = AttributeSwitcher() - private let phoneView = AttributeSwitcher() - private var cancellables = Set<AnyCancellable>() - - private var currentConstraintConstant: CGFloat = 0.0 { - didSet { bottomConstraint?.update(offset: currentConstraintConstant) } + + titleView.snp.makeConstraints { + $0.top.equalTo(imageView.snp.bottom).offset(10) + $0.left.equalToSuperview().offset(40) + $0.right.lessThanOrEqualToSuperview().offset(-40) + $0.centerY.equalToSuperview() } - - private var bottomConstraintExpanded: CGFloat { - -lowerView.frame.height - } - - private var bottomConstraintNotExpanded: CGFloat { - 0 - } - - var actionPublisher: AnyPublisher<Action, Never> { - actionSubject.eraseToAnyPublisher() + + emailView.snp.makeConstraints { + $0.top.equalToSuperview() + $0.left.equalToSuperview().offset(40) + $0.right.equalToSuperview().offset(-40) } - - private let actionSubject = PassthroughSubject<Action, Never>() - - init() { - super.init(frame: .zero) - - upperView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(didPan(_:)))) - lowerView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(didPan(_:)))) - - layer.cornerRadius = 30 - imageView.image = Asset.scanDropdown.image - backgroundColor = Asset.neutralWhite.color - clipsToBounds = true - layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] - - addSubview(upperView) - addSubview(lowerView) - - upperView.addSubview(imageView) - upperView.addSubview(titleView) - lowerView.addSubview(emailView) - lowerView.addSubview(phoneView) - - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.lineBreakMode = .byWordWrapping - - titleView.setup( - text: Localized.Scan.Display.Share.title, - attributes: [ - .foregroundColor: Asset.neutralBody.color, - .font: Fonts.Mulish.regular.font(size: 16.0) as Any, - .paragraphStyle: paragraphStyle - ], - didTapInfo: { [weak self] in self?.actionSubject.send(.info) } - ) - - emailView.switcherView - .publisher(for: .valueChanged) - .sink { [unowned self] in actionSubject.send(.toggleEmail) } - .store(in: &cancellables) - - phoneView.switcherView - .publisher(for: .valueChanged) - .sink { [unowned self] in actionSubject.send(.togglePhone) } - .store(in: &cancellables) - - emailView.addButton - .publisher(for: .touchUpInside) - .sink { [unowned self] in actionSubject.send(.addEmail) } - .store(in: &cancellables) - - phoneView.addButton - .publisher(for: .touchUpInside) - .sink { [unowned self] in actionSubject.send(.addPhone) } - .store(in: &cancellables) - - emailView.setup(state: nil, title: Localized.Scan.Display.Share.email) - phoneView.setup(state: nil, title: Localized.Scan.Display.Share.phone) - emailView.alpha = 0.0 - phoneView.alpha = 0.0 - - imageView.snp.makeConstraints { - $0.top.equalToSuperview().offset(15) - $0.centerX.equalToSuperview() - } - - titleView.snp.makeConstraints { - $0.top.equalTo(imageView.snp.bottom).offset(10) - $0.left.equalToSuperview().offset(40) - $0.right.lessThanOrEqualToSuperview().offset(-40) - $0.centerY.equalToSuperview() - } - - emailView.snp.makeConstraints { - $0.top.equalToSuperview() - $0.left.equalToSuperview().offset(40) - $0.right.equalToSuperview().offset(-40) - } - - phoneView.snp.makeConstraints { - $0.top.equalTo(emailView.snp.bottom).offset(25) - $0.left.equalToSuperview().offset(40) - $0.right.equalToSuperview().offset(-40) - $0.bottom.equalToSuperview().offset(-40) - } - - upperView.snp.makeConstraints { - $0.top.equalToSuperview() - $0.left.equalToSuperview() - $0.right.equalToSuperview() - bottomConstraint = $0.bottom - .equalTo(safeAreaLayoutGuide) - .constraint - } - - lowerView.snp.makeConstraints { - $0.top.equalTo(upperView.snp.bottom).offset(-30) - $0.left.equalToSuperview() - $0.right.equalToSuperview() - } + + phoneView.snp.makeConstraints { + $0.top.equalTo(emailView.snp.bottom).offset(25) + $0.left.equalToSuperview().offset(40) + $0.right.equalToSuperview().offset(-40) + $0.bottom.equalToSuperview().offset(-40) } - - required init?(coder: NSCoder) { nil } - - func setup(email state: AttributeSwitcher.State?) { - emailView.setup(state: state, title: Localized.Scan.Display.Share.email) + + upperView.snp.makeConstraints { + $0.top.equalToSuperview() + $0.left.equalToSuperview() + $0.right.equalToSuperview() + bottomConstraint = $0.bottom + .equalTo(safeAreaLayoutGuide) + .constraint } - - func setup(phone state: AttributeSwitcher.State?) { - phoneView.setup(state: state, title: Localized.Scan.Display.Share.phone) + + lowerView.snp.makeConstraints { + $0.top.equalTo(upperView.snp.bottom).offset(-30) + $0.left.equalToSuperview() + $0.right.equalToSuperview() } - - @objc private func didPan(_ sender: UIPanGestureRecognizer) { - switch sender.state { - case .began, .changed: - let isUpwards = sender.translation(in: self).y < 0 - let result = currentConstraintConstant + sender.translation(in: self).y - - if isUpwards { - currentConstraintConstant = max(bottomConstraintExpanded, result) - } else { - currentConstraintConstant = min(bottomConstraintNotExpanded, result) - } - - let currentMinusExpanded = currentConstraintConstant - bottomConstraintExpanded - let notExpandedMinusExpanded = bottomConstraintNotExpanded - bottomConstraintExpanded - let alpha = 1 - (currentMinusExpanded / abs(notExpandedMinusExpanded)) - emailView.alpha = alpha - phoneView.alpha = alpha - - case .cancelled, .ended, .failed: - let currentMinusExpanded = currentConstraintConstant - bottomConstraintExpanded - let notExpandedMinusExpanded = bottomConstraintNotExpanded - bottomConstraintExpanded - let percentage = currentMinusExpanded / abs(notExpandedMinusExpanded) - isExpanded = percentage < 0.5 - - case .possible: - break - @unknown default: - break - } + } + + required init?(coder: NSCoder) { nil } + + func setup(email state: AttributeSwitcher.State?) { + emailView.setup(state: state, title: Localized.Scan.Display.Share.email) + } + + func setup(phone state: AttributeSwitcher.State?) { + phoneView.setup(state: state, title: Localized.Scan.Display.Share.phone) + } + + @objc private func didPan(_ sender: UIPanGestureRecognizer) { + switch sender.state { + case .began, .changed: + let isUpwards = sender.translation(in: self).y < 0 + let result = currentConstraintConstant + sender.translation(in: self).y + + if isUpwards { + currentConstraintConstant = max(bottomConstraintExpanded, result) + } else { + currentConstraintConstant = min(bottomConstraintNotExpanded, result) + } + + let currentMinusExpanded = currentConstraintConstant - bottomConstraintExpanded + let notExpandedMinusExpanded = bottomConstraintNotExpanded - bottomConstraintExpanded + let alpha = 1 - (currentMinusExpanded / abs(notExpandedMinusExpanded)) + emailView.alpha = alpha + phoneView.alpha = alpha + + case .cancelled, .ended, .failed: + let currentMinusExpanded = currentConstraintConstant - bottomConstraintExpanded + let notExpandedMinusExpanded = bottomConstraintNotExpanded - bottomConstraintExpanded + let percentage = currentMinusExpanded / abs(notExpandedMinusExpanded) + isExpanded = percentage < 0.5 + + case .possible: + break + @unknown default: + break } - - private func updateBottomConstraint() { - if isExpanded { - emailView.alpha = 1.0 - phoneView.alpha = 1.0 - currentConstraintConstant = bottomConstraintExpanded - } else { - emailView.alpha = 0.0 - phoneView.alpha = 0.0 - currentConstraintConstant = bottomConstraintNotExpanded - } + } + + private func updateBottomConstraint() { + if isExpanded { + emailView.alpha = 1.0 + phoneView.alpha = 1.0 + currentConstraintConstant = bottomConstraintExpanded + } else { + emailView.alpha = 0.0 + phoneView.alpha = 0.0 + currentConstraintConstant = bottomConstraintNotExpanded } + } } diff --git a/Sources/ScanFeature/Views/ScanDisplayView.swift b/Sources/ScanFeature/Views/ScanDisplayView.swift index fa2b02438c0d735708b9a30ffc4a6c2228926890..5c3bf28c2f7ce73826007a228cd8f9c00e7027ad 100644 --- a/Sources/ScanFeature/Views/ScanDisplayView.swift +++ b/Sources/ScanFeature/Views/ScanDisplayView.swift @@ -1,101 +1,102 @@ import UIKit import Shared import Combine +import AppResources final class ScanDisplayView: UIView { - var actionPublisher: AnyPublisher<ScanDisplayShareView.Action, Never> { - shareSheetView.actionPublisher.eraseToAnyPublisher() + var actionPublisher: AnyPublisher<ScanDisplayShareView.Action, Never> { + shareSheetView.actionPublisher.eraseToAnyPublisher() + } + + private let copyLabel = UILabel() + private let codeButton = ScanQRButton() + private let copyImageView = UIImageView() + private let copyContainerButton = UIControl() + private var cancellables = Set<AnyCancellable>() + private let shareSheetView = ScanDisplayShareView() + + init() { + super.init(frame: .zero) + backgroundColor = Asset.neutralDark.color + + copyImageView.image = Asset.scanCopy.image + copyLabel.text = Localized.Scan.Display.copy + copyLabel.textColor = Asset.neutralDisabled.color + copyLabel.font = Fonts.Mulish.semiBold.font(size: 13.0) + + codeButton.publisher(for: .touchUpInside) + .merge(with: copyContainerButton.publisher(for: .touchUpInside)) + .sink { [unowned self] in + UIGraphicsBeginImageContext(codeButton.frame.size) + codeButton.layer.render(in: UIGraphicsGetCurrentContext()!) + let output = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + UIImageWriteToSavedPhotosAlbum(output!, nil, nil, nil) + codeButton.blinkCopied() + }.store(in: &cancellables) + + addSubview(codeButton) + addSubview(copyContainerButton) + copyContainerButton.addSubview(copyLabel) + copyContainerButton.addSubview(copyImageView) + + addSubview(shareSheetView) + + codeButton.snp.makeConstraints { + $0.centerX.equalTo(safeAreaLayoutGuide) + $0.centerY.equalTo(safeAreaLayoutGuide).multipliedBy(0.6) + $0.width.equalTo(safeAreaLayoutGuide).multipliedBy(0.6) + $0.height.equalTo(codeButton.snp.width) } - private let copyLabel = UILabel() - private let codeButton = ScanQRButton() - private let copyImageView = UIImageView() - private let copyContainerButton = UIControl() - private var cancellables = Set<AnyCancellable>() - private let shareSheetView = ScanDisplayShareView() - - init() { - super.init(frame: .zero) - backgroundColor = Asset.neutralDark.color - - copyImageView.image = Asset.scanCopy.image - copyLabel.text = Localized.Scan.Display.copy - copyLabel.textColor = Asset.neutralDisabled.color - copyLabel.font = Fonts.Mulish.semiBold.font(size: 13.0) - - codeButton.publisher(for: .touchUpInside) - .merge(with: copyContainerButton.publisher(for: .touchUpInside)) - .sink { [unowned self] in - UIGraphicsBeginImageContext(codeButton.frame.size) - codeButton.layer.render(in: UIGraphicsGetCurrentContext()!) - let output = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - - UIImageWriteToSavedPhotosAlbum(output!, nil, nil, nil) - codeButton.blinkCopied() - }.store(in: &cancellables) - - addSubview(codeButton) - addSubview(copyContainerButton) - copyContainerButton.addSubview(copyLabel) - copyContainerButton.addSubview(copyImageView) - - addSubview(shareSheetView) - - codeButton.snp.makeConstraints { - $0.centerX.equalTo(safeAreaLayoutGuide) - $0.centerY.equalTo(safeAreaLayoutGuide).multipliedBy(0.6) - $0.width.equalTo(safeAreaLayoutGuide).multipliedBy(0.6) - $0.height.equalTo(codeButton.snp.width) - } - - copyContainerButton.snp.makeConstraints { - $0.top.equalTo(codeButton.snp.bottom).offset(33) - $0.centerX.equalTo(codeButton) - } - - copyImageView.snp.makeConstraints { - $0.top.equalToSuperview() - $0.left.equalToSuperview() - $0.bottom.equalToSuperview() - } - - copyLabel.snp.makeConstraints { - $0.top.equalToSuperview() - $0.left.equalTo(copyImageView.snp.right).offset(5) - $0.right.equalToSuperview() - $0.bottom.equalToSuperview() - } - - shareSheetView.snp.makeConstraints { - $0.left.equalToSuperview() - $0.right.equalToSuperview() - $0.bottom.equalToSuperview() - } + copyContainerButton.snp.makeConstraints { + $0.top.equalTo(codeButton.snp.bottom).offset(33) + $0.centerX.equalTo(codeButton) } - required init?(coder: NSCoder) { nil } + copyImageView.snp.makeConstraints { + $0.top.equalToSuperview() + $0.left.equalToSuperview() + $0.bottom.equalToSuperview() + } + + copyLabel.snp.makeConstraints { + $0.top.equalToSuperview() + $0.left.equalTo(copyImageView.snp.right).offset(5) + $0.right.equalToSuperview() + $0.bottom.equalToSuperview() + } - func setup(code image: CIImage) { - codeButton.setup(code: image) + shareSheetView.snp.makeConstraints { + $0.left.equalToSuperview() + $0.right.equalToSuperview() + $0.bottom.equalToSuperview() + } + } + + required init?(coder: NSCoder) { nil } + + func setup(code image: CIImage) { + codeButton.setup(code: image) + } + + func setupAttributes( + email: String?, + phone: String?, + emailSharing: Bool, + phoneSharing: Bool + ) { + if let email = email { + shareSheetView.setup(email: .init(content: email, isVisible: emailSharing)) + } else { + shareSheetView.setup(email: nil) } - func setupAttributes( - email: String?, - phone: String?, - emailSharing: Bool, - phoneSharing: Bool - ) { - if let email = email { - shareSheetView.setup(email: .init(content: email, isVisible: emailSharing)) - } else { - shareSheetView.setup(email: nil) - } - - if let phone = phone { - shareSheetView.setup(phone: .init(content: phone, isVisible: phoneSharing)) - } else { - shareSheetView.setup(phone: nil) - } + if let phone = phone { + shareSheetView.setup(phone: .init(content: phone, isVisible: phoneSharing)) + } else { + shareSheetView.setup(phone: nil) } + } } diff --git a/Sources/ScanFeature/Views/ScanOverlayView.swift b/Sources/ScanFeature/Views/ScanOverlayView.swift index 14bc4c5dc736a388a41d9c04f21a51fa49467e99..060bfbdac4476f7c2a020ee654ee1b9ee7670718 100644 --- a/Sources/ScanFeature/Views/ScanOverlayView.swift +++ b/Sources/ScanFeature/Views/ScanOverlayView.swift @@ -1,181 +1,182 @@ import UIKit import Shared +import AppResources final class ScanOverlayView: UIView { - private let cropView = UIView() - private let scanViewLength = 266.0 - private let maskLayer = CAShapeLayer() - private let topLeftLayer = CAShapeLayer() - private let topRightLayer = CAShapeLayer() - private let bottomLeftLayer = CAShapeLayer() - private let bottomRightLayer = CAShapeLayer() - - init() { - super.init(frame: .zero) - backgroundColor = Asset.neutralDark.color.withAlphaComponent(0.5) - - addSubview(cropView) - - cropView.snp.makeConstraints { - $0.width.equalTo(scanViewLength) - $0.centerY.equalToSuperview().offset(-50) - $0.centerX.equalToSuperview() - $0.height.equalTo(scanViewLength) - } - - maskLayer.fillRule = .evenOdd - layer.mask = maskLayer - layer.masksToBounds = true - - [topLeftLayer, topRightLayer, bottomLeftLayer, bottomRightLayer].forEach { - $0.strokeColor = Asset.brandPrimary.color.cgColor - $0.fillColor = UIColor.clear.cgColor - $0.lineWidth = 3.0 - $0.lineCap = .round - layer.addSublayer($0) - } + private let cropView = UIView() + private let scanViewLength = 266.0 + private let maskLayer = CAShapeLayer() + private let topLeftLayer = CAShapeLayer() + private let topRightLayer = CAShapeLayer() + private let bottomLeftLayer = CAShapeLayer() + private let bottomRightLayer = CAShapeLayer() + + init() { + super.init(frame: .zero) + backgroundColor = Asset.neutralDark.color.withAlphaComponent(0.5) + + addSubview(cropView) + + cropView.snp.makeConstraints { + $0.width.equalTo(scanViewLength) + $0.centerY.equalToSuperview().offset(-50) + $0.centerX.equalToSuperview() + $0.height.equalTo(scanViewLength) } - required init?(coder: NSCoder) { nil } + maskLayer.fillRule = .evenOdd + layer.mask = maskLayer + layer.masksToBounds = true - override func layoutSubviews() { - super.layoutSubviews() - - maskLayer.frame = bounds - let path = UIBezierPath(rect: bounds) - path.append(UIBezierPath(roundedRect: cropView.frame, cornerRadius: 30.0)) - maskLayer.path = path.cgPath - - topLeftLayer.frame = bounds - topRightLayer.frame = bounds - bottomRightLayer.frame = bounds - bottomLeftLayer.frame = bounds - - topLeftLayer.path = topLeftPath() - topRightLayer.path = topRightPath() - bottomRightLayer.path = bottomRightPath() - bottomLeftLayer.path = bottomLeftPath() - } - - func updateCornerColor(_ color: UIColor) { - [topLeftLayer, topRightLayer, bottomLeftLayer, bottomRightLayer].forEach { - $0.strokeColor = color.cgColor - } - } - - func topLeftPath() -> CGPath { - let path = UIBezierPath() - - let vert0X = cropView.frame.minX - 15 - let vert0Y = cropView.frame.minY + 45 - let vert0 = CGPoint(x: vert0X, y: vert0Y) - path.move(to: vert0) - - let vertNX = cropView.frame.minX - 15 - let vertNY = cropView.frame.minY + 15 - let vertN = CGPoint(x: vertNX, y: vertNY) - path.addLine(to: vertN) - - let arcCenterX = cropView.frame.minX + 15 - let arcCenterY = cropView.frame.minY + 15 - let arcCenter = CGPoint(x: arcCenterX , y: arcCenterY) - path.addArc(center: arcCenter, startAngle: .pi) - - let horizX = cropView.frame.minX + 45 - let horizY = cropView.frame.minY - 15 - let horiz = CGPoint(x: horizX, y: horizY) - path.addLine(to: horiz) - - return path.cgPath + [topLeftLayer, topRightLayer, bottomLeftLayer, bottomRightLayer].forEach { + $0.strokeColor = Asset.brandPrimary.color.cgColor + $0.fillColor = UIColor.clear.cgColor + $0.lineWidth = 3.0 + $0.lineCap = .round + layer.addSublayer($0) } + } - func topRightPath() -> CGPath { - let path = UIBezierPath() + required init?(coder: NSCoder) { nil } - let horiz0X = cropView.frame.maxX - 45 - let horiz0Y = cropView.frame.minY - 15 - let horiz0 = CGPoint(x: horiz0X, y: horiz0Y) - path.move(to: horiz0) + override func layoutSubviews() { + super.layoutSubviews() - let horizNX = cropView.frame.maxX - 15 - let horizNY = cropView.frame.minY - 15 - let horizN = CGPoint(x: horizNX, y: horizNY) - path.addLine(to: horizN) + maskLayer.frame = bounds + let path = UIBezierPath(rect: bounds) + path.append(UIBezierPath(roundedRect: cropView.frame, cornerRadius: 30.0)) + maskLayer.path = path.cgPath - let arcCenterX = cropView.frame.maxX - 15 - let arcCenterY = cropView.frame.minY + 15 - let arcCenter = CGPoint(x: arcCenterX, y: arcCenterY) - path.addArc(center: arcCenter, startAngle: 3 * .pi/2) + topLeftLayer.frame = bounds + topRightLayer.frame = bounds + bottomRightLayer.frame = bounds + bottomLeftLayer.frame = bounds - let vertX = cropView.frame.maxX + 15 - let vertY = cropView.frame.minY + 45 - let vert = CGPoint(x: vertX, y: vertY) - path.addLine(to: vert) + topLeftLayer.path = topLeftPath() + topRightLayer.path = topRightPath() + bottomRightLayer.path = bottomRightPath() + bottomLeftLayer.path = bottomLeftPath() + } - return path.cgPath - } - - func bottomRightPath() -> CGPath { - let path = UIBezierPath() - - let vert0X = cropView.frame.maxX + 15 - let vert0Y = cropView.frame.maxY - 45 - let vert0 = CGPoint(x: vert0X, y: vert0Y) - path.move(to: vert0) - - let vertNX = cropView.frame.maxX + 15 - let vertNY = cropView.frame.maxY - 15 - let vertN = CGPoint(x: vertNX, y: vertNY) - path.addLine(to: vertN) - - let arcCenterX = cropView.frame.maxX - 15 - let arcCenterY = cropView.frame.maxY - 15 - let arcCenter = CGPoint(x: arcCenterX, y: arcCenterY) - path.addArc(center: arcCenter, startAngle: 0) - - let horizX = cropView.frame.maxX - 45 - let horizY = cropView.frame.maxY + 15 - let horiz = CGPoint(x: horizX, y: horizY) - path.addLine(to: horiz) - - return path.cgPath - } - - func bottomLeftPath() -> CGPath { - let path = UIBezierPath() - - let horiz0X = cropView.frame.minX + 45 - let horiz0Y = cropView.frame.maxY + 15 - let horiz0 = CGPoint(x: horiz0X, y: horiz0Y) - path.move(to: horiz0) - - let horizNX = cropView.frame.minX + 15 - let horizNY = cropView.frame.maxY + 15 - let horizN = CGPoint(x: horizNX, y: horizNY) - path.addLine(to: horizN) - - let arcCenterX = cropView.frame.minX + 15 - let arcCenterY = cropView.frame.maxY - 15 - let arcCenter = CGPoint(x: arcCenterX, y: arcCenterY) - path.addArc(center: arcCenter, startAngle: .pi/2) - - let vertX = cropView.frame.minX - 15 - let vertY = cropView.frame.maxY - 45 - let vert = CGPoint(x: vertX, y: vertY) - path.addLine(to: vert) - - return path.cgPath + func updateCornerColor(_ color: UIColor) { + [topLeftLayer, topRightLayer, bottomLeftLayer, bottomRightLayer].forEach { + $0.strokeColor = color.cgColor } + } + + func topLeftPath() -> CGPath { + let path = UIBezierPath() + + let vert0X = cropView.frame.minX - 15 + let vert0Y = cropView.frame.minY + 45 + let vert0 = CGPoint(x: vert0X, y: vert0Y) + path.move(to: vert0) + + let vertNX = cropView.frame.minX - 15 + let vertNY = cropView.frame.minY + 15 + let vertN = CGPoint(x: vertNX, y: vertNY) + path.addLine(to: vertN) + + let arcCenterX = cropView.frame.minX + 15 + let arcCenterY = cropView.frame.minY + 15 + let arcCenter = CGPoint(x: arcCenterX , y: arcCenterY) + path.addArc(center: arcCenter, startAngle: .pi) + + let horizX = cropView.frame.minX + 45 + let horizY = cropView.frame.minY - 15 + let horiz = CGPoint(x: horizX, y: horizY) + path.addLine(to: horiz) + + return path.cgPath + } + + func topRightPath() -> CGPath { + let path = UIBezierPath() + + let horiz0X = cropView.frame.maxX - 45 + let horiz0Y = cropView.frame.minY - 15 + let horiz0 = CGPoint(x: horiz0X, y: horiz0Y) + path.move(to: horiz0) + + let horizNX = cropView.frame.maxX - 15 + let horizNY = cropView.frame.minY - 15 + let horizN = CGPoint(x: horizNX, y: horizNY) + path.addLine(to: horizN) + + let arcCenterX = cropView.frame.maxX - 15 + let arcCenterY = cropView.frame.minY + 15 + let arcCenter = CGPoint(x: arcCenterX, y: arcCenterY) + path.addArc(center: arcCenter, startAngle: 3 * .pi/2) + + let vertX = cropView.frame.maxX + 15 + let vertY = cropView.frame.minY + 45 + let vert = CGPoint(x: vertX, y: vertY) + path.addLine(to: vert) + + return path.cgPath + } + + func bottomRightPath() -> CGPath { + let path = UIBezierPath() + + let vert0X = cropView.frame.maxX + 15 + let vert0Y = cropView.frame.maxY - 45 + let vert0 = CGPoint(x: vert0X, y: vert0Y) + path.move(to: vert0) + + let vertNX = cropView.frame.maxX + 15 + let vertNY = cropView.frame.maxY - 15 + let vertN = CGPoint(x: vertNX, y: vertNY) + path.addLine(to: vertN) + + let arcCenterX = cropView.frame.maxX - 15 + let arcCenterY = cropView.frame.maxY - 15 + let arcCenter = CGPoint(x: arcCenterX, y: arcCenterY) + path.addArc(center: arcCenter, startAngle: 0) + + let horizX = cropView.frame.maxX - 45 + let horizY = cropView.frame.maxY + 15 + let horiz = CGPoint(x: horizX, y: horizY) + path.addLine(to: horiz) + + return path.cgPath + } + + func bottomLeftPath() -> CGPath { + let path = UIBezierPath() + + let horiz0X = cropView.frame.minX + 45 + let horiz0Y = cropView.frame.maxY + 15 + let horiz0 = CGPoint(x: horiz0X, y: horiz0Y) + path.move(to: horiz0) + + let horizNX = cropView.frame.minX + 15 + let horizNY = cropView.frame.maxY + 15 + let horizN = CGPoint(x: horizNX, y: horizNY) + path.addLine(to: horizN) + + let arcCenterX = cropView.frame.minX + 15 + let arcCenterY = cropView.frame.maxY - 15 + let arcCenter = CGPoint(x: arcCenterX, y: arcCenterY) + path.addArc(center: arcCenter, startAngle: .pi/2) + + let vertX = cropView.frame.minX - 15 + let vertY = cropView.frame.maxY - 45 + let vert = CGPoint(x: vertX, y: vertY) + path.addLine(to: vert) + + return path.cgPath + } } private extension UIBezierPath { - func addArc(center: CGPoint, startAngle: CGFloat) { - addArc( - withCenter: center, - radius: 30, - startAngle: startAngle, - endAngle: startAngle + .pi/2, - clockwise: true - ) - } + func addArc(center: CGPoint, startAngle: CGFloat) { + addArc( + withCenter: center, + radius: 30, + startAngle: startAngle, + endAngle: startAngle + .pi/2, + clockwise: true + ) + } } diff --git a/Sources/ScanFeature/Views/ScanQRButton.swift b/Sources/ScanFeature/Views/ScanQRButton.swift index 66d911c602a67c4ad01a02002fc4f9de2b2563ac..35c045df11c1b3eb54da0360d653695d85a79e2b 100644 --- a/Sources/ScanFeature/Views/ScanQRButton.swift +++ b/Sources/ScanFeature/Views/ScanQRButton.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class ScanQRButton: UIControl { private let overlayView = UIView() diff --git a/Sources/ScanFeature/Views/ScanSegmentedControlButton.swift b/Sources/ScanFeature/Views/ScanSegmentedControlButton.swift index 85983848b9c60656f2f08944532e8c8b3553a165..010997ef1d52245501f61ec9a5fb1a8cb8381980 100644 --- a/Sources/ScanFeature/Views/ScanSegmentedControlButton.swift +++ b/Sources/ScanFeature/Views/ScanSegmentedControlButton.swift @@ -1,73 +1,74 @@ import UIKit import Shared +import AppResources final class ScanSegmentedControlButton: UIControl { - let titleLabel = UILabel() - let separatorView = UIView() - let imageView = UIImageView() + let titleLabel = UILabel() + let separatorView = UIView() + let imageView = UIImageView() - init() { - super.init(frame: .zero) + init() { + super.init(frame: .zero) - separatorView.alpha = 0.0 - titleLabel.textAlignment = .center - imageView.tintColor = Asset.neutralWeak.color - titleLabel.textColor = Asset.neutralWeak.color - separatorView.backgroundColor = Asset.neutralWhite.color - titleLabel.font = Fonts.Mulish.semiBold.font(size: 13) - imageView.transform = imageView.transform.scaledBy(x: 0.9, y: 0.9) - titleLabel.transform = titleLabel.transform.scaledBy(x: 0.9, y: 0.9) + separatorView.alpha = 0.0 + titleLabel.textAlignment = .center + imageView.tintColor = Asset.neutralWeak.color + titleLabel.textColor = Asset.neutralWeak.color + separatorView.backgroundColor = Asset.neutralWhite.color + titleLabel.font = Fonts.Mulish.semiBold.font(size: 13) + imageView.transform = imageView.transform.scaledBy(x: 0.9, y: 0.9) + titleLabel.transform = titleLabel.transform.scaledBy(x: 0.9, y: 0.9) - addSubview(titleLabel) - addSubview(imageView) - addSubview(separatorView) + addSubview(titleLabel) + addSubview(imageView) + addSubview(separatorView) - imageView.snp.makeConstraints { - $0.top.equalToSuperview().offset(7.5) - $0.centerX.equalToSuperview() - } + imageView.snp.makeConstraints { + $0.top.equalToSuperview().offset(7.5) + $0.centerX.equalToSuperview() + } - titleLabel.snp.makeConstraints { - $0.top.equalTo(imageView.snp.bottom).offset(2) - $0.centerX.equalToSuperview() - $0.bottom.equalToSuperview().offset(-7.5) - } + titleLabel.snp.makeConstraints { + $0.top.equalTo(imageView.snp.bottom).offset(2) + $0.centerX.equalToSuperview() + $0.bottom.equalToSuperview().offset(-7.5) + } - separatorView.snp.makeConstraints { - $0.height.equalTo(2) - $0.left.equalToSuperview().offset(20) - $0.right.equalToSuperview().offset(-20) - $0.bottom.equalToSuperview() - } + separatorView.snp.makeConstraints { + $0.height.equalTo(2) + $0.left.equalToSuperview().offset(20) + $0.right.equalToSuperview().offset(-20) + $0.bottom.equalToSuperview() } + } - required init?(coder: NSCoder) { nil } + required init?(coder: NSCoder) { nil } - func set(selected: Bool) { - switch (isSelected, selected) { - case (true, false): - UIView.animate(withDuration: 0.25) { - self.imageView.transform = self.imageView.transform.scaledBy(x: 0.9, y: 0.9) - self.titleLabel.transform = self.titleLabel.transform.scaledBy(x: 0.9, y: 0.9) - self.imageView.tintColor = Asset.neutralWeak.color - self.titleLabel.textColor = Asset.neutralWeak.color - self.separatorView.alpha = 0.0 - } completion: { _ in - self.isSelected = false - } + func set(selected: Bool) { + switch (isSelected, selected) { + case (true, false): + UIView.animate(withDuration: 0.25) { + self.imageView.transform = self.imageView.transform.scaledBy(x: 0.9, y: 0.9) + self.titleLabel.transform = self.titleLabel.transform.scaledBy(x: 0.9, y: 0.9) + self.imageView.tintColor = Asset.neutralWeak.color + self.titleLabel.textColor = Asset.neutralWeak.color + self.separatorView.alpha = 0.0 + } completion: { _ in + self.isSelected = false + } - case (false, true): - UIView.animate(withDuration: 0.25) { - self.imageView.transform = .identity - self.titleLabel.transform = .identity - self.imageView.tintColor = Asset.neutralWhite.color - self.titleLabel.textColor = Asset.neutralWhite.color - self.separatorView.alpha = 1.0 - } completion: { _ in - self.isSelected = true - } - case (true, true), (false, false): - break - } + case (false, true): + UIView.animate(withDuration: 0.25) { + self.imageView.transform = .identity + self.titleLabel.transform = .identity + self.imageView.tintColor = Asset.neutralWhite.color + self.titleLabel.textColor = Asset.neutralWhite.color + self.separatorView.alpha = 1.0 + } completion: { _ in + self.isSelected = true + } + case (true, true), (false, false): + break } + } } diff --git a/Sources/ScanFeature/Views/ScanView.swift b/Sources/ScanFeature/Views/ScanView.swift index 540f68f524bac7122bb0db21111c62e1aaddb454..8ae5933bf9a82d430ac0cd7cff31ec00faf5bee8 100644 --- a/Sources/ScanFeature/Views/ScanView.swift +++ b/Sources/ScanFeature/Views/ScanView.swift @@ -1,112 +1,113 @@ import UIKit import Shared +import AppResources final class ScanView: UIView { - private let statusLabel = UILabel() - private let imageView = UIImageView() - private let stackView = UIStackView() - private let animationView = DotAnimation() - private let overlayView = ScanOverlayView() - private(set) var actionButton = CapsuleButton() - - init() { - super.init(frame: .zero) - imageView.contentMode = .center - actionButton.setStyle(.brandColored) - - statusLabel.numberOfLines = 0 - statusLabel.textAlignment = .center - statusLabel.textColor = Asset.neutralWhite.color - statusLabel.font = Fonts.Mulish.regular.font(size: 14.0) - - stackView.spacing = 15 - stackView.axis = .vertical - stackView.addArrangedSubview(animationView) - stackView.addArrangedSubview(imageView) - stackView.addArrangedSubview(statusLabel) - stackView.addArrangedSubview(actionButton) - - imageView.isHidden = true - actionButton.isHidden = true - animationView.isHidden = false - - addSubview(overlayView) - addSubview(stackView) - - overlayView.snp.makeConstraints { - $0.top.equalToSuperview() - $0.left.equalToSuperview() - $0.right.equalToSuperview() - $0.bottom.equalToSuperview() - } - - stackView.snp.makeConstraints { - $0.left.equalToSuperview().offset(57) - $0.right.equalToSuperview().offset(-57) - $0.bottom.equalTo(safeAreaLayoutGuide).offset(-100) - } + private let statusLabel = UILabel() + private let imageView = UIImageView() + private let stackView = UIStackView() + private let animationView = DotAnimation() + private let overlayView = ScanOverlayView() + private(set) var actionButton = CapsuleButton() + + init() { + super.init(frame: .zero) + imageView.contentMode = .center + actionButton.setStyle(.brandColored) + + statusLabel.numberOfLines = 0 + statusLabel.textAlignment = .center + statusLabel.textColor = Asset.neutralWhite.color + statusLabel.font = Fonts.Mulish.regular.font(size: 14.0) + + stackView.spacing = 15 + stackView.axis = .vertical + stackView.addArrangedSubview(animationView) + stackView.addArrangedSubview(imageView) + stackView.addArrangedSubview(statusLabel) + stackView.addArrangedSubview(actionButton) + + imageView.isHidden = true + actionButton.isHidden = true + animationView.isHidden = false + + addSubview(overlayView) + addSubview(stackView) + + overlayView.snp.makeConstraints { + $0.top.equalToSuperview() + $0.left.equalToSuperview() + $0.right.equalToSuperview() + $0.bottom.equalToSuperview() } - required init?(coder: NSCoder) { nil } - - func update(with state: ScanStatus) { - var text: String - - switch state { - case .reading, .processing: - imageView.isHidden = true - actionButton.isHidden = true - text = Localized.Scan.Status.reading - overlayView.updateCornerColor(Asset.brandPrimary.color) - - case .success: - animationView.isHidden = true - actionButton.isHidden = true - imageView.isHidden = false - imageView.image = Asset.sharedSuccess.image - text = Localized.Scan.Status.success - overlayView.updateCornerColor(Asset.accentSuccess.color) - - case .failed(let error): - animationView.isHidden = true - imageView.image = Asset.scanError.image - imageView.isHidden = false - overlayView.updateCornerColor(Asset.accentDanger.color) - - switch error { - case .requestOpened: - text = Localized.Scan.Error.requested - actionButton.setTitle(Localized.Scan.requests, for: .normal) - actionButton.isHidden = false - - case .alreadyFriends(let name): - text = Localized.Scan.Error.alreadyFriends(name) - actionButton.setTitle(Localized.Scan.contact, for: .normal) - actionButton.isHidden = false - - case .cameraPermission: - text = Localized.Scan.Error.cameraPermissionNeeded - actionButton.setTitle(Localized.Scan.settings, for: .normal) - actionButton.isHidden = false - - case .unknown(let content): - text = content - } - } - - let attString = NSMutableAttributedString(string: text) - let paragraph = NSMutableParagraphStyle() - paragraph.alignment = .center - paragraph.lineHeightMultiple = 1.35 - - attString.addAttribute(.paragraphStyle, value: paragraph) - attString.addAttribute(.foregroundColor, value: Asset.neutralWhite.color) - attString.addAttribute(.font, value: Fonts.Mulish.regular.font(size: 14.0) as Any) - - if text.contains("#") { - attString.addAttribute(name: .foregroundColor, value: Asset.brandPrimary.color, betweenCharacters: "#") - } - - statusLabel.attributedText = attString + stackView.snp.makeConstraints { + $0.left.equalToSuperview().offset(57) + $0.right.equalToSuperview().offset(-57) + $0.bottom.equalTo(safeAreaLayoutGuide).offset(-100) } + } + + required init?(coder: NSCoder) { nil } + + func update(with state: ScanStatus) { + var text: String + + switch state { + case .reading, .processing: + imageView.isHidden = true + actionButton.isHidden = true + text = Localized.Scan.Status.reading + overlayView.updateCornerColor(Asset.brandPrimary.color) + + case .success: + animationView.isHidden = true + actionButton.isHidden = true + imageView.isHidden = false + imageView.image = Asset.sharedSuccess.image + text = Localized.Scan.Status.success + overlayView.updateCornerColor(Asset.accentSuccess.color) + + case .failed(let error): + animationView.isHidden = true + imageView.image = Asset.scanError.image + imageView.isHidden = false + overlayView.updateCornerColor(Asset.accentDanger.color) + + switch error { + case .requestOpened: + text = Localized.Scan.Error.requested + actionButton.setTitle(Localized.Scan.requests, for: .normal) + actionButton.isHidden = false + + case .alreadyFriends(let name): + text = Localized.Scan.Error.alreadyFriends(name) + actionButton.setTitle(Localized.Scan.contact, for: .normal) + actionButton.isHidden = false + + case .cameraPermission: + text = Localized.Scan.Error.cameraPermissionNeeded + actionButton.setTitle(Localized.Scan.settings, for: .normal) + actionButton.isHidden = false + + case .unknown(let content): + text = content + } + } + + let attString = NSMutableAttributedString(string: text) + let paragraph = NSMutableParagraphStyle() + paragraph.alignment = .center + paragraph.lineHeightMultiple = 1.35 + + attString.addAttribute(.paragraphStyle, value: paragraph) + attString.addAttribute(.foregroundColor, value: Asset.neutralWhite.color) + attString.addAttribute(.font, value: Fonts.Mulish.regular.font(size: 14.0) as Any) + + if text.contains("#") { + attString.addAttribute(name: .foregroundColor, value: Asset.brandPrimary.color, betweenCharacters: "#") + } + + statusLabel.attributedText = attString + } } diff --git a/Sources/SearchFeature/Controllers/SearchContainerController.swift b/Sources/SearchFeature/Controllers/SearchContainerController.swift index 8090af4f689290756de3a67082641cb099c37794..ec9bcd59eb990643390ba4d458c184d488b4d193 100644 --- a/Sources/SearchFeature/Controllers/SearchContainerController.swift +++ b/Sources/SearchFeature/Controllers/SearchContainerController.swift @@ -1,14 +1,16 @@ import UIKit import Shared import Combine +import AppCore import XXModels -import Navigation +import Dependencies +import AppResources +import AppNavigation import DrawerFeature -import DI public final class SearchContainerController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var barStylist: StatusBarStylist + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var screenView = SearchContainerView() @@ -39,7 +41,7 @@ public final class SearchContainerController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationItem.backButtonTitle = "" - barStylist.styleSubject.send(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar.customize( backgroundColor: Asset.neutralWhite.color ) diff --git a/Sources/SearchFeature/Controllers/SearchLeftController.swift b/Sources/SearchFeature/Controllers/SearchLeftController.swift index 5570d6c5d349b6d0140e8290189e19f6aae5a017..4485fa22e286efcc668220c973c8aa4ab9ea0b3a 100644 --- a/Sources/SearchFeature/Controllers/SearchLeftController.swift +++ b/Sources/SearchFeature/Controllers/SearchLeftController.swift @@ -3,13 +3,14 @@ import Shared import Combine import XXModels import Defaults -import Navigation +import AppResources +import Dependencies +import AppNavigation import DrawerFeature import CountryListFeature -import DI final class SearchLeftController: UIViewController { - @Dependency var navigator: Navigator + @Dependency(\.navigator) var navigator: Navigator @KeyObject(.email, defaultValue: nil) var email: String? @KeyObject(.phone, defaultValue: nil) var phone: String? @KeyObject(.sharingEmail, defaultValue: false) var isSharingEmail: Bool diff --git a/Sources/SearchFeature/Controllers/SearchRightController.swift b/Sources/SearchFeature/Controllers/SearchRightController.swift index 9f4a7f796111c96667da875b9a63baaedfffa116..3445f2266bc8a43dd479eb7cfe2bb9287e49e34b 100644 --- a/Sources/SearchFeature/Controllers/SearchRightController.swift +++ b/Sources/SearchFeature/Controllers/SearchRightController.swift @@ -1,10 +1,10 @@ import UIKit import Combine -import Navigation -import DI +import AppNavigation +import ComposableArchitecture final class SearchRightController: UIViewController { - @Dependency var navigator: Navigator + @Dependency(\.navigator) var navigator: Navigator private lazy var screenView = SearchRightView() diff --git a/Sources/SearchFeature/ViewModels/SearchContainerViewModel.swift b/Sources/SearchFeature/ViewModels/SearchContainerViewModel.swift index 7e5767a3a7180b1b554308c92067d0935a8bcb7b..1571afdae49af23f77a166b50518bffe21f92582 100644 --- a/Sources/SearchFeature/ViewModels/SearchContainerViewModel.swift +++ b/Sources/SearchFeature/ViewModels/SearchContainerViewModel.swift @@ -2,57 +2,50 @@ import UIKit import Combine import Defaults import XXClient -import PushFeature -import DI +import PermissionsFeature +import ComposableArchitecture final class SearchContainerViewModel { - @Dependency var pushHandler: PushHandling - @Dependency var dummyTrafficManager: DummyTraffic - - @KeyObject(.dummyTrafficOn, defaultValue: false) var dummyTrafficOn - @KeyObject(.pushNotifications, defaultValue: false) var pushNotifications - @KeyObject(.askedDummyTrafficOnce, defaultValue: false) var offeredCoverTraffic - - var coverTrafficPublisher: AnyPublisher<Void, Never> { - coverTrafficSubject.eraseToAnyPublisher() - } - - private let coverTrafficSubject = PassthroughSubject<Void, Never>() - - func didAppear() { - verifyCoverTraffic() - verifyNotifications() - } - - func didEnableCoverTraffic() { - try! dummyTrafficManager.setStatus(true) - dummyTrafficOn = true - } - - private func verifyCoverTraffic() { - guard offeredCoverTraffic == false else { return } - offeredCoverTraffic = true - coverTrafficSubject.send() - } - - private func verifyNotifications() { - guard pushNotifications == false else { return } - - pushHandler.requestAuthorization { [weak self] result in - guard let self else { return } - - switch result { - case .success(let granted): - if granted { - DispatchQueue.main.async { - UIApplication.shared.registerForRemoteNotifications() - } - } - - self.pushNotifications = granted - case .failure: - self.pushNotifications = false - } + @Dependency(\.permissions) var permissions: PermissionsManager + //@Dependency(\.app.dummyTraffic) var dummyTraffic: DummyTraffic + + @KeyObject(.dummyTrafficOn, defaultValue: false) var dummyTrafficOn + @KeyObject(.pushNotifications, defaultValue: false) var pushNotifications + @KeyObject(.askedDummyTrafficOnce, defaultValue: false) var offeredCoverTraffic + + var coverTrafficPublisher: AnyPublisher<Void, Never> { + coverTrafficSubject.eraseToAnyPublisher() + } + + private let coverTrafficSubject = PassthroughSubject<Void, Never>() + + func didAppear() { + verifyCoverTraffic() + verifyNotifications() + } + + func didEnableCoverTraffic() { +// try! dummyTraffic.setStatus(true) + dummyTrafficOn = true + } + + private func verifyCoverTraffic() { + guard offeredCoverTraffic == false else { return } + offeredCoverTraffic = true + coverTrafficSubject.send() + } + + private func verifyNotifications() { + guard pushNotifications == false else { return } + + permissions.push.request { [weak self] granted in + guard let self else { return } + if granted == true { + DispatchQueue.main.async { + UIApplication.shared.registerForRemoteNotifications() } + } + self.pushNotifications = granted } + } } diff --git a/Sources/SearchFeature/ViewModels/SearchLeftViewModel.swift b/Sources/SearchFeature/ViewModels/SearchLeftViewModel.swift index f2c1c947e68275031b2a0d3c561d6fa6f662bd59..9a9aacb84b1b125233beb46f38601296a4f7df6d 100644 --- a/Sources/SearchFeature/ViewModels/SearchLeftViewModel.swift +++ b/Sources/SearchFeature/ViewModels/SearchLeftViewModel.swift @@ -6,11 +6,13 @@ import XXModels import XXClient import Defaults import CustomDump +import AppResources import ReportingFeature import CombineSchedulers import XXMessengerClient import CountryListFeature -import DI +import Dependencies +import AppCore typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem> @@ -22,12 +24,12 @@ struct SearchLeftViewState { } final class SearchLeftViewModel { - @Dependency var database: Database - @Dependency var messenger: Messenger - @Dependency var hudController: HUDController - @Dependency var reportingStatus: ReportingStatus - @Dependency var toastController: ToastController - @Dependency var networkMonitor: NetworkMonitoring + @Dependency(\.app.dbManager) var dbManager + @Dependency(\.app.messenger) var messenger + @Dependency(\.app.hudManager) var hudManager + @Dependency(\.app.toastManager) var toastManager + @Dependency(\.reportingStatus) var reportingStatus + @Dependency(\.app.networkMonitor) var networkMonitor @KeyObject(.username, defaultValue: nil) var username: String? @KeyObject(.sharingEmail, defaultValue: false) var sharingEmail: Bool @@ -61,7 +63,7 @@ final class SearchLeftViewModel { if let pendingInvitation = invitation { invitation = nil stateSubject.value.input = pendingInvitation - hudController.show(.init( + hudManager.show(.init( actionTitle: Localized.Ud.Search.cancel, hasDotAnimation: true, onTapClosure: { [weak self] in @@ -72,19 +74,19 @@ final class SearchLeftViewModel { networkCancellable.removeAll() - networkMonitor - .statusPublisher - .first { $0 == .available } - .eraseToAnyPublisher() - .flatMap { _ in - self.waitForNodes(timeout: 5) - }.sink(receiveCompletion: { - if case .failure(let error) = $0 { - self.hudController.show(.init(error: error)) - } - }, receiveValue: { - self.didStartSearching() - }).store(in: &networkCancellable) +// networkMonitor +// .statusPublisher +// .first { $0 == .available } +// .eraseToAnyPublisher() +// .flatMap { _ in +// self.waitForNodes(timeout: 5) +// }.sink(receiveCompletion: { +// if case .failure(let error) = $0 { +// self.hudManager.show(.init(error: error)) +// } +// }, receiveValue: { +// self.didStartSearching() +// }).store(in: &networkCancellable) } } @@ -103,13 +105,13 @@ final class SearchLeftViewModel { func didTapCancelSearch() { searchCancellables.forEach { $0.cancel() } searchCancellables.removeAll() - hudController.dismiss() + hudManager.hide() } func didStartSearching() { guard stateSubject.value.input.isEmpty == false else { return } - hudController.show(.init( + hudManager.show(.init( actionTitle: Localized.Ud.Search.cancel, hasDotAnimation: true, onTapClosure: { [weak self] in @@ -142,11 +144,11 @@ final class SearchLeftViewModel { guard let self else { return } if case .unhealthyNet = $0 as? NodeRegistrationError { - self.hudController.show(.init(content: "Network is not healthy yet, try again within the next minute or so.")) + self.hudManager.show(.init(content: "Network is not healthy yet, try again within the next minute or so.")) } else if case .belowMinimum = $0 as? NodeRegistrationError { - self.hudController.show(.init(content:"Node registration ratio is still below 80%, try again within the next minute or so.")) + self.hudManager.show(.init(content:"Node registration ratio is still below 80%, try again within the next minute or so.")) } else { - self.hudController.show(.init(error: $0)) + self.hudManager.show(.init(error: $0)) } return @@ -173,7 +175,7 @@ final class SearchLeftViewModel { callback: .init(handle: { switch $0 { case .success(let results): - self.hudController.dismiss() + self.hudManager.hide() self.appendToLocalSearch( XXModels.Contact( id: try! results.first!.getId(), @@ -194,7 +196,7 @@ final class SearchLeftViewModel { print(">>> SearchUD error: \(error.localizedDescription)") self.appendToLocalSearch(nil) - self.hudController.show(.init(error: error)) + self.hudManager.show(.init(error: error)) } }) ) @@ -207,7 +209,7 @@ final class SearchLeftViewModel { } func didTapResend(contact: XXModels.Contact) { - hudController.show() + hudManager.show() var contact = contact contact.authStatus = .requesting @@ -216,7 +218,7 @@ final class SearchLeftViewModel { guard let self else { return } do { - try self.database.saveContact(contact) + try self.dbManager.getDB().saveContact(contact) var includedFacts: [Fact] = [] let myFacts = try self.messenger.ud.get()!.getFacts() @@ -239,20 +241,20 @@ final class SearchLeftViewModel { ) contact.authStatus = .requested - contact = try self.database.saveContact(contact) + contact = try self.dbManager.getDB().saveContact(contact) - self.hudController.dismiss() + self.hudManager.hide() self.presentSuccessToast(for: contact, resent: true) } catch { contact.authStatus = .requestFailed - _ = try? self.database.saveContact(contact) - self.hudController.show(.init(error: error)) + _ = try? self.dbManager.getDB().saveContact(contact) + self.hudManager.show(.init(error: error)) } } } func didTapRequest(contact: XXModels.Contact) { - hudController.show() + hudManager.show() var contact = contact contact.nickname = contact.username @@ -262,7 +264,7 @@ final class SearchLeftViewModel { guard let self else { return } do { - try self.database.saveContact(contact) + try self.dbManager.getDB().saveContact(contact) var includedFacts: [Fact] = [] let myFacts = try self.messenger.ud.get()!.getFacts() @@ -285,23 +287,23 @@ final class SearchLeftViewModel { ) contact.authStatus = .requested - contact = try self.database.saveContact(contact) + contact = try self.dbManager.getDB().saveContact(contact) - self.hudController.dismiss() + self.hudManager.hide() self.successSubject.send(contact) self.presentSuccessToast(for: contact, resent: false) } catch { contact.authStatus = .requestFailed - _ = try? self.database.saveContact(contact) - self.hudController.show(.init(error: error)) + _ = try? self.dbManager.getDB().saveContact(contact) + self.hudManager.show(.init(error: error)) } } } func didSet(nickname: String, for contact: XXModels.Contact) { - if var contact = try? database.fetchContacts(.init(id: [contact.id])).first { + if var contact = try? dbManager.getDB().fetchContacts(.init(id: [contact.id])).first { contact.nickname = nickname - _ = try? database.saveContact(contact) + _ = try? dbManager.getDB().saveContact(contact) } } @@ -309,7 +311,7 @@ final class SearchLeftViewModel { var snapshot = SearchSnapshot() if var user = user { - if let contact = try? database.fetchContacts(.init(id: [user.id])).first { + if let contact = try? dbManager.getDB().fetchContacts(.init(id: [user.id])).first { user.isBanned = contact.isBanned user.isBlocked = contact.isBlocked user.authStatus = contact.authStatus @@ -331,7 +333,7 @@ final class SearchLeftViewModel { isBanned: reportingStatus.isEnabled() ? false : nil ) - if let locals = try? database.fetchContacts(localsQuery), + if let locals = try? dbManager.getDB().fetchContacts(localsQuery), let localsWithoutMe = removeMyself(from: locals), localsWithoutMe.isEmpty == false { snapshot.appendSections([.connections]) @@ -353,7 +355,7 @@ final class SearchLeftViewModel { let sentTitle = Localized.Requests.Sent.Toast.sent(name ?? "") let resentTitle = Localized.Requests.Sent.Toast.resent(name ?? "") - toastController.enqueueToast(model: .init( + toastManager.enqueue(.init( title: resent ? resentTitle : sentTitle, leftImage: Asset.sharedSuccess.image )) diff --git a/Sources/SearchFeature/ViewModels/SearchRightViewModel.swift b/Sources/SearchFeature/ViewModels/SearchRightViewModel.swift index 46d54a4603a40cb3be49785eb62b691b947473f2..12dab510ef00415aaa67d419f2f3c3e75000540e 100644 --- a/Sources/SearchFeature/ViewModels/SearchRightViewModel.swift +++ b/Sources/SearchFeature/ViewModels/SearchRightViewModel.swift @@ -1,133 +1,135 @@ import Shared import Combine +import AppCore import XXModels import Defaults import XXClient import Foundation -import Permissions +import AppResources import ReportingFeature import XXMessengerClient -import DI +import PermissionsFeature +import ComposableArchitecture enum ScanningStatus: Equatable { - case reading - case processing - case success - case failed(ScanningError) + case reading + case processing + case success + case failed(ScanningError) } enum ScanningError: Equatable { - case requestOpened - case unknown(String) - case cameraPermission - case alreadyFriends(String) + case requestOpened + case unknown(String) + case cameraPermission + case alreadyFriends(String) } final class SearchRightViewModel { - @Dependency var database: Database - @Dependency var permissions: PermissionHandling - @Dependency var reportingStatus: ReportingStatus - - var foundPublisher: AnyPublisher<XXModels.Contact, Never> { - foundSubject.eraseToAnyPublisher() - } - - var cameraSemaphorePublisher: AnyPublisher<Bool, Never> { - cameraSemaphoreSubject.eraseToAnyPublisher() + @Dependency(\.app.dbManager) var dbManager: DBManager + @Dependency(\.permissions) var permissions: PermissionsManager + @Dependency(\.reportingStatus) var reportingStatus: ReportingStatus + + var foundPublisher: AnyPublisher<XXModels.Contact, Never> { + foundSubject.eraseToAnyPublisher() + } + + var cameraSemaphorePublisher: AnyPublisher<Bool, Never> { + cameraSemaphoreSubject.eraseToAnyPublisher() + } + + var statusPublisher: AnyPublisher<ScanningStatus, Never> { + statusSubject.eraseToAnyPublisher() + } + + private let foundSubject = PassthroughSubject<XXModels.Contact, Never>() + private let cameraSemaphoreSubject = PassthroughSubject<Bool, Never>() + private(set) var statusSubject = CurrentValueSubject<ScanningStatus, Never>(.reading) + + func viewWillAppear() { + permissions.camera.request { [weak self] granted in + guard let self else { return } + + if granted { + self.statusSubject.value = .reading + self.cameraSemaphoreSubject.send(true) + } else { + self.statusSubject.send(.failed(.cameraPermission)) + } } - - var statusPublisher: AnyPublisher<ScanningStatus, Never> { - statusSubject.eraseToAnyPublisher() + } + + func viewWillDisappear() { + cameraSemaphoreSubject.send(false) + } + + func didScan(data: Data) { + /// We need to be accepting new readings in order + /// to process what just got scanned. + /// + guard statusSubject.value == .reading else { return } + statusSubject.send(.processing) + + /// Whatever got scanned, needs to have id and username + /// otherwise is just noise or an unknown qr code + /// + let user = XXClient.Contact.live(data) + + guard + let uid = try? user.getId(), + let facts = try? user.getFacts(), + let username = facts.first(where: { $0.type == .username })?.value + else { + let errorTitle = Localized.Scan.Error.invalid + statusSubject.send(.failed(.unknown(errorTitle))) + return } - private let foundSubject = PassthroughSubject<XXModels.Contact, Never>() - private let cameraSemaphoreSubject = PassthroughSubject<Bool, Never>() - private(set) var statusSubject = CurrentValueSubject<ScanningStatus, Never>(.reading) - - func viewWillAppear() { - permissions.requestCamera { [weak self] granted in - guard let self else { return } - - if granted { - self.statusSubject.value = .reading - self.cameraSemaphoreSubject.send(true) - } else { - self.statusSubject.send(.failed(.cameraPermission)) - } - } + let email = facts.first { $0.type == .email }?.value + let phone = facts.first { $0.type == .phone }?.value + + /// Make sure we are not processing a contact + /// that we already have + /// + if let alreadyContact = try? dbManager.getDB().fetchContacts(.init(id: [uid])).first { + if alreadyContact.isBlocked, reportingStatus.isEnabled() { + statusSubject.send(.failed(.unknown("You previously blocked this user."))) + return + } + + if alreadyContact.isBanned, reportingStatus.isEnabled() { + statusSubject.send(.failed(.unknown("This user was banned."))) + return + } + + /// Show error accordingly to the auth status + /// + if alreadyContact.authStatus == .friend { + statusSubject.send(.failed(.alreadyFriends(username))) + } else if [.requested, .verified].contains(alreadyContact.authStatus) { + statusSubject.send(.failed(.requestOpened)) + } else { + let generalErrorTitle = Localized.Scan.Error.general + statusSubject.send(.failed(.unknown(generalErrorTitle))) + } + + return } - func viewWillDisappear() { - cameraSemaphoreSubject.send(false) - } - - func didScan(data: Data) { - /// We need to be accepting new readings in order - /// to process what just got scanned. - /// - guard statusSubject.value == .reading else { return } - statusSubject.send(.processing) - - /// Whatever got scanned, needs to have id and username - /// otherwise is just noise or an unknown qr code - /// - let user = XXClient.Contact.live(data) - - guard - let uid = try? user.getId(), - let facts = try? user.getFacts(), - let username = facts.first(where: { $0.type == .username })?.value - else { - let errorTitle = Localized.Scan.Error.invalid - statusSubject.send(.failed(.unknown(errorTitle))) - return - } - - let email = facts.first { $0.type == .email }?.value - let phone = facts.first { $0.type == .phone }?.value - - /// Make sure we are not processing a contact - /// that we already have - /// - if let alreadyContact = try? database.fetchContacts(.init(id: [uid])).first { - if alreadyContact.isBlocked, reportingStatus.isEnabled() { - statusSubject.send(.failed(.unknown("You previously blocked this user."))) - return - } - - if alreadyContact.isBanned, reportingStatus.isEnabled() { - statusSubject.send(.failed(.unknown("This user was banned."))) - return - } - - /// Show error accordingly to the auth status - /// - if alreadyContact.authStatus == .friend { - statusSubject.send(.failed(.alreadyFriends(username))) - } else if [.requested, .verified].contains(alreadyContact.authStatus) { - statusSubject.send(.failed(.requestOpened)) - } else { - let generalErrorTitle = Localized.Scan.Error.general - statusSubject.send(.failed(.unknown(generalErrorTitle))) - } - - return - } - - statusSubject.send(.success) - cameraSemaphoreSubject.send(false) - - foundSubject.send(.init( - id: uid, - marshaled: data, - username: username, - email: email, - phone: phone, - nickname: nil, - photo: nil, - authStatus: .stranger, - isRecent: false, - createdAt: Date() - )) - } + statusSubject.send(.success) + cameraSemaphoreSubject.send(false) + + foundSubject.send(.init( + id: uid, + marshaled: data, + username: username, + email: email, + phone: phone, + nickname: nil, + photo: nil, + authStatus: .stranger, + isRecent: false, + createdAt: Date() + )) + } } diff --git a/Sources/SearchFeature/Views/OverlayView.swift b/Sources/SearchFeature/Views/OverlayView.swift index 8242857716936fe49cf21a59bad280195c726165..4e85a3ea143885bd0ed21e1cb236c126a6ba9843 100644 --- a/Sources/SearchFeature/Views/OverlayView.swift +++ b/Sources/SearchFeature/Views/OverlayView.swift @@ -1,181 +1,182 @@ import UIKit import Shared +import AppResources final class OverlayView: UIView { - private let cropView = UIView() - private let scanViewLength = 266.0 - private let maskLayer = CAShapeLayer() - private let topLeftLayer = CAShapeLayer() - private let topRightLayer = CAShapeLayer() - private let bottomLeftLayer = CAShapeLayer() - private let bottomRightLayer = CAShapeLayer() - - init() { - super.init(frame: .zero) - backgroundColor = Asset.neutralDark.color.withAlphaComponent(0.5) - - addSubview(cropView) - - cropView.snp.makeConstraints { - $0.width.equalTo(scanViewLength) - $0.centerY.equalToSuperview().offset(-50) - $0.centerX.equalToSuperview() - $0.height.equalTo(scanViewLength) - } - - maskLayer.fillRule = .evenOdd - layer.mask = maskLayer - layer.masksToBounds = true - - [topLeftLayer, topRightLayer, bottomLeftLayer, bottomRightLayer].forEach { - $0.strokeColor = Asset.brandPrimary.color.cgColor - $0.fillColor = UIColor.clear.cgColor - $0.lineWidth = 3.0 - $0.lineCap = .round - layer.addSublayer($0) - } + private let cropView = UIView() + private let scanViewLength = 266.0 + private let maskLayer = CAShapeLayer() + private let topLeftLayer = CAShapeLayer() + private let topRightLayer = CAShapeLayer() + private let bottomLeftLayer = CAShapeLayer() + private let bottomRightLayer = CAShapeLayer() + + init() { + super.init(frame: .zero) + backgroundColor = Asset.neutralDark.color.withAlphaComponent(0.5) + + addSubview(cropView) + + cropView.snp.makeConstraints { + $0.width.equalTo(scanViewLength) + $0.centerY.equalToSuperview().offset(-50) + $0.centerX.equalToSuperview() + $0.height.equalTo(scanViewLength) } - required init?(coder: NSCoder) { nil } + maskLayer.fillRule = .evenOdd + layer.mask = maskLayer + layer.masksToBounds = true - override func layoutSubviews() { - super.layoutSubviews() - - maskLayer.frame = bounds - let path = UIBezierPath(rect: bounds) - path.append(UIBezierPath(roundedRect: cropView.frame, cornerRadius: 30.0)) - maskLayer.path = path.cgPath - - topLeftLayer.frame = bounds - topRightLayer.frame = bounds - bottomRightLayer.frame = bounds - bottomLeftLayer.frame = bounds - - topLeftLayer.path = topLeftPath() - topRightLayer.path = topRightPath() - bottomRightLayer.path = bottomRightPath() - bottomLeftLayer.path = bottomLeftPath() - } - - func updateCornerColor(_ color: UIColor) { - [topLeftLayer, topRightLayer, bottomLeftLayer, bottomRightLayer].forEach { - $0.strokeColor = color.cgColor - } - } - - func topLeftPath() -> CGPath { - let path = UIBezierPath() - - let vert0X = cropView.frame.minX - 15 - let vert0Y = cropView.frame.minY + 45 - let vert0 = CGPoint(x: vert0X, y: vert0Y) - path.move(to: vert0) - - let vertNX = cropView.frame.minX - 15 - let vertNY = cropView.frame.minY + 15 - let vertN = CGPoint(x: vertNX, y: vertNY) - path.addLine(to: vertN) - - let arcCenterX = cropView.frame.minX + 15 - let arcCenterY = cropView.frame.minY + 15 - let arcCenter = CGPoint(x: arcCenterX , y: arcCenterY) - path.addArc(center: arcCenter, startAngle: .pi) - - let horizX = cropView.frame.minX + 45 - let horizY = cropView.frame.minY - 15 - let horiz = CGPoint(x: horizX, y: horizY) - path.addLine(to: horiz) - - return path.cgPath + [topLeftLayer, topRightLayer, bottomLeftLayer, bottomRightLayer].forEach { + $0.strokeColor = Asset.brandPrimary.color.cgColor + $0.fillColor = UIColor.clear.cgColor + $0.lineWidth = 3.0 + $0.lineCap = .round + layer.addSublayer($0) } + } - func topRightPath() -> CGPath { - let path = UIBezierPath() + required init?(coder: NSCoder) { nil } - let horiz0X = cropView.frame.maxX - 45 - let horiz0Y = cropView.frame.minY - 15 - let horiz0 = CGPoint(x: horiz0X, y: horiz0Y) - path.move(to: horiz0) + override func layoutSubviews() { + super.layoutSubviews() - let horizNX = cropView.frame.maxX - 15 - let horizNY = cropView.frame.minY - 15 - let horizN = CGPoint(x: horizNX, y: horizNY) - path.addLine(to: horizN) + maskLayer.frame = bounds + let path = UIBezierPath(rect: bounds) + path.append(UIBezierPath(roundedRect: cropView.frame, cornerRadius: 30.0)) + maskLayer.path = path.cgPath - let arcCenterX = cropView.frame.maxX - 15 - let arcCenterY = cropView.frame.minY + 15 - let arcCenter = CGPoint(x: arcCenterX, y: arcCenterY) - path.addArc(center: arcCenter, startAngle: 3 * .pi/2) + topLeftLayer.frame = bounds + topRightLayer.frame = bounds + bottomRightLayer.frame = bounds + bottomLeftLayer.frame = bounds - let vertX = cropView.frame.maxX + 15 - let vertY = cropView.frame.minY + 45 - let vert = CGPoint(x: vertX, y: vertY) - path.addLine(to: vert) + topLeftLayer.path = topLeftPath() + topRightLayer.path = topRightPath() + bottomRightLayer.path = bottomRightPath() + bottomLeftLayer.path = bottomLeftPath() + } - return path.cgPath - } - - func bottomRightPath() -> CGPath { - let path = UIBezierPath() - - let vert0X = cropView.frame.maxX + 15 - let vert0Y = cropView.frame.maxY - 45 - let vert0 = CGPoint(x: vert0X, y: vert0Y) - path.move(to: vert0) - - let vertNX = cropView.frame.maxX + 15 - let vertNY = cropView.frame.maxY - 15 - let vertN = CGPoint(x: vertNX, y: vertNY) - path.addLine(to: vertN) - - let arcCenterX = cropView.frame.maxX - 15 - let arcCenterY = cropView.frame.maxY - 15 - let arcCenter = CGPoint(x: arcCenterX, y: arcCenterY) - path.addArc(center: arcCenter, startAngle: 0) - - let horizX = cropView.frame.maxX - 45 - let horizY = cropView.frame.maxY + 15 - let horiz = CGPoint(x: horizX, y: horizY) - path.addLine(to: horiz) - - return path.cgPath - } - - func bottomLeftPath() -> CGPath { - let path = UIBezierPath() - - let horiz0X = cropView.frame.minX + 45 - let horiz0Y = cropView.frame.maxY + 15 - let horiz0 = CGPoint(x: horiz0X, y: horiz0Y) - path.move(to: horiz0) - - let horizNX = cropView.frame.minX + 15 - let horizNY = cropView.frame.maxY + 15 - let horizN = CGPoint(x: horizNX, y: horizNY) - path.addLine(to: horizN) - - let arcCenterX = cropView.frame.minX + 15 - let arcCenterY = cropView.frame.maxY - 15 - let arcCenter = CGPoint(x: arcCenterX, y: arcCenterY) - path.addArc(center: arcCenter, startAngle: .pi/2) - - let vertX = cropView.frame.minX - 15 - let vertY = cropView.frame.maxY - 45 - let vert = CGPoint(x: vertX, y: vertY) - path.addLine(to: vert) - - return path.cgPath + func updateCornerColor(_ color: UIColor) { + [topLeftLayer, topRightLayer, bottomLeftLayer, bottomRightLayer].forEach { + $0.strokeColor = color.cgColor } + } + + func topLeftPath() -> CGPath { + let path = UIBezierPath() + + let vert0X = cropView.frame.minX - 15 + let vert0Y = cropView.frame.minY + 45 + let vert0 = CGPoint(x: vert0X, y: vert0Y) + path.move(to: vert0) + + let vertNX = cropView.frame.minX - 15 + let vertNY = cropView.frame.minY + 15 + let vertN = CGPoint(x: vertNX, y: vertNY) + path.addLine(to: vertN) + + let arcCenterX = cropView.frame.minX + 15 + let arcCenterY = cropView.frame.minY + 15 + let arcCenter = CGPoint(x: arcCenterX , y: arcCenterY) + path.addArc(center: arcCenter, startAngle: .pi) + + let horizX = cropView.frame.minX + 45 + let horizY = cropView.frame.minY - 15 + let horiz = CGPoint(x: horizX, y: horizY) + path.addLine(to: horiz) + + return path.cgPath + } + + func topRightPath() -> CGPath { + let path = UIBezierPath() + + let horiz0X = cropView.frame.maxX - 45 + let horiz0Y = cropView.frame.minY - 15 + let horiz0 = CGPoint(x: horiz0X, y: horiz0Y) + path.move(to: horiz0) + + let horizNX = cropView.frame.maxX - 15 + let horizNY = cropView.frame.minY - 15 + let horizN = CGPoint(x: horizNX, y: horizNY) + path.addLine(to: horizN) + + let arcCenterX = cropView.frame.maxX - 15 + let arcCenterY = cropView.frame.minY + 15 + let arcCenter = CGPoint(x: arcCenterX, y: arcCenterY) + path.addArc(center: arcCenter, startAngle: 3 * .pi/2) + + let vertX = cropView.frame.maxX + 15 + let vertY = cropView.frame.minY + 45 + let vert = CGPoint(x: vertX, y: vertY) + path.addLine(to: vert) + + return path.cgPath + } + + func bottomRightPath() -> CGPath { + let path = UIBezierPath() + + let vert0X = cropView.frame.maxX + 15 + let vert0Y = cropView.frame.maxY - 45 + let vert0 = CGPoint(x: vert0X, y: vert0Y) + path.move(to: vert0) + + let vertNX = cropView.frame.maxX + 15 + let vertNY = cropView.frame.maxY - 15 + let vertN = CGPoint(x: vertNX, y: vertNY) + path.addLine(to: vertN) + + let arcCenterX = cropView.frame.maxX - 15 + let arcCenterY = cropView.frame.maxY - 15 + let arcCenter = CGPoint(x: arcCenterX, y: arcCenterY) + path.addArc(center: arcCenter, startAngle: 0) + + let horizX = cropView.frame.maxX - 45 + let horizY = cropView.frame.maxY + 15 + let horiz = CGPoint(x: horizX, y: horizY) + path.addLine(to: horiz) + + return path.cgPath + } + + func bottomLeftPath() -> CGPath { + let path = UIBezierPath() + + let horiz0X = cropView.frame.minX + 45 + let horiz0Y = cropView.frame.maxY + 15 + let horiz0 = CGPoint(x: horiz0X, y: horiz0Y) + path.move(to: horiz0) + + let horizNX = cropView.frame.minX + 15 + let horizNY = cropView.frame.maxY + 15 + let horizN = CGPoint(x: horizNX, y: horizNY) + path.addLine(to: horizN) + + let arcCenterX = cropView.frame.minX + 15 + let arcCenterY = cropView.frame.maxY - 15 + let arcCenter = CGPoint(x: arcCenterX, y: arcCenterY) + path.addArc(center: arcCenter, startAngle: .pi/2) + + let vertX = cropView.frame.minX - 15 + let vertY = cropView.frame.maxY - 45 + let vert = CGPoint(x: vertX, y: vertY) + path.addLine(to: vert) + + return path.cgPath + } } private extension UIBezierPath { - func addArc(center: CGPoint, startAngle: CGFloat) { - addArc( - withCenter: center, - radius: 30, - startAngle: startAngle, - endAngle: startAngle + .pi/2, - clockwise: true - ) - } + func addArc(center: CGPoint, startAngle: CGFloat) { + addArc( + withCenter: center, + radius: 30, + startAngle: startAngle, + endAngle: startAngle + .pi/2, + clockwise: true + ) + } } diff --git a/Sources/SearchFeature/Views/SearchContainerView.swift b/Sources/SearchFeature/Views/SearchContainerView.swift index 3a270bc5a52891fc915951bf68f738f2e5cdcd6f..878d438a464f7c22006518517f5f534f92c17a77 100644 --- a/Sources/SearchFeature/Views/SearchContainerView.swift +++ b/Sources/SearchFeature/Views/SearchContainerView.swift @@ -1,35 +1,32 @@ import UIKit import Shared +import AppResources final class SearchContainerView: UIView { - let scrollView = UIScrollView() - let segmentedControl = SearchSegmentedControl() + let scrollView = UIScrollView() + let segmentedControl = SearchSegmentedControl() - init() { - super.init(frame: .zero) + init() { + super.init(frame: .zero) - backgroundColor = Asset.neutralWhite.color - addSubview(segmentedControl) - addSubview(scrollView) + backgroundColor = Asset.neutralWhite.color + addSubview(segmentedControl) + addSubview(scrollView) - setupConstraints() + segmentedControl.snp.makeConstraints { + $0.top.equalTo(safeAreaLayoutGuide).offset(10) + $0.left.equalToSuperview() + $0.right.equalToSuperview() + $0.height.equalTo(60) } - required init?(coder: NSCoder) { nil } - - private func setupConstraints() { - segmentedControl.snp.makeConstraints { - $0.top.equalTo(safeAreaLayoutGuide).offset(10) - $0.left.equalToSuperview() - $0.right.equalToSuperview() - $0.height.equalTo(60) - } - - scrollView.snp.makeConstraints { - $0.top.equalTo(segmentedControl.snp.bottom) - $0.left.equalToSuperview() - $0.right.equalToSuperview() - $0.bottom.equalToSuperview() - } + scrollView.snp.makeConstraints { + $0.top.equalTo(segmentedControl.snp.bottom) + $0.left.equalToSuperview() + $0.right.equalToSuperview() + $0.bottom.equalToSuperview() } + } + + required init?(coder: NSCoder) { nil } } diff --git a/Sources/SearchFeature/Views/SearchLeftEmptyView.swift b/Sources/SearchFeature/Views/SearchLeftEmptyView.swift index 84c64c87a6096a16b2bbf793a9eadc1c95495324..4f33b053f0a104cd83a8d9c59e5ebbefdb40fc7b 100644 --- a/Sources/SearchFeature/Views/SearchLeftEmptyView.swift +++ b/Sources/SearchFeature/Views/SearchLeftEmptyView.swift @@ -1,26 +1,27 @@ import UIKit import Shared +import AppResources final class SearchLeftEmptyView: UIView { - let titleLabel = UILabel() - - init() { - super.init(frame: .zero) - backgroundColor = Asset.neutralWhite.color - - titleLabel.numberOfLines = 0 - titleLabel.textAlignment = .center - titleLabel.font = Fonts.Mulish.regular.font(size: 15.0) - titleLabel.textColor = Asset.neutralSecondaryAlternative.color - - addSubview(titleLabel) - - titleLabel.snp.makeConstraints { - $0.center.equalToSuperview() - $0.left.equalToSuperview().offset(20) - $0.right.equalToSuperview().offset(-20) - } + let titleLabel = UILabel() + + init() { + super.init(frame: .zero) + backgroundColor = Asset.neutralWhite.color + + titleLabel.numberOfLines = 0 + titleLabel.textAlignment = .center + titleLabel.font = Fonts.Mulish.regular.font(size: 15.0) + titleLabel.textColor = Asset.neutralSecondaryAlternative.color + + addSubview(titleLabel) + + titleLabel.snp.makeConstraints { + $0.center.equalToSuperview() + $0.left.equalToSuperview().offset(20) + $0.right.equalToSuperview().offset(-20) } - - required init?(coder: NSCoder) { nil } + } + + required init?(coder: NSCoder) { nil } } diff --git a/Sources/SearchFeature/Views/SearchLeftPlaceholderView.swift b/Sources/SearchFeature/Views/SearchLeftPlaceholderView.swift index 001c6687f16c73fab73f9c0bb2734b549a79d2ed..fa7f5c4fd37a6ade7e3105bf2f17fd3f45ab6938 100644 --- a/Sources/SearchFeature/Views/SearchLeftPlaceholderView.swift +++ b/Sources/SearchFeature/Views/SearchLeftPlaceholderView.swift @@ -1,74 +1,75 @@ import UIKit import Shared import Combine +import AppResources final class SearchLeftPlaceholderView: UIView { - let titleLabel = UILabel() - let subtitleWithInfo = TextWithInfoView() - - var infoPublisher: AnyPublisher<Void, Never> { - infoSubject.eraseToAnyPublisher() - } - - private let infoSubject = PassthroughSubject<Void, Never>() - - init() { - super.init(frame: .zero) - backgroundColor = Asset.neutralWhite.color - - let attrString = NSMutableAttributedString( - string: Localized.Ud.Search.Placeholder.title, - attributes: [ - .foregroundColor: Asset.neutralDark.color, - .font: Fonts.Mulish.bold.font(size: 32.0) - ] - ) - - attrString.addAttribute( - name: .foregroundColor, - value: Asset.brandPrimary.color, - betweenCharacters: "#" - ) - - titleLabel.numberOfLines = 0 - titleLabel.attributedText = attrString - - let paragraph = NSMutableParagraphStyle() - paragraph.lineHeightMultiple = 1.3 - - subtitleWithInfo.setup( - text: Localized.Ud.Search.Placeholder.subtitle, - attributes: [ - .paragraphStyle: paragraph, - .foregroundColor: Asset.neutralBody.color, - .font: Fonts.Mulish.regular.font(size: 16.0) - ], - didTapInfo: { [weak self] in - guard let self else { return } - self.infoSubject.send(()) - } - ) - - addSubview(titleLabel) - addSubview(subtitleWithInfo) - - setupConstraints() + let titleLabel = UILabel() + let subtitleWithInfo = TextWithInfoView() + + var infoPublisher: AnyPublisher<Void, Never> { + infoSubject.eraseToAnyPublisher() + } + + private let infoSubject = PassthroughSubject<Void, Never>() + + init() { + super.init(frame: .zero) + backgroundColor = Asset.neutralWhite.color + + let attrString = NSMutableAttributedString( + string: Localized.Ud.Search.Placeholder.title, + attributes: [ + .foregroundColor: Asset.neutralDark.color, + .font: Fonts.Mulish.bold.font(size: 32.0) + ] + ) + + attrString.addAttribute( + name: .foregroundColor, + value: Asset.brandPrimary.color, + betweenCharacters: "#" + ) + + titleLabel.numberOfLines = 0 + titleLabel.attributedText = attrString + + let paragraph = NSMutableParagraphStyle() + paragraph.lineHeightMultiple = 1.3 + + subtitleWithInfo.setup( + text: Localized.Ud.Search.Placeholder.subtitle, + attributes: [ + .paragraphStyle: paragraph, + .foregroundColor: Asset.neutralBody.color, + .font: Fonts.Mulish.regular.font(size: 16.0) + ], + didTapInfo: { [weak self] in + guard let self else { return } + self.infoSubject.send(()) + } + ) + + addSubview(titleLabel) + addSubview(subtitleWithInfo) + + setupConstraints() + } + + required init?(coder: NSCoder) { nil } + + private func setupConstraints() { + titleLabel.snp.makeConstraints { + $0.top.equalToSuperview().offset(50) + $0.left.equalToSuperview().offset(32.5) + $0.right.equalToSuperview().offset(-32.5) } - - required init?(coder: NSCoder) { nil } - - private func setupConstraints() { - titleLabel.snp.makeConstraints { - $0.top.equalToSuperview().offset(50) - $0.left.equalToSuperview().offset(32.5) - $0.right.equalToSuperview().offset(-32.5) - } - - subtitleWithInfo.snp.makeConstraints { - $0.top.equalTo(titleLabel.snp.bottom).offset(30) - $0.left.equalToSuperview().offset(32.5) - $0.right.equalToSuperview().offset(-32.5) - $0.bottom.equalToSuperview() - } + + subtitleWithInfo.snp.makeConstraints { + $0.top.equalTo(titleLabel.snp.bottom).offset(30) + $0.left.equalToSuperview().offset(32.5) + $0.right.equalToSuperview().offset(-32.5) + $0.bottom.equalToSuperview() } + } } diff --git a/Sources/SearchFeature/Views/SearchLeftView.swift b/Sources/SearchFeature/Views/SearchLeftView.swift index bbdda3f926818439e4c28d5beb020239a623048a..4eefca9a834265e687fee288e19a9a88689529d7 100644 --- a/Sources/SearchFeature/Views/SearchLeftView.swift +++ b/Sources/SearchFeature/Views/SearchLeftView.swift @@ -1,71 +1,72 @@ import UIKit import Shared +import AppResources final class SearchLeftView: UIView { - let tableView = UITableView() - let inputStackView = UIStackView() - let inputField = SearchComponent() - let emptyView = SearchLeftEmptyView() - let countryButton = SearchCountryComponent() - let placeholderView = SearchLeftPlaceholderView() + let tableView = UITableView() + let inputStackView = UIStackView() + let inputField = SearchComponent() + let emptyView = SearchLeftEmptyView() + let countryButton = SearchCountryComponent() + let placeholderView = SearchLeftPlaceholderView() - init() { - super.init(frame: .zero) + init() { + super.init(frame: .zero) - emptyView.isHidden = true - backgroundColor = Asset.neutralWhite.color - tableView.backgroundColor = Asset.neutralWhite.color + emptyView.isHidden = true + backgroundColor = Asset.neutralWhite.color + tableView.backgroundColor = Asset.neutralWhite.color - inputStackView.spacing = 5 - inputStackView.addArrangedSubview(countryButton) - inputStackView.addArrangedSubview(inputField) + inputStackView.spacing = 5 + inputStackView.addArrangedSubview(countryButton) + inputStackView.addArrangedSubview(inputField) - addSubview(inputStackView) - addSubview(tableView) - addSubview(emptyView) - addSubview(placeholderView) + addSubview(inputStackView) + addSubview(tableView) + addSubview(emptyView) + addSubview(placeholderView) - setupConstraints() - } + setupConstraints() + } - required init?(coder: NSCoder) { nil } + required init?(coder: NSCoder) { nil } - func updateUIForItem(item: SearchSegmentedControl.Item) { - countryButton.isHidden = item != .phone + func updateUIForItem(item: SearchSegmentedControl.Item) { + countryButton.isHidden = item != .phone - let emptyTitle = Localized.Ud.Search.empty(item.written) - emptyView.titleLabel.text = emptyTitle + let emptyTitle = Localized.Ud.Search.empty(item.written) + emptyView.titleLabel.text = emptyTitle - let inputFieldTitle = Localized.Ud.Search.input(item.written) - inputField.set(placeholder: inputFieldTitle, imageAtRight: nil) - } + let inputFieldTitle = Localized.Ud.Search.input(item.written) + inputField.set(placeholder: inputFieldTitle, imageAtRight: nil) + } - private func setupConstraints() { - inputStackView.snp.makeConstraints { - $0.top.equalToSuperview().offset(20) - $0.left.equalToSuperview().offset(20) - $0.right.equalToSuperview().offset(-20) - } + private func setupConstraints() { + inputStackView.snp.makeConstraints { + $0.top.equalToSuperview().offset(20) + $0.left.equalToSuperview().offset(20) + $0.right.equalToSuperview().offset(-20) + } - tableView.snp.makeConstraints { - $0.top.equalTo(inputField.snp.bottom).offset(20) - $0.left.equalToSuperview() - $0.right.equalToSuperview() - $0.bottom.equalToSuperview() - } + tableView.snp.makeConstraints { + $0.top.equalTo(inputField.snp.bottom).offset(20) + $0.left.equalToSuperview() + $0.right.equalToSuperview() + $0.bottom.equalToSuperview() + } - emptyView.snp.makeConstraints { - $0.top.equalTo(inputField.snp.bottom).offset(20) - $0.left.equalToSuperview() - $0.right.equalToSuperview() - $0.bottom.equalToSuperview() - } + emptyView.snp.makeConstraints { + $0.top.equalTo(inputField.snp.bottom).offset(20) + $0.left.equalToSuperview() + $0.right.equalToSuperview() + $0.bottom.equalToSuperview() + } - placeholderView.snp.makeConstraints { - $0.top.equalTo(inputField.snp.bottom) - $0.left.equalToSuperview() - $0.right.equalToSuperview() - $0.bottom.equalToSuperview() - } + placeholderView.snp.makeConstraints { + $0.top.equalTo(inputField.snp.bottom) + $0.left.equalToSuperview() + $0.right.equalToSuperview() + $0.bottom.equalToSuperview() } + } } diff --git a/Sources/SearchFeature/Views/SearchRightView.swift b/Sources/SearchFeature/Views/SearchRightView.swift index c2fd74760c79e8799c31bb91b0f73a85d4d5cd98..a374439fb729112f781ce56848daa2ebf78cf60d 100644 --- a/Sources/SearchFeature/Views/SearchRightView.swift +++ b/Sources/SearchFeature/Views/SearchRightView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class SearchRightView: UIView { let statusLabel = UILabel() diff --git a/Sources/SearchFeature/Views/SearchSegmentedButton.swift b/Sources/SearchFeature/Views/SearchSegmentedButton.swift index 3b8e65fb1b778703a748626155d6f2b0b18ec94b..ead382a4d69a9caab5e0e48fce4e7f89a1b737e7 100644 --- a/Sources/SearchFeature/Views/SearchSegmentedButton.swift +++ b/Sources/SearchFeature/Views/SearchSegmentedButton.swift @@ -1,49 +1,50 @@ import UIKit import Shared +import AppResources final class SearchSegmentedButton: UIControl { - private let titleLabel = UILabel() - private let imageView = UIImageView() - private let highlightColor = Asset.brandPrimary.color - private let discreteColor = Asset.neutralDisabled.color - - init() { - super.init(frame: .zero) - - imageView.contentMode = .center - titleLabel.textAlignment = .center - titleLabel.font = Fonts.Mulish.semiBold.font(size: 13.0) - - addSubview(titleLabel) - addSubview(imageView) - - setupConstraints() - } - - required init?(coder: NSCoder) { nil } - - func setup(title: String, icon: UIImage) { - imageView.image = icon - titleLabel.text = title - imageView.tintColor = discreteColor - titleLabel.textColor = discreteColor - } - - func setSelected(_ bool: Bool) { - imageView.tintColor = bool ? highlightColor : discreteColor - titleLabel.textColor = bool ? highlightColor : discreteColor + private let titleLabel = UILabel() + private let imageView = UIImageView() + private let highlightColor = Asset.brandPrimary.color + private let discreteColor = Asset.neutralDisabled.color + + init() { + super.init(frame: .zero) + + imageView.contentMode = .center + titleLabel.textAlignment = .center + titleLabel.font = Fonts.Mulish.semiBold.font(size: 13.0) + + addSubview(titleLabel) + addSubview(imageView) + + setupConstraints() + } + + required init?(coder: NSCoder) { nil } + + func setup(title: String, icon: UIImage) { + imageView.image = icon + titleLabel.text = title + imageView.tintColor = discreteColor + titleLabel.textColor = discreteColor + } + + func setSelected(_ bool: Bool) { + imageView.tintColor = bool ? highlightColor : discreteColor + titleLabel.textColor = bool ? highlightColor : discreteColor + } + + private func setupConstraints() { + imageView.snp.makeConstraints { + $0.top.equalToSuperview().offset(7.5) + $0.centerX.equalToSuperview() } - private func setupConstraints() { - imageView.snp.makeConstraints { - $0.top.equalToSuperview().offset(7.5) - $0.centerX.equalToSuperview() - } - - titleLabel.snp.makeConstraints { - $0.top.equalTo(imageView.snp.bottom).offset(2) - $0.centerX.equalToSuperview() - $0.bottom.equalToSuperview().offset(-7.5) - } + titleLabel.snp.makeConstraints { + $0.top.equalTo(imageView.snp.bottom).offset(2) + $0.centerX.equalToSuperview() + $0.bottom.equalToSuperview().offset(-7.5) } + } } diff --git a/Sources/SearchFeature/Views/SearchSegmentedControl.swift b/Sources/SearchFeature/Views/SearchSegmentedControl.swift index 6141360378cd659e89dbdf67c4180f4a0e9b4ce6..94833095934ff15522cbf2f01255bdaaf652fb91 100644 --- a/Sources/SearchFeature/Views/SearchSegmentedControl.swift +++ b/Sources/SearchFeature/Views/SearchSegmentedControl.swift @@ -2,6 +2,7 @@ import UIKit import Shared import SnapKit import Combine +import AppResources final class SearchSegmentedControl: UIView { enum Item: Int { diff --git a/Sources/SettingsFeature/Controllers/SettingsAdvancedController.swift b/Sources/SettingsFeature/Controllers/SettingsAdvancedController.swift index c4b434cda7e72f62b278ee82ac05da178f69ad92..96225a7dbebff485ce4289417abe0646150fc6f1 100644 --- a/Sources/SettingsFeature/Controllers/SettingsAdvancedController.swift +++ b/Sources/SettingsFeature/Controllers/SettingsAdvancedController.swift @@ -1,11 +1,11 @@ import UIKit -import Shared import Combine -import Navigation -import DI +import Dependencies +import AppResources +import AppNavigation public final class SettingsAdvancedController: UIViewController { - @Dependency var navigator: Navigator + @Dependency(\.navigator) var navigator: Navigator private lazy var screenView = SettingsAdvancedView() diff --git a/Sources/SettingsFeature/Controllers/AccountDeleteController.swift b/Sources/SettingsFeature/Controllers/SettingsDeleteController.swift similarity index 89% rename from Sources/SettingsFeature/Controllers/AccountDeleteController.swift rename to Sources/SettingsFeature/Controllers/SettingsDeleteController.swift index 41fe4cacffab08c5fb743a4214493a5909b15e0f..0c64d28d92144c28ed4718a281b352f8ab749669 100644 --- a/Sources/SettingsFeature/Controllers/AccountDeleteController.swift +++ b/Sources/SettingsFeature/Controllers/SettingsDeleteController.swift @@ -2,19 +2,21 @@ import UIKit import Shared import Combine import Defaults -import Navigation +import Dependencies +import AppResources +import AppNavigation import DrawerFeature import ScrollViewController -import DI -public final class AccountDeleteController: UIViewController { - @Dependency var navigator: Navigator +public final class SettingsDeleteController: UIViewController { + @Dependency(\.navigator) var navigator: Navigator + @KeyObject(.username, defaultValue: "") var username: String - private lazy var screenView = AccountDeleteView() + private lazy var screenView = SettingsDeleteView() private lazy var scrollViewController = ScrollViewController() - private let viewModel = AccountDeleteViewModel() + private let viewModel = SettingsDeleteViewModel() private var cancellables = Set<AnyCancellable>() private var drawerCancellables = Set<AnyCancellable>() @@ -43,7 +45,9 @@ public final class AccountDeleteController: UIViewController { private func setupScrollView() { addChild(scrollViewController) view.addSubview(scrollViewController.view) - scrollViewController.view.snp.makeConstraints { $0.edges.equalToSuperview() } + scrollViewController.view.snp.makeConstraints { + $0.edges.equalToSuperview() + } scrollViewController.didMove(toParent: self) scrollViewController.contentView = screenView scrollViewController.scrollView.backgroundColor = Asset.neutralWhite.color diff --git a/Sources/SettingsFeature/Controllers/SettingsController.swift b/Sources/SettingsFeature/Controllers/SettingsMainController.swift similarity index 95% rename from Sources/SettingsFeature/Controllers/SettingsController.swift rename to Sources/SettingsFeature/Controllers/SettingsMainController.swift index 9186199140e0a55b37b2b48fd611cdd7b9afc795..574c0dca110aa011dd7643eae8e60a2b028827e5 100644 --- a/Sources/SettingsFeature/Controllers/SettingsController.swift +++ b/Sources/SettingsFeature/Controllers/SettingsMainController.swift @@ -1,17 +1,19 @@ import UIKit import Shared import Combine -import Navigation +import AppCore +import Dependencies +import AppResources +import AppNavigation import DrawerFeature -import DI import ScrollViewController -public final class SettingsController: UIViewController { - @Dependency var navigator: Navigator - @Dependency var barStylist: StatusBarStylist +public final class SettingsMainController: UIViewController { + @Dependency(\.navigator) var navigator: Navigator + @Dependency(\.app.statusBar) var statusBar: StatusBarStylist private lazy var scrollViewController = ScrollViewController() - private lazy var screenView = SettingsView { + private lazy var screenView = SettingsMainView { switch $0 { case .icognitoKeyboard: self.presentInfo( @@ -39,13 +41,13 @@ public final class SettingsController: UIViewController { } } - private let viewModel = SettingsViewModel() + private let viewModel = SettingsMainViewModel() private var cancellables = Set<AnyCancellable>() private var drawerCancellables = Set<AnyCancellable>() public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - barStylist.styleSubject.send(.darkContent) + statusBar.set(.darkContent) navigationController?.navigationBar .customize(backgroundColor: Asset.neutralWhite.color) } @@ -192,7 +194,7 @@ public final class SettingsController: UIViewController { }.store(in: &cancellables) viewModel - .state + .statePublisher .map(\.isBiometricsPossible) .removeDuplicates() .receive(on: DispatchQueue.main) @@ -201,7 +203,7 @@ public final class SettingsController: UIViewController { }.store(in: &cancellables) viewModel - .state + .statePublisher .removeDuplicates() .receive(on: DispatchQueue.main) .sink { [unowned self] state in @@ -276,7 +278,7 @@ public final class SettingsController: UIViewController { } } -extension SettingsController { +extension SettingsMainController { private func presentInfo( title: String, subtitle: String, diff --git a/Sources/SettingsFeature/ViewModels/SettingsAdvancedViewModel.swift b/Sources/SettingsFeature/ViewModels/SettingsAdvancedViewModel.swift index 09985d189d79a3ded8e34a9506e07418833ee6cc..585880a067a7664e55035300cf9deeb2e15c0c70 100644 --- a/Sources/SettingsFeature/ViewModels/SettingsAdvancedViewModel.swift +++ b/Sources/SettingsFeature/ViewModels/SettingsAdvancedViewModel.swift @@ -1,10 +1,10 @@ import Combine -import XXLogger +import AppCore import Defaults import Foundation -import CrashReporting +import CrashReport import ReportingFeature -import DI +import ComposableArchitecture struct AdvancedViewState: Equatable { var isRecordingLogs = false @@ -21,14 +21,19 @@ final class SettingsAdvancedViewModel { private var cancellables = Set<AnyCancellable>() private let isShowingUsernamesKey = "isShowingUsernames" - @Dependency private var logger: XXLogger - @Dependency private var crashReporter: CrashReporter - @Dependency private var reportingStatus: ReportingStatus + @Dependency(\.app.log) var logger: Logger + @Dependency(\.crashReport) var crashReport: CrashReport + @Dependency(\.reportingStatus) var reportingStatus: ReportingStatus + + var sharePublisher: AnyPublisher<URL, Never> { + shareRelay.eraseToAnyPublisher() + } - var sharePublisher: AnyPublisher<URL, Never> { shareRelay.eraseToAnyPublisher() } private let shareRelay = PassthroughSubject<URL, Never>() - var state: AnyPublisher<AdvancedViewState, Never> { stateRelay.eraseToAnyPublisher() } + var state: AnyPublisher<AdvancedViewState, Never> { + stateRelay.eraseToAnyPublisher() + } private let stateRelay = CurrentValueSubject<AdvancedViewState, Never>(.init()) func loadCachedSettings() { @@ -67,9 +72,9 @@ final class SettingsAdvancedViewModel { func didToggleRecordLogs() { if isRecordingLogs == true { - XXLogger.stop() +// XXLogger.stop() } else { - XXLogger.start() +// XXLogger.start() } isRecordingLogs.toggle() @@ -84,7 +89,7 @@ final class SettingsAdvancedViewModel { func didToggleCrashReporting() { isCrashReporting.toggle() stateRelay.value.isCrashReporting.toggle() - crashReporter.setEnabled(isCrashReporting) + crashReport.setEnabled(isCrashReporting) } func didSetReporting(enabled: Bool) { diff --git a/Sources/SettingsFeature/ViewModels/AccountDeleteViewModel.swift b/Sources/SettingsFeature/ViewModels/SettingsDeleteViewModel.swift similarity index 55% rename from Sources/SettingsFeature/ViewModels/AccountDeleteViewModel.swift rename to Sources/SettingsFeature/ViewModels/SettingsDeleteViewModel.swift index 08d8d5749bec6f760c604cb78543b0db248c3479..87dadb95c894fa10d06b82a53b094ccc04d00a67 100644 --- a/Sources/SettingsFeature/ViewModels/AccountDeleteViewModel.swift +++ b/Sources/SettingsFeature/ViewModels/SettingsDeleteViewModel.swift @@ -1,20 +1,15 @@ -import Retry -import Shared -import Combine +import AppCore import Defaults import Keychain -import XXModels -import XXClient import Foundation +import Dependencies import XXMessengerClient -import DI -final class AccountDeleteViewModel { - @Dependency var database: Database - @Dependency var messenger: Messenger - @Dependency var keychain: KeychainHandling - @Dependency var hudController: HUDController - +final class SettingsDeleteViewModel { + @Dependency(\.keychain) var keychain: KeychainManager + @Dependency(\.app.dbManager) var dbManager: DBManager + @Dependency(\.app.messenger) var messenger: Messenger + @Dependency(\.app.hudManager) var hudManager: HUDManager @KeyObject(.username, defaultValue: nil) var username: String? private var isCurrentlyDeleting = false @@ -23,48 +18,38 @@ final class AccountDeleteViewModel { guard isCurrentlyDeleting == false else { return } isCurrentlyDeleting = true - hudController.show() + hudManager.show() do { - print(">>> try self.cleanUD()") try cleanUD() - - print(">>> try self.messenger.destroy()") try messenger.destroy() - - print(">>> try self.keychain.clear()") - try keychain.clear() - - print(">>> try database.drop()") - try database.drop() - - print(">>> try self.deleteDatabase()") + try keychain.destroy() + try dbManager.removeDB() try deleteDatabase() UserDefaults.resetStandardUserDefaults() UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!) UserDefaults.standard.synchronize() - hudController.show(.init( + hudManager.show(.init( title: "Account deleted", content: "Now kill the app and re-open" )) } catch { DispatchQueue.main.async { [weak self] in guard let self else { return } - self.hudController.show(.init(error: error)) + self.hudManager.show(.init(error: error)) } } } private func cleanUD() throws { - print(">>> Deleting my username (\(username ?? "NO_USERNAME")) from ud") - try messenger.ud.get()!.permanentDeleteAccount(username: .init(type: .username, value: username!)) + try messenger.ud.get()!.permanentDeleteAccount( + username: .init(type: .username, value: username!) + ) } private func deleteDatabase() throws { - print(">>> Deleting database...") - let dbPath = FileManager.default .containerURL(forSecurityApplicationGroupIdentifier: "group.elixxir.messenger")! .appendingPathComponent("xxm_database") diff --git a/Sources/SettingsFeature/ViewModels/SettingsMainViewModel.swift b/Sources/SettingsFeature/ViewModels/SettingsMainViewModel.swift new file mode 100644 index 0000000000000000000000000000000000000000..4fd4323dd0086207e7be26d2acd82bc117779a8b --- /dev/null +++ b/Sources/SettingsFeature/ViewModels/SettingsMainViewModel.swift @@ -0,0 +1,128 @@ +import UIKit +import AppCore +import Combine +import XXClient +import Defaults +import XXMessengerClient +import PermissionsFeature +import ComposableArchitecture + +final class SettingsMainViewModel { + struct ViewState: Equatable { + var isHideActiveApps: Bool = false + var isPushNotification: Bool = false + var isIcognitoKeyboard: Bool = false + var isInAppNotification: Bool = false + var isBiometricsEnabled: Bool = false + var isBiometricsPossible: Bool = false + var isDummyTrafficOn = false + } + + @Dependency(\.app.bgQueue) var bgQueue + @Dependency(\.permissions) var permissions + @Dependency(\.app.messenger) var messenger + @Dependency(\.dummyTraffic) var dummyTraffic + @Dependency(\.app.hudManager) var hudManager + + @KeyObject(.biometrics, defaultValue: false) var biometrics + @KeyObject(.hideAppList, defaultValue: false) var hideAppList + @KeyObject(.dummyTrafficOn, defaultValue: false) var dummyTrafficOn + @KeyObject(.icognitoKeyboard, defaultValue: false) var icognitoKeyboard + @KeyObject(.pushNotifications, defaultValue: false) var pushNotifications + @KeyObject(.inappnotifications, defaultValue: true) var inAppNotifications + + var statePublisher: AnyPublisher<ViewState, Never> { + stateSubject.eraseToAnyPublisher() + } + + private let stateSubject = CurrentValueSubject<ViewState, Never>(.init()) + + func loadCachedSettings() { + stateSubject.value.isHideActiveApps = hideAppList + stateSubject.value.isBiometricsEnabled = biometrics + stateSubject.value.isIcognitoKeyboard = icognitoKeyboard + stateSubject.value.isPushNotification = pushNotifications + stateSubject.value.isInAppNotification = inAppNotifications + stateSubject.value.isBiometricsPossible = permissions.biometrics.status() + stateSubject.value.isDummyTrafficOn = dummyTraffic.get()!.getStatus() + } + + func didToggleBiometrics() { + biometricAuthentication(enable: !biometrics) + } + + func didToggleInAppNotifications() { + inAppNotifications.toggle() + stateSubject.value.isInAppNotification.toggle() + } + + func didTogglePushNotifications() { + pushNotifications(enable: !pushNotifications) + } + + func didToggleDummyTraffic() { + let currently = dummyTraffic.get()!.getStatus() + try! dummyTraffic.get()!.setStatus(!currently) + stateSubject.value.isDummyTrafficOn = !currently + dummyTrafficOn = stateSubject.value.isDummyTrafficOn + } + + func didToggleHideActiveApps() { + hideAppList.toggle() + stateSubject.value.isHideActiveApps.toggle() + } + + func didToggleIcognitoKeyboard() { + icognitoKeyboard.toggle() + stateSubject.value.isIcognitoKeyboard.toggle() + } + + private func biometricAuthentication(enable: Bool) { + stateSubject.value.isBiometricsEnabled = enable + + guard enable == true else { + biometrics = false + stateSubject.value.isBiometricsEnabled = false + return + } + + permissions.biometrics.request { [weak self] in + guard let self else { return } + self.biometrics = $0 + self.stateSubject.value.isBiometricsEnabled = $0 + } + } + + private func pushNotifications(enable: Bool) { + hudManager.show() + + if enable == true { + permissions.push.request { [weak self] granted in + guard let self else { return } + self.pushNotifications = granted + self.stateSubject.value.isPushNotification = granted + if granted { + DispatchQueue.main.async { + UIApplication.shared.registerForRemoteNotifications() + } + } + self.hudManager.hide() + } + } else { + bgQueue.schedule { [weak self] in + guard let self else { return } + do { + try UnregisterForNotifications.live( + e2eId: self.messenger.e2e.get()!.getId() + ) + self.hudManager.hide() + } catch { + let xxError = CreateUserFriendlyErrorMessage.live(error.localizedDescription) + self.hudManager.show(.init(content: xxError)) + } + self.pushNotifications = false + self.stateSubject.value.isPushNotification = false + } + } + } +} diff --git a/Sources/SettingsFeature/ViewModels/SettingsViewModel.swift b/Sources/SettingsFeature/ViewModels/SettingsViewModel.swift deleted file mode 100644 index f2fa2b54acb06a3836cdbc0021f6ce7423df5691..0000000000000000000000000000000000000000 --- a/Sources/SettingsFeature/ViewModels/SettingsViewModel.swift +++ /dev/null @@ -1,150 +0,0 @@ -import UIKit -import Shared -import Combine -import XXClient -import Defaults -import Permissions -import PushFeature -import XXMessengerClient -import UserNotifications -import CombineSchedulers -import DI - -struct SettingsViewState: Equatable { - var isHideActiveApps: Bool = false - var isPushNotification: Bool = false - var isIcognitoKeyboard: Bool = false - var isInAppNotification: Bool = false - var isBiometricsEnabled: Bool = false - var isBiometricsPossible: Bool = false - var isDummyTrafficOn = false -} - -final class SettingsViewModel { - @Dependency var messenger: Messenger - @Dependency var pushHandler: PushHandling - @Dependency var hudController: HUDController - @Dependency var permissions: PermissionHandling - @Dependency var dummyTrafficManager: DummyTraffic - - @KeyObject(.biometrics, defaultValue: false) var biometrics - @KeyObject(.hideAppList, defaultValue: false) var hideAppList - @KeyObject(.dummyTrafficOn, defaultValue: false) var dummyTrafficOn - @KeyObject(.icognitoKeyboard, defaultValue: false) var icognitoKeyboard - @KeyObject(.pushNotifications, defaultValue: false) var pushNotifications - @KeyObject(.inappnotifications, defaultValue: true) var inAppNotifications - - var backgroundScheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler() - - var state: AnyPublisher<SettingsViewState, Never> { stateRelay.eraseToAnyPublisher() } - private let stateRelay = CurrentValueSubject<SettingsViewState, Never>(.init()) - - func loadCachedSettings() { - stateRelay.value.isHideActiveApps = hideAppList - stateRelay.value.isBiometricsEnabled = biometrics - stateRelay.value.isIcognitoKeyboard = icognitoKeyboard - stateRelay.value.isPushNotification = pushNotifications - stateRelay.value.isInAppNotification = inAppNotifications - stateRelay.value.isBiometricsPossible = permissions.isBiometricsAvailable - stateRelay.value.isDummyTrafficOn = dummyTrafficManager.getStatus() - } - - func didToggleBiometrics() { - biometricAuthentication(enable: !biometrics) - } - - func didToggleInAppNotifications() { - inAppNotifications.toggle() - stateRelay.value.isInAppNotification.toggle() - } - - func didTogglePushNotifications() { - pushNotifications(enable: !pushNotifications) - } - - func didToggleDummyTraffic() { - let currently = dummyTrafficManager.getStatus() - try! dummyTrafficManager.setStatus(!currently) - stateRelay.value.isDummyTrafficOn = !currently - dummyTrafficOn = stateRelay.value.isDummyTrafficOn - } - - func didToggleHideActiveApps() { - hideAppList.toggle() - stateRelay.value.isHideActiveApps.toggle() - } - - func didToggleIcognitoKeyboard() { - icognitoKeyboard.toggle() - stateRelay.value.isIcognitoKeyboard.toggle() - } - - private func biometricAuthentication(enable: Bool) { - stateRelay.value.isBiometricsEnabled = enable - - guard enable == true else { - biometrics = false - stateRelay.value.isBiometricsEnabled = false - return - } - - permissions.requestBiometrics { [weak self] result in - guard let self else { return } - - switch result { - case .success(let granted): - if granted { - self.biometrics = true - self.stateRelay.value.isBiometricsEnabled = true - } else { - self.biometrics = false - self.stateRelay.value.isBiometricsEnabled = false - } - case .failure: - self.biometrics = false - self.stateRelay.value.isBiometricsEnabled = false - } - } - } - - private func pushNotifications(enable: Bool) { - hudController.show() - - if enable == true { - pushHandler.requestAuthorization { [weak self] result in - guard let self else { return } - - switch result { - case .success(let granted): - self.pushNotifications = granted - self.stateRelay.value.isPushNotification = granted - if granted { - DispatchQueue.main.async { - UIApplication.shared.registerForRemoteNotifications() - } - } - self.hudController.dismiss() - case .failure(let error): - self.hudController.show(.init(error: error)) - self.pushNotifications = false - self.stateRelay.value.isPushNotification = false - } - } - } else { - backgroundScheduler.schedule { [weak self] in - guard let self else { return } - do { - try UnregisterForNotifications.live( - e2eId: self.messenger.e2e.get()!.getId() - ) - self.hudController.dismiss() - } catch { - let xxError = CreateUserFriendlyErrorMessage.live(error.localizedDescription) - self.hudController.show(.init(content: xxError)) - } - self.pushNotifications = false - self.stateRelay.value.isPushNotification = false - } - } - } -} diff --git a/Sources/SettingsFeature/Views/SettingsAdvancedView.swift b/Sources/SettingsFeature/Views/SettingsAdvancedView.swift index 750a175c208d29335c7c3354724de1b920c82d64..3d976f2505a4e8c23090724abdbbcd1a00fe70f5 100644 --- a/Sources/SettingsFeature/Views/SettingsAdvancedView.swift +++ b/Sources/SettingsFeature/Views/SettingsAdvancedView.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class SettingsAdvancedView: UIView { let stackView = UIStackView() diff --git a/Sources/SettingsFeature/Views/AccountDeleteView.swift b/Sources/SettingsFeature/Views/SettingsDeleteView.swift similarity index 98% rename from Sources/SettingsFeature/Views/AccountDeleteView.swift rename to Sources/SettingsFeature/Views/SettingsDeleteView.swift index 791bec294559a098af1594448b41e0b30a7e4ad3..7ac68bccbbd95c0f299936ba726744fc3e26023b 100644 --- a/Sources/SettingsFeature/Views/AccountDeleteView.swift +++ b/Sources/SettingsFeature/Views/SettingsDeleteView.swift @@ -1,8 +1,9 @@ import UIKit import Shared import InputField +import AppResources -final class AccountDeleteView: UIView { +final class SettingsDeleteView: UIView { let titleLabel = UILabel() let subtitleView = TextWithInfoView() let iconImageView = UIImageView() diff --git a/Sources/SettingsFeature/Views/SettingsView.swift b/Sources/SettingsFeature/Views/SettingsMainView.swift similarity index 98% rename from Sources/SettingsFeature/Views/SettingsView.swift rename to Sources/SettingsFeature/Views/SettingsMainView.swift index 450e8aa60f347de50a3b9efc46316f9f73dbf81d..f1f84e5438f9a25b9fd823e4a53e3f37806e508d 100644 --- a/Sources/SettingsFeature/Views/SettingsView.swift +++ b/Sources/SettingsFeature/Views/SettingsMainView.swift @@ -1,7 +1,8 @@ import UIKit import Shared +import AppResources -final class SettingsView: UIView { +final class SettingsMainView: UIView { enum InfoTapped { case dummyTraffic case biometrics diff --git a/Sources/SettingsFeature/Views/SettingsSwitcher.swift b/Sources/SettingsFeature/Views/SettingsSwitcher.swift index 5a59b17fa3d7274215d498cd8d9ea050fc4201ee..8656cb7861506e22d51ff3dc22d7cde46ef3ac42 100644 --- a/Sources/SettingsFeature/Views/SettingsSwitcher.swift +++ b/Sources/SettingsFeature/Views/SettingsSwitcher.swift @@ -1,5 +1,6 @@ import UIKit import Shared +import AppResources final class SettingsSwitcher: UIView { let titleLabel = UILabel() diff --git a/Sources/Shared/Aliases.swift b/Sources/Shared/Aliases.swift deleted file mode 100644 index 6b3859ff2b6f5bdbc00a47fb1e352a7378868e04..0000000000000000000000000000000000000000 --- a/Sources/Shared/Aliases.swift +++ /dev/null @@ -1,4 +0,0 @@ -import UIKit - -public typealias EmptyClosure = () -> Void -public typealias StringClosure = (String) -> Void diff --git a/Sources/Shared/EditStateHandler.swift b/Sources/Shared/EditStateHandler.swift index ca1fd06b2259b0cb3eafa4508cb0d8fab9ff9521..5b2407d8233a4b17797f43e8a285743e6bbe136e 100644 --- a/Sources/Shared/EditStateHandler.swift +++ b/Sources/Shared/EditStateHandler.swift @@ -1,12 +1,15 @@ import Combine public final class EditStateHandler { - public var isEditing: AnyPublisher<Bool, Never> { stateRelay.eraseToAnyPublisher() } - private let stateRelay = CurrentValueSubject<Bool, Never>(false) + public var isEditing: AnyPublisher<Bool, Never> { + stateSubject.eraseToAnyPublisher() + } + + private let stateSubject = CurrentValueSubject<Bool, Never>(false) public init() {} public func didSwitchEditing() { - stateRelay.value.toggle() + stateSubject.value.toggle() } } diff --git a/Sources/Shared/Models/Payload.swift b/Sources/Shared/Models/Payload.swift deleted file mode 100644 index eb7e67e3731adfcb6eb277fea1ede8554ed1cb60..0000000000000000000000000000000000000000 --- a/Sources/Shared/Models/Payload.swift +++ /dev/null @@ -1,37 +0,0 @@ -import Foundation - -public struct Payload: Codable, Equatable, Hashable { - public var text: String - public var reply: Reply? - - public init(text: String, reply: Reply?) { - self.text = text - self.reply = reply - } - - public init(with marshaled: Data) throws { - let proto = try CMIXText(serializedData: marshaled) - - var reply: Reply? - - if proto.hasReply { - reply = Reply( - messageId: proto.reply.messageID, - senderId: proto.reply.senderID - ) - } - - self.init(text: proto.text, reply: reply) - } - - public func asData() -> Data { - var protoModel = CMIXText() - protoModel.text = text - - if let reply = reply { - protoModel.reply = reply.asTextReply() - } - - return try! protoModel.serializedData() - } -} diff --git a/Sources/Shared/Models/Reply.swift b/Sources/Shared/Models/Reply.swift index edc61b01db652a5c57c93a4b893959655c7b37da..21ffda352cf1631cd638e6a66ef6248a6d55bd87 100644 --- a/Sources/Shared/Models/Reply.swift +++ b/Sources/Shared/Models/Reply.swift @@ -3,17 +3,17 @@ import Foundation public struct Reply: Codable, Equatable, Hashable { public let messageId: Data public let senderId: Data - + public init(messageId: Data, senderId: Data) { self.messageId = messageId self.senderId = senderId } - + func asTextReply() -> TextReply { var reply = TextReply() reply.messageID = messageId reply.senderID = senderId - + return reply } } diff --git a/Sources/StatusBarFeature/StatusBarDependency.swift b/Sources/StatusBarFeature/StatusBarDependency.swift deleted file mode 100644 index 2d04e3932df4074598f04beeb297baed54cbaf50..0000000000000000000000000000000000000000 --- a/Sources/StatusBarFeature/StatusBarDependency.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Dependencies - -private enum StatusBarDependencyKey: DependencyKey { - static let liveValue: StatusBarStyleManager = .live() - static let testValue: StatusBarStyleManager = .unimplemented -} - -extension DependencyValues { - public var statusBar: StatusBarStyleManager { - get { self[StatusBarDependencyKey.self] } - set { self[StatusBarDependencyKey.self] = newValue } - } -} diff --git a/Sources/StatusBarFeature/StatusBarStyleFetch.swift b/Sources/StatusBarFeature/StatusBarStyleFetch.swift deleted file mode 100644 index e57027ce0f9e9df7d87bf7e850ed06c2698d8a1a..0000000000000000000000000000000000000000 --- a/Sources/StatusBarFeature/StatusBarStyleFetch.swift +++ /dev/null @@ -1,16 +0,0 @@ -import UIKit -import XCTestDynamicOverlay - -public struct StatusBarStyleFetch { - public var run: () -> UIStatusBarStyle - - public func callAsFunction() -> UIStatusBarStyle { - run() - } -} - -extension StatusBarStyleFetch { - public static let unimplemented = StatusBarStyleFetch( - run: XCTUnimplemented("\(Self.self)") - ) -} diff --git a/Sources/StatusBarFeature/StatusBarStyleManager.swift b/Sources/StatusBarFeature/StatusBarStyleManager.swift deleted file mode 100644 index 3dbfe089beefd930fb13eafd35bea7fcd2734a3f..0000000000000000000000000000000000000000 --- a/Sources/StatusBarFeature/StatusBarStyleManager.swift +++ /dev/null @@ -1,39 +0,0 @@ -import UIKit -import Combine -import XCTestDynamicOverlay - -public struct StatusBarStyleManager { - public var update: StatusBarStyleUpdate - public var current: StatusBarStyleFetch - public var observe: StatusBarStyleObserve -} - -extension StatusBarStyleManager { - public static func live() -> StatusBarStyleManager { - class Context { - let styleSubject = CurrentValueSubject<UIStatusBarStyle, Never>(.lightContent) - } - - let context = Context() - - return .init( - update: .init { - context.styleSubject.send($0) - }, - current: .init { - context.styleSubject.value - }, - observe: .init { - context.styleSubject.eraseToAnyPublisher() - } - ) - } -} - -extension StatusBarStyleManager { - public static let unimplemented = StatusBarStyleManager( - update: .unimplemented, - current: .unimplemented, - observe: .unimplemented - ) -} diff --git a/Sources/StatusBarFeature/StatusBarStyleObserve.swift b/Sources/StatusBarFeature/StatusBarStyleObserve.swift deleted file mode 100644 index 77c70223e0893b91a9df3b13365f76910adce573..0000000000000000000000000000000000000000 --- a/Sources/StatusBarFeature/StatusBarStyleObserve.swift +++ /dev/null @@ -1,17 +0,0 @@ -import UIKit -import Combine -import XCTestDynamicOverlay - -public struct StatusBarStyleObserve { - public var run: () -> AnyPublisher<UIStatusBarStyle, Never> - - public func callAsFunction() -> AnyPublisher<UIStatusBarStyle, Never> { - run() - } -} - -extension StatusBarStyleObserve { - public static let unimplemented = StatusBarStyleObserve( - run: XCTUnimplemented("\(Self.self)") - ) -} diff --git a/Sources/StatusBarFeature/StatusBarStyleUpdate.swift b/Sources/StatusBarFeature/StatusBarStyleUpdate.swift deleted file mode 100644 index 95831faa90eb248a64de1e5e9b31b2d3d2820b8e..0000000000000000000000000000000000000000 --- a/Sources/StatusBarFeature/StatusBarStyleUpdate.swift +++ /dev/null @@ -1,16 +0,0 @@ -import UIKit -import XCTestDynamicOverlay - -public struct StatusBarStyleUpdate { - public var run: (UIStatusBarStyle) -> Void - - public func callAsFunction(_ style: UIStatusBarStyle) -> Void { - run(style) - } -} - -extension StatusBarStyleUpdate { - public static let unimplemented = StatusBarStyleUpdate( - run: XCTUnimplemented("\(Self.self)") - ) -} diff --git a/Sources/TermsFeature/TermsConditionsController.swift b/Sources/TermsFeature/TermsConditionsController.swift index 23fd543b54b13ff0599436d5e4301677795cab37..ce67d0e4e30f1de7a45b7013ab32615319dc799f 100644 --- a/Sources/TermsFeature/TermsConditionsController.swift +++ b/Sources/TermsFeature/TermsConditionsController.swift @@ -1,14 +1,14 @@ -import DI import UIKit import WebKit import Shared import Combine import Defaults -import Navigation import AppResources +import AppNavigation +import ComposableArchitecture public final class TermsConditionsController: UIViewController { - @Dependency var navigator: Navigator + @Dependency(\.navigator) var navigator: Navigator @KeyObject(.username, defaultValue: nil) var username: String? @KeyObject(.acceptedTerms, defaultValue: false) var didAcceptTerms: Bool @@ -72,12 +72,7 @@ public final class TermsConditionsController: UIViewController { .showTermsButton .publisher(for: .touchUpInside) .sink { [unowned self] _ in - let webView = WKWebView() - let webController = UIViewController() - webController.view.addSubview(webView) - webView.snp.makeConstraints { $0.edges.equalToSuperview() } - webView.load(URLRequest(url: URL(string: "https://elixxir.io/eula")!)) - present(webController, animated: true) + navigator.perform(PresentWebsite(urlString: "https://elixxir.io/eula", from: self)) }.store(in: &cancellables) } } diff --git a/Sources/UpdateErrors/Dependency.swift b/Sources/UpdateErrors/Dependency.swift new file mode 100644 index 0000000000000000000000000000000000000000..7294a48614e8353a88079224bc6cf82f551517ee --- /dev/null +++ b/Sources/UpdateErrors/Dependency.swift @@ -0,0 +1,13 @@ +import Dependencies + +private enum UpdateErrorsDependencyKey: DependencyKey { + static let liveValue: UpdateErrors = .live + static let testValue: UpdateErrors = .unimplemented +} + +extension DependencyValues { + public var updateErrors: UpdateErrors { + get { self[UpdateErrorsDependencyKey.self] } + set { self[UpdateErrorsDependencyKey.self] = newValue } + } +} diff --git a/Sources/UpdateErrors/UpdateErrors.swift b/Sources/UpdateErrors/UpdateErrors.swift new file mode 100644 index 0000000000000000000000000000000000000000..6240d77c68672223e2ed8831fa85a876945bb1cf --- /dev/null +++ b/Sources/UpdateErrors/UpdateErrors.swift @@ -0,0 +1,52 @@ +import XXClient +import Foundation +import XCTestDynamicOverlay + +public struct UpdateErrors { + public enum Error: Swift.Error { + case noData + case decodeFailure + case network(URLError) + case bindingsException + } + + public typealias Completion = (Result<Void, Error>) -> Void + + public var run: (@escaping Completion) -> Void + + public func callAsFunction(_ completion: @escaping Completion) -> Void { + run(completion) + } +} + +extension UpdateErrors { + public static let live = UpdateErrors { completion in + let url = URL(string: "https://git.xx.network/elixxir/client-error-database/-/raw/main/clientErrors.json") + URLSession.shared.dataTask(with: url!) { data, _, error in + if let error { + completion(.failure(.network(error as! URLError))) + return + } + guard let data else { + completion(.failure(.noData)) + return + } + guard let string = String(data: data, encoding: .utf8) else { + completion(.failure(.decodeFailure)) + return + } + do { + try UpdateCommonErrors.live(jsonFile: string) + completion(.success(())) + } catch { + completion(.failure(.bindingsException)) + } + }.resume() + } +} + +extension UpdateErrors { + public static let unimplemented = UpdateErrors( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/ChatInputFeature/Voxophone.swift b/Sources/Voxophone/Voxophone.swift similarity index 100% rename from Sources/ChatInputFeature/Voxophone.swift rename to Sources/Voxophone/Voxophone.swift diff --git a/Sources/WebsiteFeature/WebsiteController.swift b/Sources/WebsiteFeature/WebsiteController.swift new file mode 100644 index 0000000000000000000000000000000000000000..822ebe38b883047de4c3f02157b883a2c0980bb6 --- /dev/null +++ b/Sources/WebsiteFeature/WebsiteController.swift @@ -0,0 +1,31 @@ +import UIKit +import WebKit + +public final class WebsiteController: UIViewController { + private lazy var webView = WKWebView() + + private let url: URL + + public init(_ string: String) { + self.url = .init(string: string)! + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { nil } + + public override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .white + view.addSubview(webView) + + webView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true + webView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true + webView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true + webView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true + + DispatchQueue.main.async { [weak self] in + guard let self else { return } + self.webView.load(URLRequest(url: self.url)) + } + } +} diff --git a/Tests/DefaultsTests/KeyObjectTests.swift b/Tests/DefaultsTests/KeyObjectTests.swift index 0cbf3b4fae776c082350548bd4fbf3b34232cdf4..e35e13ebf2d34edd1b5e5a3e2b1423699be7a21d 100644 --- a/Tests/DefaultsTests/KeyObjectTests.swift +++ b/Tests/DefaultsTests/KeyObjectTests.swift @@ -1,4 +1,3 @@ -import DI import XCTest @testable import Defaults diff --git a/Tests/ThemeTests/ThemeTests.swift b/Tests/ThemeTests/ThemeTests.swift index 1cc6794c92935ea93bf54b5a155075a88f58d0be..55049b48a6fdd1dd9bdc096336c1ca1da517f3a8 100644 --- a/Tests/ThemeTests/ThemeTests.swift +++ b/Tests/ThemeTests/ThemeTests.swift @@ -1,4 +1,3 @@ -import DI import Quick import Nimble import Defaults diff --git a/XCFrameworks/Bindings.xcframework/Info.plist b/XCFrameworks/Bindings.xcframework/Info.plist deleted file mode 100644 index 5da456bbdabbf3d610daca4ce17734b523413a53..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/Info.plist +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>AvailableLibraries</key> - <array> - <dict> - <key>LibraryIdentifier</key> - <string>ios-arm64</string> - <key>LibraryPath</key> - <string>Bindings.framework</string> - <key>SupportedArchitectures</key> - <array> - <string>arm64</string> - </array> - <key>SupportedPlatform</key> - <string>ios</string> - </dict> - <dict> - <key>LibraryIdentifier</key> - <string>ios-arm64_x86_64-simulator</string> - <key>LibraryPath</key> - <string>Bindings.framework</string> - <key>SupportedArchitectures</key> - <array> - <string>arm64</string> - <string>x86_64</string> - </array> - <key>SupportedPlatform</key> - <string>ios</string> - <key>SupportedPlatformVariant</key> - <string>simulator</string> - </dict> - </array> - <key>CFBundlePackageType</key> - <string>XFWK</string> - <key>XCFrameworkFormatVersion</key> - <string>1.0</string> -</dict> -</plist> diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Bindings b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Bindings deleted file mode 100644 index 8d78f84d50a0b0719b795f7b342f6109c2b848ec..0000000000000000000000000000000000000000 Binary files a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Bindings and /dev/null differ diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/Bindings.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/Bindings.h deleted file mode 100644 index 8906a7da239705b790cb2bb64de92f806640cb38..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/Bindings.h +++ /dev/null @@ -1,13 +0,0 @@ - -// Objective-C API for talking to the following Go packages -// -// gitlab.com/elixxir/client/bindings -// -// File is generated by gomobile bind. Do not edit. -#ifndef __Bindings_FRAMEWORK_H__ -#define __Bindings_FRAMEWORK_H__ - -#include "Bindings.objc.h" -#include "Universe.objc.h" - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/Bindings.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/Bindings.objc.h deleted file mode 100644 index 32bf6d116888f787ced27b01b95cb4e1b2c1138b..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/Bindings.objc.h +++ /dev/null @@ -1,2083 +0,0 @@ -// Objective-C API for talking to gitlab.com/elixxir/client/bindings Go package. -// gobind -lang=objc gitlab.com/elixxir/client/bindings -// -// File is generated by gobind. Do not edit. - -#ifndef __Bindings_H__ -#define __Bindings_H__ - -@import Foundation; -#include "ref.h" -#include "Universe.objc.h" - - -@class BindingsBackup; -@class BindingsBackupReport; -@class BindingsClient; -@class BindingsContact; -@class BindingsContactList; -@class BindingsDummyTraffic; -@class BindingsFact; -@class BindingsFactList; -@class BindingsFilePartTracker; -@class BindingsFileTransfer; -@class BindingsGroup; -@class BindingsGroupChat; -@class BindingsGroupMember; -@class BindingsGroupMembership; -@class BindingsGroupMessageReceive; -@class BindingsGroupReportDisk; -@class BindingsGroupSendReport; -@class BindingsIdList; -@class BindingsIntList; -@class BindingsManyNotificationForMeReport; -@class BindingsMessage; -@class BindingsNewGroupReport; -@class BindingsNodeRegistrationsStatus; -@class BindingsNotificationForMeReport; -@class BindingsRestoreContactsReport; -@class BindingsRoundList; -@class BindingsSendReport; -@class BindingsSendReportDisk; -@class BindingsUnregister; -@class BindingsUser; -@class BindingsUserDiscovery; -@protocol BindingsAuthConfirmCallback; -@class BindingsAuthConfirmCallback; -@protocol BindingsAuthRequestCallback; -@class BindingsAuthRequestCallback; -@protocol BindingsAuthResetNotificationCallback; -@class BindingsAuthResetNotificationCallback; -@protocol BindingsClientError; -@class BindingsClientError; -@protocol BindingsEventCallbackFunctionObject; -@class BindingsEventCallbackFunctionObject; -@protocol BindingsFileTransferReceiveFunc; -@class BindingsFileTransferReceiveFunc; -@protocol BindingsFileTransferReceivedProgressFunc; -@class BindingsFileTransferReceivedProgressFunc; -@protocol BindingsFileTransferSentProgressFunc; -@class BindingsFileTransferSentProgressFunc; -@protocol BindingsGroupReceiveFunc; -@class BindingsGroupReceiveFunc; -@protocol BindingsGroupRequestFunc; -@class BindingsGroupRequestFunc; -@protocol BindingsListener; -@class BindingsListener; -@protocol BindingsLogWriter; -@class BindingsLogWriter; -@protocol BindingsLookupCallback; -@class BindingsLookupCallback; -@protocol BindingsMessageDeliveryCallback; -@class BindingsMessageDeliveryCallback; -@protocol BindingsMultiLookupCallback; -@class BindingsMultiLookupCallback; -@protocol BindingsNetworkHealthCallback; -@class BindingsNetworkHealthCallback; -@protocol BindingsPreimageNotification; -@class BindingsPreimageNotification; -@protocol BindingsRestoreContactsUpdater; -@class BindingsRestoreContactsUpdater; -@protocol BindingsRoundCompletionCallback; -@class BindingsRoundCompletionCallback; -@protocol BindingsRoundEventCallback; -@class BindingsRoundEventCallback; -@protocol BindingsSearchCallback; -@class BindingsSearchCallback; -@protocol BindingsSingleSearchCallback; -@class BindingsSingleSearchCallback; -@protocol BindingsTimeSource; -@class BindingsTimeSource; -@protocol BindingsUpdateBackupFunc; -@class BindingsUpdateBackupFunc; - -@protocol BindingsAuthConfirmCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)partner; -@end - -@protocol BindingsAuthRequestCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@protocol BindingsAuthResetNotificationCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@protocol BindingsClientError <NSObject> -- (void)report:(NSString* _Nullable)source message:(NSString* _Nullable)message trace:(NSString* _Nullable)trace; -@end - -@protocol BindingsEventCallbackFunctionObject <NSObject> -- (void)reportEvent:(long)priority category:(NSString* _Nullable)category evtType:(NSString* _Nullable)evtType details:(NSString* _Nullable)details; -@end - -@protocol BindingsFileTransferReceiveFunc <NSObject> -- (void)receiveCallback:(NSData* _Nullable)tid fileName:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType sender:(NSData* _Nullable)sender size:(long)size preview:(NSData* _Nullable)preview; -@end - -@protocol BindingsFileTransferReceivedProgressFunc <NSObject> -- (void)receivedProgressCallback:(BOOL)completed received:(long)received total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -@protocol BindingsFileTransferSentProgressFunc <NSObject> -- (void)sentProgressCallback:(BOOL)completed sent:(long)sent arrived:(long)arrived total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -@protocol BindingsGroupReceiveFunc <NSObject> -- (void)groupReceiveCallback:(BindingsGroupMessageReceive* _Nullable)msg; -@end - -@protocol BindingsGroupRequestFunc <NSObject> -- (void)groupRequestCallback:(BindingsGroup* _Nullable)g; -@end - -@protocol BindingsListener <NSObject> -/** - * Hear is called to receive a message in the UI - */ -- (void)hear:(BindingsMessage* _Nullable)message; -/** - * Returns a name, used for debugging - */ -- (NSString* _Nonnull)name; -@end - -@protocol BindingsLogWriter <NSObject> -- (void)log:(NSString* _Nullable)p0; -@end - -@protocol BindingsLookupCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@protocol BindingsMessageDeliveryCallback <NSObject> -- (void)eventCallback:(NSData* _Nullable)msgID delivered:(BOOL)delivered timedOut:(BOOL)timedOut roundResults:(NSData* _Nullable)roundResults; -@end - -@protocol BindingsMultiLookupCallback <NSObject> -- (void)callback:(BindingsContactList* _Nullable)Succeeded failed:(BindingsIdList* _Nullable)failed errors:(NSString* _Nullable)errors; -@end - -@protocol BindingsNetworkHealthCallback <NSObject> -- (void)callback:(BOOL)p0; -@end - -@protocol BindingsPreimageNotification <NSObject> -- (void)notify:(NSData* _Nullable)identity deleted:(BOOL)deleted; -@end - -@protocol BindingsRestoreContactsUpdater <NSObject> -/** - * RestoreContactsCallback is called to report the current # of contacts -that have been found and how many have been restored -against the total number that need to be -processed. If an error occurs it it set on the err variable as a -plain string. - */ -- (void)restoreContactsCallback:(long)numFound numRestored:(long)numRestored total:(long)total err:(NSString* _Nullable)err; -@end - -@protocol BindingsRoundCompletionCallback <NSObject> -- (void)eventCallback:(long)rid success:(BOOL)success timedOut:(BOOL)timedOut; -@end - -@protocol BindingsRoundEventCallback <NSObject> -- (void)eventCallback:(long)rid state:(long)state timedOut:(BOOL)timedOut; -@end - -@protocol BindingsSearchCallback <NSObject> -- (void)callback:(BindingsContactList* _Nullable)contacts error:(NSString* _Nullable)error; -@end - -@protocol BindingsSingleSearchCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@protocol BindingsTimeSource <NSObject> -- (int64_t)nowMs; -@end - -@protocol BindingsUpdateBackupFunc <NSObject> -- (void)updateBackup:(NSData* _Nullable)encryptedBackup; -@end - -@interface BindingsBackup : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * AddJson stores a passed in json string in the backup structure - */ -- (void)addJson:(NSString* _Nullable)json; -/** - * IsBackupRunning returns true if the backup has been initialized and is -running. Returns false if it has been stopped. - */ -- (BOOL)isBackupRunning; -/** - * StopBackup stops the backup processes and deletes the user's password from -storage. To enable backups again, call InitializeBackup. - */ -- (BOOL)stopBackup:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsBackupReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field BackupReport.RestoredContacts with unsupported type: []*gitlab.com/xx_network/primitives/id.ID - -@property (nonatomic) NSString* _Nonnull params; -@end - -/** - * BindingsClient wraps the api.Client, implementing additional functions -to support the gomobile Client interface - */ -@interface BindingsClient : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BOOL)confirmAuthenticatedChannel:(NSData* _Nullable)recipientMarshaled ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteAllRequests clears all requests from Client's auth storage. - */ -- (BOOL)deleteAllRequests:(NSError* _Nullable* _Nullable)error; -/** - * DeleteContact is a function which removes a contact from Client's storage - */ -- (BOOL)deleteContact:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteReceiveRequests clears receive requests from Client's auth storage. - */ -- (BOOL)deleteReceiveRequests:(NSError* _Nullable* _Nullable)error; -/** - * DeleteRequest will delete a request, agnostic of request type -for the given partner ID. If no request exists for this -partner ID an error will be returned. - */ -- (BOOL)deleteRequest:(NSData* _Nullable)requesterUserId error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteSentRequests clears sent requests from Client's auth storage. - */ -- (BOOL)deleteSentRequests:(NSError* _Nullable* _Nullable)error; -// skipped method Client.GetInternalClient with unsupported parameter or return types - -/** - * GetNodeRegistrationStatus returns a struct with the number of nodes the -client is registered with and the number total. - */ -- (BindingsNodeRegistrationsStatus* _Nullable)getNodeRegistrationStatus:(NSError* _Nullable* _Nullable)error; -/** - * GetPartners returns a list of - */ -- (NSData* _Nullable)getPartners:(NSError* _Nullable* _Nullable)error; -/** - * GetPreferredBins returns the geographic bin or bins that the provided two -character country code is a part of. The bins are returned as CSV. - */ -- (NSString* _Nonnull)getPreferredBins:(NSString* _Nullable)countryCode error:(NSError* _Nullable* _Nullable)error; -- (NSString* _Nonnull)getPreimages:(NSData* _Nullable)identity; -// skipped method Client.GetRateLimitParams with unsupported parameter or return types - -- (NSString* _Nonnull)getRelationshipFingerprint:(NSData* _Nullable)partnerID error:(NSError* _Nullable* _Nullable)error; -/** - * Returns a user object from which all information about the current user -can be gleaned - */ -- (BindingsUser* _Nullable)getUser; -/** - * HasRunningProcessies checks if any background threads are running. -returns true if none are running. This is meant to be -used when NetworkFollowerStatus() returns Stopping. -Due to the handling of comms on iOS, where the OS can -block indefiently, it may not enter the stopped -state apropreatly. This can be used instead. - */ -- (BOOL)hasRunningProcessies; -/** - * returns true if the network is read to be in a healthy state where -messages can be sent - */ -- (BOOL)isNetworkHealthy; -- (BindingsContact* _Nullable)makePrecannedAuthenticatedChannel:(long)precannedID error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the state of the network follower. Returns: -Stopped - 0 -Starting - 1000 -Running - 2000 -Stopping - 3000 - */ -- (long)networkFollowerStatus; -- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetNotificationCallback> _Nullable)reset; -/** - * RegisterClientErrorCallback registers the callback to handle errors from the -long running threads controlled by StartNetworkFollower and StopNetworkFollower - */ -- (void)registerClientErrorCallback:(id<BindingsClientError> _Nullable)clientError; -/** - * RegisterEventCallback records the given function to receive -ReportableEvent objects. It returns the internal index -of the callback so that it can be deleted later. - */ -- (BOOL)registerEventCallback:(NSString* _Nullable)name myObj:(id<BindingsEventCallbackFunctionObject> _Nullable)myObj error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterForNotifications accepts firebase messaging token - */ -- (BOOL)registerForNotifications:(NSString* _Nullable)token error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterListener records and installs a listener for messages -matching specific uid, msgType, and/or username -Returns a ListenerUnregister interface which can be - -to register for any userID, pass in an id with length 0 or an id with -all zeroes - -to register for any message type, pass in a message type of 0 - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types - */ -- (BindingsUnregister* _Nullable)registerListener:(NSData* _Nullable)uid msgType:(long)msgType listener:(id<BindingsListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterNetworkHealthCB registers the network health callback to be called -any time the network health changes. Returns a unique ID that can be used to -unregister the network health callback. - */ -- (int64_t)registerNetworkHealthCB:(id<BindingsNetworkHealthCallback> _Nullable)nhc; -- (void)registerPreimageCallback:(NSData* _Nullable)identity pin:(id<BindingsPreimageNotification> _Nullable)pin; -/** - * RegisterRoundEventsHandler registers a callback interface for round -events. -The rid is the round the event attaches to -The timeoutMS is the number of milliseconds until the event fails, and the -validStates are a list of states (one per byte) on which the event gets -triggered -States: - 0x00 - PENDING (Never seen by client) - 0x01 - PRECOMPUTING - 0x02 - STANDBY - 0x03 - QUEUED - 0x04 - REALTIME - 0x05 - COMPLETED - 0x06 - FAILED -These states are defined in elixxir/primitives/states/state.go - */ -- (BindingsUnregister* _Nullable)registerRoundEventsHandler:(long)rid cb:(id<BindingsRoundEventCallback> _Nullable)cb timeoutMS:(long)timeoutMS il:(BindingsIntList* _Nullable)il; -- (void)replayRequests; -- (BOOL)requestAuthenticatedChannel:(NSData* _Nullable)recipientMarshaled meMarshaled:(NSData* _Nullable)meMarshaled message:(NSString* _Nullable)message ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -- (BOOL)resetSession:(NSData* _Nullable)recipientMarshaled meMarshaled:(NSData* _Nullable)meMarshaled message:(NSString* _Nullable)message ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * This will return the round the message was sent on if it is successfully sent -This can be used to register a round event to learn about message delivery. -on failure a round id of -1 is returned - */ -- (BOOL)sendCmix:(NSData* _Nullable)recipient contents:(NSData* _Nullable)contents parameters:(NSString* _Nullable)parameters ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * SendE2E sends an end-to-end payload to the provided recipient with -the provided msgType. Returns the list of rounds in which parts of -the message were sent or an error if it fails. - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types - */ -- (BindingsSendReport* _Nullable)sendE2E:(NSData* _Nullable)recipient payload:(NSData* _Nullable)payload messageType:(long)messageType parameters:(NSString* _Nullable)parameters error:(NSError* _Nullable* _Nullable)error; -/** - * SendUnsafe sends an unencrypted payload to the provided recipient -with the provided msgType. Returns the list of rounds in which parts -of the message were sent or an error if it fails. -NOTE: Do not use this function unless you know what you are doing. -This function always produces an error message in client logging. - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types with custom types - */ -- (BindingsRoundList* _Nullable)sendUnsafe:(NSData* _Nullable)recipient payload:(NSData* _Nullable)payload messageType:(long)messageType parameters:(NSString* _Nullable)parameters error:(NSError* _Nullable* _Nullable)error; -/** - * SetProxiedBins updates the host pool filter that filters out gateways that -are not in one of the specified bins. The provided bins should be CSV. - */ -- (BOOL)setProxiedBins:(NSString* _Nullable)binStringsCSV error:(NSError* _Nullable* _Nullable)error; -/** - * StartNetworkFollower kicks off the tracking of the network. It starts -long running network client threads and returns an object for checking -state and stopping those threads. -Call this when returning from sleep and close when going back to -sleep. -These threads may become a significant drain on battery when offline, ensure -they are stopped if there is no internet access -Threads Started: - - Network Follower (/network/follow.go) - tracks the network events and hands them off to workers for handling - - Historical Round Retrieval (/network/rounds/historical.go) - Retrieves data about rounds which are too old to be stored by the client - - Message Retrieval Worker Group (/network/rounds/retrieve.go) - Requests all messages in a given round from the gateway of the last node - - Message Handling Worker Group (/network/message/handle.go) - Decrypts and partitions messages when signals via the Switchboard - - Health Tracker (/network/health) - Via the network instance tracks the state of the network - - Garbled Messages (/network/message/garbled.go) - Can be signaled to check all recent messages which could be be decoded - Uses a message store on disk for persistence - - Critical Messages (/network/message/critical.go) - Ensures all protocol layer mandatory messages are sent - Uses a message store on disk for persistence - - KeyExchange Trigger (/keyExchange/trigger.go) - Responds to sent rekeys and executes them - - KeyExchange Confirm (/keyExchange/confirm.go) - Responds to confirmations of successful rekey operations - */ -- (BOOL)startNetworkFollower:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * StopNetworkFollower stops the network follower if it is running. -It returns errors if the Follower is in the wrong status to stop or if it -fails to stop it. -if the network follower is running and this fails, the client object will -most likely be in an unrecoverable state and need to be trashed. - */ -- (BOOL)stopNetworkFollower:(NSError* _Nullable* _Nullable)error; -/** - * UnregisterEventCallback deletes the callback identified by the -index. It returns an error if it fails. - */ -- (void)unregisterEventCallback:(NSString* _Nullable)name; -/** - * UnregisterForNotifications unregister user for notifications - */ -- (BOOL)unregisterForNotifications:(NSError* _Nullable* _Nullable)error; -- (void)unregisterNetworkHealthCB:(int64_t)funcID; -- (BOOL)verifyOwnership:(NSData* _Nullable)receivedMarshaled verifiedMarshaled:(NSData* _Nullable)verifiedMarshaled ret0_:(BOOL* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * WaitForMessageDelivery allows the caller to get notified if the rounds a -message was sent in successfully completed. Under the hood, this uses an API -which uses the internal round data, network historical round lookup, and -waiting on network events to determine what has (or will) occur. - -The callbacks will return at timeoutMS if no state update occurs - -This function takes the marshaled send report to ensure a memory leak does -not occur as a result of both sides of the bindings holding a reference to -the same pointer. - */ -- (BOOL)waitForMessageDelivery:(NSData* _Nullable)marshaledSendReport mdc:(id<BindingsMessageDeliveryCallback> _Nullable)mdc timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * WaitForNewtwork will block until either the network is healthy or the -passed timeout. It will return true if the network is healthy - */ -- (BOOL)waitForNetwork:(long)timeoutMS; -/** - * WaitForRoundCompletion allows the caller to get notified if a round -has completed (or failed). Under the hood, this uses an API which uses the internal -round data, network historical round lookup, and waiting on network events -to determine what has (or will) occur. - -The callbacks will return at timeoutMS if no state update occurs - */ -- (BOOL)waitForRoundCompletion:(long)roundID rec:(id<BindingsRoundCompletionCallback> _Nullable)rec timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * contact object - */ -@interface BindingsContact : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped method Contact.GetAPIContact with unsupported parameter or return types - -/** - * GetDHPublicKey returns the public key associated with the Contact. - */ -- (NSData* _Nullable)getDHPublicKey; -/** - * Returns a fact list for adding and getting facts to and from the contact - */ -- (BindingsFactList* _Nullable)getFactList; -/** - * GetID returns the user ID for this user. - */ -- (NSData* _Nullable)getID; -/** - * GetDHPublicKey returns hash of a DH proof of key ownership. - */ -- (NSData* _Nullable)getOwnershipProof; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsContactList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Gets a stored round ID at the given index - */ -- (BindingsContact* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the number of round IDs stored - */ -- (long)len; -@end - -/** - * DummyTraffic contains the file dummy traffic manager. The manager can be used -to set and get the status of the send thread. - */ -@interface BindingsDummyTraffic : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewDummyTrafficManager creates a DummyTraffic manager and initialises the -dummy traffic send thread. Note that the manager does not start sending dummy -traffic until its status is set to true using DummyTraffic.SetStatus. -The maxNumMessages is the upper bound of the random number of messages sent -each send. avgSendDeltaMS is the average duration, in milliseconds, to wait -between sends. Sends occur every avgSendDeltaMS +/- a random duration with an -upper bound of randomRangeMS. - */ -- (nullable instancetype)initManager:(BindingsClient* _Nullable)client maxNumMessages:(long)maxNumMessages avgSendDeltaMS:(long)avgSendDeltaMS randomRangeMS:(long)randomRangeMS; -/** - * GetStatus returns the current state of the dummy traffic send thread. It has -the following return values: - true = send thread is sending dummy messages - false = send thread is paused/stopped and not sending dummy messages -Note that this function does not return the status set by SetStatus directly; -it returns the current status of the send thread, which means any call to -SetStatus will have a small delay before it is returned by GetStatus. - */ -- (BOOL)getStatus; -/** - * SetStatus sets the state of the dummy traffic send thread, which determines -if the thread is running or paused. The possible statuses are: - true = send thread is sending dummy messages - false = send thread is paused/stopped and not sending dummy messages -Returns an error if the channel is full. -Note that this function cannot change the status of the send thread if it has -yet to be started or stopped. - */ -- (BOOL)setStatus:(BOOL)status error:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsFact : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * fact object -creates a new fact. The factType must be either: - 0 - Username - 1 - Email - 2 - Phone Number -The fact must be well formed for the type and must not include commas or -semicolons. If it is not well formed, it will be rejected. Phone numbers -must have the two letter country codes appended. For the complete set of -validation, see /elixxir/primitives/fact/fact.go - */ -- (nullable instancetype)init:(long)factType factStr:(NSString* _Nullable)factStr; -- (NSString* _Nonnull)get; -- (NSString* _Nonnull)stringify; -- (long)type; -@end - -@interface BindingsFactList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * FactList - */ -- (nullable instancetype)init; -- (BOOL)add:(NSString* _Nullable)factData factType:(long)factType error:(NSError* _Nullable* _Nullable)error; -- (BindingsFact* _Nullable)get:(long)i; -- (long)num; -- (NSString* _Nonnull)stringify:(NSError* _Nullable* _Nullable)error; -@end - -/** - * FilePartTracker contains the interfaces.FilePartTracker. - */ -@interface BindingsFilePartTracker : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetNumParts returns the total number of file parts in the transfer. - */ -- (long)getNumParts; -/** - * GetPartStatus returns the status of the file part with the given part number. -The possible values for the status are: -0 = unsent -1 = sent (sender has sent a part, but it has not arrived) -2 = arrived (sender has sent a part, and it has arrived) -3 = received (receiver has received a part) - */ -- (long)getPartStatus:(long)partNum; -@end - -/** - * FileTransfer contains the file transfer manager. - */ -@interface BindingsFileTransfer : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewFileTransferManager creates a new file transfer manager and starts the -sending and receiving threads. The receiveFunc is called everytime a new file -transfer is received. -The parameters string contains file transfer network configuration options -and is a JSON formatted string of the fileTransfer.Params object. If it is -left empty, then defaults are used. It is highly recommended that defaults -are used. If it is set, it must match the following format: - {"MaxThroughput":150000,"SendTimeout":500000000} -MaxThroughput is in bytes/sec and SendTimeout is in nanoseconds. - */ -- (nullable instancetype)initManager:(BindingsClient* _Nullable)client receiveFunc:(id<BindingsFileTransferReceiveFunc> _Nullable)receiveFunc parameters:(NSString* _Nullable)parameters; -/** - * CloseSend deletes a sent file transfer from the sent transfer map and from -storage once a transfer has completed or reached the retry limit. Returns an -error if the transfer has not run out of retries. - */ -- (BOOL)closeSend:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error; -/** - * GetMaxFileNameByteLength returns the maximum length, in bytes, allowed for a -file name. - */ -- (long)getMaxFileNameByteLength; -/** - * GetMaxFilePreviewSize returns the maximum file preview size, in bytes. - */ -- (long)getMaxFilePreviewSize; -/** - * GetMaxFileSize returns the maximum file size, in bytes, allowed to be -transferred. - */ -- (long)getMaxFileSize; -/** - * GetMaxFileTypeByteLength returns the maximum length, in bytes, allowed for a -file type. - */ -- (long)getMaxFileTypeByteLength; -/** - * Receive returns the fully assembled file on the completion of the transfer. -It deletes the transfer from the received transfer map and from storage. -Returns an error if the transfer is not complete, the full file cannot be -verified, or if the transfer cannot be found. - */ -- (NSData* _Nullable)receive:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterReceiveProgressCallback allows for the registration of a callback to -track the progress of an individual received file transfer. The callback will -be called immediately when added to report the current status of the -transfer. It will then call every time a file part is received, the transfer -completes, or an error occurs. It is called at most once ever period, which -means if events occur faster than the period, then they will not be reported -and instead, the progress will be reported once at the end of the period. -Once the callback reports that the transfer has completed, the recipient -can get the full file by calling Receive. -The period is specified in milliseconds. - */ -- (BOOL)registerReceiveProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferReceivedProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterSendProgressCallback allows for the registration of a callback to -track the progress of an individual sent file transfer. The callback will be -called immediately when added to report the current status of the transfer. -It will then call every time a file part is sent, a file part arrives, the -transfer completes, or an error occurs. It is called at most once every -period, which means if events occur faster than the period, then they will -not be reported and instead, the progress will be reported once at the end of -the period. -The period is specified in milliseconds. - */ -- (BOOL)registerSendProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -/** - * Send sends a file to the recipient. The sender must have an E2E relationship -with the recipient. -The file name is the name of the file to show a user. It has a max length of -48 bytes. -The file type identifies what type of file is being sent. It has a max length -of 8 bytes. -The file data cannot be larger than 256 kB -The retry float is the total amount of data to send relative to the data -size. Data will be resent on error and will resend up to [(1 + retry) * -fileSize]. -The preview stores a preview of the data (such as a thumbnail) and is -capped at 4 kB in size. -Returns a unique transfer ID used to identify the transfer. -PeriodMS is the duration, in milliseconds, to wait between progress callback -calls. Set this large enough to prevent spamming. - */ -- (NSData* _Nullable)send:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType fileData:(NSData* _Nullable)fileData recipientID:(NSData* _Nullable)recipientID retry:(float)retry preview:(NSData* _Nullable)preview progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * Group structure contains the identifying and membership information of a -group chat. - */ -@interface BindingsGroup : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetCreatedMS returns the time the group was created in milliseconds. This is -also the time the group requests were sent. - */ -- (int64_t)getCreatedMS; -/** - * GetCreatedNano returns the time the group was created in nanoseconds. This is -also the time the group requests were sent. - */ -- (int64_t)getCreatedNano; -/** - * GetID return the 33-byte unique group ID. - */ -- (NSData* _Nullable)getID; -/** - * GetInitMessage returns initial message sent with the group request. - */ -- (NSData* _Nullable)getInitMessage; -/** - * GetMembership returns a list of contacts, one for each member in the group. -The list is in order; the first contact is the leader/creator of the group. -All subsequent members are ordered by their ID. - */ -- (BindingsGroupMembership* _Nullable)getMembership; -/** - * GetName returns the name set by the user for the group. - */ -- (NSData* _Nullable)getName; -/** - * Serialize serializes the Group. - */ -- (NSData* _Nullable)serialize; -@end - -/** - * GroupChat object contains the group chat manager. - */ -@interface BindingsGroupChat : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetGroup returns the group with the group ID. If no group exists, then the -error "failed to find group" is returned. - */ -- (BindingsGroup* _Nullable)getGroup:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * GetGroups returns an IdList containing a list of group IDs that the user is a -part of. - */ -- (BindingsIdList* _Nullable)getGroups; -/** - * JoinGroup allows a user to join a group when they receive a request. The -caller must pass in the serialized bytes of a Group. - */ -- (BOOL)joinGroup:(NSData* _Nullable)serializedGroupData error:(NSError* _Nullable* _Nullable)error; -/** - * LeaveGroup deletes a group so a user no longer has access. - */ -- (BOOL)leaveGroup:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * MakeGroup creates a new group and sends a group request to all members in the -group. The ID of the new group, the rounds the requests were sent on, and the -status of the send are contained in NewGroupReport. - */ -- (BindingsNewGroupReport* _Nullable)makeGroup:(BindingsIdList* _Nullable)membership name:(NSData* _Nullable)name message:(NSData* _Nullable)message; -/** - * NumGroups returns the number of groups the user is a part of. - */ -- (long)numGroups; -/** - * ResendRequest resends a group request to all members in the group. The rounds -they were sent on and the status of the send are contained in NewGroupReport. - */ -- (BindingsNewGroupReport* _Nullable)resendRequest:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * Send sends the message to the specified group. Returns the round the messages -were sent on. - */ -- (BindingsGroupSendReport* _Nullable)send:(NSData* _Nullable)groupIdBytes message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * // -Member Structure -// -GroupMember represents a member in the group membership list. - */ -@interface BindingsGroupMember : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupMember.Member with unsupported type: gitlab.com/elixxir/crypto/group.Member - -// skipped method GroupMember.DeepCopy with unsupported parameter or return types - -// skipped method GroupMember.Equal with unsupported parameter or return types - -/** - * GetDhKey returns the byte representation of the public Diffie–Hellman key of -the member. - */ -- (NSData* _Nullable)getDhKey; -/** - * GetID returns the 33-byte user ID of the member. - */ -- (NSData* _Nullable)getID; -- (NSString* _Nonnull)goString; -- (NSData* _Nullable)serialize; -- (NSString* _Nonnull)string; -@end - -/** - * GroupMembership structure contains a list of members that are part of a -group. The first member is the group leader. - */ -@interface BindingsGroupMembership : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Get returns the member at the index. The member at index 0 is always the -group leader. An error is returned if the index is out of range. - */ -- (BindingsGroupMember* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Len returns the number of members in the group membership. - */ -- (long)len; -@end - -/** - * GroupMessageReceive contains a group message, its ID, and its data that a -user receives. - */ -@interface BindingsGroupMessageReceive : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupMessageReceive.MessageReceive with unsupported type: gitlab.com/elixxir/client/groupChat.MessageReceive - -/** - * GetEphemeralID returns the ephemeral ID of the recipient. - */ -- (int64_t)getEphemeralID; -/** - * GetGroupID returns the 33-byte group ID. - */ -- (NSData* _Nullable)getGroupID; -/** - * GetMessageID returns the message ID. - */ -- (NSData* _Nullable)getMessageID; -/** - * GetPayload returns the message payload. - */ -- (NSData* _Nullable)getPayload; -/** - * GetRecipientID returns the 33-byte user ID of the recipient. - */ -- (NSData* _Nullable)getRecipientID; -/** - * GetRoundID returns the ID of the round the message was sent on. - */ -- (int64_t)getRoundID; -/** - * GetRoundTimestampMS returns the timestamp, in milliseconds, of the round the -message was sent on. - */ -- (int64_t)getRoundTimestampMS; -/** - * GetRoundTimestampNano returns the timestamp, in nanoseconds, of the round the -message was sent on. - */ -- (int64_t)getRoundTimestampNano; -/** - * GetRoundURL returns the ID of the round the message was sent on. - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetSenderID returns the 33-byte user ID of the sender. - */ -- (NSData* _Nullable)getSenderID; -/** - * GetTimestampMS returns the message timestamp in milliseconds. - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message timestamp in nanoseconds. - */ -- (int64_t)getTimestampNano; -- (NSString* _Nonnull)string; -@end - -@interface BindingsGroupReportDisk : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupReportDisk.List with unsupported type: []gitlab.com/xx_network/primitives/id.Round - -@property (nonatomic) NSData* _Nullable grpId; -@property (nonatomic) long status; -@end - -/** - * GroupSendReport is returned when sending a group message. It contains the -round ID sent on and the timestamp of the send. - */ -@interface BindingsGroupSendReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetMessageID returns the ID of the round that the send occurred on. - */ -- (NSData* _Nullable)getMessageID; -/** - * GetRoundID returns the ID of the round that the send occurred on. - */ -- (int64_t)getRoundID; -/** - * GetRoundURL returns the URL of the round that the send occurred on. - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetTimestampMS returns the timestamp of the send in milliseconds. - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the timestamp of the send in nanoseconds. - */ -- (int64_t)getTimestampNano; -@end - -/** - * ID list -IdList contains a list of IDs. - */ -@interface BindingsIdList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Add appends the ID bytes to the end of the list. - */ -- (BOOL)add:(NSData* _Nullable)idBytes error:(NSError* _Nullable* _Nullable)error; -/** - * Get returns the ID at the index. An error is returned if the index is out of -range. - */ -- (NSData* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Len returns the number of IDs in the list. - */ -- (long)len; -@end - -@interface BindingsIntList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (void)add:(long)i; -- (BOOL)get:(long)i ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -- (long)len; -@end - -@interface BindingsManyNotificationForMeReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BindingsNotificationForMeReport* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -- (long)len; -@end - -/** - * Message is a message received from the cMix network in the clear -or that has been decrypted using established E2E keys. - */ -@interface BindingsMessage : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetID returns the id of the message - */ -- (NSData* _Nullable)getID; -/** - * GetMessageType returns the message's type - */ -- (long)getMessageType; -/** - * GetPayload returns the message's payload/contents - */ -- (NSData* _Nullable)getPayload; -/** - * GetRoundId returns the message's round ID - */ -- (int64_t)getRoundId; -/** - * GetRoundTimestampMS returns the message's round timestamp in milliseconds - */ -- (int64_t)getRoundTimestampMS; -/** - * GetRoundTimestampNano returns the message's round timestamp in nanoseconds - */ -- (int64_t)getRoundTimestampNano; -/** - * GetRoundURL returns the message's round URL - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetSender returns the message's sender ID, if available - */ -- (NSData* _Nullable)getSender; -/** - * GetTimestampMS returns the message's timestamp in milliseconds - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message's timestamp in nanoseconds - */ -- (int64_t)getTimestampNano; -@end - -/** - * NewGroupReport is returned when creating a new group and contains the ID of -the group, a list of rounds that the group requests were sent on, and the -status of the send. - */ -@interface BindingsNewGroupReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetError returns the string of an error. -Will be an empty string if no error occured - */ -- (NSString* _Nonnull)getError; -/** - * GetGroup returns the Group. - */ -- (BindingsGroup* _Nullable)getGroup; -/** - * GetRoundList returns the RoundList containing a list of rounds requests were -sent on. - */ -- (BindingsRoundList* _Nullable)getRoundList; -/** - * GetStatus returns the status of the requests sent when creating a new group. -status = 0 an error occurred before any requests could be sent - 1 all requests failed to send (call Resend Group) - 2 some request failed and some succeeded (call Resend Group) - 3, all requests sent successfully (call Resend Group) - */ -- (long)getStatus; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -- (BOOL)unmarshal:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * NodeRegistrationsStatus structure for returning node registration statuses -for bindings. - */ -@interface BindingsNodeRegistrationsStatus : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetRegistered returns the number of nodes registered with the client. - */ -- (long)getRegistered; -/** - * GetTotal return the total of nodes currently in the network. - */ -- (long)getTotal; -@end - -@interface BindingsNotificationForMeReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BOOL)forMe; -- (NSData* _Nullable)source; -- (NSString* _Nonnull)type; -@end - -/** - * RestoreContactsReport is a gomobile friendly report structure -for determining which IDs restored, which failed, and why. - */ -@interface BindingsRestoreContactsReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetErrorAt returns the error string at index - */ -- (NSString* _Nonnull)getErrorAt:(long)index; -/** - * GetFailedAt returns the failed ID at index - */ -- (NSData* _Nullable)getFailedAt:(long)index; -/** - * GetRestoreContactsError returns an error string. Empty if no error. - */ -- (NSString* _Nonnull)getRestoreContactsError; -/** - * GetRestoredAt returns the restored ID at index - */ -- (NSData* _Nullable)getRestoredAt:(long)index; -/** - * LenFailed returns the length of the ID's failed. - */ -- (long)lenFailed; -/** - * LenRestored returns the length of ID's restored. - */ -- (long)lenRestored; -@end - -@interface BindingsRoundList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Gets a stored round ID at the given index - */ -- (BOOL)get:(long)i ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the number of round IDs stored - */ -- (long)len; -@end - -/** - * the send report is the mechanisim by which sendE2E returns a single - */ -@interface BindingsSendReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (NSData* _Nullable)getMessageID; -- (BindingsRoundList* _Nullable)getRoundList; -- (NSString* _Nonnull)getRoundURL; -/** - * GetTimestampMS returns the message's timestamp in milliseconds - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message's timestamp in nanoseconds - */ -- (int64_t)getTimestampNano; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -- (BOOL)unmarshal:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsSendReportDisk : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field SendReportDisk.List with unsupported type: []gitlab.com/xx_network/primitives/id.Round - -@property (nonatomic) NSData* _Nullable mid; -@property (nonatomic) int64_t ts; -@end - -/** - * Generic Unregister - a generic return used for all callbacks which can be -unregistered -Interface which allows the un-registration of a listener - */ -@interface BindingsUnregister : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Call unregisters a callback - */ -- (void)unregister; -@end - -@interface BindingsUser : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BindingsContact* _Nullable)getContact; -- (NSData* _Nullable)getE2EDhPrivateKey; -- (NSData* _Nullable)getE2EDhPublicKey; -- (NSData* _Nullable)getReceptionID; -- (NSData* _Nullable)getReceptionRSAPrivateKeyPem; -- (NSData* _Nullable)getReceptionRSAPublicKeyPem; -- (NSData* _Nullable)getReceptionSalt; -- (NSData* _Nullable)getTransmissionID; -- (NSData* _Nullable)getTransmissionRSAPrivateKeyPem; -- (NSData* _Nullable)getTransmissionRSAPublicKeyPem; -- (NSData* _Nullable)getTransmissionSalt; -- (BOOL)isPrecanned; -@end - -@interface BindingsUserDiscovery : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewUserDiscovery returns a new user discovery object. Only call this once. It must be called -after StartNetworkFollower is called and will fail if the network has never -been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -- (nullable instancetype)init:(BindingsClient* _Nullable)client; -/** - * NewUserDiscoveryFromBackup returns a new user discovery object. It -wil set up the manager with the backup data. Pass into it the backed up -facts, one email and phone number each. This will add the registered facts -to the backed Store. Any one of these fields may be empty, -however both fields being empty will cause an error. Any other fact that is not -an email or phone number will return an error. You may only add a fact for the -accepted types once each. If you attempt to back up a fact type that has already -been backed up, an error will be returned. Anytime an error is returned, it means -the backup was not successful. -NOTE: Do not use this as a direct store operation. This feature is intended to add facts -to a backend store that have ALREADY BEEN REGISTERED on the account. -THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend. -Only call this once. It must be called after StartNetworkFollower -is called and will fail if the network has never been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -- (nullable instancetype)initFromBackup:(BindingsClient* _Nullable)client email:(NSString* _Nullable)email phone:(NSString* _Nullable)phone; -/** - * AddFact adds a fact for the user to user discovery. Will only succeed if the -user is already registered and the system does not have the fact currently -registered for any user. -Will fail if the fact string is not well formed. -This does not complete the fact registration process, it returns a -confirmation id instead. Over the communications system the fact is -associated with, a code will be sent. This confirmation ID needs to be -called along with the code to finalize the fact. - */ -- (NSString* _Nonnull)addFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * ConfirmFact confirms a fact first registered via AddFact. The confirmation ID comes from -AddFact while the code will come over the associated communications system - */ -- (BOOL)confirmFact:(NSString* _Nullable)confirmationID code:(NSString* _Nullable)code error:(NSError* _Nullable* _Nullable)error; -/** - * Lookup the contact object associated with the given userID. The -id is the byte representation of an id. -This will reject if that id is malformed. The LookupCallback will return -the associated contact if it exists. - */ -- (BOOL)lookup:(NSData* _Nullable)idBytes callback:(id<BindingsLookupCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * MultiLookup Looks for the contact object associated with all given userIDs. -The ids are the byte representation of an id stored in an IDList object. -This will reject if that id is malformed or if the indexing on the IDList -object is wrong. The MultiLookupCallback will return with all contacts -returned within the timeout. - */ -- (BOOL)multiLookup:(BindingsIdList* _Nullable)ids callback:(id<BindingsMultiLookupCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * Register registers a user with user discovery. Will return an error if the -network signatures are malformed or if the username is taken. Usernames -cannot be changed after registration at this time. Will fail if the user is -already registered. -Identity does not go over cmix, it occurs over normal communications - */ -- (BOOL)register:(NSString* _Nullable)username error:(NSError* _Nullable* _Nullable)error; -/** - * RemoveFact removes a previously confirmed fact. Will fail if the passed fact string is -not well-formed or if the fact is not associated with this client. -Users cannot remove username facts and must instead remove the user. - */ -- (BOOL)removeFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * RemoveUser deletes a user. The fact sent must be the username. -This function preserves the username forever and makes it -unusable. - */ -- (BOOL)removeUser:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * Search for the passed Facts. The factList is the stringification of a -fact list object, look at /bindings/list.go for more on that object. -This will reject if that object is malformed. The SearchCallback will return -a list of contacts, each having the facts it hit against. -This is NOT intended to be used to search for multiple users at once, that -can have a privacy reduction. Instead, it is intended to be used to search -for a user where multiple pieces of information is known. - */ -- (BOOL)search:(NSString* _Nullable)fl callback:(id<BindingsSearchCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * SearchSingle searches for the passed Facts. The fact is the stringification of a -fact object, look at /bindings/contact.go for more on that object. -This will reject if that object is malformed. The SearchCallback will return -a list of contacts, each having the facts it hit against. -This only searches for a single fact at a time. It is intended to make some -simple use cases of the API easier. - */ -- (BOOL)searchSingle:(NSString* _Nullable)f callback:(id<BindingsSingleSearchCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * SetAlternativeUserDiscovery sets the alternativeUd object within manager. -Once set, any user discovery operation will go through the alternative -user discovery service. -To undo this operation, use UnsetAlternativeUserDiscovery. -The contact file is the already read in bytes, not the file path for the contact file. - */ -- (BOOL)setAlternativeUserDiscovery:(NSData* _Nullable)address cert:(NSData* _Nullable)cert contactFile:(NSData* _Nullable)contactFile error:(NSError* _Nullable* _Nullable)error; -/** - * UnsetAlternativeUserDiscovery clears out the information from -the Manager object. - */ -- (BOOL)unsetAlternativeUserDiscovery:(NSError* _Nullable* _Nullable)error; -@end - -/** - * Error codes - */ -FOUNDATION_EXPORT NSString* _Nonnull const BindingsUnrecognizedCode; -FOUNDATION_EXPORT NSString* _Nonnull const BindingsUnrecognizedMessage; - -/** - * CompressJpeg takes a JPEG image in byte format -and compresses it based on desired output size - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsCompressJpeg(NSData* _Nullable imgBytes, NSError* _Nullable* _Nullable error); - -/** - * CompressJpegForPreview takes a JPEG image in byte format -and compresses it based on desired output size - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsCompressJpegForPreview(NSData* _Nullable imgBytes, NSError* _Nullable* _Nullable error); - -/** - * DownloadAndVerifySignedNdfWithUrl retrieves the NDF from a specified URL. -The NDF is processed into a protobuf containing a signature which -is verified using the cert string passed in. The NDF is returned as marshaled -byte data which may be used to start a client. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadAndVerifySignedNdfWithUrl(NSString* _Nullable url, NSString* _Nullable cert, NSError* _Nullable* _Nullable error); - -/** - * DownloadDAppRegistrationDB returns a []byte containing -the JSON data describing registered dApps. -See https://git.xx.network/elixxir/registered-dapps - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadDAppRegistrationDB(NSError* _Nullable* _Nullable error); - -/** - * DownloadErrorDB returns a []byte containing the JSON data -describing client errors. -See https://git.xx.network/elixxir/client-error-database/ - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadErrorDB(NSError* _Nullable* _Nullable error); - -/** - * DumpStack returns a string with the stack trace of every running thread. - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsDumpStack(NSError* _Nullable* _Nullable error); - -/** - * EnableGrpcLogs sets GRPC trace logging - */ -FOUNDATION_EXPORT void BindingsEnableGrpcLogs(id<BindingsLogWriter> _Nullable writer); - -/** - * ErrorStringToUserFriendlyMessage takes a passed in errStr which will be -a backend generated error. These may be error specifically written by -the backend team or lower level errors gotten from low level dependencies. -This function will parse the error string for common errors provided from -errToUserErr to provide a more user-friendly error message for the front end. -If the error is not common, some simple parsing is done on the error message -to make it more user-accessible, removing backend specific jargon. - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsErrorStringToUserFriendlyMessage(NSString* _Nullable errStr); - -/** - * GenerateSecret creates a secret password using a system-based -pseudorandom number generator. It takes 1 parameter, `numBytes`, -which should be set to 32, but can be set higher in certain cases. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsGenerateSecret(long numBytes); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetCMIXParams(NSError* _Nullable* _Nullable error); - -/** - * returns a previously created client. IF be used if the garbage collector -removes the client instance on the app side. Is NOT thread safe relative to -login, newClient, or newPrecannedClient - */ -FOUNDATION_EXPORT BindingsClient* _Nullable BindingsGetClientSingleton(void); - -/** - * GetDependencies returns the api DEPENDENCIES - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetDependencies(void); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetE2EParams(NSError* _Nullable* _Nullable error); - -/** - * GetGitVersion rturns the api GITVERSION - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetGitVersion(void); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetNetworkParams(NSError* _Nullable* _Nullable error); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetUnsafeParams(NSError* _Nullable* _Nullable error); - -/** - * GetVersion returns the api SEMVER - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetVersion(void); - -/** - * InitializeBackup starts the backup processes that returns backup updates when -they occur. Any time an event occurs that changes the contents of the backup, -such as adding or deleting a contact, the backup is triggered and an -encrypted backup is generated and returned on the updateBackupCb callback. -Call this function only when enabling backup if it has not already been -initialized or when the user wants to change their password. -To resume backup process on app recovery, use ResumeBackup. - */ -FOUNDATION_EXPORT BindingsBackup* _Nullable BindingsInitializeBackup(NSString* _Nullable password, id<BindingsUpdateBackupFunc> _Nullable updateBackupCb, BindingsClient* _Nullable c, NSError* _Nullable* _Nullable error); - -/** - * LoadSecretWithMnemonic loads the secret stored from the call to -StoreSecretWithMnemonic. The path given should be the same filepath -as the path given in StoreSecretWithMnemonic. There should be a file -in this path called ".recovery". This operation is not tied -to client operations, as the user will not have a client when trying to -recover their account. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsLoadSecretWithMnemonic(NSString* _Nullable mnemonic, NSString* _Nullable path, NSError* _Nullable* _Nullable error); - -/** - * sets level of logging. All logs the set level and above will be displayed -options are: - TRACE - 0 - DEBUG - 1 - INFO - 2 - WARN - 3 - ERROR - 4 - CRITICAL - 5 - FATAL - 6 -The default state without updates is: INFO - */ -FOUNDATION_EXPORT BOOL BindingsLogLevel(long level, NSError* _Nullable* _Nullable error); - -/** - * Login will load an existing client from the storageDir -using the password. This will fail if the client doesn't exist or -the password is incorrect. -The password is passed as a byte array so that it can be cleared from -memory and stored as securely as possible using the memguard library. -Login does not block on network connection, and instead loads and -starts subprocesses to perform network operations. - */ -FOUNDATION_EXPORT BindingsClient* _Nullable BindingsLogin(NSString* _Nullable storageDir, NSData* _Nullable password, NSString* _Nullable parameters, NSError* _Nullable* _Nullable error); - -/** - * MakeIdList creates a new empty IdList. - */ -FOUNDATION_EXPORT BindingsIdList* _Nullable BindingsMakeIdList(void); - -FOUNDATION_EXPORT BindingsIntList* _Nullable BindingsMakeIntList(void); - -/** - * NewClient creates client storage, generates keys, connects, and registers -with the network. Note that this does not register a username/identity, but -merely creates a new cryptographic identity for adding such information -at a later date. - -Users of this function should delete the storage directory on error. - */ -FOUNDATION_EXPORT BOOL BindingsNewClient(NSString* _Nullable network, NSString* _Nullable storageDir, NSData* _Nullable password, NSString* _Nullable regCode, NSError* _Nullable* _Nullable error); - -/** - * NewClientFromBackup constructs a new Client from an encrypted backup. The backup -is decrypted using the backupPassphrase. On success a successful client creation, -the function will return a JSON encoded list of the E2E partners -contained in the backup and a json-encoded string of the parameters stored in the backup - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsNewClientFromBackup(NSString* _Nullable ndfJSON, NSString* _Nullable storageDir, NSData* _Nullable sessionPassword, NSData* _Nullable backupPassphrase, NSData* _Nullable backupFileContents, NSError* _Nullable* _Nullable error); - -/** - * NewDummyTrafficManager creates a DummyTraffic manager and initialises the -dummy traffic send thread. Note that the manager does not start sending dummy -traffic until its status is set to true using DummyTraffic.SetStatus. -The maxNumMessages is the upper bound of the random number of messages sent -each send. avgSendDeltaMS is the average duration, in milliseconds, to wait -between sends. Sends occur every avgSendDeltaMS +/- a random duration with an -upper bound of randomRangeMS. - */ -FOUNDATION_EXPORT BindingsDummyTraffic* _Nullable BindingsNewDummyTrafficManager(BindingsClient* _Nullable client, long maxNumMessages, long avgSendDeltaMS, long randomRangeMS, NSError* _Nullable* _Nullable error); - -/** - * fact object -creates a new fact. The factType must be either: - 0 - Username - 1 - Email - 2 - Phone Number -The fact must be well formed for the type and must not include commas or -semicolons. If it is not well formed, it will be rejected. Phone numbers -must have the two letter country codes appended. For the complete set of -validation, see /elixxir/primitives/fact/fact.go - */ -FOUNDATION_EXPORT BindingsFact* _Nullable BindingsNewFact(long factType, NSString* _Nullable factStr, NSError* _Nullable* _Nullable error); - -/** - * FactList - */ -FOUNDATION_EXPORT BindingsFactList* _Nullable BindingsNewFactList(void); - -/** - * NewFileTransferManager creates a new file transfer manager and starts the -sending and receiving threads. The receiveFunc is called everytime a new file -transfer is received. -The parameters string contains file transfer network configuration options -and is a JSON formatted string of the fileTransfer.Params object. If it is -left empty, then defaults are used. It is highly recommended that defaults -are used. If it is set, it must match the following format: - {"MaxThroughput":150000,"SendTimeout":500000000} -MaxThroughput is in bytes/sec and SendTimeout is in nanoseconds. - */ -FOUNDATION_EXPORT BindingsFileTransfer* _Nullable BindingsNewFileTransferManager(BindingsClient* _Nullable client, id<BindingsFileTransferReceiveFunc> _Nullable receiveFunc, NSString* _Nullable parameters, NSError* _Nullable* _Nullable error); - -/** - * NewGroupManager creates a new group chat manager. - */ -FOUNDATION_EXPORT BindingsGroupChat* _Nullable BindingsNewGroupManager(BindingsClient* _Nullable client, id<BindingsGroupRequestFunc> _Nullable requestFunc, id<BindingsGroupReceiveFunc> _Nullable receiveFunc, NSError* _Nullable* _Nullable error); - -/** - * NewPrecannedClient creates an insecure user with predetermined keys with nodes -It creates client storage, generates keys, connects, and registers -with the network. Note that this does not register a username/identity, but -merely creates a new cryptographic identity for adding such information -at a later date. - -Users of this function should delete the storage directory on error. - */ -FOUNDATION_EXPORT BOOL BindingsNewPrecannedClient(long precannedID, NSString* _Nullable network, NSString* _Nullable storageDir, NSData* _Nullable password, NSError* _Nullable* _Nullable error); - -/** - * NewUserDiscovery returns a new user discovery object. Only call this once. It must be called -after StartNetworkFollower is called and will fail if the network has never -been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscovery(BindingsClient* _Nullable client, NSError* _Nullable* _Nullable error); - -/** - * NewUserDiscoveryFromBackup returns a new user discovery object. It -wil set up the manager with the backup data. Pass into it the backed up -facts, one email and phone number each. This will add the registered facts -to the backed Store. Any one of these fields may be empty, -however both fields being empty will cause an error. Any other fact that is not -an email or phone number will return an error. You may only add a fact for the -accepted types once each. If you attempt to back up a fact type that has already -been backed up, an error will be returned. Anytime an error is returned, it means -the backup was not successful. -NOTE: Do not use this as a direct store operation. This feature is intended to add facts -to a backend store that have ALREADY BEEN REGISTERED on the account. -THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend. -Only call this once. It must be called after StartNetworkFollower -is called and will fail if the network has never been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscoveryFromBackup(BindingsClient* _Nullable client, NSString* _Nullable email, NSString* _Nullable phone, NSError* _Nullable* _Nullable error); - -/** - * NotificationsForMe Check if a notification received is for me -It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller, -a Type, and a source. These are as follows: - TYPE SOURCE DESCRIPTION - "default" recipient user ID A message with no association - "request" sender user ID A channel request has been received - "reset" sender user ID A channel reset has been received - "confirm" sender user ID A channel request has been accepted - "silent" sender user ID A message which should not be notified on - "e2e" sender user ID reception of an E2E message - "group" group ID reception of a group chat message - "endFT" sender user ID Last message sent confirming end of file transfer - "groupRQ" sender user ID Request from sender to join a group chat - */ -FOUNDATION_EXPORT BindingsManyNotificationForMeReport* _Nullable BindingsNotificationsForMe(NSString* _Nullable notifCSV, NSString* _Nullable preimages, NSError* _Nullable* _Nullable error); - -/** - * RegisterLogWriter registers a callback on which logs are written. - */ -FOUNDATION_EXPORT void BindingsRegisterLogWriter(id<BindingsLogWriter> _Nullable writer); - -/** - * RestoreContactsFromBackup takes as input the jason output of the -`NewClientFromBackup` function, unmarshals it into IDs, looks up -each ID in user discovery, and initiates a session reset request. -This function will not return until every id in the list has been sent a -request. It should be called again and again until it completes. -xxDK users should not use this function. This function is used by -the mobile phone apps and are not intended to be part of the xxDK. It -should be treated as internal functions specific to the phone apps. - */ -FOUNDATION_EXPORT BindingsRestoreContactsReport* _Nullable BindingsRestoreContactsFromBackup(NSData* _Nullable backupPartnerIDs, BindingsClient* _Nullable client, BindingsUserDiscovery* _Nullable udManager, id<BindingsLookupCallback> _Nullable lookupCB, id<BindingsRestoreContactsUpdater> _Nullable updatesCb); - -/** - * ResumeBackup starts the backup processes back up with a new callback after it -has been initialized. -Call this function only when resuming a backup that has already been -initialized or to replace the callback. -To start the backup for the first time or to use a new password, use -InitializeBackup. - */ -FOUNDATION_EXPORT BindingsBackup* _Nullable BindingsResumeBackup(id<BindingsUpdateBackupFunc> _Nullable cb, BindingsClient* _Nullable c, NSError* _Nullable* _Nullable error); - -/** - * SetTimeSource sets the network time to a custom source. - */ -FOUNDATION_EXPORT void BindingsSetTimeSource(id<BindingsTimeSource> _Nullable timeNow); - -/** - * StoreSecretWithMnemonic stores the secret tied with the mnemonic to storage. -Unlike other storage operations, this does not use EKV, as that is -intrinsically tied to client operations, which the user will not have while -trying to recover their account. As such, we store the encrypted data -directly, with a specified path. Path will be a valid filepath in which the -recover file will be stored as ".recovery". - -As an example, given "home/user/xxmessenger/storagePath", -the recovery file will be stored at -"home/user/xxmessenger/storagePath/.recovery" - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsStoreSecretWithMnemonic(NSData* _Nullable secret, NSString* _Nullable path, NSError* _Nullable* _Nullable error); - -/** - * Unmarshals a marshaled contact object, returns an error if it fails - */ -FOUNDATION_EXPORT BindingsContact* _Nullable BindingsUnmarshalContact(NSData* _Nullable b, NSError* _Nullable* _Nullable error); - -/** - * Unmarshals a marshaled send report object, returns an error if it fails - */ -FOUNDATION_EXPORT BindingsSendReport* _Nullable BindingsUnmarshalSendReport(NSData* _Nullable b, NSError* _Nullable* _Nullable error); - -/** - * UpdateCommonErrors takes the passed in contents of a JSON file and updates the -errToUserErr map with the contents of the json file. The JSON's expected format -conform with the commented examples provides in errToUserErr above. -NOTE that you should not pass in a file path, but a preloaded JSON file - */ -FOUNDATION_EXPORT BOOL BindingsUpdateCommonErrors(NSString* _Nullable jsonFile, NSError* _Nullable* _Nullable error); - -// skipped function WrapAPIClient with unsupported parameter or return types - - -// skipped function WrapUserDiscovery with unsupported parameter or return types - - -@class BindingsAuthConfirmCallback; - -@class BindingsAuthRequestCallback; - -@class BindingsAuthResetNotificationCallback; - -@class BindingsClientError; - -@class BindingsEventCallbackFunctionObject; - -@class BindingsFileTransferReceiveFunc; - -@class BindingsFileTransferReceivedProgressFunc; - -@class BindingsFileTransferSentProgressFunc; - -@class BindingsGroupReceiveFunc; - -@class BindingsGroupRequestFunc; - -@class BindingsListener; - -@class BindingsLogWriter; - -@class BindingsLookupCallback; - -@class BindingsMessageDeliveryCallback; - -@class BindingsMultiLookupCallback; - -@class BindingsNetworkHealthCallback; - -@class BindingsPreimageNotification; - -@class BindingsRestoreContactsUpdater; - -@class BindingsRoundCompletionCallback; - -@class BindingsRoundEventCallback; - -@class BindingsSearchCallback; - -@class BindingsSingleSearchCallback; - -@class BindingsTimeSource; - -@class BindingsUpdateBackupFunc; - -/** - * AuthConfirmCallback notifies the register whenever they receive an auth -request confirmation - */ -@interface BindingsAuthConfirmCallback : NSObject <goSeqRefInterface, BindingsAuthConfirmCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)partner; -@end - -/** - * AuthRequestCallback notifies the register whenever they receive an auth -request - */ -@interface BindingsAuthRequestCallback : NSObject <goSeqRefInterface, BindingsAuthRequestCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -/** - * AuthRequestCallback notifies the register whenever they receive an auth -request - */ -@interface BindingsAuthResetNotificationCallback : NSObject <goSeqRefInterface, BindingsAuthResetNotificationCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@interface BindingsClientError : NSObject <goSeqRefInterface, BindingsClientError> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)report:(NSString* _Nullable)source message:(NSString* _Nullable)message trace:(NSString* _Nullable)trace; -@end - -/** - * EventCallbackFunctionObject bindings interface which contains function -that implements the EventCallbackFunction - */ -@interface BindingsEventCallbackFunctionObject : NSObject <goSeqRefInterface, BindingsEventCallbackFunctionObject> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)reportEvent:(long)priority category:(NSString* _Nullable)category evtType:(NSString* _Nullable)evtType details:(NSString* _Nullable)details; -@end - -/** - * FileTransferReceiveFunc contains a function callback that notifies the -receiver of an incoming file transfer. It is called on the reception of the -initial file transfer message. - */ -@interface BindingsFileTransferReceiveFunc : NSObject <goSeqRefInterface, BindingsFileTransferReceiveFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)receiveCallback:(NSData* _Nullable)tid fileName:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType sender:(NSData* _Nullable)sender size:(long)size preview:(NSData* _Nullable)preview; -@end - -/** - * FileTransferReceivedProgressFunc contains a function callback that tracks the -progress of receiving a file. It is called when a file part is received, the -transfer completes, or on error. - */ -@interface BindingsFileTransferReceivedProgressFunc : NSObject <goSeqRefInterface, BindingsFileTransferReceivedProgressFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)receivedProgressCallback:(BOOL)completed received:(long)received total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -/** - * FileTransferSentProgressFunc contains a function callback that tracks the -progress of sending a file. It is called when a file part is sent, a file -part arrives, the transfer completes, or on error. - */ -@interface BindingsFileTransferSentProgressFunc : NSObject <goSeqRefInterface, BindingsFileTransferSentProgressFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)sentProgressCallback:(BOOL)completed sent:(long)sent arrived:(long)arrived total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -/** - * GroupReceiveFunc contains a function callback that is called when a group -message is received. - */ -@interface BindingsGroupReceiveFunc : NSObject <goSeqRefInterface, BindingsGroupReceiveFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)groupReceiveCallback:(BindingsGroupMessageReceive* _Nullable)msg; -@end - -/** - * GroupRequestFunc contains a function callback that is called when a group -request is received. - */ -@interface BindingsGroupRequestFunc : NSObject <goSeqRefInterface, BindingsGroupRequestFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)groupRequestCallback:(BindingsGroup* _Nullable)g; -@end - -/** - * Listener provides a callback to hear a message -An object implementing this interface can be called back when the client -gets a message of the type that the registerer specified at registration -time. - */ -@interface BindingsListener : NSObject <goSeqRefInterface, BindingsListener> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * Hear is called to receive a message in the UI - */ -- (void)hear:(BindingsMessage* _Nullable)message; -/** - * Returns a name, used for debugging - */ -- (NSString* _Nonnull)name; -@end - -@interface BindingsLogWriter : NSObject <goSeqRefInterface, BindingsLogWriter> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)log:(NSString* _Nullable)p0; -@end - -/** - * LookupCallback returns the result of a single lookup - */ -@interface BindingsLookupCallback : NSObject <goSeqRefInterface, BindingsLookupCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -/** - * MessageDeliveryCallback gets called on the determination if all events -related to a message send were successful. - */ -@interface BindingsMessageDeliveryCallback : NSObject <goSeqRefInterface, BindingsMessageDeliveryCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(NSData* _Nullable)msgID delivered:(BOOL)delivered timedOut:(BOOL)timedOut roundResults:(NSData* _Nullable)roundResults; -@end - -/** - * MultiLookupCallback returns the result of many parallel lookups - */ -@interface BindingsMultiLookupCallback : NSObject <goSeqRefInterface, BindingsMultiLookupCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContactList* _Nullable)Succeeded failed:(BindingsIdList* _Nullable)failed errors:(NSString* _Nullable)errors; -@end - -/** - * A callback when which is used to receive notification if network health -changes - */ -@interface BindingsNetworkHealthCallback : NSObject <goSeqRefInterface, BindingsNetworkHealthCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BOOL)p0; -@end - -@interface BindingsPreimageNotification : NSObject <goSeqRefInterface, BindingsPreimageNotification> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)notify:(NSData* _Nullable)identity deleted:(BOOL)deleted; -@end - -/** - * RestoreContactsUpdater interface provides a callback function -for receiving update information from RestoreContactsFromBackup. - */ -@interface BindingsRestoreContactsUpdater : NSObject <goSeqRefInterface, BindingsRestoreContactsUpdater> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * RestoreContactsCallback is called to report the current # of contacts -that have been found and how many have been restored -against the total number that need to be -processed. If an error occurs it it set on the err variable as a -plain string. - */ -- (void)restoreContactsCallback:(long)numFound numRestored:(long)numRestored total:(long)total err:(NSString* _Nullable)err; -@end - -/** - * RoundCompletionCallback is returned when the completion of a round is known. - */ -@interface BindingsRoundCompletionCallback : NSObject <goSeqRefInterface, BindingsRoundCompletionCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(long)rid success:(BOOL)success timedOut:(BOOL)timedOut; -@end - -/** - * RoundEventCallback handles waiting on the exact state of a round on -the cMix network. - */ -@interface BindingsRoundEventCallback : NSObject <goSeqRefInterface, BindingsRoundEventCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(long)rid state:(long)state timedOut:(BOOL)timedOut; -@end - -/** - * SearchCallback returns the result of a search - */ -@interface BindingsSearchCallback : NSObject <goSeqRefInterface, BindingsSearchCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContactList* _Nullable)contacts error:(NSString* _Nullable)error; -@end - -/** - * SingleSearchCallback returns the result of a single search - */ -@interface BindingsSingleSearchCallback : NSObject <goSeqRefInterface, BindingsSingleSearchCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@interface BindingsTimeSource : NSObject <goSeqRefInterface, BindingsTimeSource> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (int64_t)nowMs; -@end - -/** - * UpdateBackupFunc contains a function callback that returns new backups. - */ -@interface BindingsUpdateBackupFunc : NSObject <goSeqRefInterface, BindingsUpdateBackupFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)updateBackup:(NSData* _Nullable)encryptedBackup; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/Universe.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/Universe.objc.h deleted file mode 100644 index 019e7502d581983722a15bf30799e85cbc5dd766..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/Universe.objc.h +++ /dev/null @@ -1,29 +0,0 @@ -// Objective-C API for talking to Go package. -// gobind -lang=objc -// -// File is generated by gobind. Do not edit. - -#ifndef __Universe_H__ -#define __Universe_H__ - -@import Foundation; -#include "ref.h" - -@protocol Universeerror; -@class Universeerror; - -@protocol Universeerror <NSObject> -- (NSString* _Nonnull)error; -@end - -@class Universeerror; - -@interface Universeerror : NSError <goSeqRefInterface, Universeerror> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (NSString* _Nonnull)error; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/ref.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/ref.h deleted file mode 100644 index b8036a4d85c7387f3def61473a071b5d8c4c8208..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/ref.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef __GO_REF_HDR__ -#define __GO_REF_HDR__ - -#include <Foundation/Foundation.h> - -// GoSeqRef is an object tagged with an integer for passing back and -// forth across the language boundary. A GoSeqRef may represent either -// an instance of a Go object, or an Objective-C object passed to Go. -// The explicit allocation of a GoSeqRef is used to pin a Go object -// when it is passed to Objective-C. The Go seq package maintains a -// reference to the Go object in a map keyed by the refnum along with -// a reference count. When the reference count reaches zero, the Go -// seq package will clear the corresponding entry in the map. -@interface GoSeqRef : NSObject { -} -@property(readonly) int32_t refnum; -@property(strong) id obj; // NULL when representing a Go object. - -// new GoSeqRef object to proxy a Go object. The refnum must be -// provided from Go side. -- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj; - -- (int32_t)incNum; - -@end - -@protocol goSeqRefInterface --(GoSeqRef*) _ref; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Modules/module.modulemap b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Modules/module.modulemap deleted file mode 100644 index 4316a5b24058edfc18ffb2dc7f7a982e8353441a..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Modules/module.modulemap +++ /dev/null @@ -1,8 +0,0 @@ -framework module "Bindings" { - header "ref.h" - header "Bindings.objc.h" - header "Universe.objc.h" - header "Bindings.h" - - export * -} \ No newline at end of file diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Resources/Info.plist b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Resources/Info.plist deleted file mode 100644 index 0d1a4b8ab9b1fc8e9357197398f73353470cb636..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Resources/Info.plist +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> - <plist version="1.0"> - <dict> - </dict> - </plist> diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Bindings b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Bindings deleted file mode 100644 index 8d78f84d50a0b0719b795f7b342f6109c2b848ec..0000000000000000000000000000000000000000 Binary files a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Bindings and /dev/null differ diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Bindings.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Bindings.h deleted file mode 100644 index 8906a7da239705b790cb2bb64de92f806640cb38..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Bindings.h +++ /dev/null @@ -1,13 +0,0 @@ - -// Objective-C API for talking to the following Go packages -// -// gitlab.com/elixxir/client/bindings -// -// File is generated by gomobile bind. Do not edit. -#ifndef __Bindings_FRAMEWORK_H__ -#define __Bindings_FRAMEWORK_H__ - -#include "Bindings.objc.h" -#include "Universe.objc.h" - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Bindings.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Bindings.objc.h deleted file mode 100644 index 32bf6d116888f787ced27b01b95cb4e1b2c1138b..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Bindings.objc.h +++ /dev/null @@ -1,2083 +0,0 @@ -// Objective-C API for talking to gitlab.com/elixxir/client/bindings Go package. -// gobind -lang=objc gitlab.com/elixxir/client/bindings -// -// File is generated by gobind. Do not edit. - -#ifndef __Bindings_H__ -#define __Bindings_H__ - -@import Foundation; -#include "ref.h" -#include "Universe.objc.h" - - -@class BindingsBackup; -@class BindingsBackupReport; -@class BindingsClient; -@class BindingsContact; -@class BindingsContactList; -@class BindingsDummyTraffic; -@class BindingsFact; -@class BindingsFactList; -@class BindingsFilePartTracker; -@class BindingsFileTransfer; -@class BindingsGroup; -@class BindingsGroupChat; -@class BindingsGroupMember; -@class BindingsGroupMembership; -@class BindingsGroupMessageReceive; -@class BindingsGroupReportDisk; -@class BindingsGroupSendReport; -@class BindingsIdList; -@class BindingsIntList; -@class BindingsManyNotificationForMeReport; -@class BindingsMessage; -@class BindingsNewGroupReport; -@class BindingsNodeRegistrationsStatus; -@class BindingsNotificationForMeReport; -@class BindingsRestoreContactsReport; -@class BindingsRoundList; -@class BindingsSendReport; -@class BindingsSendReportDisk; -@class BindingsUnregister; -@class BindingsUser; -@class BindingsUserDiscovery; -@protocol BindingsAuthConfirmCallback; -@class BindingsAuthConfirmCallback; -@protocol BindingsAuthRequestCallback; -@class BindingsAuthRequestCallback; -@protocol BindingsAuthResetNotificationCallback; -@class BindingsAuthResetNotificationCallback; -@protocol BindingsClientError; -@class BindingsClientError; -@protocol BindingsEventCallbackFunctionObject; -@class BindingsEventCallbackFunctionObject; -@protocol BindingsFileTransferReceiveFunc; -@class BindingsFileTransferReceiveFunc; -@protocol BindingsFileTransferReceivedProgressFunc; -@class BindingsFileTransferReceivedProgressFunc; -@protocol BindingsFileTransferSentProgressFunc; -@class BindingsFileTransferSentProgressFunc; -@protocol BindingsGroupReceiveFunc; -@class BindingsGroupReceiveFunc; -@protocol BindingsGroupRequestFunc; -@class BindingsGroupRequestFunc; -@protocol BindingsListener; -@class BindingsListener; -@protocol BindingsLogWriter; -@class BindingsLogWriter; -@protocol BindingsLookupCallback; -@class BindingsLookupCallback; -@protocol BindingsMessageDeliveryCallback; -@class BindingsMessageDeliveryCallback; -@protocol BindingsMultiLookupCallback; -@class BindingsMultiLookupCallback; -@protocol BindingsNetworkHealthCallback; -@class BindingsNetworkHealthCallback; -@protocol BindingsPreimageNotification; -@class BindingsPreimageNotification; -@protocol BindingsRestoreContactsUpdater; -@class BindingsRestoreContactsUpdater; -@protocol BindingsRoundCompletionCallback; -@class BindingsRoundCompletionCallback; -@protocol BindingsRoundEventCallback; -@class BindingsRoundEventCallback; -@protocol BindingsSearchCallback; -@class BindingsSearchCallback; -@protocol BindingsSingleSearchCallback; -@class BindingsSingleSearchCallback; -@protocol BindingsTimeSource; -@class BindingsTimeSource; -@protocol BindingsUpdateBackupFunc; -@class BindingsUpdateBackupFunc; - -@protocol BindingsAuthConfirmCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)partner; -@end - -@protocol BindingsAuthRequestCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@protocol BindingsAuthResetNotificationCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@protocol BindingsClientError <NSObject> -- (void)report:(NSString* _Nullable)source message:(NSString* _Nullable)message trace:(NSString* _Nullable)trace; -@end - -@protocol BindingsEventCallbackFunctionObject <NSObject> -- (void)reportEvent:(long)priority category:(NSString* _Nullable)category evtType:(NSString* _Nullable)evtType details:(NSString* _Nullable)details; -@end - -@protocol BindingsFileTransferReceiveFunc <NSObject> -- (void)receiveCallback:(NSData* _Nullable)tid fileName:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType sender:(NSData* _Nullable)sender size:(long)size preview:(NSData* _Nullable)preview; -@end - -@protocol BindingsFileTransferReceivedProgressFunc <NSObject> -- (void)receivedProgressCallback:(BOOL)completed received:(long)received total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -@protocol BindingsFileTransferSentProgressFunc <NSObject> -- (void)sentProgressCallback:(BOOL)completed sent:(long)sent arrived:(long)arrived total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -@protocol BindingsGroupReceiveFunc <NSObject> -- (void)groupReceiveCallback:(BindingsGroupMessageReceive* _Nullable)msg; -@end - -@protocol BindingsGroupRequestFunc <NSObject> -- (void)groupRequestCallback:(BindingsGroup* _Nullable)g; -@end - -@protocol BindingsListener <NSObject> -/** - * Hear is called to receive a message in the UI - */ -- (void)hear:(BindingsMessage* _Nullable)message; -/** - * Returns a name, used for debugging - */ -- (NSString* _Nonnull)name; -@end - -@protocol BindingsLogWriter <NSObject> -- (void)log:(NSString* _Nullable)p0; -@end - -@protocol BindingsLookupCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@protocol BindingsMessageDeliveryCallback <NSObject> -- (void)eventCallback:(NSData* _Nullable)msgID delivered:(BOOL)delivered timedOut:(BOOL)timedOut roundResults:(NSData* _Nullable)roundResults; -@end - -@protocol BindingsMultiLookupCallback <NSObject> -- (void)callback:(BindingsContactList* _Nullable)Succeeded failed:(BindingsIdList* _Nullable)failed errors:(NSString* _Nullable)errors; -@end - -@protocol BindingsNetworkHealthCallback <NSObject> -- (void)callback:(BOOL)p0; -@end - -@protocol BindingsPreimageNotification <NSObject> -- (void)notify:(NSData* _Nullable)identity deleted:(BOOL)deleted; -@end - -@protocol BindingsRestoreContactsUpdater <NSObject> -/** - * RestoreContactsCallback is called to report the current # of contacts -that have been found and how many have been restored -against the total number that need to be -processed. If an error occurs it it set on the err variable as a -plain string. - */ -- (void)restoreContactsCallback:(long)numFound numRestored:(long)numRestored total:(long)total err:(NSString* _Nullable)err; -@end - -@protocol BindingsRoundCompletionCallback <NSObject> -- (void)eventCallback:(long)rid success:(BOOL)success timedOut:(BOOL)timedOut; -@end - -@protocol BindingsRoundEventCallback <NSObject> -- (void)eventCallback:(long)rid state:(long)state timedOut:(BOOL)timedOut; -@end - -@protocol BindingsSearchCallback <NSObject> -- (void)callback:(BindingsContactList* _Nullable)contacts error:(NSString* _Nullable)error; -@end - -@protocol BindingsSingleSearchCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@protocol BindingsTimeSource <NSObject> -- (int64_t)nowMs; -@end - -@protocol BindingsUpdateBackupFunc <NSObject> -- (void)updateBackup:(NSData* _Nullable)encryptedBackup; -@end - -@interface BindingsBackup : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * AddJson stores a passed in json string in the backup structure - */ -- (void)addJson:(NSString* _Nullable)json; -/** - * IsBackupRunning returns true if the backup has been initialized and is -running. Returns false if it has been stopped. - */ -- (BOOL)isBackupRunning; -/** - * StopBackup stops the backup processes and deletes the user's password from -storage. To enable backups again, call InitializeBackup. - */ -- (BOOL)stopBackup:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsBackupReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field BackupReport.RestoredContacts with unsupported type: []*gitlab.com/xx_network/primitives/id.ID - -@property (nonatomic) NSString* _Nonnull params; -@end - -/** - * BindingsClient wraps the api.Client, implementing additional functions -to support the gomobile Client interface - */ -@interface BindingsClient : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BOOL)confirmAuthenticatedChannel:(NSData* _Nullable)recipientMarshaled ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteAllRequests clears all requests from Client's auth storage. - */ -- (BOOL)deleteAllRequests:(NSError* _Nullable* _Nullable)error; -/** - * DeleteContact is a function which removes a contact from Client's storage - */ -- (BOOL)deleteContact:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteReceiveRequests clears receive requests from Client's auth storage. - */ -- (BOOL)deleteReceiveRequests:(NSError* _Nullable* _Nullable)error; -/** - * DeleteRequest will delete a request, agnostic of request type -for the given partner ID. If no request exists for this -partner ID an error will be returned. - */ -- (BOOL)deleteRequest:(NSData* _Nullable)requesterUserId error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteSentRequests clears sent requests from Client's auth storage. - */ -- (BOOL)deleteSentRequests:(NSError* _Nullable* _Nullable)error; -// skipped method Client.GetInternalClient with unsupported parameter or return types - -/** - * GetNodeRegistrationStatus returns a struct with the number of nodes the -client is registered with and the number total. - */ -- (BindingsNodeRegistrationsStatus* _Nullable)getNodeRegistrationStatus:(NSError* _Nullable* _Nullable)error; -/** - * GetPartners returns a list of - */ -- (NSData* _Nullable)getPartners:(NSError* _Nullable* _Nullable)error; -/** - * GetPreferredBins returns the geographic bin or bins that the provided two -character country code is a part of. The bins are returned as CSV. - */ -- (NSString* _Nonnull)getPreferredBins:(NSString* _Nullable)countryCode error:(NSError* _Nullable* _Nullable)error; -- (NSString* _Nonnull)getPreimages:(NSData* _Nullable)identity; -// skipped method Client.GetRateLimitParams with unsupported parameter or return types - -- (NSString* _Nonnull)getRelationshipFingerprint:(NSData* _Nullable)partnerID error:(NSError* _Nullable* _Nullable)error; -/** - * Returns a user object from which all information about the current user -can be gleaned - */ -- (BindingsUser* _Nullable)getUser; -/** - * HasRunningProcessies checks if any background threads are running. -returns true if none are running. This is meant to be -used when NetworkFollowerStatus() returns Stopping. -Due to the handling of comms on iOS, where the OS can -block indefiently, it may not enter the stopped -state apropreatly. This can be used instead. - */ -- (BOOL)hasRunningProcessies; -/** - * returns true if the network is read to be in a healthy state where -messages can be sent - */ -- (BOOL)isNetworkHealthy; -- (BindingsContact* _Nullable)makePrecannedAuthenticatedChannel:(long)precannedID error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the state of the network follower. Returns: -Stopped - 0 -Starting - 1000 -Running - 2000 -Stopping - 3000 - */ -- (long)networkFollowerStatus; -- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetNotificationCallback> _Nullable)reset; -/** - * RegisterClientErrorCallback registers the callback to handle errors from the -long running threads controlled by StartNetworkFollower and StopNetworkFollower - */ -- (void)registerClientErrorCallback:(id<BindingsClientError> _Nullable)clientError; -/** - * RegisterEventCallback records the given function to receive -ReportableEvent objects. It returns the internal index -of the callback so that it can be deleted later. - */ -- (BOOL)registerEventCallback:(NSString* _Nullable)name myObj:(id<BindingsEventCallbackFunctionObject> _Nullable)myObj error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterForNotifications accepts firebase messaging token - */ -- (BOOL)registerForNotifications:(NSString* _Nullable)token error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterListener records and installs a listener for messages -matching specific uid, msgType, and/or username -Returns a ListenerUnregister interface which can be - -to register for any userID, pass in an id with length 0 or an id with -all zeroes - -to register for any message type, pass in a message type of 0 - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types - */ -- (BindingsUnregister* _Nullable)registerListener:(NSData* _Nullable)uid msgType:(long)msgType listener:(id<BindingsListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterNetworkHealthCB registers the network health callback to be called -any time the network health changes. Returns a unique ID that can be used to -unregister the network health callback. - */ -- (int64_t)registerNetworkHealthCB:(id<BindingsNetworkHealthCallback> _Nullable)nhc; -- (void)registerPreimageCallback:(NSData* _Nullable)identity pin:(id<BindingsPreimageNotification> _Nullable)pin; -/** - * RegisterRoundEventsHandler registers a callback interface for round -events. -The rid is the round the event attaches to -The timeoutMS is the number of milliseconds until the event fails, and the -validStates are a list of states (one per byte) on which the event gets -triggered -States: - 0x00 - PENDING (Never seen by client) - 0x01 - PRECOMPUTING - 0x02 - STANDBY - 0x03 - QUEUED - 0x04 - REALTIME - 0x05 - COMPLETED - 0x06 - FAILED -These states are defined in elixxir/primitives/states/state.go - */ -- (BindingsUnregister* _Nullable)registerRoundEventsHandler:(long)rid cb:(id<BindingsRoundEventCallback> _Nullable)cb timeoutMS:(long)timeoutMS il:(BindingsIntList* _Nullable)il; -- (void)replayRequests; -- (BOOL)requestAuthenticatedChannel:(NSData* _Nullable)recipientMarshaled meMarshaled:(NSData* _Nullable)meMarshaled message:(NSString* _Nullable)message ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -- (BOOL)resetSession:(NSData* _Nullable)recipientMarshaled meMarshaled:(NSData* _Nullable)meMarshaled message:(NSString* _Nullable)message ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * This will return the round the message was sent on if it is successfully sent -This can be used to register a round event to learn about message delivery. -on failure a round id of -1 is returned - */ -- (BOOL)sendCmix:(NSData* _Nullable)recipient contents:(NSData* _Nullable)contents parameters:(NSString* _Nullable)parameters ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * SendE2E sends an end-to-end payload to the provided recipient with -the provided msgType. Returns the list of rounds in which parts of -the message were sent or an error if it fails. - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types - */ -- (BindingsSendReport* _Nullable)sendE2E:(NSData* _Nullable)recipient payload:(NSData* _Nullable)payload messageType:(long)messageType parameters:(NSString* _Nullable)parameters error:(NSError* _Nullable* _Nullable)error; -/** - * SendUnsafe sends an unencrypted payload to the provided recipient -with the provided msgType. Returns the list of rounds in which parts -of the message were sent or an error if it fails. -NOTE: Do not use this function unless you know what you are doing. -This function always produces an error message in client logging. - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types with custom types - */ -- (BindingsRoundList* _Nullable)sendUnsafe:(NSData* _Nullable)recipient payload:(NSData* _Nullable)payload messageType:(long)messageType parameters:(NSString* _Nullable)parameters error:(NSError* _Nullable* _Nullable)error; -/** - * SetProxiedBins updates the host pool filter that filters out gateways that -are not in one of the specified bins. The provided bins should be CSV. - */ -- (BOOL)setProxiedBins:(NSString* _Nullable)binStringsCSV error:(NSError* _Nullable* _Nullable)error; -/** - * StartNetworkFollower kicks off the tracking of the network. It starts -long running network client threads and returns an object for checking -state and stopping those threads. -Call this when returning from sleep and close when going back to -sleep. -These threads may become a significant drain on battery when offline, ensure -they are stopped if there is no internet access -Threads Started: - - Network Follower (/network/follow.go) - tracks the network events and hands them off to workers for handling - - Historical Round Retrieval (/network/rounds/historical.go) - Retrieves data about rounds which are too old to be stored by the client - - Message Retrieval Worker Group (/network/rounds/retrieve.go) - Requests all messages in a given round from the gateway of the last node - - Message Handling Worker Group (/network/message/handle.go) - Decrypts and partitions messages when signals via the Switchboard - - Health Tracker (/network/health) - Via the network instance tracks the state of the network - - Garbled Messages (/network/message/garbled.go) - Can be signaled to check all recent messages which could be be decoded - Uses a message store on disk for persistence - - Critical Messages (/network/message/critical.go) - Ensures all protocol layer mandatory messages are sent - Uses a message store on disk for persistence - - KeyExchange Trigger (/keyExchange/trigger.go) - Responds to sent rekeys and executes them - - KeyExchange Confirm (/keyExchange/confirm.go) - Responds to confirmations of successful rekey operations - */ -- (BOOL)startNetworkFollower:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * StopNetworkFollower stops the network follower if it is running. -It returns errors if the Follower is in the wrong status to stop or if it -fails to stop it. -if the network follower is running and this fails, the client object will -most likely be in an unrecoverable state and need to be trashed. - */ -- (BOOL)stopNetworkFollower:(NSError* _Nullable* _Nullable)error; -/** - * UnregisterEventCallback deletes the callback identified by the -index. It returns an error if it fails. - */ -- (void)unregisterEventCallback:(NSString* _Nullable)name; -/** - * UnregisterForNotifications unregister user for notifications - */ -- (BOOL)unregisterForNotifications:(NSError* _Nullable* _Nullable)error; -- (void)unregisterNetworkHealthCB:(int64_t)funcID; -- (BOOL)verifyOwnership:(NSData* _Nullable)receivedMarshaled verifiedMarshaled:(NSData* _Nullable)verifiedMarshaled ret0_:(BOOL* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * WaitForMessageDelivery allows the caller to get notified if the rounds a -message was sent in successfully completed. Under the hood, this uses an API -which uses the internal round data, network historical round lookup, and -waiting on network events to determine what has (or will) occur. - -The callbacks will return at timeoutMS if no state update occurs - -This function takes the marshaled send report to ensure a memory leak does -not occur as a result of both sides of the bindings holding a reference to -the same pointer. - */ -- (BOOL)waitForMessageDelivery:(NSData* _Nullable)marshaledSendReport mdc:(id<BindingsMessageDeliveryCallback> _Nullable)mdc timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * WaitForNewtwork will block until either the network is healthy or the -passed timeout. It will return true if the network is healthy - */ -- (BOOL)waitForNetwork:(long)timeoutMS; -/** - * WaitForRoundCompletion allows the caller to get notified if a round -has completed (or failed). Under the hood, this uses an API which uses the internal -round data, network historical round lookup, and waiting on network events -to determine what has (or will) occur. - -The callbacks will return at timeoutMS if no state update occurs - */ -- (BOOL)waitForRoundCompletion:(long)roundID rec:(id<BindingsRoundCompletionCallback> _Nullable)rec timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * contact object - */ -@interface BindingsContact : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped method Contact.GetAPIContact with unsupported parameter or return types - -/** - * GetDHPublicKey returns the public key associated with the Contact. - */ -- (NSData* _Nullable)getDHPublicKey; -/** - * Returns a fact list for adding and getting facts to and from the contact - */ -- (BindingsFactList* _Nullable)getFactList; -/** - * GetID returns the user ID for this user. - */ -- (NSData* _Nullable)getID; -/** - * GetDHPublicKey returns hash of a DH proof of key ownership. - */ -- (NSData* _Nullable)getOwnershipProof; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsContactList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Gets a stored round ID at the given index - */ -- (BindingsContact* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the number of round IDs stored - */ -- (long)len; -@end - -/** - * DummyTraffic contains the file dummy traffic manager. The manager can be used -to set and get the status of the send thread. - */ -@interface BindingsDummyTraffic : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewDummyTrafficManager creates a DummyTraffic manager and initialises the -dummy traffic send thread. Note that the manager does not start sending dummy -traffic until its status is set to true using DummyTraffic.SetStatus. -The maxNumMessages is the upper bound of the random number of messages sent -each send. avgSendDeltaMS is the average duration, in milliseconds, to wait -between sends. Sends occur every avgSendDeltaMS +/- a random duration with an -upper bound of randomRangeMS. - */ -- (nullable instancetype)initManager:(BindingsClient* _Nullable)client maxNumMessages:(long)maxNumMessages avgSendDeltaMS:(long)avgSendDeltaMS randomRangeMS:(long)randomRangeMS; -/** - * GetStatus returns the current state of the dummy traffic send thread. It has -the following return values: - true = send thread is sending dummy messages - false = send thread is paused/stopped and not sending dummy messages -Note that this function does not return the status set by SetStatus directly; -it returns the current status of the send thread, which means any call to -SetStatus will have a small delay before it is returned by GetStatus. - */ -- (BOOL)getStatus; -/** - * SetStatus sets the state of the dummy traffic send thread, which determines -if the thread is running or paused. The possible statuses are: - true = send thread is sending dummy messages - false = send thread is paused/stopped and not sending dummy messages -Returns an error if the channel is full. -Note that this function cannot change the status of the send thread if it has -yet to be started or stopped. - */ -- (BOOL)setStatus:(BOOL)status error:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsFact : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * fact object -creates a new fact. The factType must be either: - 0 - Username - 1 - Email - 2 - Phone Number -The fact must be well formed for the type and must not include commas or -semicolons. If it is not well formed, it will be rejected. Phone numbers -must have the two letter country codes appended. For the complete set of -validation, see /elixxir/primitives/fact/fact.go - */ -- (nullable instancetype)init:(long)factType factStr:(NSString* _Nullable)factStr; -- (NSString* _Nonnull)get; -- (NSString* _Nonnull)stringify; -- (long)type; -@end - -@interface BindingsFactList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * FactList - */ -- (nullable instancetype)init; -- (BOOL)add:(NSString* _Nullable)factData factType:(long)factType error:(NSError* _Nullable* _Nullable)error; -- (BindingsFact* _Nullable)get:(long)i; -- (long)num; -- (NSString* _Nonnull)stringify:(NSError* _Nullable* _Nullable)error; -@end - -/** - * FilePartTracker contains the interfaces.FilePartTracker. - */ -@interface BindingsFilePartTracker : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetNumParts returns the total number of file parts in the transfer. - */ -- (long)getNumParts; -/** - * GetPartStatus returns the status of the file part with the given part number. -The possible values for the status are: -0 = unsent -1 = sent (sender has sent a part, but it has not arrived) -2 = arrived (sender has sent a part, and it has arrived) -3 = received (receiver has received a part) - */ -- (long)getPartStatus:(long)partNum; -@end - -/** - * FileTransfer contains the file transfer manager. - */ -@interface BindingsFileTransfer : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewFileTransferManager creates a new file transfer manager and starts the -sending and receiving threads. The receiveFunc is called everytime a new file -transfer is received. -The parameters string contains file transfer network configuration options -and is a JSON formatted string of the fileTransfer.Params object. If it is -left empty, then defaults are used. It is highly recommended that defaults -are used. If it is set, it must match the following format: - {"MaxThroughput":150000,"SendTimeout":500000000} -MaxThroughput is in bytes/sec and SendTimeout is in nanoseconds. - */ -- (nullable instancetype)initManager:(BindingsClient* _Nullable)client receiveFunc:(id<BindingsFileTransferReceiveFunc> _Nullable)receiveFunc parameters:(NSString* _Nullable)parameters; -/** - * CloseSend deletes a sent file transfer from the sent transfer map and from -storage once a transfer has completed or reached the retry limit. Returns an -error if the transfer has not run out of retries. - */ -- (BOOL)closeSend:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error; -/** - * GetMaxFileNameByteLength returns the maximum length, in bytes, allowed for a -file name. - */ -- (long)getMaxFileNameByteLength; -/** - * GetMaxFilePreviewSize returns the maximum file preview size, in bytes. - */ -- (long)getMaxFilePreviewSize; -/** - * GetMaxFileSize returns the maximum file size, in bytes, allowed to be -transferred. - */ -- (long)getMaxFileSize; -/** - * GetMaxFileTypeByteLength returns the maximum length, in bytes, allowed for a -file type. - */ -- (long)getMaxFileTypeByteLength; -/** - * Receive returns the fully assembled file on the completion of the transfer. -It deletes the transfer from the received transfer map and from storage. -Returns an error if the transfer is not complete, the full file cannot be -verified, or if the transfer cannot be found. - */ -- (NSData* _Nullable)receive:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterReceiveProgressCallback allows for the registration of a callback to -track the progress of an individual received file transfer. The callback will -be called immediately when added to report the current status of the -transfer. It will then call every time a file part is received, the transfer -completes, or an error occurs. It is called at most once ever period, which -means if events occur faster than the period, then they will not be reported -and instead, the progress will be reported once at the end of the period. -Once the callback reports that the transfer has completed, the recipient -can get the full file by calling Receive. -The period is specified in milliseconds. - */ -- (BOOL)registerReceiveProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferReceivedProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterSendProgressCallback allows for the registration of a callback to -track the progress of an individual sent file transfer. The callback will be -called immediately when added to report the current status of the transfer. -It will then call every time a file part is sent, a file part arrives, the -transfer completes, or an error occurs. It is called at most once every -period, which means if events occur faster than the period, then they will -not be reported and instead, the progress will be reported once at the end of -the period. -The period is specified in milliseconds. - */ -- (BOOL)registerSendProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -/** - * Send sends a file to the recipient. The sender must have an E2E relationship -with the recipient. -The file name is the name of the file to show a user. It has a max length of -48 bytes. -The file type identifies what type of file is being sent. It has a max length -of 8 bytes. -The file data cannot be larger than 256 kB -The retry float is the total amount of data to send relative to the data -size. Data will be resent on error and will resend up to [(1 + retry) * -fileSize]. -The preview stores a preview of the data (such as a thumbnail) and is -capped at 4 kB in size. -Returns a unique transfer ID used to identify the transfer. -PeriodMS is the duration, in milliseconds, to wait between progress callback -calls. Set this large enough to prevent spamming. - */ -- (NSData* _Nullable)send:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType fileData:(NSData* _Nullable)fileData recipientID:(NSData* _Nullable)recipientID retry:(float)retry preview:(NSData* _Nullable)preview progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * Group structure contains the identifying and membership information of a -group chat. - */ -@interface BindingsGroup : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetCreatedMS returns the time the group was created in milliseconds. This is -also the time the group requests were sent. - */ -- (int64_t)getCreatedMS; -/** - * GetCreatedNano returns the time the group was created in nanoseconds. This is -also the time the group requests were sent. - */ -- (int64_t)getCreatedNano; -/** - * GetID return the 33-byte unique group ID. - */ -- (NSData* _Nullable)getID; -/** - * GetInitMessage returns initial message sent with the group request. - */ -- (NSData* _Nullable)getInitMessage; -/** - * GetMembership returns a list of contacts, one for each member in the group. -The list is in order; the first contact is the leader/creator of the group. -All subsequent members are ordered by their ID. - */ -- (BindingsGroupMembership* _Nullable)getMembership; -/** - * GetName returns the name set by the user for the group. - */ -- (NSData* _Nullable)getName; -/** - * Serialize serializes the Group. - */ -- (NSData* _Nullable)serialize; -@end - -/** - * GroupChat object contains the group chat manager. - */ -@interface BindingsGroupChat : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetGroup returns the group with the group ID. If no group exists, then the -error "failed to find group" is returned. - */ -- (BindingsGroup* _Nullable)getGroup:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * GetGroups returns an IdList containing a list of group IDs that the user is a -part of. - */ -- (BindingsIdList* _Nullable)getGroups; -/** - * JoinGroup allows a user to join a group when they receive a request. The -caller must pass in the serialized bytes of a Group. - */ -- (BOOL)joinGroup:(NSData* _Nullable)serializedGroupData error:(NSError* _Nullable* _Nullable)error; -/** - * LeaveGroup deletes a group so a user no longer has access. - */ -- (BOOL)leaveGroup:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * MakeGroup creates a new group and sends a group request to all members in the -group. The ID of the new group, the rounds the requests were sent on, and the -status of the send are contained in NewGroupReport. - */ -- (BindingsNewGroupReport* _Nullable)makeGroup:(BindingsIdList* _Nullable)membership name:(NSData* _Nullable)name message:(NSData* _Nullable)message; -/** - * NumGroups returns the number of groups the user is a part of. - */ -- (long)numGroups; -/** - * ResendRequest resends a group request to all members in the group. The rounds -they were sent on and the status of the send are contained in NewGroupReport. - */ -- (BindingsNewGroupReport* _Nullable)resendRequest:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * Send sends the message to the specified group. Returns the round the messages -were sent on. - */ -- (BindingsGroupSendReport* _Nullable)send:(NSData* _Nullable)groupIdBytes message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * // -Member Structure -// -GroupMember represents a member in the group membership list. - */ -@interface BindingsGroupMember : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupMember.Member with unsupported type: gitlab.com/elixxir/crypto/group.Member - -// skipped method GroupMember.DeepCopy with unsupported parameter or return types - -// skipped method GroupMember.Equal with unsupported parameter or return types - -/** - * GetDhKey returns the byte representation of the public Diffie–Hellman key of -the member. - */ -- (NSData* _Nullable)getDhKey; -/** - * GetID returns the 33-byte user ID of the member. - */ -- (NSData* _Nullable)getID; -- (NSString* _Nonnull)goString; -- (NSData* _Nullable)serialize; -- (NSString* _Nonnull)string; -@end - -/** - * GroupMembership structure contains a list of members that are part of a -group. The first member is the group leader. - */ -@interface BindingsGroupMembership : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Get returns the member at the index. The member at index 0 is always the -group leader. An error is returned if the index is out of range. - */ -- (BindingsGroupMember* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Len returns the number of members in the group membership. - */ -- (long)len; -@end - -/** - * GroupMessageReceive contains a group message, its ID, and its data that a -user receives. - */ -@interface BindingsGroupMessageReceive : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupMessageReceive.MessageReceive with unsupported type: gitlab.com/elixxir/client/groupChat.MessageReceive - -/** - * GetEphemeralID returns the ephemeral ID of the recipient. - */ -- (int64_t)getEphemeralID; -/** - * GetGroupID returns the 33-byte group ID. - */ -- (NSData* _Nullable)getGroupID; -/** - * GetMessageID returns the message ID. - */ -- (NSData* _Nullable)getMessageID; -/** - * GetPayload returns the message payload. - */ -- (NSData* _Nullable)getPayload; -/** - * GetRecipientID returns the 33-byte user ID of the recipient. - */ -- (NSData* _Nullable)getRecipientID; -/** - * GetRoundID returns the ID of the round the message was sent on. - */ -- (int64_t)getRoundID; -/** - * GetRoundTimestampMS returns the timestamp, in milliseconds, of the round the -message was sent on. - */ -- (int64_t)getRoundTimestampMS; -/** - * GetRoundTimestampNano returns the timestamp, in nanoseconds, of the round the -message was sent on. - */ -- (int64_t)getRoundTimestampNano; -/** - * GetRoundURL returns the ID of the round the message was sent on. - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetSenderID returns the 33-byte user ID of the sender. - */ -- (NSData* _Nullable)getSenderID; -/** - * GetTimestampMS returns the message timestamp in milliseconds. - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message timestamp in nanoseconds. - */ -- (int64_t)getTimestampNano; -- (NSString* _Nonnull)string; -@end - -@interface BindingsGroupReportDisk : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupReportDisk.List with unsupported type: []gitlab.com/xx_network/primitives/id.Round - -@property (nonatomic) NSData* _Nullable grpId; -@property (nonatomic) long status; -@end - -/** - * GroupSendReport is returned when sending a group message. It contains the -round ID sent on and the timestamp of the send. - */ -@interface BindingsGroupSendReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetMessageID returns the ID of the round that the send occurred on. - */ -- (NSData* _Nullable)getMessageID; -/** - * GetRoundID returns the ID of the round that the send occurred on. - */ -- (int64_t)getRoundID; -/** - * GetRoundURL returns the URL of the round that the send occurred on. - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetTimestampMS returns the timestamp of the send in milliseconds. - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the timestamp of the send in nanoseconds. - */ -- (int64_t)getTimestampNano; -@end - -/** - * ID list -IdList contains a list of IDs. - */ -@interface BindingsIdList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Add appends the ID bytes to the end of the list. - */ -- (BOOL)add:(NSData* _Nullable)idBytes error:(NSError* _Nullable* _Nullable)error; -/** - * Get returns the ID at the index. An error is returned if the index is out of -range. - */ -- (NSData* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Len returns the number of IDs in the list. - */ -- (long)len; -@end - -@interface BindingsIntList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (void)add:(long)i; -- (BOOL)get:(long)i ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -- (long)len; -@end - -@interface BindingsManyNotificationForMeReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BindingsNotificationForMeReport* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -- (long)len; -@end - -/** - * Message is a message received from the cMix network in the clear -or that has been decrypted using established E2E keys. - */ -@interface BindingsMessage : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetID returns the id of the message - */ -- (NSData* _Nullable)getID; -/** - * GetMessageType returns the message's type - */ -- (long)getMessageType; -/** - * GetPayload returns the message's payload/contents - */ -- (NSData* _Nullable)getPayload; -/** - * GetRoundId returns the message's round ID - */ -- (int64_t)getRoundId; -/** - * GetRoundTimestampMS returns the message's round timestamp in milliseconds - */ -- (int64_t)getRoundTimestampMS; -/** - * GetRoundTimestampNano returns the message's round timestamp in nanoseconds - */ -- (int64_t)getRoundTimestampNano; -/** - * GetRoundURL returns the message's round URL - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetSender returns the message's sender ID, if available - */ -- (NSData* _Nullable)getSender; -/** - * GetTimestampMS returns the message's timestamp in milliseconds - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message's timestamp in nanoseconds - */ -- (int64_t)getTimestampNano; -@end - -/** - * NewGroupReport is returned when creating a new group and contains the ID of -the group, a list of rounds that the group requests were sent on, and the -status of the send. - */ -@interface BindingsNewGroupReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetError returns the string of an error. -Will be an empty string if no error occured - */ -- (NSString* _Nonnull)getError; -/** - * GetGroup returns the Group. - */ -- (BindingsGroup* _Nullable)getGroup; -/** - * GetRoundList returns the RoundList containing a list of rounds requests were -sent on. - */ -- (BindingsRoundList* _Nullable)getRoundList; -/** - * GetStatus returns the status of the requests sent when creating a new group. -status = 0 an error occurred before any requests could be sent - 1 all requests failed to send (call Resend Group) - 2 some request failed and some succeeded (call Resend Group) - 3, all requests sent successfully (call Resend Group) - */ -- (long)getStatus; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -- (BOOL)unmarshal:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * NodeRegistrationsStatus structure for returning node registration statuses -for bindings. - */ -@interface BindingsNodeRegistrationsStatus : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetRegistered returns the number of nodes registered with the client. - */ -- (long)getRegistered; -/** - * GetTotal return the total of nodes currently in the network. - */ -- (long)getTotal; -@end - -@interface BindingsNotificationForMeReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BOOL)forMe; -- (NSData* _Nullable)source; -- (NSString* _Nonnull)type; -@end - -/** - * RestoreContactsReport is a gomobile friendly report structure -for determining which IDs restored, which failed, and why. - */ -@interface BindingsRestoreContactsReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetErrorAt returns the error string at index - */ -- (NSString* _Nonnull)getErrorAt:(long)index; -/** - * GetFailedAt returns the failed ID at index - */ -- (NSData* _Nullable)getFailedAt:(long)index; -/** - * GetRestoreContactsError returns an error string. Empty if no error. - */ -- (NSString* _Nonnull)getRestoreContactsError; -/** - * GetRestoredAt returns the restored ID at index - */ -- (NSData* _Nullable)getRestoredAt:(long)index; -/** - * LenFailed returns the length of the ID's failed. - */ -- (long)lenFailed; -/** - * LenRestored returns the length of ID's restored. - */ -- (long)lenRestored; -@end - -@interface BindingsRoundList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Gets a stored round ID at the given index - */ -- (BOOL)get:(long)i ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the number of round IDs stored - */ -- (long)len; -@end - -/** - * the send report is the mechanisim by which sendE2E returns a single - */ -@interface BindingsSendReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (NSData* _Nullable)getMessageID; -- (BindingsRoundList* _Nullable)getRoundList; -- (NSString* _Nonnull)getRoundURL; -/** - * GetTimestampMS returns the message's timestamp in milliseconds - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message's timestamp in nanoseconds - */ -- (int64_t)getTimestampNano; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -- (BOOL)unmarshal:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsSendReportDisk : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field SendReportDisk.List with unsupported type: []gitlab.com/xx_network/primitives/id.Round - -@property (nonatomic) NSData* _Nullable mid; -@property (nonatomic) int64_t ts; -@end - -/** - * Generic Unregister - a generic return used for all callbacks which can be -unregistered -Interface which allows the un-registration of a listener - */ -@interface BindingsUnregister : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Call unregisters a callback - */ -- (void)unregister; -@end - -@interface BindingsUser : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BindingsContact* _Nullable)getContact; -- (NSData* _Nullable)getE2EDhPrivateKey; -- (NSData* _Nullable)getE2EDhPublicKey; -- (NSData* _Nullable)getReceptionID; -- (NSData* _Nullable)getReceptionRSAPrivateKeyPem; -- (NSData* _Nullable)getReceptionRSAPublicKeyPem; -- (NSData* _Nullable)getReceptionSalt; -- (NSData* _Nullable)getTransmissionID; -- (NSData* _Nullable)getTransmissionRSAPrivateKeyPem; -- (NSData* _Nullable)getTransmissionRSAPublicKeyPem; -- (NSData* _Nullable)getTransmissionSalt; -- (BOOL)isPrecanned; -@end - -@interface BindingsUserDiscovery : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewUserDiscovery returns a new user discovery object. Only call this once. It must be called -after StartNetworkFollower is called and will fail if the network has never -been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -- (nullable instancetype)init:(BindingsClient* _Nullable)client; -/** - * NewUserDiscoveryFromBackup returns a new user discovery object. It -wil set up the manager with the backup data. Pass into it the backed up -facts, one email and phone number each. This will add the registered facts -to the backed Store. Any one of these fields may be empty, -however both fields being empty will cause an error. Any other fact that is not -an email or phone number will return an error. You may only add a fact for the -accepted types once each. If you attempt to back up a fact type that has already -been backed up, an error will be returned. Anytime an error is returned, it means -the backup was not successful. -NOTE: Do not use this as a direct store operation. This feature is intended to add facts -to a backend store that have ALREADY BEEN REGISTERED on the account. -THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend. -Only call this once. It must be called after StartNetworkFollower -is called and will fail if the network has never been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -- (nullable instancetype)initFromBackup:(BindingsClient* _Nullable)client email:(NSString* _Nullable)email phone:(NSString* _Nullable)phone; -/** - * AddFact adds a fact for the user to user discovery. Will only succeed if the -user is already registered and the system does not have the fact currently -registered for any user. -Will fail if the fact string is not well formed. -This does not complete the fact registration process, it returns a -confirmation id instead. Over the communications system the fact is -associated with, a code will be sent. This confirmation ID needs to be -called along with the code to finalize the fact. - */ -- (NSString* _Nonnull)addFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * ConfirmFact confirms a fact first registered via AddFact. The confirmation ID comes from -AddFact while the code will come over the associated communications system - */ -- (BOOL)confirmFact:(NSString* _Nullable)confirmationID code:(NSString* _Nullable)code error:(NSError* _Nullable* _Nullable)error; -/** - * Lookup the contact object associated with the given userID. The -id is the byte representation of an id. -This will reject if that id is malformed. The LookupCallback will return -the associated contact if it exists. - */ -- (BOOL)lookup:(NSData* _Nullable)idBytes callback:(id<BindingsLookupCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * MultiLookup Looks for the contact object associated with all given userIDs. -The ids are the byte representation of an id stored in an IDList object. -This will reject if that id is malformed or if the indexing on the IDList -object is wrong. The MultiLookupCallback will return with all contacts -returned within the timeout. - */ -- (BOOL)multiLookup:(BindingsIdList* _Nullable)ids callback:(id<BindingsMultiLookupCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * Register registers a user with user discovery. Will return an error if the -network signatures are malformed or if the username is taken. Usernames -cannot be changed after registration at this time. Will fail if the user is -already registered. -Identity does not go over cmix, it occurs over normal communications - */ -- (BOOL)register:(NSString* _Nullable)username error:(NSError* _Nullable* _Nullable)error; -/** - * RemoveFact removes a previously confirmed fact. Will fail if the passed fact string is -not well-formed or if the fact is not associated with this client. -Users cannot remove username facts and must instead remove the user. - */ -- (BOOL)removeFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * RemoveUser deletes a user. The fact sent must be the username. -This function preserves the username forever and makes it -unusable. - */ -- (BOOL)removeUser:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * Search for the passed Facts. The factList is the stringification of a -fact list object, look at /bindings/list.go for more on that object. -This will reject if that object is malformed. The SearchCallback will return -a list of contacts, each having the facts it hit against. -This is NOT intended to be used to search for multiple users at once, that -can have a privacy reduction. Instead, it is intended to be used to search -for a user where multiple pieces of information is known. - */ -- (BOOL)search:(NSString* _Nullable)fl callback:(id<BindingsSearchCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * SearchSingle searches for the passed Facts. The fact is the stringification of a -fact object, look at /bindings/contact.go for more on that object. -This will reject if that object is malformed. The SearchCallback will return -a list of contacts, each having the facts it hit against. -This only searches for a single fact at a time. It is intended to make some -simple use cases of the API easier. - */ -- (BOOL)searchSingle:(NSString* _Nullable)f callback:(id<BindingsSingleSearchCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * SetAlternativeUserDiscovery sets the alternativeUd object within manager. -Once set, any user discovery operation will go through the alternative -user discovery service. -To undo this operation, use UnsetAlternativeUserDiscovery. -The contact file is the already read in bytes, not the file path for the contact file. - */ -- (BOOL)setAlternativeUserDiscovery:(NSData* _Nullable)address cert:(NSData* _Nullable)cert contactFile:(NSData* _Nullable)contactFile error:(NSError* _Nullable* _Nullable)error; -/** - * UnsetAlternativeUserDiscovery clears out the information from -the Manager object. - */ -- (BOOL)unsetAlternativeUserDiscovery:(NSError* _Nullable* _Nullable)error; -@end - -/** - * Error codes - */ -FOUNDATION_EXPORT NSString* _Nonnull const BindingsUnrecognizedCode; -FOUNDATION_EXPORT NSString* _Nonnull const BindingsUnrecognizedMessage; - -/** - * CompressJpeg takes a JPEG image in byte format -and compresses it based on desired output size - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsCompressJpeg(NSData* _Nullable imgBytes, NSError* _Nullable* _Nullable error); - -/** - * CompressJpegForPreview takes a JPEG image in byte format -and compresses it based on desired output size - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsCompressJpegForPreview(NSData* _Nullable imgBytes, NSError* _Nullable* _Nullable error); - -/** - * DownloadAndVerifySignedNdfWithUrl retrieves the NDF from a specified URL. -The NDF is processed into a protobuf containing a signature which -is verified using the cert string passed in. The NDF is returned as marshaled -byte data which may be used to start a client. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadAndVerifySignedNdfWithUrl(NSString* _Nullable url, NSString* _Nullable cert, NSError* _Nullable* _Nullable error); - -/** - * DownloadDAppRegistrationDB returns a []byte containing -the JSON data describing registered dApps. -See https://git.xx.network/elixxir/registered-dapps - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadDAppRegistrationDB(NSError* _Nullable* _Nullable error); - -/** - * DownloadErrorDB returns a []byte containing the JSON data -describing client errors. -See https://git.xx.network/elixxir/client-error-database/ - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadErrorDB(NSError* _Nullable* _Nullable error); - -/** - * DumpStack returns a string with the stack trace of every running thread. - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsDumpStack(NSError* _Nullable* _Nullable error); - -/** - * EnableGrpcLogs sets GRPC trace logging - */ -FOUNDATION_EXPORT void BindingsEnableGrpcLogs(id<BindingsLogWriter> _Nullable writer); - -/** - * ErrorStringToUserFriendlyMessage takes a passed in errStr which will be -a backend generated error. These may be error specifically written by -the backend team or lower level errors gotten from low level dependencies. -This function will parse the error string for common errors provided from -errToUserErr to provide a more user-friendly error message for the front end. -If the error is not common, some simple parsing is done on the error message -to make it more user-accessible, removing backend specific jargon. - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsErrorStringToUserFriendlyMessage(NSString* _Nullable errStr); - -/** - * GenerateSecret creates a secret password using a system-based -pseudorandom number generator. It takes 1 parameter, `numBytes`, -which should be set to 32, but can be set higher in certain cases. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsGenerateSecret(long numBytes); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetCMIXParams(NSError* _Nullable* _Nullable error); - -/** - * returns a previously created client. IF be used if the garbage collector -removes the client instance on the app side. Is NOT thread safe relative to -login, newClient, or newPrecannedClient - */ -FOUNDATION_EXPORT BindingsClient* _Nullable BindingsGetClientSingleton(void); - -/** - * GetDependencies returns the api DEPENDENCIES - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetDependencies(void); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetE2EParams(NSError* _Nullable* _Nullable error); - -/** - * GetGitVersion rturns the api GITVERSION - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetGitVersion(void); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetNetworkParams(NSError* _Nullable* _Nullable error); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetUnsafeParams(NSError* _Nullable* _Nullable error); - -/** - * GetVersion returns the api SEMVER - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetVersion(void); - -/** - * InitializeBackup starts the backup processes that returns backup updates when -they occur. Any time an event occurs that changes the contents of the backup, -such as adding or deleting a contact, the backup is triggered and an -encrypted backup is generated and returned on the updateBackupCb callback. -Call this function only when enabling backup if it has not already been -initialized or when the user wants to change their password. -To resume backup process on app recovery, use ResumeBackup. - */ -FOUNDATION_EXPORT BindingsBackup* _Nullable BindingsInitializeBackup(NSString* _Nullable password, id<BindingsUpdateBackupFunc> _Nullable updateBackupCb, BindingsClient* _Nullable c, NSError* _Nullable* _Nullable error); - -/** - * LoadSecretWithMnemonic loads the secret stored from the call to -StoreSecretWithMnemonic. The path given should be the same filepath -as the path given in StoreSecretWithMnemonic. There should be a file -in this path called ".recovery". This operation is not tied -to client operations, as the user will not have a client when trying to -recover their account. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsLoadSecretWithMnemonic(NSString* _Nullable mnemonic, NSString* _Nullable path, NSError* _Nullable* _Nullable error); - -/** - * sets level of logging. All logs the set level and above will be displayed -options are: - TRACE - 0 - DEBUG - 1 - INFO - 2 - WARN - 3 - ERROR - 4 - CRITICAL - 5 - FATAL - 6 -The default state without updates is: INFO - */ -FOUNDATION_EXPORT BOOL BindingsLogLevel(long level, NSError* _Nullable* _Nullable error); - -/** - * Login will load an existing client from the storageDir -using the password. This will fail if the client doesn't exist or -the password is incorrect. -The password is passed as a byte array so that it can be cleared from -memory and stored as securely as possible using the memguard library. -Login does not block on network connection, and instead loads and -starts subprocesses to perform network operations. - */ -FOUNDATION_EXPORT BindingsClient* _Nullable BindingsLogin(NSString* _Nullable storageDir, NSData* _Nullable password, NSString* _Nullable parameters, NSError* _Nullable* _Nullable error); - -/** - * MakeIdList creates a new empty IdList. - */ -FOUNDATION_EXPORT BindingsIdList* _Nullable BindingsMakeIdList(void); - -FOUNDATION_EXPORT BindingsIntList* _Nullable BindingsMakeIntList(void); - -/** - * NewClient creates client storage, generates keys, connects, and registers -with the network. Note that this does not register a username/identity, but -merely creates a new cryptographic identity for adding such information -at a later date. - -Users of this function should delete the storage directory on error. - */ -FOUNDATION_EXPORT BOOL BindingsNewClient(NSString* _Nullable network, NSString* _Nullable storageDir, NSData* _Nullable password, NSString* _Nullable regCode, NSError* _Nullable* _Nullable error); - -/** - * NewClientFromBackup constructs a new Client from an encrypted backup. The backup -is decrypted using the backupPassphrase. On success a successful client creation, -the function will return a JSON encoded list of the E2E partners -contained in the backup and a json-encoded string of the parameters stored in the backup - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsNewClientFromBackup(NSString* _Nullable ndfJSON, NSString* _Nullable storageDir, NSData* _Nullable sessionPassword, NSData* _Nullable backupPassphrase, NSData* _Nullable backupFileContents, NSError* _Nullable* _Nullable error); - -/** - * NewDummyTrafficManager creates a DummyTraffic manager and initialises the -dummy traffic send thread. Note that the manager does not start sending dummy -traffic until its status is set to true using DummyTraffic.SetStatus. -The maxNumMessages is the upper bound of the random number of messages sent -each send. avgSendDeltaMS is the average duration, in milliseconds, to wait -between sends. Sends occur every avgSendDeltaMS +/- a random duration with an -upper bound of randomRangeMS. - */ -FOUNDATION_EXPORT BindingsDummyTraffic* _Nullable BindingsNewDummyTrafficManager(BindingsClient* _Nullable client, long maxNumMessages, long avgSendDeltaMS, long randomRangeMS, NSError* _Nullable* _Nullable error); - -/** - * fact object -creates a new fact. The factType must be either: - 0 - Username - 1 - Email - 2 - Phone Number -The fact must be well formed for the type and must not include commas or -semicolons. If it is not well formed, it will be rejected. Phone numbers -must have the two letter country codes appended. For the complete set of -validation, see /elixxir/primitives/fact/fact.go - */ -FOUNDATION_EXPORT BindingsFact* _Nullable BindingsNewFact(long factType, NSString* _Nullable factStr, NSError* _Nullable* _Nullable error); - -/** - * FactList - */ -FOUNDATION_EXPORT BindingsFactList* _Nullable BindingsNewFactList(void); - -/** - * NewFileTransferManager creates a new file transfer manager and starts the -sending and receiving threads. The receiveFunc is called everytime a new file -transfer is received. -The parameters string contains file transfer network configuration options -and is a JSON formatted string of the fileTransfer.Params object. If it is -left empty, then defaults are used. It is highly recommended that defaults -are used. If it is set, it must match the following format: - {"MaxThroughput":150000,"SendTimeout":500000000} -MaxThroughput is in bytes/sec and SendTimeout is in nanoseconds. - */ -FOUNDATION_EXPORT BindingsFileTransfer* _Nullable BindingsNewFileTransferManager(BindingsClient* _Nullable client, id<BindingsFileTransferReceiveFunc> _Nullable receiveFunc, NSString* _Nullable parameters, NSError* _Nullable* _Nullable error); - -/** - * NewGroupManager creates a new group chat manager. - */ -FOUNDATION_EXPORT BindingsGroupChat* _Nullable BindingsNewGroupManager(BindingsClient* _Nullable client, id<BindingsGroupRequestFunc> _Nullable requestFunc, id<BindingsGroupReceiveFunc> _Nullable receiveFunc, NSError* _Nullable* _Nullable error); - -/** - * NewPrecannedClient creates an insecure user with predetermined keys with nodes -It creates client storage, generates keys, connects, and registers -with the network. Note that this does not register a username/identity, but -merely creates a new cryptographic identity for adding such information -at a later date. - -Users of this function should delete the storage directory on error. - */ -FOUNDATION_EXPORT BOOL BindingsNewPrecannedClient(long precannedID, NSString* _Nullable network, NSString* _Nullable storageDir, NSData* _Nullable password, NSError* _Nullable* _Nullable error); - -/** - * NewUserDiscovery returns a new user discovery object. Only call this once. It must be called -after StartNetworkFollower is called and will fail if the network has never -been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscovery(BindingsClient* _Nullable client, NSError* _Nullable* _Nullable error); - -/** - * NewUserDiscoveryFromBackup returns a new user discovery object. It -wil set up the manager with the backup data. Pass into it the backed up -facts, one email and phone number each. This will add the registered facts -to the backed Store. Any one of these fields may be empty, -however both fields being empty will cause an error. Any other fact that is not -an email or phone number will return an error. You may only add a fact for the -accepted types once each. If you attempt to back up a fact type that has already -been backed up, an error will be returned. Anytime an error is returned, it means -the backup was not successful. -NOTE: Do not use this as a direct store operation. This feature is intended to add facts -to a backend store that have ALREADY BEEN REGISTERED on the account. -THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend. -Only call this once. It must be called after StartNetworkFollower -is called and will fail if the network has never been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscoveryFromBackup(BindingsClient* _Nullable client, NSString* _Nullable email, NSString* _Nullable phone, NSError* _Nullable* _Nullable error); - -/** - * NotificationsForMe Check if a notification received is for me -It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller, -a Type, and a source. These are as follows: - TYPE SOURCE DESCRIPTION - "default" recipient user ID A message with no association - "request" sender user ID A channel request has been received - "reset" sender user ID A channel reset has been received - "confirm" sender user ID A channel request has been accepted - "silent" sender user ID A message which should not be notified on - "e2e" sender user ID reception of an E2E message - "group" group ID reception of a group chat message - "endFT" sender user ID Last message sent confirming end of file transfer - "groupRQ" sender user ID Request from sender to join a group chat - */ -FOUNDATION_EXPORT BindingsManyNotificationForMeReport* _Nullable BindingsNotificationsForMe(NSString* _Nullable notifCSV, NSString* _Nullable preimages, NSError* _Nullable* _Nullable error); - -/** - * RegisterLogWriter registers a callback on which logs are written. - */ -FOUNDATION_EXPORT void BindingsRegisterLogWriter(id<BindingsLogWriter> _Nullable writer); - -/** - * RestoreContactsFromBackup takes as input the jason output of the -`NewClientFromBackup` function, unmarshals it into IDs, looks up -each ID in user discovery, and initiates a session reset request. -This function will not return until every id in the list has been sent a -request. It should be called again and again until it completes. -xxDK users should not use this function. This function is used by -the mobile phone apps and are not intended to be part of the xxDK. It -should be treated as internal functions specific to the phone apps. - */ -FOUNDATION_EXPORT BindingsRestoreContactsReport* _Nullable BindingsRestoreContactsFromBackup(NSData* _Nullable backupPartnerIDs, BindingsClient* _Nullable client, BindingsUserDiscovery* _Nullable udManager, id<BindingsLookupCallback> _Nullable lookupCB, id<BindingsRestoreContactsUpdater> _Nullable updatesCb); - -/** - * ResumeBackup starts the backup processes back up with a new callback after it -has been initialized. -Call this function only when resuming a backup that has already been -initialized or to replace the callback. -To start the backup for the first time or to use a new password, use -InitializeBackup. - */ -FOUNDATION_EXPORT BindingsBackup* _Nullable BindingsResumeBackup(id<BindingsUpdateBackupFunc> _Nullable cb, BindingsClient* _Nullable c, NSError* _Nullable* _Nullable error); - -/** - * SetTimeSource sets the network time to a custom source. - */ -FOUNDATION_EXPORT void BindingsSetTimeSource(id<BindingsTimeSource> _Nullable timeNow); - -/** - * StoreSecretWithMnemonic stores the secret tied with the mnemonic to storage. -Unlike other storage operations, this does not use EKV, as that is -intrinsically tied to client operations, which the user will not have while -trying to recover their account. As such, we store the encrypted data -directly, with a specified path. Path will be a valid filepath in which the -recover file will be stored as ".recovery". - -As an example, given "home/user/xxmessenger/storagePath", -the recovery file will be stored at -"home/user/xxmessenger/storagePath/.recovery" - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsStoreSecretWithMnemonic(NSData* _Nullable secret, NSString* _Nullable path, NSError* _Nullable* _Nullable error); - -/** - * Unmarshals a marshaled contact object, returns an error if it fails - */ -FOUNDATION_EXPORT BindingsContact* _Nullable BindingsUnmarshalContact(NSData* _Nullable b, NSError* _Nullable* _Nullable error); - -/** - * Unmarshals a marshaled send report object, returns an error if it fails - */ -FOUNDATION_EXPORT BindingsSendReport* _Nullable BindingsUnmarshalSendReport(NSData* _Nullable b, NSError* _Nullable* _Nullable error); - -/** - * UpdateCommonErrors takes the passed in contents of a JSON file and updates the -errToUserErr map with the contents of the json file. The JSON's expected format -conform with the commented examples provides in errToUserErr above. -NOTE that you should not pass in a file path, but a preloaded JSON file - */ -FOUNDATION_EXPORT BOOL BindingsUpdateCommonErrors(NSString* _Nullable jsonFile, NSError* _Nullable* _Nullable error); - -// skipped function WrapAPIClient with unsupported parameter or return types - - -// skipped function WrapUserDiscovery with unsupported parameter or return types - - -@class BindingsAuthConfirmCallback; - -@class BindingsAuthRequestCallback; - -@class BindingsAuthResetNotificationCallback; - -@class BindingsClientError; - -@class BindingsEventCallbackFunctionObject; - -@class BindingsFileTransferReceiveFunc; - -@class BindingsFileTransferReceivedProgressFunc; - -@class BindingsFileTransferSentProgressFunc; - -@class BindingsGroupReceiveFunc; - -@class BindingsGroupRequestFunc; - -@class BindingsListener; - -@class BindingsLogWriter; - -@class BindingsLookupCallback; - -@class BindingsMessageDeliveryCallback; - -@class BindingsMultiLookupCallback; - -@class BindingsNetworkHealthCallback; - -@class BindingsPreimageNotification; - -@class BindingsRestoreContactsUpdater; - -@class BindingsRoundCompletionCallback; - -@class BindingsRoundEventCallback; - -@class BindingsSearchCallback; - -@class BindingsSingleSearchCallback; - -@class BindingsTimeSource; - -@class BindingsUpdateBackupFunc; - -/** - * AuthConfirmCallback notifies the register whenever they receive an auth -request confirmation - */ -@interface BindingsAuthConfirmCallback : NSObject <goSeqRefInterface, BindingsAuthConfirmCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)partner; -@end - -/** - * AuthRequestCallback notifies the register whenever they receive an auth -request - */ -@interface BindingsAuthRequestCallback : NSObject <goSeqRefInterface, BindingsAuthRequestCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -/** - * AuthRequestCallback notifies the register whenever they receive an auth -request - */ -@interface BindingsAuthResetNotificationCallback : NSObject <goSeqRefInterface, BindingsAuthResetNotificationCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@interface BindingsClientError : NSObject <goSeqRefInterface, BindingsClientError> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)report:(NSString* _Nullable)source message:(NSString* _Nullable)message trace:(NSString* _Nullable)trace; -@end - -/** - * EventCallbackFunctionObject bindings interface which contains function -that implements the EventCallbackFunction - */ -@interface BindingsEventCallbackFunctionObject : NSObject <goSeqRefInterface, BindingsEventCallbackFunctionObject> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)reportEvent:(long)priority category:(NSString* _Nullable)category evtType:(NSString* _Nullable)evtType details:(NSString* _Nullable)details; -@end - -/** - * FileTransferReceiveFunc contains a function callback that notifies the -receiver of an incoming file transfer. It is called on the reception of the -initial file transfer message. - */ -@interface BindingsFileTransferReceiveFunc : NSObject <goSeqRefInterface, BindingsFileTransferReceiveFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)receiveCallback:(NSData* _Nullable)tid fileName:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType sender:(NSData* _Nullable)sender size:(long)size preview:(NSData* _Nullable)preview; -@end - -/** - * FileTransferReceivedProgressFunc contains a function callback that tracks the -progress of receiving a file. It is called when a file part is received, the -transfer completes, or on error. - */ -@interface BindingsFileTransferReceivedProgressFunc : NSObject <goSeqRefInterface, BindingsFileTransferReceivedProgressFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)receivedProgressCallback:(BOOL)completed received:(long)received total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -/** - * FileTransferSentProgressFunc contains a function callback that tracks the -progress of sending a file. It is called when a file part is sent, a file -part arrives, the transfer completes, or on error. - */ -@interface BindingsFileTransferSentProgressFunc : NSObject <goSeqRefInterface, BindingsFileTransferSentProgressFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)sentProgressCallback:(BOOL)completed sent:(long)sent arrived:(long)arrived total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -/** - * GroupReceiveFunc contains a function callback that is called when a group -message is received. - */ -@interface BindingsGroupReceiveFunc : NSObject <goSeqRefInterface, BindingsGroupReceiveFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)groupReceiveCallback:(BindingsGroupMessageReceive* _Nullable)msg; -@end - -/** - * GroupRequestFunc contains a function callback that is called when a group -request is received. - */ -@interface BindingsGroupRequestFunc : NSObject <goSeqRefInterface, BindingsGroupRequestFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)groupRequestCallback:(BindingsGroup* _Nullable)g; -@end - -/** - * Listener provides a callback to hear a message -An object implementing this interface can be called back when the client -gets a message of the type that the registerer specified at registration -time. - */ -@interface BindingsListener : NSObject <goSeqRefInterface, BindingsListener> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * Hear is called to receive a message in the UI - */ -- (void)hear:(BindingsMessage* _Nullable)message; -/** - * Returns a name, used for debugging - */ -- (NSString* _Nonnull)name; -@end - -@interface BindingsLogWriter : NSObject <goSeqRefInterface, BindingsLogWriter> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)log:(NSString* _Nullable)p0; -@end - -/** - * LookupCallback returns the result of a single lookup - */ -@interface BindingsLookupCallback : NSObject <goSeqRefInterface, BindingsLookupCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -/** - * MessageDeliveryCallback gets called on the determination if all events -related to a message send were successful. - */ -@interface BindingsMessageDeliveryCallback : NSObject <goSeqRefInterface, BindingsMessageDeliveryCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(NSData* _Nullable)msgID delivered:(BOOL)delivered timedOut:(BOOL)timedOut roundResults:(NSData* _Nullable)roundResults; -@end - -/** - * MultiLookupCallback returns the result of many parallel lookups - */ -@interface BindingsMultiLookupCallback : NSObject <goSeqRefInterface, BindingsMultiLookupCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContactList* _Nullable)Succeeded failed:(BindingsIdList* _Nullable)failed errors:(NSString* _Nullable)errors; -@end - -/** - * A callback when which is used to receive notification if network health -changes - */ -@interface BindingsNetworkHealthCallback : NSObject <goSeqRefInterface, BindingsNetworkHealthCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BOOL)p0; -@end - -@interface BindingsPreimageNotification : NSObject <goSeqRefInterface, BindingsPreimageNotification> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)notify:(NSData* _Nullable)identity deleted:(BOOL)deleted; -@end - -/** - * RestoreContactsUpdater interface provides a callback function -for receiving update information from RestoreContactsFromBackup. - */ -@interface BindingsRestoreContactsUpdater : NSObject <goSeqRefInterface, BindingsRestoreContactsUpdater> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * RestoreContactsCallback is called to report the current # of contacts -that have been found and how many have been restored -against the total number that need to be -processed. If an error occurs it it set on the err variable as a -plain string. - */ -- (void)restoreContactsCallback:(long)numFound numRestored:(long)numRestored total:(long)total err:(NSString* _Nullable)err; -@end - -/** - * RoundCompletionCallback is returned when the completion of a round is known. - */ -@interface BindingsRoundCompletionCallback : NSObject <goSeqRefInterface, BindingsRoundCompletionCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(long)rid success:(BOOL)success timedOut:(BOOL)timedOut; -@end - -/** - * RoundEventCallback handles waiting on the exact state of a round on -the cMix network. - */ -@interface BindingsRoundEventCallback : NSObject <goSeqRefInterface, BindingsRoundEventCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(long)rid state:(long)state timedOut:(BOOL)timedOut; -@end - -/** - * SearchCallback returns the result of a search - */ -@interface BindingsSearchCallback : NSObject <goSeqRefInterface, BindingsSearchCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContactList* _Nullable)contacts error:(NSString* _Nullable)error; -@end - -/** - * SingleSearchCallback returns the result of a single search - */ -@interface BindingsSingleSearchCallback : NSObject <goSeqRefInterface, BindingsSingleSearchCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@interface BindingsTimeSource : NSObject <goSeqRefInterface, BindingsTimeSource> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (int64_t)nowMs; -@end - -/** - * UpdateBackupFunc contains a function callback that returns new backups. - */ -@interface BindingsUpdateBackupFunc : NSObject <goSeqRefInterface, BindingsUpdateBackupFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)updateBackup:(NSData* _Nullable)encryptedBackup; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Universe.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Universe.objc.h deleted file mode 100644 index 019e7502d581983722a15bf30799e85cbc5dd766..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Universe.objc.h +++ /dev/null @@ -1,29 +0,0 @@ -// Objective-C API for talking to Go package. -// gobind -lang=objc -// -// File is generated by gobind. Do not edit. - -#ifndef __Universe_H__ -#define __Universe_H__ - -@import Foundation; -#include "ref.h" - -@protocol Universeerror; -@class Universeerror; - -@protocol Universeerror <NSObject> -- (NSString* _Nonnull)error; -@end - -@class Universeerror; - -@interface Universeerror : NSError <goSeqRefInterface, Universeerror> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (NSString* _Nonnull)error; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/ref.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/ref.h deleted file mode 100644 index b8036a4d85c7387f3def61473a071b5d8c4c8208..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/ref.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef __GO_REF_HDR__ -#define __GO_REF_HDR__ - -#include <Foundation/Foundation.h> - -// GoSeqRef is an object tagged with an integer for passing back and -// forth across the language boundary. A GoSeqRef may represent either -// an instance of a Go object, or an Objective-C object passed to Go. -// The explicit allocation of a GoSeqRef is used to pin a Go object -// when it is passed to Objective-C. The Go seq package maintains a -// reference to the Go object in a map keyed by the refnum along with -// a reference count. When the reference count reaches zero, the Go -// seq package will clear the corresponding entry in the map. -@interface GoSeqRef : NSObject { -} -@property(readonly) int32_t refnum; -@property(strong) id obj; // NULL when representing a Go object. - -// new GoSeqRef object to proxy a Go object. The refnum must be -// provided from Go side. -- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj; - -- (int32_t)incNum; - -@end - -@protocol goSeqRefInterface --(GoSeqRef*) _ref; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Modules/module.modulemap b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Modules/module.modulemap deleted file mode 100644 index 4316a5b24058edfc18ffb2dc7f7a982e8353441a..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Modules/module.modulemap +++ /dev/null @@ -1,8 +0,0 @@ -framework module "Bindings" { - header "ref.h" - header "Bindings.objc.h" - header "Universe.objc.h" - header "Bindings.h" - - export * -} \ No newline at end of file diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Resources/Info.plist b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Resources/Info.plist deleted file mode 100644 index 0d1a4b8ab9b1fc8e9357197398f73353470cb636..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Resources/Info.plist +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> - <plist version="1.0"> - <dict> - </dict> - </plist> diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Bindings b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Bindings deleted file mode 100644 index 8d78f84d50a0b0719b795f7b342f6109c2b848ec..0000000000000000000000000000000000000000 Binary files a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Bindings and /dev/null differ diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/Bindings.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/Bindings.h deleted file mode 100644 index 8906a7da239705b790cb2bb64de92f806640cb38..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/Bindings.h +++ /dev/null @@ -1,13 +0,0 @@ - -// Objective-C API for talking to the following Go packages -// -// gitlab.com/elixxir/client/bindings -// -// File is generated by gomobile bind. Do not edit. -#ifndef __Bindings_FRAMEWORK_H__ -#define __Bindings_FRAMEWORK_H__ - -#include "Bindings.objc.h" -#include "Universe.objc.h" - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/Bindings.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/Bindings.objc.h deleted file mode 100644 index 32bf6d116888f787ced27b01b95cb4e1b2c1138b..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/Bindings.objc.h +++ /dev/null @@ -1,2083 +0,0 @@ -// Objective-C API for talking to gitlab.com/elixxir/client/bindings Go package. -// gobind -lang=objc gitlab.com/elixxir/client/bindings -// -// File is generated by gobind. Do not edit. - -#ifndef __Bindings_H__ -#define __Bindings_H__ - -@import Foundation; -#include "ref.h" -#include "Universe.objc.h" - - -@class BindingsBackup; -@class BindingsBackupReport; -@class BindingsClient; -@class BindingsContact; -@class BindingsContactList; -@class BindingsDummyTraffic; -@class BindingsFact; -@class BindingsFactList; -@class BindingsFilePartTracker; -@class BindingsFileTransfer; -@class BindingsGroup; -@class BindingsGroupChat; -@class BindingsGroupMember; -@class BindingsGroupMembership; -@class BindingsGroupMessageReceive; -@class BindingsGroupReportDisk; -@class BindingsGroupSendReport; -@class BindingsIdList; -@class BindingsIntList; -@class BindingsManyNotificationForMeReport; -@class BindingsMessage; -@class BindingsNewGroupReport; -@class BindingsNodeRegistrationsStatus; -@class BindingsNotificationForMeReport; -@class BindingsRestoreContactsReport; -@class BindingsRoundList; -@class BindingsSendReport; -@class BindingsSendReportDisk; -@class BindingsUnregister; -@class BindingsUser; -@class BindingsUserDiscovery; -@protocol BindingsAuthConfirmCallback; -@class BindingsAuthConfirmCallback; -@protocol BindingsAuthRequestCallback; -@class BindingsAuthRequestCallback; -@protocol BindingsAuthResetNotificationCallback; -@class BindingsAuthResetNotificationCallback; -@protocol BindingsClientError; -@class BindingsClientError; -@protocol BindingsEventCallbackFunctionObject; -@class BindingsEventCallbackFunctionObject; -@protocol BindingsFileTransferReceiveFunc; -@class BindingsFileTransferReceiveFunc; -@protocol BindingsFileTransferReceivedProgressFunc; -@class BindingsFileTransferReceivedProgressFunc; -@protocol BindingsFileTransferSentProgressFunc; -@class BindingsFileTransferSentProgressFunc; -@protocol BindingsGroupReceiveFunc; -@class BindingsGroupReceiveFunc; -@protocol BindingsGroupRequestFunc; -@class BindingsGroupRequestFunc; -@protocol BindingsListener; -@class BindingsListener; -@protocol BindingsLogWriter; -@class BindingsLogWriter; -@protocol BindingsLookupCallback; -@class BindingsLookupCallback; -@protocol BindingsMessageDeliveryCallback; -@class BindingsMessageDeliveryCallback; -@protocol BindingsMultiLookupCallback; -@class BindingsMultiLookupCallback; -@protocol BindingsNetworkHealthCallback; -@class BindingsNetworkHealthCallback; -@protocol BindingsPreimageNotification; -@class BindingsPreimageNotification; -@protocol BindingsRestoreContactsUpdater; -@class BindingsRestoreContactsUpdater; -@protocol BindingsRoundCompletionCallback; -@class BindingsRoundCompletionCallback; -@protocol BindingsRoundEventCallback; -@class BindingsRoundEventCallback; -@protocol BindingsSearchCallback; -@class BindingsSearchCallback; -@protocol BindingsSingleSearchCallback; -@class BindingsSingleSearchCallback; -@protocol BindingsTimeSource; -@class BindingsTimeSource; -@protocol BindingsUpdateBackupFunc; -@class BindingsUpdateBackupFunc; - -@protocol BindingsAuthConfirmCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)partner; -@end - -@protocol BindingsAuthRequestCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@protocol BindingsAuthResetNotificationCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@protocol BindingsClientError <NSObject> -- (void)report:(NSString* _Nullable)source message:(NSString* _Nullable)message trace:(NSString* _Nullable)trace; -@end - -@protocol BindingsEventCallbackFunctionObject <NSObject> -- (void)reportEvent:(long)priority category:(NSString* _Nullable)category evtType:(NSString* _Nullable)evtType details:(NSString* _Nullable)details; -@end - -@protocol BindingsFileTransferReceiveFunc <NSObject> -- (void)receiveCallback:(NSData* _Nullable)tid fileName:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType sender:(NSData* _Nullable)sender size:(long)size preview:(NSData* _Nullable)preview; -@end - -@protocol BindingsFileTransferReceivedProgressFunc <NSObject> -- (void)receivedProgressCallback:(BOOL)completed received:(long)received total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -@protocol BindingsFileTransferSentProgressFunc <NSObject> -- (void)sentProgressCallback:(BOOL)completed sent:(long)sent arrived:(long)arrived total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -@protocol BindingsGroupReceiveFunc <NSObject> -- (void)groupReceiveCallback:(BindingsGroupMessageReceive* _Nullable)msg; -@end - -@protocol BindingsGroupRequestFunc <NSObject> -- (void)groupRequestCallback:(BindingsGroup* _Nullable)g; -@end - -@protocol BindingsListener <NSObject> -/** - * Hear is called to receive a message in the UI - */ -- (void)hear:(BindingsMessage* _Nullable)message; -/** - * Returns a name, used for debugging - */ -- (NSString* _Nonnull)name; -@end - -@protocol BindingsLogWriter <NSObject> -- (void)log:(NSString* _Nullable)p0; -@end - -@protocol BindingsLookupCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@protocol BindingsMessageDeliveryCallback <NSObject> -- (void)eventCallback:(NSData* _Nullable)msgID delivered:(BOOL)delivered timedOut:(BOOL)timedOut roundResults:(NSData* _Nullable)roundResults; -@end - -@protocol BindingsMultiLookupCallback <NSObject> -- (void)callback:(BindingsContactList* _Nullable)Succeeded failed:(BindingsIdList* _Nullable)failed errors:(NSString* _Nullable)errors; -@end - -@protocol BindingsNetworkHealthCallback <NSObject> -- (void)callback:(BOOL)p0; -@end - -@protocol BindingsPreimageNotification <NSObject> -- (void)notify:(NSData* _Nullable)identity deleted:(BOOL)deleted; -@end - -@protocol BindingsRestoreContactsUpdater <NSObject> -/** - * RestoreContactsCallback is called to report the current # of contacts -that have been found and how many have been restored -against the total number that need to be -processed. If an error occurs it it set on the err variable as a -plain string. - */ -- (void)restoreContactsCallback:(long)numFound numRestored:(long)numRestored total:(long)total err:(NSString* _Nullable)err; -@end - -@protocol BindingsRoundCompletionCallback <NSObject> -- (void)eventCallback:(long)rid success:(BOOL)success timedOut:(BOOL)timedOut; -@end - -@protocol BindingsRoundEventCallback <NSObject> -- (void)eventCallback:(long)rid state:(long)state timedOut:(BOOL)timedOut; -@end - -@protocol BindingsSearchCallback <NSObject> -- (void)callback:(BindingsContactList* _Nullable)contacts error:(NSString* _Nullable)error; -@end - -@protocol BindingsSingleSearchCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@protocol BindingsTimeSource <NSObject> -- (int64_t)nowMs; -@end - -@protocol BindingsUpdateBackupFunc <NSObject> -- (void)updateBackup:(NSData* _Nullable)encryptedBackup; -@end - -@interface BindingsBackup : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * AddJson stores a passed in json string in the backup structure - */ -- (void)addJson:(NSString* _Nullable)json; -/** - * IsBackupRunning returns true if the backup has been initialized and is -running. Returns false if it has been stopped. - */ -- (BOOL)isBackupRunning; -/** - * StopBackup stops the backup processes and deletes the user's password from -storage. To enable backups again, call InitializeBackup. - */ -- (BOOL)stopBackup:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsBackupReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field BackupReport.RestoredContacts with unsupported type: []*gitlab.com/xx_network/primitives/id.ID - -@property (nonatomic) NSString* _Nonnull params; -@end - -/** - * BindingsClient wraps the api.Client, implementing additional functions -to support the gomobile Client interface - */ -@interface BindingsClient : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BOOL)confirmAuthenticatedChannel:(NSData* _Nullable)recipientMarshaled ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteAllRequests clears all requests from Client's auth storage. - */ -- (BOOL)deleteAllRequests:(NSError* _Nullable* _Nullable)error; -/** - * DeleteContact is a function which removes a contact from Client's storage - */ -- (BOOL)deleteContact:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteReceiveRequests clears receive requests from Client's auth storage. - */ -- (BOOL)deleteReceiveRequests:(NSError* _Nullable* _Nullable)error; -/** - * DeleteRequest will delete a request, agnostic of request type -for the given partner ID. If no request exists for this -partner ID an error will be returned. - */ -- (BOOL)deleteRequest:(NSData* _Nullable)requesterUserId error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteSentRequests clears sent requests from Client's auth storage. - */ -- (BOOL)deleteSentRequests:(NSError* _Nullable* _Nullable)error; -// skipped method Client.GetInternalClient with unsupported parameter or return types - -/** - * GetNodeRegistrationStatus returns a struct with the number of nodes the -client is registered with and the number total. - */ -- (BindingsNodeRegistrationsStatus* _Nullable)getNodeRegistrationStatus:(NSError* _Nullable* _Nullable)error; -/** - * GetPartners returns a list of - */ -- (NSData* _Nullable)getPartners:(NSError* _Nullable* _Nullable)error; -/** - * GetPreferredBins returns the geographic bin or bins that the provided two -character country code is a part of. The bins are returned as CSV. - */ -- (NSString* _Nonnull)getPreferredBins:(NSString* _Nullable)countryCode error:(NSError* _Nullable* _Nullable)error; -- (NSString* _Nonnull)getPreimages:(NSData* _Nullable)identity; -// skipped method Client.GetRateLimitParams with unsupported parameter or return types - -- (NSString* _Nonnull)getRelationshipFingerprint:(NSData* _Nullable)partnerID error:(NSError* _Nullable* _Nullable)error; -/** - * Returns a user object from which all information about the current user -can be gleaned - */ -- (BindingsUser* _Nullable)getUser; -/** - * HasRunningProcessies checks if any background threads are running. -returns true if none are running. This is meant to be -used when NetworkFollowerStatus() returns Stopping. -Due to the handling of comms on iOS, where the OS can -block indefiently, it may not enter the stopped -state apropreatly. This can be used instead. - */ -- (BOOL)hasRunningProcessies; -/** - * returns true if the network is read to be in a healthy state where -messages can be sent - */ -- (BOOL)isNetworkHealthy; -- (BindingsContact* _Nullable)makePrecannedAuthenticatedChannel:(long)precannedID error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the state of the network follower. Returns: -Stopped - 0 -Starting - 1000 -Running - 2000 -Stopping - 3000 - */ -- (long)networkFollowerStatus; -- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetNotificationCallback> _Nullable)reset; -/** - * RegisterClientErrorCallback registers the callback to handle errors from the -long running threads controlled by StartNetworkFollower and StopNetworkFollower - */ -- (void)registerClientErrorCallback:(id<BindingsClientError> _Nullable)clientError; -/** - * RegisterEventCallback records the given function to receive -ReportableEvent objects. It returns the internal index -of the callback so that it can be deleted later. - */ -- (BOOL)registerEventCallback:(NSString* _Nullable)name myObj:(id<BindingsEventCallbackFunctionObject> _Nullable)myObj error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterForNotifications accepts firebase messaging token - */ -- (BOOL)registerForNotifications:(NSString* _Nullable)token error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterListener records and installs a listener for messages -matching specific uid, msgType, and/or username -Returns a ListenerUnregister interface which can be - -to register for any userID, pass in an id with length 0 or an id with -all zeroes - -to register for any message type, pass in a message type of 0 - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types - */ -- (BindingsUnregister* _Nullable)registerListener:(NSData* _Nullable)uid msgType:(long)msgType listener:(id<BindingsListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterNetworkHealthCB registers the network health callback to be called -any time the network health changes. Returns a unique ID that can be used to -unregister the network health callback. - */ -- (int64_t)registerNetworkHealthCB:(id<BindingsNetworkHealthCallback> _Nullable)nhc; -- (void)registerPreimageCallback:(NSData* _Nullable)identity pin:(id<BindingsPreimageNotification> _Nullable)pin; -/** - * RegisterRoundEventsHandler registers a callback interface for round -events. -The rid is the round the event attaches to -The timeoutMS is the number of milliseconds until the event fails, and the -validStates are a list of states (one per byte) on which the event gets -triggered -States: - 0x00 - PENDING (Never seen by client) - 0x01 - PRECOMPUTING - 0x02 - STANDBY - 0x03 - QUEUED - 0x04 - REALTIME - 0x05 - COMPLETED - 0x06 - FAILED -These states are defined in elixxir/primitives/states/state.go - */ -- (BindingsUnregister* _Nullable)registerRoundEventsHandler:(long)rid cb:(id<BindingsRoundEventCallback> _Nullable)cb timeoutMS:(long)timeoutMS il:(BindingsIntList* _Nullable)il; -- (void)replayRequests; -- (BOOL)requestAuthenticatedChannel:(NSData* _Nullable)recipientMarshaled meMarshaled:(NSData* _Nullable)meMarshaled message:(NSString* _Nullable)message ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -- (BOOL)resetSession:(NSData* _Nullable)recipientMarshaled meMarshaled:(NSData* _Nullable)meMarshaled message:(NSString* _Nullable)message ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * This will return the round the message was sent on if it is successfully sent -This can be used to register a round event to learn about message delivery. -on failure a round id of -1 is returned - */ -- (BOOL)sendCmix:(NSData* _Nullable)recipient contents:(NSData* _Nullable)contents parameters:(NSString* _Nullable)parameters ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * SendE2E sends an end-to-end payload to the provided recipient with -the provided msgType. Returns the list of rounds in which parts of -the message were sent or an error if it fails. - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types - */ -- (BindingsSendReport* _Nullable)sendE2E:(NSData* _Nullable)recipient payload:(NSData* _Nullable)payload messageType:(long)messageType parameters:(NSString* _Nullable)parameters error:(NSError* _Nullable* _Nullable)error; -/** - * SendUnsafe sends an unencrypted payload to the provided recipient -with the provided msgType. Returns the list of rounds in which parts -of the message were sent or an error if it fails. -NOTE: Do not use this function unless you know what you are doing. -This function always produces an error message in client logging. - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types with custom types - */ -- (BindingsRoundList* _Nullable)sendUnsafe:(NSData* _Nullable)recipient payload:(NSData* _Nullable)payload messageType:(long)messageType parameters:(NSString* _Nullable)parameters error:(NSError* _Nullable* _Nullable)error; -/** - * SetProxiedBins updates the host pool filter that filters out gateways that -are not in one of the specified bins. The provided bins should be CSV. - */ -- (BOOL)setProxiedBins:(NSString* _Nullable)binStringsCSV error:(NSError* _Nullable* _Nullable)error; -/** - * StartNetworkFollower kicks off the tracking of the network. It starts -long running network client threads and returns an object for checking -state and stopping those threads. -Call this when returning from sleep and close when going back to -sleep. -These threads may become a significant drain on battery when offline, ensure -they are stopped if there is no internet access -Threads Started: - - Network Follower (/network/follow.go) - tracks the network events and hands them off to workers for handling - - Historical Round Retrieval (/network/rounds/historical.go) - Retrieves data about rounds which are too old to be stored by the client - - Message Retrieval Worker Group (/network/rounds/retrieve.go) - Requests all messages in a given round from the gateway of the last node - - Message Handling Worker Group (/network/message/handle.go) - Decrypts and partitions messages when signals via the Switchboard - - Health Tracker (/network/health) - Via the network instance tracks the state of the network - - Garbled Messages (/network/message/garbled.go) - Can be signaled to check all recent messages which could be be decoded - Uses a message store on disk for persistence - - Critical Messages (/network/message/critical.go) - Ensures all protocol layer mandatory messages are sent - Uses a message store on disk for persistence - - KeyExchange Trigger (/keyExchange/trigger.go) - Responds to sent rekeys and executes them - - KeyExchange Confirm (/keyExchange/confirm.go) - Responds to confirmations of successful rekey operations - */ -- (BOOL)startNetworkFollower:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * StopNetworkFollower stops the network follower if it is running. -It returns errors if the Follower is in the wrong status to stop or if it -fails to stop it. -if the network follower is running and this fails, the client object will -most likely be in an unrecoverable state and need to be trashed. - */ -- (BOOL)stopNetworkFollower:(NSError* _Nullable* _Nullable)error; -/** - * UnregisterEventCallback deletes the callback identified by the -index. It returns an error if it fails. - */ -- (void)unregisterEventCallback:(NSString* _Nullable)name; -/** - * UnregisterForNotifications unregister user for notifications - */ -- (BOOL)unregisterForNotifications:(NSError* _Nullable* _Nullable)error; -- (void)unregisterNetworkHealthCB:(int64_t)funcID; -- (BOOL)verifyOwnership:(NSData* _Nullable)receivedMarshaled verifiedMarshaled:(NSData* _Nullable)verifiedMarshaled ret0_:(BOOL* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * WaitForMessageDelivery allows the caller to get notified if the rounds a -message was sent in successfully completed. Under the hood, this uses an API -which uses the internal round data, network historical round lookup, and -waiting on network events to determine what has (or will) occur. - -The callbacks will return at timeoutMS if no state update occurs - -This function takes the marshaled send report to ensure a memory leak does -not occur as a result of both sides of the bindings holding a reference to -the same pointer. - */ -- (BOOL)waitForMessageDelivery:(NSData* _Nullable)marshaledSendReport mdc:(id<BindingsMessageDeliveryCallback> _Nullable)mdc timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * WaitForNewtwork will block until either the network is healthy or the -passed timeout. It will return true if the network is healthy - */ -- (BOOL)waitForNetwork:(long)timeoutMS; -/** - * WaitForRoundCompletion allows the caller to get notified if a round -has completed (or failed). Under the hood, this uses an API which uses the internal -round data, network historical round lookup, and waiting on network events -to determine what has (or will) occur. - -The callbacks will return at timeoutMS if no state update occurs - */ -- (BOOL)waitForRoundCompletion:(long)roundID rec:(id<BindingsRoundCompletionCallback> _Nullable)rec timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * contact object - */ -@interface BindingsContact : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped method Contact.GetAPIContact with unsupported parameter or return types - -/** - * GetDHPublicKey returns the public key associated with the Contact. - */ -- (NSData* _Nullable)getDHPublicKey; -/** - * Returns a fact list for adding and getting facts to and from the contact - */ -- (BindingsFactList* _Nullable)getFactList; -/** - * GetID returns the user ID for this user. - */ -- (NSData* _Nullable)getID; -/** - * GetDHPublicKey returns hash of a DH proof of key ownership. - */ -- (NSData* _Nullable)getOwnershipProof; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsContactList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Gets a stored round ID at the given index - */ -- (BindingsContact* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the number of round IDs stored - */ -- (long)len; -@end - -/** - * DummyTraffic contains the file dummy traffic manager. The manager can be used -to set and get the status of the send thread. - */ -@interface BindingsDummyTraffic : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewDummyTrafficManager creates a DummyTraffic manager and initialises the -dummy traffic send thread. Note that the manager does not start sending dummy -traffic until its status is set to true using DummyTraffic.SetStatus. -The maxNumMessages is the upper bound of the random number of messages sent -each send. avgSendDeltaMS is the average duration, in milliseconds, to wait -between sends. Sends occur every avgSendDeltaMS +/- a random duration with an -upper bound of randomRangeMS. - */ -- (nullable instancetype)initManager:(BindingsClient* _Nullable)client maxNumMessages:(long)maxNumMessages avgSendDeltaMS:(long)avgSendDeltaMS randomRangeMS:(long)randomRangeMS; -/** - * GetStatus returns the current state of the dummy traffic send thread. It has -the following return values: - true = send thread is sending dummy messages - false = send thread is paused/stopped and not sending dummy messages -Note that this function does not return the status set by SetStatus directly; -it returns the current status of the send thread, which means any call to -SetStatus will have a small delay before it is returned by GetStatus. - */ -- (BOOL)getStatus; -/** - * SetStatus sets the state of the dummy traffic send thread, which determines -if the thread is running or paused. The possible statuses are: - true = send thread is sending dummy messages - false = send thread is paused/stopped and not sending dummy messages -Returns an error if the channel is full. -Note that this function cannot change the status of the send thread if it has -yet to be started or stopped. - */ -- (BOOL)setStatus:(BOOL)status error:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsFact : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * fact object -creates a new fact. The factType must be either: - 0 - Username - 1 - Email - 2 - Phone Number -The fact must be well formed for the type and must not include commas or -semicolons. If it is not well formed, it will be rejected. Phone numbers -must have the two letter country codes appended. For the complete set of -validation, see /elixxir/primitives/fact/fact.go - */ -- (nullable instancetype)init:(long)factType factStr:(NSString* _Nullable)factStr; -- (NSString* _Nonnull)get; -- (NSString* _Nonnull)stringify; -- (long)type; -@end - -@interface BindingsFactList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * FactList - */ -- (nullable instancetype)init; -- (BOOL)add:(NSString* _Nullable)factData factType:(long)factType error:(NSError* _Nullable* _Nullable)error; -- (BindingsFact* _Nullable)get:(long)i; -- (long)num; -- (NSString* _Nonnull)stringify:(NSError* _Nullable* _Nullable)error; -@end - -/** - * FilePartTracker contains the interfaces.FilePartTracker. - */ -@interface BindingsFilePartTracker : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetNumParts returns the total number of file parts in the transfer. - */ -- (long)getNumParts; -/** - * GetPartStatus returns the status of the file part with the given part number. -The possible values for the status are: -0 = unsent -1 = sent (sender has sent a part, but it has not arrived) -2 = arrived (sender has sent a part, and it has arrived) -3 = received (receiver has received a part) - */ -- (long)getPartStatus:(long)partNum; -@end - -/** - * FileTransfer contains the file transfer manager. - */ -@interface BindingsFileTransfer : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewFileTransferManager creates a new file transfer manager and starts the -sending and receiving threads. The receiveFunc is called everytime a new file -transfer is received. -The parameters string contains file transfer network configuration options -and is a JSON formatted string of the fileTransfer.Params object. If it is -left empty, then defaults are used. It is highly recommended that defaults -are used. If it is set, it must match the following format: - {"MaxThroughput":150000,"SendTimeout":500000000} -MaxThroughput is in bytes/sec and SendTimeout is in nanoseconds. - */ -- (nullable instancetype)initManager:(BindingsClient* _Nullable)client receiveFunc:(id<BindingsFileTransferReceiveFunc> _Nullable)receiveFunc parameters:(NSString* _Nullable)parameters; -/** - * CloseSend deletes a sent file transfer from the sent transfer map and from -storage once a transfer has completed or reached the retry limit. Returns an -error if the transfer has not run out of retries. - */ -- (BOOL)closeSend:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error; -/** - * GetMaxFileNameByteLength returns the maximum length, in bytes, allowed for a -file name. - */ -- (long)getMaxFileNameByteLength; -/** - * GetMaxFilePreviewSize returns the maximum file preview size, in bytes. - */ -- (long)getMaxFilePreviewSize; -/** - * GetMaxFileSize returns the maximum file size, in bytes, allowed to be -transferred. - */ -- (long)getMaxFileSize; -/** - * GetMaxFileTypeByteLength returns the maximum length, in bytes, allowed for a -file type. - */ -- (long)getMaxFileTypeByteLength; -/** - * Receive returns the fully assembled file on the completion of the transfer. -It deletes the transfer from the received transfer map and from storage. -Returns an error if the transfer is not complete, the full file cannot be -verified, or if the transfer cannot be found. - */ -- (NSData* _Nullable)receive:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterReceiveProgressCallback allows for the registration of a callback to -track the progress of an individual received file transfer. The callback will -be called immediately when added to report the current status of the -transfer. It will then call every time a file part is received, the transfer -completes, or an error occurs. It is called at most once ever period, which -means if events occur faster than the period, then they will not be reported -and instead, the progress will be reported once at the end of the period. -Once the callback reports that the transfer has completed, the recipient -can get the full file by calling Receive. -The period is specified in milliseconds. - */ -- (BOOL)registerReceiveProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferReceivedProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterSendProgressCallback allows for the registration of a callback to -track the progress of an individual sent file transfer. The callback will be -called immediately when added to report the current status of the transfer. -It will then call every time a file part is sent, a file part arrives, the -transfer completes, or an error occurs. It is called at most once every -period, which means if events occur faster than the period, then they will -not be reported and instead, the progress will be reported once at the end of -the period. -The period is specified in milliseconds. - */ -- (BOOL)registerSendProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -/** - * Send sends a file to the recipient. The sender must have an E2E relationship -with the recipient. -The file name is the name of the file to show a user. It has a max length of -48 bytes. -The file type identifies what type of file is being sent. It has a max length -of 8 bytes. -The file data cannot be larger than 256 kB -The retry float is the total amount of data to send relative to the data -size. Data will be resent on error and will resend up to [(1 + retry) * -fileSize]. -The preview stores a preview of the data (such as a thumbnail) and is -capped at 4 kB in size. -Returns a unique transfer ID used to identify the transfer. -PeriodMS is the duration, in milliseconds, to wait between progress callback -calls. Set this large enough to prevent spamming. - */ -- (NSData* _Nullable)send:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType fileData:(NSData* _Nullable)fileData recipientID:(NSData* _Nullable)recipientID retry:(float)retry preview:(NSData* _Nullable)preview progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * Group structure contains the identifying and membership information of a -group chat. - */ -@interface BindingsGroup : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetCreatedMS returns the time the group was created in milliseconds. This is -also the time the group requests were sent. - */ -- (int64_t)getCreatedMS; -/** - * GetCreatedNano returns the time the group was created in nanoseconds. This is -also the time the group requests were sent. - */ -- (int64_t)getCreatedNano; -/** - * GetID return the 33-byte unique group ID. - */ -- (NSData* _Nullable)getID; -/** - * GetInitMessage returns initial message sent with the group request. - */ -- (NSData* _Nullable)getInitMessage; -/** - * GetMembership returns a list of contacts, one for each member in the group. -The list is in order; the first contact is the leader/creator of the group. -All subsequent members are ordered by their ID. - */ -- (BindingsGroupMembership* _Nullable)getMembership; -/** - * GetName returns the name set by the user for the group. - */ -- (NSData* _Nullable)getName; -/** - * Serialize serializes the Group. - */ -- (NSData* _Nullable)serialize; -@end - -/** - * GroupChat object contains the group chat manager. - */ -@interface BindingsGroupChat : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetGroup returns the group with the group ID. If no group exists, then the -error "failed to find group" is returned. - */ -- (BindingsGroup* _Nullable)getGroup:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * GetGroups returns an IdList containing a list of group IDs that the user is a -part of. - */ -- (BindingsIdList* _Nullable)getGroups; -/** - * JoinGroup allows a user to join a group when they receive a request. The -caller must pass in the serialized bytes of a Group. - */ -- (BOOL)joinGroup:(NSData* _Nullable)serializedGroupData error:(NSError* _Nullable* _Nullable)error; -/** - * LeaveGroup deletes a group so a user no longer has access. - */ -- (BOOL)leaveGroup:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * MakeGroup creates a new group and sends a group request to all members in the -group. The ID of the new group, the rounds the requests were sent on, and the -status of the send are contained in NewGroupReport. - */ -- (BindingsNewGroupReport* _Nullable)makeGroup:(BindingsIdList* _Nullable)membership name:(NSData* _Nullable)name message:(NSData* _Nullable)message; -/** - * NumGroups returns the number of groups the user is a part of. - */ -- (long)numGroups; -/** - * ResendRequest resends a group request to all members in the group. The rounds -they were sent on and the status of the send are contained in NewGroupReport. - */ -- (BindingsNewGroupReport* _Nullable)resendRequest:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * Send sends the message to the specified group. Returns the round the messages -were sent on. - */ -- (BindingsGroupSendReport* _Nullable)send:(NSData* _Nullable)groupIdBytes message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * // -Member Structure -// -GroupMember represents a member in the group membership list. - */ -@interface BindingsGroupMember : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupMember.Member with unsupported type: gitlab.com/elixxir/crypto/group.Member - -// skipped method GroupMember.DeepCopy with unsupported parameter or return types - -// skipped method GroupMember.Equal with unsupported parameter or return types - -/** - * GetDhKey returns the byte representation of the public Diffie–Hellman key of -the member. - */ -- (NSData* _Nullable)getDhKey; -/** - * GetID returns the 33-byte user ID of the member. - */ -- (NSData* _Nullable)getID; -- (NSString* _Nonnull)goString; -- (NSData* _Nullable)serialize; -- (NSString* _Nonnull)string; -@end - -/** - * GroupMembership structure contains a list of members that are part of a -group. The first member is the group leader. - */ -@interface BindingsGroupMembership : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Get returns the member at the index. The member at index 0 is always the -group leader. An error is returned if the index is out of range. - */ -- (BindingsGroupMember* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Len returns the number of members in the group membership. - */ -- (long)len; -@end - -/** - * GroupMessageReceive contains a group message, its ID, and its data that a -user receives. - */ -@interface BindingsGroupMessageReceive : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupMessageReceive.MessageReceive with unsupported type: gitlab.com/elixxir/client/groupChat.MessageReceive - -/** - * GetEphemeralID returns the ephemeral ID of the recipient. - */ -- (int64_t)getEphemeralID; -/** - * GetGroupID returns the 33-byte group ID. - */ -- (NSData* _Nullable)getGroupID; -/** - * GetMessageID returns the message ID. - */ -- (NSData* _Nullable)getMessageID; -/** - * GetPayload returns the message payload. - */ -- (NSData* _Nullable)getPayload; -/** - * GetRecipientID returns the 33-byte user ID of the recipient. - */ -- (NSData* _Nullable)getRecipientID; -/** - * GetRoundID returns the ID of the round the message was sent on. - */ -- (int64_t)getRoundID; -/** - * GetRoundTimestampMS returns the timestamp, in milliseconds, of the round the -message was sent on. - */ -- (int64_t)getRoundTimestampMS; -/** - * GetRoundTimestampNano returns the timestamp, in nanoseconds, of the round the -message was sent on. - */ -- (int64_t)getRoundTimestampNano; -/** - * GetRoundURL returns the ID of the round the message was sent on. - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetSenderID returns the 33-byte user ID of the sender. - */ -- (NSData* _Nullable)getSenderID; -/** - * GetTimestampMS returns the message timestamp in milliseconds. - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message timestamp in nanoseconds. - */ -- (int64_t)getTimestampNano; -- (NSString* _Nonnull)string; -@end - -@interface BindingsGroupReportDisk : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupReportDisk.List with unsupported type: []gitlab.com/xx_network/primitives/id.Round - -@property (nonatomic) NSData* _Nullable grpId; -@property (nonatomic) long status; -@end - -/** - * GroupSendReport is returned when sending a group message. It contains the -round ID sent on and the timestamp of the send. - */ -@interface BindingsGroupSendReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetMessageID returns the ID of the round that the send occurred on. - */ -- (NSData* _Nullable)getMessageID; -/** - * GetRoundID returns the ID of the round that the send occurred on. - */ -- (int64_t)getRoundID; -/** - * GetRoundURL returns the URL of the round that the send occurred on. - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetTimestampMS returns the timestamp of the send in milliseconds. - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the timestamp of the send in nanoseconds. - */ -- (int64_t)getTimestampNano; -@end - -/** - * ID list -IdList contains a list of IDs. - */ -@interface BindingsIdList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Add appends the ID bytes to the end of the list. - */ -- (BOOL)add:(NSData* _Nullable)idBytes error:(NSError* _Nullable* _Nullable)error; -/** - * Get returns the ID at the index. An error is returned if the index is out of -range. - */ -- (NSData* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Len returns the number of IDs in the list. - */ -- (long)len; -@end - -@interface BindingsIntList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (void)add:(long)i; -- (BOOL)get:(long)i ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -- (long)len; -@end - -@interface BindingsManyNotificationForMeReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BindingsNotificationForMeReport* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -- (long)len; -@end - -/** - * Message is a message received from the cMix network in the clear -or that has been decrypted using established E2E keys. - */ -@interface BindingsMessage : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetID returns the id of the message - */ -- (NSData* _Nullable)getID; -/** - * GetMessageType returns the message's type - */ -- (long)getMessageType; -/** - * GetPayload returns the message's payload/contents - */ -- (NSData* _Nullable)getPayload; -/** - * GetRoundId returns the message's round ID - */ -- (int64_t)getRoundId; -/** - * GetRoundTimestampMS returns the message's round timestamp in milliseconds - */ -- (int64_t)getRoundTimestampMS; -/** - * GetRoundTimestampNano returns the message's round timestamp in nanoseconds - */ -- (int64_t)getRoundTimestampNano; -/** - * GetRoundURL returns the message's round URL - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetSender returns the message's sender ID, if available - */ -- (NSData* _Nullable)getSender; -/** - * GetTimestampMS returns the message's timestamp in milliseconds - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message's timestamp in nanoseconds - */ -- (int64_t)getTimestampNano; -@end - -/** - * NewGroupReport is returned when creating a new group and contains the ID of -the group, a list of rounds that the group requests were sent on, and the -status of the send. - */ -@interface BindingsNewGroupReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetError returns the string of an error. -Will be an empty string if no error occured - */ -- (NSString* _Nonnull)getError; -/** - * GetGroup returns the Group. - */ -- (BindingsGroup* _Nullable)getGroup; -/** - * GetRoundList returns the RoundList containing a list of rounds requests were -sent on. - */ -- (BindingsRoundList* _Nullable)getRoundList; -/** - * GetStatus returns the status of the requests sent when creating a new group. -status = 0 an error occurred before any requests could be sent - 1 all requests failed to send (call Resend Group) - 2 some request failed and some succeeded (call Resend Group) - 3, all requests sent successfully (call Resend Group) - */ -- (long)getStatus; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -- (BOOL)unmarshal:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * NodeRegistrationsStatus structure for returning node registration statuses -for bindings. - */ -@interface BindingsNodeRegistrationsStatus : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetRegistered returns the number of nodes registered with the client. - */ -- (long)getRegistered; -/** - * GetTotal return the total of nodes currently in the network. - */ -- (long)getTotal; -@end - -@interface BindingsNotificationForMeReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BOOL)forMe; -- (NSData* _Nullable)source; -- (NSString* _Nonnull)type; -@end - -/** - * RestoreContactsReport is a gomobile friendly report structure -for determining which IDs restored, which failed, and why. - */ -@interface BindingsRestoreContactsReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetErrorAt returns the error string at index - */ -- (NSString* _Nonnull)getErrorAt:(long)index; -/** - * GetFailedAt returns the failed ID at index - */ -- (NSData* _Nullable)getFailedAt:(long)index; -/** - * GetRestoreContactsError returns an error string. Empty if no error. - */ -- (NSString* _Nonnull)getRestoreContactsError; -/** - * GetRestoredAt returns the restored ID at index - */ -- (NSData* _Nullable)getRestoredAt:(long)index; -/** - * LenFailed returns the length of the ID's failed. - */ -- (long)lenFailed; -/** - * LenRestored returns the length of ID's restored. - */ -- (long)lenRestored; -@end - -@interface BindingsRoundList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Gets a stored round ID at the given index - */ -- (BOOL)get:(long)i ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the number of round IDs stored - */ -- (long)len; -@end - -/** - * the send report is the mechanisim by which sendE2E returns a single - */ -@interface BindingsSendReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (NSData* _Nullable)getMessageID; -- (BindingsRoundList* _Nullable)getRoundList; -- (NSString* _Nonnull)getRoundURL; -/** - * GetTimestampMS returns the message's timestamp in milliseconds - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message's timestamp in nanoseconds - */ -- (int64_t)getTimestampNano; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -- (BOOL)unmarshal:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsSendReportDisk : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field SendReportDisk.List with unsupported type: []gitlab.com/xx_network/primitives/id.Round - -@property (nonatomic) NSData* _Nullable mid; -@property (nonatomic) int64_t ts; -@end - -/** - * Generic Unregister - a generic return used for all callbacks which can be -unregistered -Interface which allows the un-registration of a listener - */ -@interface BindingsUnregister : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Call unregisters a callback - */ -- (void)unregister; -@end - -@interface BindingsUser : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BindingsContact* _Nullable)getContact; -- (NSData* _Nullable)getE2EDhPrivateKey; -- (NSData* _Nullable)getE2EDhPublicKey; -- (NSData* _Nullable)getReceptionID; -- (NSData* _Nullable)getReceptionRSAPrivateKeyPem; -- (NSData* _Nullable)getReceptionRSAPublicKeyPem; -- (NSData* _Nullable)getReceptionSalt; -- (NSData* _Nullable)getTransmissionID; -- (NSData* _Nullable)getTransmissionRSAPrivateKeyPem; -- (NSData* _Nullable)getTransmissionRSAPublicKeyPem; -- (NSData* _Nullable)getTransmissionSalt; -- (BOOL)isPrecanned; -@end - -@interface BindingsUserDiscovery : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewUserDiscovery returns a new user discovery object. Only call this once. It must be called -after StartNetworkFollower is called and will fail if the network has never -been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -- (nullable instancetype)init:(BindingsClient* _Nullable)client; -/** - * NewUserDiscoveryFromBackup returns a new user discovery object. It -wil set up the manager with the backup data. Pass into it the backed up -facts, one email and phone number each. This will add the registered facts -to the backed Store. Any one of these fields may be empty, -however both fields being empty will cause an error. Any other fact that is not -an email or phone number will return an error. You may only add a fact for the -accepted types once each. If you attempt to back up a fact type that has already -been backed up, an error will be returned. Anytime an error is returned, it means -the backup was not successful. -NOTE: Do not use this as a direct store operation. This feature is intended to add facts -to a backend store that have ALREADY BEEN REGISTERED on the account. -THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend. -Only call this once. It must be called after StartNetworkFollower -is called and will fail if the network has never been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -- (nullable instancetype)initFromBackup:(BindingsClient* _Nullable)client email:(NSString* _Nullable)email phone:(NSString* _Nullable)phone; -/** - * AddFact adds a fact for the user to user discovery. Will only succeed if the -user is already registered and the system does not have the fact currently -registered for any user. -Will fail if the fact string is not well formed. -This does not complete the fact registration process, it returns a -confirmation id instead. Over the communications system the fact is -associated with, a code will be sent. This confirmation ID needs to be -called along with the code to finalize the fact. - */ -- (NSString* _Nonnull)addFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * ConfirmFact confirms a fact first registered via AddFact. The confirmation ID comes from -AddFact while the code will come over the associated communications system - */ -- (BOOL)confirmFact:(NSString* _Nullable)confirmationID code:(NSString* _Nullable)code error:(NSError* _Nullable* _Nullable)error; -/** - * Lookup the contact object associated with the given userID. The -id is the byte representation of an id. -This will reject if that id is malformed. The LookupCallback will return -the associated contact if it exists. - */ -- (BOOL)lookup:(NSData* _Nullable)idBytes callback:(id<BindingsLookupCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * MultiLookup Looks for the contact object associated with all given userIDs. -The ids are the byte representation of an id stored in an IDList object. -This will reject if that id is malformed or if the indexing on the IDList -object is wrong. The MultiLookupCallback will return with all contacts -returned within the timeout. - */ -- (BOOL)multiLookup:(BindingsIdList* _Nullable)ids callback:(id<BindingsMultiLookupCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * Register registers a user with user discovery. Will return an error if the -network signatures are malformed or if the username is taken. Usernames -cannot be changed after registration at this time. Will fail if the user is -already registered. -Identity does not go over cmix, it occurs over normal communications - */ -- (BOOL)register:(NSString* _Nullable)username error:(NSError* _Nullable* _Nullable)error; -/** - * RemoveFact removes a previously confirmed fact. Will fail if the passed fact string is -not well-formed or if the fact is not associated with this client. -Users cannot remove username facts and must instead remove the user. - */ -- (BOOL)removeFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * RemoveUser deletes a user. The fact sent must be the username. -This function preserves the username forever and makes it -unusable. - */ -- (BOOL)removeUser:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * Search for the passed Facts. The factList is the stringification of a -fact list object, look at /bindings/list.go for more on that object. -This will reject if that object is malformed. The SearchCallback will return -a list of contacts, each having the facts it hit against. -This is NOT intended to be used to search for multiple users at once, that -can have a privacy reduction. Instead, it is intended to be used to search -for a user where multiple pieces of information is known. - */ -- (BOOL)search:(NSString* _Nullable)fl callback:(id<BindingsSearchCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * SearchSingle searches for the passed Facts. The fact is the stringification of a -fact object, look at /bindings/contact.go for more on that object. -This will reject if that object is malformed. The SearchCallback will return -a list of contacts, each having the facts it hit against. -This only searches for a single fact at a time. It is intended to make some -simple use cases of the API easier. - */ -- (BOOL)searchSingle:(NSString* _Nullable)f callback:(id<BindingsSingleSearchCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * SetAlternativeUserDiscovery sets the alternativeUd object within manager. -Once set, any user discovery operation will go through the alternative -user discovery service. -To undo this operation, use UnsetAlternativeUserDiscovery. -The contact file is the already read in bytes, not the file path for the contact file. - */ -- (BOOL)setAlternativeUserDiscovery:(NSData* _Nullable)address cert:(NSData* _Nullable)cert contactFile:(NSData* _Nullable)contactFile error:(NSError* _Nullable* _Nullable)error; -/** - * UnsetAlternativeUserDiscovery clears out the information from -the Manager object. - */ -- (BOOL)unsetAlternativeUserDiscovery:(NSError* _Nullable* _Nullable)error; -@end - -/** - * Error codes - */ -FOUNDATION_EXPORT NSString* _Nonnull const BindingsUnrecognizedCode; -FOUNDATION_EXPORT NSString* _Nonnull const BindingsUnrecognizedMessage; - -/** - * CompressJpeg takes a JPEG image in byte format -and compresses it based on desired output size - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsCompressJpeg(NSData* _Nullable imgBytes, NSError* _Nullable* _Nullable error); - -/** - * CompressJpegForPreview takes a JPEG image in byte format -and compresses it based on desired output size - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsCompressJpegForPreview(NSData* _Nullable imgBytes, NSError* _Nullable* _Nullable error); - -/** - * DownloadAndVerifySignedNdfWithUrl retrieves the NDF from a specified URL. -The NDF is processed into a protobuf containing a signature which -is verified using the cert string passed in. The NDF is returned as marshaled -byte data which may be used to start a client. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadAndVerifySignedNdfWithUrl(NSString* _Nullable url, NSString* _Nullable cert, NSError* _Nullable* _Nullable error); - -/** - * DownloadDAppRegistrationDB returns a []byte containing -the JSON data describing registered dApps. -See https://git.xx.network/elixxir/registered-dapps - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadDAppRegistrationDB(NSError* _Nullable* _Nullable error); - -/** - * DownloadErrorDB returns a []byte containing the JSON data -describing client errors. -See https://git.xx.network/elixxir/client-error-database/ - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadErrorDB(NSError* _Nullable* _Nullable error); - -/** - * DumpStack returns a string with the stack trace of every running thread. - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsDumpStack(NSError* _Nullable* _Nullable error); - -/** - * EnableGrpcLogs sets GRPC trace logging - */ -FOUNDATION_EXPORT void BindingsEnableGrpcLogs(id<BindingsLogWriter> _Nullable writer); - -/** - * ErrorStringToUserFriendlyMessage takes a passed in errStr which will be -a backend generated error. These may be error specifically written by -the backend team or lower level errors gotten from low level dependencies. -This function will parse the error string for common errors provided from -errToUserErr to provide a more user-friendly error message for the front end. -If the error is not common, some simple parsing is done on the error message -to make it more user-accessible, removing backend specific jargon. - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsErrorStringToUserFriendlyMessage(NSString* _Nullable errStr); - -/** - * GenerateSecret creates a secret password using a system-based -pseudorandom number generator. It takes 1 parameter, `numBytes`, -which should be set to 32, but can be set higher in certain cases. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsGenerateSecret(long numBytes); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetCMIXParams(NSError* _Nullable* _Nullable error); - -/** - * returns a previously created client. IF be used if the garbage collector -removes the client instance on the app side. Is NOT thread safe relative to -login, newClient, or newPrecannedClient - */ -FOUNDATION_EXPORT BindingsClient* _Nullable BindingsGetClientSingleton(void); - -/** - * GetDependencies returns the api DEPENDENCIES - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetDependencies(void); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetE2EParams(NSError* _Nullable* _Nullable error); - -/** - * GetGitVersion rturns the api GITVERSION - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetGitVersion(void); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetNetworkParams(NSError* _Nullable* _Nullable error); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetUnsafeParams(NSError* _Nullable* _Nullable error); - -/** - * GetVersion returns the api SEMVER - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetVersion(void); - -/** - * InitializeBackup starts the backup processes that returns backup updates when -they occur. Any time an event occurs that changes the contents of the backup, -such as adding or deleting a contact, the backup is triggered and an -encrypted backup is generated and returned on the updateBackupCb callback. -Call this function only when enabling backup if it has not already been -initialized or when the user wants to change their password. -To resume backup process on app recovery, use ResumeBackup. - */ -FOUNDATION_EXPORT BindingsBackup* _Nullable BindingsInitializeBackup(NSString* _Nullable password, id<BindingsUpdateBackupFunc> _Nullable updateBackupCb, BindingsClient* _Nullable c, NSError* _Nullable* _Nullable error); - -/** - * LoadSecretWithMnemonic loads the secret stored from the call to -StoreSecretWithMnemonic. The path given should be the same filepath -as the path given in StoreSecretWithMnemonic. There should be a file -in this path called ".recovery". This operation is not tied -to client operations, as the user will not have a client when trying to -recover their account. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsLoadSecretWithMnemonic(NSString* _Nullable mnemonic, NSString* _Nullable path, NSError* _Nullable* _Nullable error); - -/** - * sets level of logging. All logs the set level and above will be displayed -options are: - TRACE - 0 - DEBUG - 1 - INFO - 2 - WARN - 3 - ERROR - 4 - CRITICAL - 5 - FATAL - 6 -The default state without updates is: INFO - */ -FOUNDATION_EXPORT BOOL BindingsLogLevel(long level, NSError* _Nullable* _Nullable error); - -/** - * Login will load an existing client from the storageDir -using the password. This will fail if the client doesn't exist or -the password is incorrect. -The password is passed as a byte array so that it can be cleared from -memory and stored as securely as possible using the memguard library. -Login does not block on network connection, and instead loads and -starts subprocesses to perform network operations. - */ -FOUNDATION_EXPORT BindingsClient* _Nullable BindingsLogin(NSString* _Nullable storageDir, NSData* _Nullable password, NSString* _Nullable parameters, NSError* _Nullable* _Nullable error); - -/** - * MakeIdList creates a new empty IdList. - */ -FOUNDATION_EXPORT BindingsIdList* _Nullable BindingsMakeIdList(void); - -FOUNDATION_EXPORT BindingsIntList* _Nullable BindingsMakeIntList(void); - -/** - * NewClient creates client storage, generates keys, connects, and registers -with the network. Note that this does not register a username/identity, but -merely creates a new cryptographic identity for adding such information -at a later date. - -Users of this function should delete the storage directory on error. - */ -FOUNDATION_EXPORT BOOL BindingsNewClient(NSString* _Nullable network, NSString* _Nullable storageDir, NSData* _Nullable password, NSString* _Nullable regCode, NSError* _Nullable* _Nullable error); - -/** - * NewClientFromBackup constructs a new Client from an encrypted backup. The backup -is decrypted using the backupPassphrase. On success a successful client creation, -the function will return a JSON encoded list of the E2E partners -contained in the backup and a json-encoded string of the parameters stored in the backup - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsNewClientFromBackup(NSString* _Nullable ndfJSON, NSString* _Nullable storageDir, NSData* _Nullable sessionPassword, NSData* _Nullable backupPassphrase, NSData* _Nullable backupFileContents, NSError* _Nullable* _Nullable error); - -/** - * NewDummyTrafficManager creates a DummyTraffic manager and initialises the -dummy traffic send thread. Note that the manager does not start sending dummy -traffic until its status is set to true using DummyTraffic.SetStatus. -The maxNumMessages is the upper bound of the random number of messages sent -each send. avgSendDeltaMS is the average duration, in milliseconds, to wait -between sends. Sends occur every avgSendDeltaMS +/- a random duration with an -upper bound of randomRangeMS. - */ -FOUNDATION_EXPORT BindingsDummyTraffic* _Nullable BindingsNewDummyTrafficManager(BindingsClient* _Nullable client, long maxNumMessages, long avgSendDeltaMS, long randomRangeMS, NSError* _Nullable* _Nullable error); - -/** - * fact object -creates a new fact. The factType must be either: - 0 - Username - 1 - Email - 2 - Phone Number -The fact must be well formed for the type and must not include commas or -semicolons. If it is not well formed, it will be rejected. Phone numbers -must have the two letter country codes appended. For the complete set of -validation, see /elixxir/primitives/fact/fact.go - */ -FOUNDATION_EXPORT BindingsFact* _Nullable BindingsNewFact(long factType, NSString* _Nullable factStr, NSError* _Nullable* _Nullable error); - -/** - * FactList - */ -FOUNDATION_EXPORT BindingsFactList* _Nullable BindingsNewFactList(void); - -/** - * NewFileTransferManager creates a new file transfer manager and starts the -sending and receiving threads. The receiveFunc is called everytime a new file -transfer is received. -The parameters string contains file transfer network configuration options -and is a JSON formatted string of the fileTransfer.Params object. If it is -left empty, then defaults are used. It is highly recommended that defaults -are used. If it is set, it must match the following format: - {"MaxThroughput":150000,"SendTimeout":500000000} -MaxThroughput is in bytes/sec and SendTimeout is in nanoseconds. - */ -FOUNDATION_EXPORT BindingsFileTransfer* _Nullable BindingsNewFileTransferManager(BindingsClient* _Nullable client, id<BindingsFileTransferReceiveFunc> _Nullable receiveFunc, NSString* _Nullable parameters, NSError* _Nullable* _Nullable error); - -/** - * NewGroupManager creates a new group chat manager. - */ -FOUNDATION_EXPORT BindingsGroupChat* _Nullable BindingsNewGroupManager(BindingsClient* _Nullable client, id<BindingsGroupRequestFunc> _Nullable requestFunc, id<BindingsGroupReceiveFunc> _Nullable receiveFunc, NSError* _Nullable* _Nullable error); - -/** - * NewPrecannedClient creates an insecure user with predetermined keys with nodes -It creates client storage, generates keys, connects, and registers -with the network. Note that this does not register a username/identity, but -merely creates a new cryptographic identity for adding such information -at a later date. - -Users of this function should delete the storage directory on error. - */ -FOUNDATION_EXPORT BOOL BindingsNewPrecannedClient(long precannedID, NSString* _Nullable network, NSString* _Nullable storageDir, NSData* _Nullable password, NSError* _Nullable* _Nullable error); - -/** - * NewUserDiscovery returns a new user discovery object. Only call this once. It must be called -after StartNetworkFollower is called and will fail if the network has never -been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscovery(BindingsClient* _Nullable client, NSError* _Nullable* _Nullable error); - -/** - * NewUserDiscoveryFromBackup returns a new user discovery object. It -wil set up the manager with the backup data. Pass into it the backed up -facts, one email and phone number each. This will add the registered facts -to the backed Store. Any one of these fields may be empty, -however both fields being empty will cause an error. Any other fact that is not -an email or phone number will return an error. You may only add a fact for the -accepted types once each. If you attempt to back up a fact type that has already -been backed up, an error will be returned. Anytime an error is returned, it means -the backup was not successful. -NOTE: Do not use this as a direct store operation. This feature is intended to add facts -to a backend store that have ALREADY BEEN REGISTERED on the account. -THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend. -Only call this once. It must be called after StartNetworkFollower -is called and will fail if the network has never been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscoveryFromBackup(BindingsClient* _Nullable client, NSString* _Nullable email, NSString* _Nullable phone, NSError* _Nullable* _Nullable error); - -/** - * NotificationsForMe Check if a notification received is for me -It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller, -a Type, and a source. These are as follows: - TYPE SOURCE DESCRIPTION - "default" recipient user ID A message with no association - "request" sender user ID A channel request has been received - "reset" sender user ID A channel reset has been received - "confirm" sender user ID A channel request has been accepted - "silent" sender user ID A message which should not be notified on - "e2e" sender user ID reception of an E2E message - "group" group ID reception of a group chat message - "endFT" sender user ID Last message sent confirming end of file transfer - "groupRQ" sender user ID Request from sender to join a group chat - */ -FOUNDATION_EXPORT BindingsManyNotificationForMeReport* _Nullable BindingsNotificationsForMe(NSString* _Nullable notifCSV, NSString* _Nullable preimages, NSError* _Nullable* _Nullable error); - -/** - * RegisterLogWriter registers a callback on which logs are written. - */ -FOUNDATION_EXPORT void BindingsRegisterLogWriter(id<BindingsLogWriter> _Nullable writer); - -/** - * RestoreContactsFromBackup takes as input the jason output of the -`NewClientFromBackup` function, unmarshals it into IDs, looks up -each ID in user discovery, and initiates a session reset request. -This function will not return until every id in the list has been sent a -request. It should be called again and again until it completes. -xxDK users should not use this function. This function is used by -the mobile phone apps and are not intended to be part of the xxDK. It -should be treated as internal functions specific to the phone apps. - */ -FOUNDATION_EXPORT BindingsRestoreContactsReport* _Nullable BindingsRestoreContactsFromBackup(NSData* _Nullable backupPartnerIDs, BindingsClient* _Nullable client, BindingsUserDiscovery* _Nullable udManager, id<BindingsLookupCallback> _Nullable lookupCB, id<BindingsRestoreContactsUpdater> _Nullable updatesCb); - -/** - * ResumeBackup starts the backup processes back up with a new callback after it -has been initialized. -Call this function only when resuming a backup that has already been -initialized or to replace the callback. -To start the backup for the first time or to use a new password, use -InitializeBackup. - */ -FOUNDATION_EXPORT BindingsBackup* _Nullable BindingsResumeBackup(id<BindingsUpdateBackupFunc> _Nullable cb, BindingsClient* _Nullable c, NSError* _Nullable* _Nullable error); - -/** - * SetTimeSource sets the network time to a custom source. - */ -FOUNDATION_EXPORT void BindingsSetTimeSource(id<BindingsTimeSource> _Nullable timeNow); - -/** - * StoreSecretWithMnemonic stores the secret tied with the mnemonic to storage. -Unlike other storage operations, this does not use EKV, as that is -intrinsically tied to client operations, which the user will not have while -trying to recover their account. As such, we store the encrypted data -directly, with a specified path. Path will be a valid filepath in which the -recover file will be stored as ".recovery". - -As an example, given "home/user/xxmessenger/storagePath", -the recovery file will be stored at -"home/user/xxmessenger/storagePath/.recovery" - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsStoreSecretWithMnemonic(NSData* _Nullable secret, NSString* _Nullable path, NSError* _Nullable* _Nullable error); - -/** - * Unmarshals a marshaled contact object, returns an error if it fails - */ -FOUNDATION_EXPORT BindingsContact* _Nullable BindingsUnmarshalContact(NSData* _Nullable b, NSError* _Nullable* _Nullable error); - -/** - * Unmarshals a marshaled send report object, returns an error if it fails - */ -FOUNDATION_EXPORT BindingsSendReport* _Nullable BindingsUnmarshalSendReport(NSData* _Nullable b, NSError* _Nullable* _Nullable error); - -/** - * UpdateCommonErrors takes the passed in contents of a JSON file and updates the -errToUserErr map with the contents of the json file. The JSON's expected format -conform with the commented examples provides in errToUserErr above. -NOTE that you should not pass in a file path, but a preloaded JSON file - */ -FOUNDATION_EXPORT BOOL BindingsUpdateCommonErrors(NSString* _Nullable jsonFile, NSError* _Nullable* _Nullable error); - -// skipped function WrapAPIClient with unsupported parameter or return types - - -// skipped function WrapUserDiscovery with unsupported parameter or return types - - -@class BindingsAuthConfirmCallback; - -@class BindingsAuthRequestCallback; - -@class BindingsAuthResetNotificationCallback; - -@class BindingsClientError; - -@class BindingsEventCallbackFunctionObject; - -@class BindingsFileTransferReceiveFunc; - -@class BindingsFileTransferReceivedProgressFunc; - -@class BindingsFileTransferSentProgressFunc; - -@class BindingsGroupReceiveFunc; - -@class BindingsGroupRequestFunc; - -@class BindingsListener; - -@class BindingsLogWriter; - -@class BindingsLookupCallback; - -@class BindingsMessageDeliveryCallback; - -@class BindingsMultiLookupCallback; - -@class BindingsNetworkHealthCallback; - -@class BindingsPreimageNotification; - -@class BindingsRestoreContactsUpdater; - -@class BindingsRoundCompletionCallback; - -@class BindingsRoundEventCallback; - -@class BindingsSearchCallback; - -@class BindingsSingleSearchCallback; - -@class BindingsTimeSource; - -@class BindingsUpdateBackupFunc; - -/** - * AuthConfirmCallback notifies the register whenever they receive an auth -request confirmation - */ -@interface BindingsAuthConfirmCallback : NSObject <goSeqRefInterface, BindingsAuthConfirmCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)partner; -@end - -/** - * AuthRequestCallback notifies the register whenever they receive an auth -request - */ -@interface BindingsAuthRequestCallback : NSObject <goSeqRefInterface, BindingsAuthRequestCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -/** - * AuthRequestCallback notifies the register whenever they receive an auth -request - */ -@interface BindingsAuthResetNotificationCallback : NSObject <goSeqRefInterface, BindingsAuthResetNotificationCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@interface BindingsClientError : NSObject <goSeqRefInterface, BindingsClientError> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)report:(NSString* _Nullable)source message:(NSString* _Nullable)message trace:(NSString* _Nullable)trace; -@end - -/** - * EventCallbackFunctionObject bindings interface which contains function -that implements the EventCallbackFunction - */ -@interface BindingsEventCallbackFunctionObject : NSObject <goSeqRefInterface, BindingsEventCallbackFunctionObject> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)reportEvent:(long)priority category:(NSString* _Nullable)category evtType:(NSString* _Nullable)evtType details:(NSString* _Nullable)details; -@end - -/** - * FileTransferReceiveFunc contains a function callback that notifies the -receiver of an incoming file transfer. It is called on the reception of the -initial file transfer message. - */ -@interface BindingsFileTransferReceiveFunc : NSObject <goSeqRefInterface, BindingsFileTransferReceiveFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)receiveCallback:(NSData* _Nullable)tid fileName:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType sender:(NSData* _Nullable)sender size:(long)size preview:(NSData* _Nullable)preview; -@end - -/** - * FileTransferReceivedProgressFunc contains a function callback that tracks the -progress of receiving a file. It is called when a file part is received, the -transfer completes, or on error. - */ -@interface BindingsFileTransferReceivedProgressFunc : NSObject <goSeqRefInterface, BindingsFileTransferReceivedProgressFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)receivedProgressCallback:(BOOL)completed received:(long)received total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -/** - * FileTransferSentProgressFunc contains a function callback that tracks the -progress of sending a file. It is called when a file part is sent, a file -part arrives, the transfer completes, or on error. - */ -@interface BindingsFileTransferSentProgressFunc : NSObject <goSeqRefInterface, BindingsFileTransferSentProgressFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)sentProgressCallback:(BOOL)completed sent:(long)sent arrived:(long)arrived total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -/** - * GroupReceiveFunc contains a function callback that is called when a group -message is received. - */ -@interface BindingsGroupReceiveFunc : NSObject <goSeqRefInterface, BindingsGroupReceiveFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)groupReceiveCallback:(BindingsGroupMessageReceive* _Nullable)msg; -@end - -/** - * GroupRequestFunc contains a function callback that is called when a group -request is received. - */ -@interface BindingsGroupRequestFunc : NSObject <goSeqRefInterface, BindingsGroupRequestFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)groupRequestCallback:(BindingsGroup* _Nullable)g; -@end - -/** - * Listener provides a callback to hear a message -An object implementing this interface can be called back when the client -gets a message of the type that the registerer specified at registration -time. - */ -@interface BindingsListener : NSObject <goSeqRefInterface, BindingsListener> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * Hear is called to receive a message in the UI - */ -- (void)hear:(BindingsMessage* _Nullable)message; -/** - * Returns a name, used for debugging - */ -- (NSString* _Nonnull)name; -@end - -@interface BindingsLogWriter : NSObject <goSeqRefInterface, BindingsLogWriter> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)log:(NSString* _Nullable)p0; -@end - -/** - * LookupCallback returns the result of a single lookup - */ -@interface BindingsLookupCallback : NSObject <goSeqRefInterface, BindingsLookupCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -/** - * MessageDeliveryCallback gets called on the determination if all events -related to a message send were successful. - */ -@interface BindingsMessageDeliveryCallback : NSObject <goSeqRefInterface, BindingsMessageDeliveryCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(NSData* _Nullable)msgID delivered:(BOOL)delivered timedOut:(BOOL)timedOut roundResults:(NSData* _Nullable)roundResults; -@end - -/** - * MultiLookupCallback returns the result of many parallel lookups - */ -@interface BindingsMultiLookupCallback : NSObject <goSeqRefInterface, BindingsMultiLookupCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContactList* _Nullable)Succeeded failed:(BindingsIdList* _Nullable)failed errors:(NSString* _Nullable)errors; -@end - -/** - * A callback when which is used to receive notification if network health -changes - */ -@interface BindingsNetworkHealthCallback : NSObject <goSeqRefInterface, BindingsNetworkHealthCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BOOL)p0; -@end - -@interface BindingsPreimageNotification : NSObject <goSeqRefInterface, BindingsPreimageNotification> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)notify:(NSData* _Nullable)identity deleted:(BOOL)deleted; -@end - -/** - * RestoreContactsUpdater interface provides a callback function -for receiving update information from RestoreContactsFromBackup. - */ -@interface BindingsRestoreContactsUpdater : NSObject <goSeqRefInterface, BindingsRestoreContactsUpdater> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * RestoreContactsCallback is called to report the current # of contacts -that have been found and how many have been restored -against the total number that need to be -processed. If an error occurs it it set on the err variable as a -plain string. - */ -- (void)restoreContactsCallback:(long)numFound numRestored:(long)numRestored total:(long)total err:(NSString* _Nullable)err; -@end - -/** - * RoundCompletionCallback is returned when the completion of a round is known. - */ -@interface BindingsRoundCompletionCallback : NSObject <goSeqRefInterface, BindingsRoundCompletionCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(long)rid success:(BOOL)success timedOut:(BOOL)timedOut; -@end - -/** - * RoundEventCallback handles waiting on the exact state of a round on -the cMix network. - */ -@interface BindingsRoundEventCallback : NSObject <goSeqRefInterface, BindingsRoundEventCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(long)rid state:(long)state timedOut:(BOOL)timedOut; -@end - -/** - * SearchCallback returns the result of a search - */ -@interface BindingsSearchCallback : NSObject <goSeqRefInterface, BindingsSearchCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContactList* _Nullable)contacts error:(NSString* _Nullable)error; -@end - -/** - * SingleSearchCallback returns the result of a single search - */ -@interface BindingsSingleSearchCallback : NSObject <goSeqRefInterface, BindingsSingleSearchCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@interface BindingsTimeSource : NSObject <goSeqRefInterface, BindingsTimeSource> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (int64_t)nowMs; -@end - -/** - * UpdateBackupFunc contains a function callback that returns new backups. - */ -@interface BindingsUpdateBackupFunc : NSObject <goSeqRefInterface, BindingsUpdateBackupFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)updateBackup:(NSData* _Nullable)encryptedBackup; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/Universe.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/Universe.objc.h deleted file mode 100644 index 019e7502d581983722a15bf30799e85cbc5dd766..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/Universe.objc.h +++ /dev/null @@ -1,29 +0,0 @@ -// Objective-C API for talking to Go package. -// gobind -lang=objc -// -// File is generated by gobind. Do not edit. - -#ifndef __Universe_H__ -#define __Universe_H__ - -@import Foundation; -#include "ref.h" - -@protocol Universeerror; -@class Universeerror; - -@protocol Universeerror <NSObject> -- (NSString* _Nonnull)error; -@end - -@class Universeerror; - -@interface Universeerror : NSError <goSeqRefInterface, Universeerror> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (NSString* _Nonnull)error; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/ref.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/ref.h deleted file mode 100644 index b8036a4d85c7387f3def61473a071b5d8c4c8208..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/ref.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef __GO_REF_HDR__ -#define __GO_REF_HDR__ - -#include <Foundation/Foundation.h> - -// GoSeqRef is an object tagged with an integer for passing back and -// forth across the language boundary. A GoSeqRef may represent either -// an instance of a Go object, or an Objective-C object passed to Go. -// The explicit allocation of a GoSeqRef is used to pin a Go object -// when it is passed to Objective-C. The Go seq package maintains a -// reference to the Go object in a map keyed by the refnum along with -// a reference count. When the reference count reaches zero, the Go -// seq package will clear the corresponding entry in the map. -@interface GoSeqRef : NSObject { -} -@property(readonly) int32_t refnum; -@property(strong) id obj; // NULL when representing a Go object. - -// new GoSeqRef object to proxy a Go object. The refnum must be -// provided from Go side. -- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj; - -- (int32_t)incNum; - -@end - -@protocol goSeqRefInterface --(GoSeqRef*) _ref; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Modules/module.modulemap b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Modules/module.modulemap deleted file mode 100644 index 4316a5b24058edfc18ffb2dc7f7a982e8353441a..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Modules/module.modulemap +++ /dev/null @@ -1,8 +0,0 @@ -framework module "Bindings" { - header "ref.h" - header "Bindings.objc.h" - header "Universe.objc.h" - header "Bindings.h" - - export * -} \ No newline at end of file diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Resources/Info.plist b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Resources/Info.plist deleted file mode 100644 index 0d1a4b8ab9b1fc8e9357197398f73353470cb636..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Resources/Info.plist +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> - <plist version="1.0"> - <dict> - </dict> - </plist> diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Bindings b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Bindings deleted file mode 100644 index 1e3b1dc25b4d65bf1e21a5d169dc3ae4de6c4fd7..0000000000000000000000000000000000000000 Binary files a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Bindings and /dev/null differ diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/Bindings.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/Bindings.h deleted file mode 100644 index 8906a7da239705b790cb2bb64de92f806640cb38..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/Bindings.h +++ /dev/null @@ -1,13 +0,0 @@ - -// Objective-C API for talking to the following Go packages -// -// gitlab.com/elixxir/client/bindings -// -// File is generated by gomobile bind. Do not edit. -#ifndef __Bindings_FRAMEWORK_H__ -#define __Bindings_FRAMEWORK_H__ - -#include "Bindings.objc.h" -#include "Universe.objc.h" - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/Bindings.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/Bindings.objc.h deleted file mode 100644 index 32bf6d116888f787ced27b01b95cb4e1b2c1138b..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/Bindings.objc.h +++ /dev/null @@ -1,2083 +0,0 @@ -// Objective-C API for talking to gitlab.com/elixxir/client/bindings Go package. -// gobind -lang=objc gitlab.com/elixxir/client/bindings -// -// File is generated by gobind. Do not edit. - -#ifndef __Bindings_H__ -#define __Bindings_H__ - -@import Foundation; -#include "ref.h" -#include "Universe.objc.h" - - -@class BindingsBackup; -@class BindingsBackupReport; -@class BindingsClient; -@class BindingsContact; -@class BindingsContactList; -@class BindingsDummyTraffic; -@class BindingsFact; -@class BindingsFactList; -@class BindingsFilePartTracker; -@class BindingsFileTransfer; -@class BindingsGroup; -@class BindingsGroupChat; -@class BindingsGroupMember; -@class BindingsGroupMembership; -@class BindingsGroupMessageReceive; -@class BindingsGroupReportDisk; -@class BindingsGroupSendReport; -@class BindingsIdList; -@class BindingsIntList; -@class BindingsManyNotificationForMeReport; -@class BindingsMessage; -@class BindingsNewGroupReport; -@class BindingsNodeRegistrationsStatus; -@class BindingsNotificationForMeReport; -@class BindingsRestoreContactsReport; -@class BindingsRoundList; -@class BindingsSendReport; -@class BindingsSendReportDisk; -@class BindingsUnregister; -@class BindingsUser; -@class BindingsUserDiscovery; -@protocol BindingsAuthConfirmCallback; -@class BindingsAuthConfirmCallback; -@protocol BindingsAuthRequestCallback; -@class BindingsAuthRequestCallback; -@protocol BindingsAuthResetNotificationCallback; -@class BindingsAuthResetNotificationCallback; -@protocol BindingsClientError; -@class BindingsClientError; -@protocol BindingsEventCallbackFunctionObject; -@class BindingsEventCallbackFunctionObject; -@protocol BindingsFileTransferReceiveFunc; -@class BindingsFileTransferReceiveFunc; -@protocol BindingsFileTransferReceivedProgressFunc; -@class BindingsFileTransferReceivedProgressFunc; -@protocol BindingsFileTransferSentProgressFunc; -@class BindingsFileTransferSentProgressFunc; -@protocol BindingsGroupReceiveFunc; -@class BindingsGroupReceiveFunc; -@protocol BindingsGroupRequestFunc; -@class BindingsGroupRequestFunc; -@protocol BindingsListener; -@class BindingsListener; -@protocol BindingsLogWriter; -@class BindingsLogWriter; -@protocol BindingsLookupCallback; -@class BindingsLookupCallback; -@protocol BindingsMessageDeliveryCallback; -@class BindingsMessageDeliveryCallback; -@protocol BindingsMultiLookupCallback; -@class BindingsMultiLookupCallback; -@protocol BindingsNetworkHealthCallback; -@class BindingsNetworkHealthCallback; -@protocol BindingsPreimageNotification; -@class BindingsPreimageNotification; -@protocol BindingsRestoreContactsUpdater; -@class BindingsRestoreContactsUpdater; -@protocol BindingsRoundCompletionCallback; -@class BindingsRoundCompletionCallback; -@protocol BindingsRoundEventCallback; -@class BindingsRoundEventCallback; -@protocol BindingsSearchCallback; -@class BindingsSearchCallback; -@protocol BindingsSingleSearchCallback; -@class BindingsSingleSearchCallback; -@protocol BindingsTimeSource; -@class BindingsTimeSource; -@protocol BindingsUpdateBackupFunc; -@class BindingsUpdateBackupFunc; - -@protocol BindingsAuthConfirmCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)partner; -@end - -@protocol BindingsAuthRequestCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@protocol BindingsAuthResetNotificationCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@protocol BindingsClientError <NSObject> -- (void)report:(NSString* _Nullable)source message:(NSString* _Nullable)message trace:(NSString* _Nullable)trace; -@end - -@protocol BindingsEventCallbackFunctionObject <NSObject> -- (void)reportEvent:(long)priority category:(NSString* _Nullable)category evtType:(NSString* _Nullable)evtType details:(NSString* _Nullable)details; -@end - -@protocol BindingsFileTransferReceiveFunc <NSObject> -- (void)receiveCallback:(NSData* _Nullable)tid fileName:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType sender:(NSData* _Nullable)sender size:(long)size preview:(NSData* _Nullable)preview; -@end - -@protocol BindingsFileTransferReceivedProgressFunc <NSObject> -- (void)receivedProgressCallback:(BOOL)completed received:(long)received total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -@protocol BindingsFileTransferSentProgressFunc <NSObject> -- (void)sentProgressCallback:(BOOL)completed sent:(long)sent arrived:(long)arrived total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -@protocol BindingsGroupReceiveFunc <NSObject> -- (void)groupReceiveCallback:(BindingsGroupMessageReceive* _Nullable)msg; -@end - -@protocol BindingsGroupRequestFunc <NSObject> -- (void)groupRequestCallback:(BindingsGroup* _Nullable)g; -@end - -@protocol BindingsListener <NSObject> -/** - * Hear is called to receive a message in the UI - */ -- (void)hear:(BindingsMessage* _Nullable)message; -/** - * Returns a name, used for debugging - */ -- (NSString* _Nonnull)name; -@end - -@protocol BindingsLogWriter <NSObject> -- (void)log:(NSString* _Nullable)p0; -@end - -@protocol BindingsLookupCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@protocol BindingsMessageDeliveryCallback <NSObject> -- (void)eventCallback:(NSData* _Nullable)msgID delivered:(BOOL)delivered timedOut:(BOOL)timedOut roundResults:(NSData* _Nullable)roundResults; -@end - -@protocol BindingsMultiLookupCallback <NSObject> -- (void)callback:(BindingsContactList* _Nullable)Succeeded failed:(BindingsIdList* _Nullable)failed errors:(NSString* _Nullable)errors; -@end - -@protocol BindingsNetworkHealthCallback <NSObject> -- (void)callback:(BOOL)p0; -@end - -@protocol BindingsPreimageNotification <NSObject> -- (void)notify:(NSData* _Nullable)identity deleted:(BOOL)deleted; -@end - -@protocol BindingsRestoreContactsUpdater <NSObject> -/** - * RestoreContactsCallback is called to report the current # of contacts -that have been found and how many have been restored -against the total number that need to be -processed. If an error occurs it it set on the err variable as a -plain string. - */ -- (void)restoreContactsCallback:(long)numFound numRestored:(long)numRestored total:(long)total err:(NSString* _Nullable)err; -@end - -@protocol BindingsRoundCompletionCallback <NSObject> -- (void)eventCallback:(long)rid success:(BOOL)success timedOut:(BOOL)timedOut; -@end - -@protocol BindingsRoundEventCallback <NSObject> -- (void)eventCallback:(long)rid state:(long)state timedOut:(BOOL)timedOut; -@end - -@protocol BindingsSearchCallback <NSObject> -- (void)callback:(BindingsContactList* _Nullable)contacts error:(NSString* _Nullable)error; -@end - -@protocol BindingsSingleSearchCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@protocol BindingsTimeSource <NSObject> -- (int64_t)nowMs; -@end - -@protocol BindingsUpdateBackupFunc <NSObject> -- (void)updateBackup:(NSData* _Nullable)encryptedBackup; -@end - -@interface BindingsBackup : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * AddJson stores a passed in json string in the backup structure - */ -- (void)addJson:(NSString* _Nullable)json; -/** - * IsBackupRunning returns true if the backup has been initialized and is -running. Returns false if it has been stopped. - */ -- (BOOL)isBackupRunning; -/** - * StopBackup stops the backup processes and deletes the user's password from -storage. To enable backups again, call InitializeBackup. - */ -- (BOOL)stopBackup:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsBackupReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field BackupReport.RestoredContacts with unsupported type: []*gitlab.com/xx_network/primitives/id.ID - -@property (nonatomic) NSString* _Nonnull params; -@end - -/** - * BindingsClient wraps the api.Client, implementing additional functions -to support the gomobile Client interface - */ -@interface BindingsClient : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BOOL)confirmAuthenticatedChannel:(NSData* _Nullable)recipientMarshaled ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteAllRequests clears all requests from Client's auth storage. - */ -- (BOOL)deleteAllRequests:(NSError* _Nullable* _Nullable)error; -/** - * DeleteContact is a function which removes a contact from Client's storage - */ -- (BOOL)deleteContact:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteReceiveRequests clears receive requests from Client's auth storage. - */ -- (BOOL)deleteReceiveRequests:(NSError* _Nullable* _Nullable)error; -/** - * DeleteRequest will delete a request, agnostic of request type -for the given partner ID. If no request exists for this -partner ID an error will be returned. - */ -- (BOOL)deleteRequest:(NSData* _Nullable)requesterUserId error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteSentRequests clears sent requests from Client's auth storage. - */ -- (BOOL)deleteSentRequests:(NSError* _Nullable* _Nullable)error; -// skipped method Client.GetInternalClient with unsupported parameter or return types - -/** - * GetNodeRegistrationStatus returns a struct with the number of nodes the -client is registered with and the number total. - */ -- (BindingsNodeRegistrationsStatus* _Nullable)getNodeRegistrationStatus:(NSError* _Nullable* _Nullable)error; -/** - * GetPartners returns a list of - */ -- (NSData* _Nullable)getPartners:(NSError* _Nullable* _Nullable)error; -/** - * GetPreferredBins returns the geographic bin or bins that the provided two -character country code is a part of. The bins are returned as CSV. - */ -- (NSString* _Nonnull)getPreferredBins:(NSString* _Nullable)countryCode error:(NSError* _Nullable* _Nullable)error; -- (NSString* _Nonnull)getPreimages:(NSData* _Nullable)identity; -// skipped method Client.GetRateLimitParams with unsupported parameter or return types - -- (NSString* _Nonnull)getRelationshipFingerprint:(NSData* _Nullable)partnerID error:(NSError* _Nullable* _Nullable)error; -/** - * Returns a user object from which all information about the current user -can be gleaned - */ -- (BindingsUser* _Nullable)getUser; -/** - * HasRunningProcessies checks if any background threads are running. -returns true if none are running. This is meant to be -used when NetworkFollowerStatus() returns Stopping. -Due to the handling of comms on iOS, where the OS can -block indefiently, it may not enter the stopped -state apropreatly. This can be used instead. - */ -- (BOOL)hasRunningProcessies; -/** - * returns true if the network is read to be in a healthy state where -messages can be sent - */ -- (BOOL)isNetworkHealthy; -- (BindingsContact* _Nullable)makePrecannedAuthenticatedChannel:(long)precannedID error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the state of the network follower. Returns: -Stopped - 0 -Starting - 1000 -Running - 2000 -Stopping - 3000 - */ -- (long)networkFollowerStatus; -- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetNotificationCallback> _Nullable)reset; -/** - * RegisterClientErrorCallback registers the callback to handle errors from the -long running threads controlled by StartNetworkFollower and StopNetworkFollower - */ -- (void)registerClientErrorCallback:(id<BindingsClientError> _Nullable)clientError; -/** - * RegisterEventCallback records the given function to receive -ReportableEvent objects. It returns the internal index -of the callback so that it can be deleted later. - */ -- (BOOL)registerEventCallback:(NSString* _Nullable)name myObj:(id<BindingsEventCallbackFunctionObject> _Nullable)myObj error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterForNotifications accepts firebase messaging token - */ -- (BOOL)registerForNotifications:(NSString* _Nullable)token error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterListener records and installs a listener for messages -matching specific uid, msgType, and/or username -Returns a ListenerUnregister interface which can be - -to register for any userID, pass in an id with length 0 or an id with -all zeroes - -to register for any message type, pass in a message type of 0 - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types - */ -- (BindingsUnregister* _Nullable)registerListener:(NSData* _Nullable)uid msgType:(long)msgType listener:(id<BindingsListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterNetworkHealthCB registers the network health callback to be called -any time the network health changes. Returns a unique ID that can be used to -unregister the network health callback. - */ -- (int64_t)registerNetworkHealthCB:(id<BindingsNetworkHealthCallback> _Nullable)nhc; -- (void)registerPreimageCallback:(NSData* _Nullable)identity pin:(id<BindingsPreimageNotification> _Nullable)pin; -/** - * RegisterRoundEventsHandler registers a callback interface for round -events. -The rid is the round the event attaches to -The timeoutMS is the number of milliseconds until the event fails, and the -validStates are a list of states (one per byte) on which the event gets -triggered -States: - 0x00 - PENDING (Never seen by client) - 0x01 - PRECOMPUTING - 0x02 - STANDBY - 0x03 - QUEUED - 0x04 - REALTIME - 0x05 - COMPLETED - 0x06 - FAILED -These states are defined in elixxir/primitives/states/state.go - */ -- (BindingsUnregister* _Nullable)registerRoundEventsHandler:(long)rid cb:(id<BindingsRoundEventCallback> _Nullable)cb timeoutMS:(long)timeoutMS il:(BindingsIntList* _Nullable)il; -- (void)replayRequests; -- (BOOL)requestAuthenticatedChannel:(NSData* _Nullable)recipientMarshaled meMarshaled:(NSData* _Nullable)meMarshaled message:(NSString* _Nullable)message ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -- (BOOL)resetSession:(NSData* _Nullable)recipientMarshaled meMarshaled:(NSData* _Nullable)meMarshaled message:(NSString* _Nullable)message ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * This will return the round the message was sent on if it is successfully sent -This can be used to register a round event to learn about message delivery. -on failure a round id of -1 is returned - */ -- (BOOL)sendCmix:(NSData* _Nullable)recipient contents:(NSData* _Nullable)contents parameters:(NSString* _Nullable)parameters ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * SendE2E sends an end-to-end payload to the provided recipient with -the provided msgType. Returns the list of rounds in which parts of -the message were sent or an error if it fails. - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types - */ -- (BindingsSendReport* _Nullable)sendE2E:(NSData* _Nullable)recipient payload:(NSData* _Nullable)payload messageType:(long)messageType parameters:(NSString* _Nullable)parameters error:(NSError* _Nullable* _Nullable)error; -/** - * SendUnsafe sends an unencrypted payload to the provided recipient -with the provided msgType. Returns the list of rounds in which parts -of the message were sent or an error if it fails. -NOTE: Do not use this function unless you know what you are doing. -This function always produces an error message in client logging. - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types with custom types - */ -- (BindingsRoundList* _Nullable)sendUnsafe:(NSData* _Nullable)recipient payload:(NSData* _Nullable)payload messageType:(long)messageType parameters:(NSString* _Nullable)parameters error:(NSError* _Nullable* _Nullable)error; -/** - * SetProxiedBins updates the host pool filter that filters out gateways that -are not in one of the specified bins. The provided bins should be CSV. - */ -- (BOOL)setProxiedBins:(NSString* _Nullable)binStringsCSV error:(NSError* _Nullable* _Nullable)error; -/** - * StartNetworkFollower kicks off the tracking of the network. It starts -long running network client threads and returns an object for checking -state and stopping those threads. -Call this when returning from sleep and close when going back to -sleep. -These threads may become a significant drain on battery when offline, ensure -they are stopped if there is no internet access -Threads Started: - - Network Follower (/network/follow.go) - tracks the network events and hands them off to workers for handling - - Historical Round Retrieval (/network/rounds/historical.go) - Retrieves data about rounds which are too old to be stored by the client - - Message Retrieval Worker Group (/network/rounds/retrieve.go) - Requests all messages in a given round from the gateway of the last node - - Message Handling Worker Group (/network/message/handle.go) - Decrypts and partitions messages when signals via the Switchboard - - Health Tracker (/network/health) - Via the network instance tracks the state of the network - - Garbled Messages (/network/message/garbled.go) - Can be signaled to check all recent messages which could be be decoded - Uses a message store on disk for persistence - - Critical Messages (/network/message/critical.go) - Ensures all protocol layer mandatory messages are sent - Uses a message store on disk for persistence - - KeyExchange Trigger (/keyExchange/trigger.go) - Responds to sent rekeys and executes them - - KeyExchange Confirm (/keyExchange/confirm.go) - Responds to confirmations of successful rekey operations - */ -- (BOOL)startNetworkFollower:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * StopNetworkFollower stops the network follower if it is running. -It returns errors if the Follower is in the wrong status to stop or if it -fails to stop it. -if the network follower is running and this fails, the client object will -most likely be in an unrecoverable state and need to be trashed. - */ -- (BOOL)stopNetworkFollower:(NSError* _Nullable* _Nullable)error; -/** - * UnregisterEventCallback deletes the callback identified by the -index. It returns an error if it fails. - */ -- (void)unregisterEventCallback:(NSString* _Nullable)name; -/** - * UnregisterForNotifications unregister user for notifications - */ -- (BOOL)unregisterForNotifications:(NSError* _Nullable* _Nullable)error; -- (void)unregisterNetworkHealthCB:(int64_t)funcID; -- (BOOL)verifyOwnership:(NSData* _Nullable)receivedMarshaled verifiedMarshaled:(NSData* _Nullable)verifiedMarshaled ret0_:(BOOL* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * WaitForMessageDelivery allows the caller to get notified if the rounds a -message was sent in successfully completed. Under the hood, this uses an API -which uses the internal round data, network historical round lookup, and -waiting on network events to determine what has (or will) occur. - -The callbacks will return at timeoutMS if no state update occurs - -This function takes the marshaled send report to ensure a memory leak does -not occur as a result of both sides of the bindings holding a reference to -the same pointer. - */ -- (BOOL)waitForMessageDelivery:(NSData* _Nullable)marshaledSendReport mdc:(id<BindingsMessageDeliveryCallback> _Nullable)mdc timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * WaitForNewtwork will block until either the network is healthy or the -passed timeout. It will return true if the network is healthy - */ -- (BOOL)waitForNetwork:(long)timeoutMS; -/** - * WaitForRoundCompletion allows the caller to get notified if a round -has completed (or failed). Under the hood, this uses an API which uses the internal -round data, network historical round lookup, and waiting on network events -to determine what has (or will) occur. - -The callbacks will return at timeoutMS if no state update occurs - */ -- (BOOL)waitForRoundCompletion:(long)roundID rec:(id<BindingsRoundCompletionCallback> _Nullable)rec timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * contact object - */ -@interface BindingsContact : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped method Contact.GetAPIContact with unsupported parameter or return types - -/** - * GetDHPublicKey returns the public key associated with the Contact. - */ -- (NSData* _Nullable)getDHPublicKey; -/** - * Returns a fact list for adding and getting facts to and from the contact - */ -- (BindingsFactList* _Nullable)getFactList; -/** - * GetID returns the user ID for this user. - */ -- (NSData* _Nullable)getID; -/** - * GetDHPublicKey returns hash of a DH proof of key ownership. - */ -- (NSData* _Nullable)getOwnershipProof; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsContactList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Gets a stored round ID at the given index - */ -- (BindingsContact* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the number of round IDs stored - */ -- (long)len; -@end - -/** - * DummyTraffic contains the file dummy traffic manager. The manager can be used -to set and get the status of the send thread. - */ -@interface BindingsDummyTraffic : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewDummyTrafficManager creates a DummyTraffic manager and initialises the -dummy traffic send thread. Note that the manager does not start sending dummy -traffic until its status is set to true using DummyTraffic.SetStatus. -The maxNumMessages is the upper bound of the random number of messages sent -each send. avgSendDeltaMS is the average duration, in milliseconds, to wait -between sends. Sends occur every avgSendDeltaMS +/- a random duration with an -upper bound of randomRangeMS. - */ -- (nullable instancetype)initManager:(BindingsClient* _Nullable)client maxNumMessages:(long)maxNumMessages avgSendDeltaMS:(long)avgSendDeltaMS randomRangeMS:(long)randomRangeMS; -/** - * GetStatus returns the current state of the dummy traffic send thread. It has -the following return values: - true = send thread is sending dummy messages - false = send thread is paused/stopped and not sending dummy messages -Note that this function does not return the status set by SetStatus directly; -it returns the current status of the send thread, which means any call to -SetStatus will have a small delay before it is returned by GetStatus. - */ -- (BOOL)getStatus; -/** - * SetStatus sets the state of the dummy traffic send thread, which determines -if the thread is running or paused. The possible statuses are: - true = send thread is sending dummy messages - false = send thread is paused/stopped and not sending dummy messages -Returns an error if the channel is full. -Note that this function cannot change the status of the send thread if it has -yet to be started or stopped. - */ -- (BOOL)setStatus:(BOOL)status error:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsFact : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * fact object -creates a new fact. The factType must be either: - 0 - Username - 1 - Email - 2 - Phone Number -The fact must be well formed for the type and must not include commas or -semicolons. If it is not well formed, it will be rejected. Phone numbers -must have the two letter country codes appended. For the complete set of -validation, see /elixxir/primitives/fact/fact.go - */ -- (nullable instancetype)init:(long)factType factStr:(NSString* _Nullable)factStr; -- (NSString* _Nonnull)get; -- (NSString* _Nonnull)stringify; -- (long)type; -@end - -@interface BindingsFactList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * FactList - */ -- (nullable instancetype)init; -- (BOOL)add:(NSString* _Nullable)factData factType:(long)factType error:(NSError* _Nullable* _Nullable)error; -- (BindingsFact* _Nullable)get:(long)i; -- (long)num; -- (NSString* _Nonnull)stringify:(NSError* _Nullable* _Nullable)error; -@end - -/** - * FilePartTracker contains the interfaces.FilePartTracker. - */ -@interface BindingsFilePartTracker : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetNumParts returns the total number of file parts in the transfer. - */ -- (long)getNumParts; -/** - * GetPartStatus returns the status of the file part with the given part number. -The possible values for the status are: -0 = unsent -1 = sent (sender has sent a part, but it has not arrived) -2 = arrived (sender has sent a part, and it has arrived) -3 = received (receiver has received a part) - */ -- (long)getPartStatus:(long)partNum; -@end - -/** - * FileTransfer contains the file transfer manager. - */ -@interface BindingsFileTransfer : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewFileTransferManager creates a new file transfer manager and starts the -sending and receiving threads. The receiveFunc is called everytime a new file -transfer is received. -The parameters string contains file transfer network configuration options -and is a JSON formatted string of the fileTransfer.Params object. If it is -left empty, then defaults are used. It is highly recommended that defaults -are used. If it is set, it must match the following format: - {"MaxThroughput":150000,"SendTimeout":500000000} -MaxThroughput is in bytes/sec and SendTimeout is in nanoseconds. - */ -- (nullable instancetype)initManager:(BindingsClient* _Nullable)client receiveFunc:(id<BindingsFileTransferReceiveFunc> _Nullable)receiveFunc parameters:(NSString* _Nullable)parameters; -/** - * CloseSend deletes a sent file transfer from the sent transfer map and from -storage once a transfer has completed or reached the retry limit. Returns an -error if the transfer has not run out of retries. - */ -- (BOOL)closeSend:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error; -/** - * GetMaxFileNameByteLength returns the maximum length, in bytes, allowed for a -file name. - */ -- (long)getMaxFileNameByteLength; -/** - * GetMaxFilePreviewSize returns the maximum file preview size, in bytes. - */ -- (long)getMaxFilePreviewSize; -/** - * GetMaxFileSize returns the maximum file size, in bytes, allowed to be -transferred. - */ -- (long)getMaxFileSize; -/** - * GetMaxFileTypeByteLength returns the maximum length, in bytes, allowed for a -file type. - */ -- (long)getMaxFileTypeByteLength; -/** - * Receive returns the fully assembled file on the completion of the transfer. -It deletes the transfer from the received transfer map and from storage. -Returns an error if the transfer is not complete, the full file cannot be -verified, or if the transfer cannot be found. - */ -- (NSData* _Nullable)receive:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterReceiveProgressCallback allows for the registration of a callback to -track the progress of an individual received file transfer. The callback will -be called immediately when added to report the current status of the -transfer. It will then call every time a file part is received, the transfer -completes, or an error occurs. It is called at most once ever period, which -means if events occur faster than the period, then they will not be reported -and instead, the progress will be reported once at the end of the period. -Once the callback reports that the transfer has completed, the recipient -can get the full file by calling Receive. -The period is specified in milliseconds. - */ -- (BOOL)registerReceiveProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferReceivedProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterSendProgressCallback allows for the registration of a callback to -track the progress of an individual sent file transfer. The callback will be -called immediately when added to report the current status of the transfer. -It will then call every time a file part is sent, a file part arrives, the -transfer completes, or an error occurs. It is called at most once every -period, which means if events occur faster than the period, then they will -not be reported and instead, the progress will be reported once at the end of -the period. -The period is specified in milliseconds. - */ -- (BOOL)registerSendProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -/** - * Send sends a file to the recipient. The sender must have an E2E relationship -with the recipient. -The file name is the name of the file to show a user. It has a max length of -48 bytes. -The file type identifies what type of file is being sent. It has a max length -of 8 bytes. -The file data cannot be larger than 256 kB -The retry float is the total amount of data to send relative to the data -size. Data will be resent on error and will resend up to [(1 + retry) * -fileSize]. -The preview stores a preview of the data (such as a thumbnail) and is -capped at 4 kB in size. -Returns a unique transfer ID used to identify the transfer. -PeriodMS is the duration, in milliseconds, to wait between progress callback -calls. Set this large enough to prevent spamming. - */ -- (NSData* _Nullable)send:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType fileData:(NSData* _Nullable)fileData recipientID:(NSData* _Nullable)recipientID retry:(float)retry preview:(NSData* _Nullable)preview progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * Group structure contains the identifying and membership information of a -group chat. - */ -@interface BindingsGroup : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetCreatedMS returns the time the group was created in milliseconds. This is -also the time the group requests were sent. - */ -- (int64_t)getCreatedMS; -/** - * GetCreatedNano returns the time the group was created in nanoseconds. This is -also the time the group requests were sent. - */ -- (int64_t)getCreatedNano; -/** - * GetID return the 33-byte unique group ID. - */ -- (NSData* _Nullable)getID; -/** - * GetInitMessage returns initial message sent with the group request. - */ -- (NSData* _Nullable)getInitMessage; -/** - * GetMembership returns a list of contacts, one for each member in the group. -The list is in order; the first contact is the leader/creator of the group. -All subsequent members are ordered by their ID. - */ -- (BindingsGroupMembership* _Nullable)getMembership; -/** - * GetName returns the name set by the user for the group. - */ -- (NSData* _Nullable)getName; -/** - * Serialize serializes the Group. - */ -- (NSData* _Nullable)serialize; -@end - -/** - * GroupChat object contains the group chat manager. - */ -@interface BindingsGroupChat : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetGroup returns the group with the group ID. If no group exists, then the -error "failed to find group" is returned. - */ -- (BindingsGroup* _Nullable)getGroup:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * GetGroups returns an IdList containing a list of group IDs that the user is a -part of. - */ -- (BindingsIdList* _Nullable)getGroups; -/** - * JoinGroup allows a user to join a group when they receive a request. The -caller must pass in the serialized bytes of a Group. - */ -- (BOOL)joinGroup:(NSData* _Nullable)serializedGroupData error:(NSError* _Nullable* _Nullable)error; -/** - * LeaveGroup deletes a group so a user no longer has access. - */ -- (BOOL)leaveGroup:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * MakeGroup creates a new group and sends a group request to all members in the -group. The ID of the new group, the rounds the requests were sent on, and the -status of the send are contained in NewGroupReport. - */ -- (BindingsNewGroupReport* _Nullable)makeGroup:(BindingsIdList* _Nullable)membership name:(NSData* _Nullable)name message:(NSData* _Nullable)message; -/** - * NumGroups returns the number of groups the user is a part of. - */ -- (long)numGroups; -/** - * ResendRequest resends a group request to all members in the group. The rounds -they were sent on and the status of the send are contained in NewGroupReport. - */ -- (BindingsNewGroupReport* _Nullable)resendRequest:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * Send sends the message to the specified group. Returns the round the messages -were sent on. - */ -- (BindingsGroupSendReport* _Nullable)send:(NSData* _Nullable)groupIdBytes message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * // -Member Structure -// -GroupMember represents a member in the group membership list. - */ -@interface BindingsGroupMember : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupMember.Member with unsupported type: gitlab.com/elixxir/crypto/group.Member - -// skipped method GroupMember.DeepCopy with unsupported parameter or return types - -// skipped method GroupMember.Equal with unsupported parameter or return types - -/** - * GetDhKey returns the byte representation of the public Diffie–Hellman key of -the member. - */ -- (NSData* _Nullable)getDhKey; -/** - * GetID returns the 33-byte user ID of the member. - */ -- (NSData* _Nullable)getID; -- (NSString* _Nonnull)goString; -- (NSData* _Nullable)serialize; -- (NSString* _Nonnull)string; -@end - -/** - * GroupMembership structure contains a list of members that are part of a -group. The first member is the group leader. - */ -@interface BindingsGroupMembership : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Get returns the member at the index. The member at index 0 is always the -group leader. An error is returned if the index is out of range. - */ -- (BindingsGroupMember* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Len returns the number of members in the group membership. - */ -- (long)len; -@end - -/** - * GroupMessageReceive contains a group message, its ID, and its data that a -user receives. - */ -@interface BindingsGroupMessageReceive : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupMessageReceive.MessageReceive with unsupported type: gitlab.com/elixxir/client/groupChat.MessageReceive - -/** - * GetEphemeralID returns the ephemeral ID of the recipient. - */ -- (int64_t)getEphemeralID; -/** - * GetGroupID returns the 33-byte group ID. - */ -- (NSData* _Nullable)getGroupID; -/** - * GetMessageID returns the message ID. - */ -- (NSData* _Nullable)getMessageID; -/** - * GetPayload returns the message payload. - */ -- (NSData* _Nullable)getPayload; -/** - * GetRecipientID returns the 33-byte user ID of the recipient. - */ -- (NSData* _Nullable)getRecipientID; -/** - * GetRoundID returns the ID of the round the message was sent on. - */ -- (int64_t)getRoundID; -/** - * GetRoundTimestampMS returns the timestamp, in milliseconds, of the round the -message was sent on. - */ -- (int64_t)getRoundTimestampMS; -/** - * GetRoundTimestampNano returns the timestamp, in nanoseconds, of the round the -message was sent on. - */ -- (int64_t)getRoundTimestampNano; -/** - * GetRoundURL returns the ID of the round the message was sent on. - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetSenderID returns the 33-byte user ID of the sender. - */ -- (NSData* _Nullable)getSenderID; -/** - * GetTimestampMS returns the message timestamp in milliseconds. - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message timestamp in nanoseconds. - */ -- (int64_t)getTimestampNano; -- (NSString* _Nonnull)string; -@end - -@interface BindingsGroupReportDisk : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupReportDisk.List with unsupported type: []gitlab.com/xx_network/primitives/id.Round - -@property (nonatomic) NSData* _Nullable grpId; -@property (nonatomic) long status; -@end - -/** - * GroupSendReport is returned when sending a group message. It contains the -round ID sent on and the timestamp of the send. - */ -@interface BindingsGroupSendReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetMessageID returns the ID of the round that the send occurred on. - */ -- (NSData* _Nullable)getMessageID; -/** - * GetRoundID returns the ID of the round that the send occurred on. - */ -- (int64_t)getRoundID; -/** - * GetRoundURL returns the URL of the round that the send occurred on. - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetTimestampMS returns the timestamp of the send in milliseconds. - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the timestamp of the send in nanoseconds. - */ -- (int64_t)getTimestampNano; -@end - -/** - * ID list -IdList contains a list of IDs. - */ -@interface BindingsIdList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Add appends the ID bytes to the end of the list. - */ -- (BOOL)add:(NSData* _Nullable)idBytes error:(NSError* _Nullable* _Nullable)error; -/** - * Get returns the ID at the index. An error is returned if the index is out of -range. - */ -- (NSData* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Len returns the number of IDs in the list. - */ -- (long)len; -@end - -@interface BindingsIntList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (void)add:(long)i; -- (BOOL)get:(long)i ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -- (long)len; -@end - -@interface BindingsManyNotificationForMeReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BindingsNotificationForMeReport* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -- (long)len; -@end - -/** - * Message is a message received from the cMix network in the clear -or that has been decrypted using established E2E keys. - */ -@interface BindingsMessage : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetID returns the id of the message - */ -- (NSData* _Nullable)getID; -/** - * GetMessageType returns the message's type - */ -- (long)getMessageType; -/** - * GetPayload returns the message's payload/contents - */ -- (NSData* _Nullable)getPayload; -/** - * GetRoundId returns the message's round ID - */ -- (int64_t)getRoundId; -/** - * GetRoundTimestampMS returns the message's round timestamp in milliseconds - */ -- (int64_t)getRoundTimestampMS; -/** - * GetRoundTimestampNano returns the message's round timestamp in nanoseconds - */ -- (int64_t)getRoundTimestampNano; -/** - * GetRoundURL returns the message's round URL - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetSender returns the message's sender ID, if available - */ -- (NSData* _Nullable)getSender; -/** - * GetTimestampMS returns the message's timestamp in milliseconds - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message's timestamp in nanoseconds - */ -- (int64_t)getTimestampNano; -@end - -/** - * NewGroupReport is returned when creating a new group and contains the ID of -the group, a list of rounds that the group requests were sent on, and the -status of the send. - */ -@interface BindingsNewGroupReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetError returns the string of an error. -Will be an empty string if no error occured - */ -- (NSString* _Nonnull)getError; -/** - * GetGroup returns the Group. - */ -- (BindingsGroup* _Nullable)getGroup; -/** - * GetRoundList returns the RoundList containing a list of rounds requests were -sent on. - */ -- (BindingsRoundList* _Nullable)getRoundList; -/** - * GetStatus returns the status of the requests sent when creating a new group. -status = 0 an error occurred before any requests could be sent - 1 all requests failed to send (call Resend Group) - 2 some request failed and some succeeded (call Resend Group) - 3, all requests sent successfully (call Resend Group) - */ -- (long)getStatus; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -- (BOOL)unmarshal:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * NodeRegistrationsStatus structure for returning node registration statuses -for bindings. - */ -@interface BindingsNodeRegistrationsStatus : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetRegistered returns the number of nodes registered with the client. - */ -- (long)getRegistered; -/** - * GetTotal return the total of nodes currently in the network. - */ -- (long)getTotal; -@end - -@interface BindingsNotificationForMeReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BOOL)forMe; -- (NSData* _Nullable)source; -- (NSString* _Nonnull)type; -@end - -/** - * RestoreContactsReport is a gomobile friendly report structure -for determining which IDs restored, which failed, and why. - */ -@interface BindingsRestoreContactsReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetErrorAt returns the error string at index - */ -- (NSString* _Nonnull)getErrorAt:(long)index; -/** - * GetFailedAt returns the failed ID at index - */ -- (NSData* _Nullable)getFailedAt:(long)index; -/** - * GetRestoreContactsError returns an error string. Empty if no error. - */ -- (NSString* _Nonnull)getRestoreContactsError; -/** - * GetRestoredAt returns the restored ID at index - */ -- (NSData* _Nullable)getRestoredAt:(long)index; -/** - * LenFailed returns the length of the ID's failed. - */ -- (long)lenFailed; -/** - * LenRestored returns the length of ID's restored. - */ -- (long)lenRestored; -@end - -@interface BindingsRoundList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Gets a stored round ID at the given index - */ -- (BOOL)get:(long)i ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the number of round IDs stored - */ -- (long)len; -@end - -/** - * the send report is the mechanisim by which sendE2E returns a single - */ -@interface BindingsSendReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (NSData* _Nullable)getMessageID; -- (BindingsRoundList* _Nullable)getRoundList; -- (NSString* _Nonnull)getRoundURL; -/** - * GetTimestampMS returns the message's timestamp in milliseconds - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message's timestamp in nanoseconds - */ -- (int64_t)getTimestampNano; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -- (BOOL)unmarshal:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsSendReportDisk : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field SendReportDisk.List with unsupported type: []gitlab.com/xx_network/primitives/id.Round - -@property (nonatomic) NSData* _Nullable mid; -@property (nonatomic) int64_t ts; -@end - -/** - * Generic Unregister - a generic return used for all callbacks which can be -unregistered -Interface which allows the un-registration of a listener - */ -@interface BindingsUnregister : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Call unregisters a callback - */ -- (void)unregister; -@end - -@interface BindingsUser : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BindingsContact* _Nullable)getContact; -- (NSData* _Nullable)getE2EDhPrivateKey; -- (NSData* _Nullable)getE2EDhPublicKey; -- (NSData* _Nullable)getReceptionID; -- (NSData* _Nullable)getReceptionRSAPrivateKeyPem; -- (NSData* _Nullable)getReceptionRSAPublicKeyPem; -- (NSData* _Nullable)getReceptionSalt; -- (NSData* _Nullable)getTransmissionID; -- (NSData* _Nullable)getTransmissionRSAPrivateKeyPem; -- (NSData* _Nullable)getTransmissionRSAPublicKeyPem; -- (NSData* _Nullable)getTransmissionSalt; -- (BOOL)isPrecanned; -@end - -@interface BindingsUserDiscovery : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewUserDiscovery returns a new user discovery object. Only call this once. It must be called -after StartNetworkFollower is called and will fail if the network has never -been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -- (nullable instancetype)init:(BindingsClient* _Nullable)client; -/** - * NewUserDiscoveryFromBackup returns a new user discovery object. It -wil set up the manager with the backup data. Pass into it the backed up -facts, one email and phone number each. This will add the registered facts -to the backed Store. Any one of these fields may be empty, -however both fields being empty will cause an error. Any other fact that is not -an email or phone number will return an error. You may only add a fact for the -accepted types once each. If you attempt to back up a fact type that has already -been backed up, an error will be returned. Anytime an error is returned, it means -the backup was not successful. -NOTE: Do not use this as a direct store operation. This feature is intended to add facts -to a backend store that have ALREADY BEEN REGISTERED on the account. -THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend. -Only call this once. It must be called after StartNetworkFollower -is called and will fail if the network has never been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -- (nullable instancetype)initFromBackup:(BindingsClient* _Nullable)client email:(NSString* _Nullable)email phone:(NSString* _Nullable)phone; -/** - * AddFact adds a fact for the user to user discovery. Will only succeed if the -user is already registered and the system does not have the fact currently -registered for any user. -Will fail if the fact string is not well formed. -This does not complete the fact registration process, it returns a -confirmation id instead. Over the communications system the fact is -associated with, a code will be sent. This confirmation ID needs to be -called along with the code to finalize the fact. - */ -- (NSString* _Nonnull)addFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * ConfirmFact confirms a fact first registered via AddFact. The confirmation ID comes from -AddFact while the code will come over the associated communications system - */ -- (BOOL)confirmFact:(NSString* _Nullable)confirmationID code:(NSString* _Nullable)code error:(NSError* _Nullable* _Nullable)error; -/** - * Lookup the contact object associated with the given userID. The -id is the byte representation of an id. -This will reject if that id is malformed. The LookupCallback will return -the associated contact if it exists. - */ -- (BOOL)lookup:(NSData* _Nullable)idBytes callback:(id<BindingsLookupCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * MultiLookup Looks for the contact object associated with all given userIDs. -The ids are the byte representation of an id stored in an IDList object. -This will reject if that id is malformed or if the indexing on the IDList -object is wrong. The MultiLookupCallback will return with all contacts -returned within the timeout. - */ -- (BOOL)multiLookup:(BindingsIdList* _Nullable)ids callback:(id<BindingsMultiLookupCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * Register registers a user with user discovery. Will return an error if the -network signatures are malformed or if the username is taken. Usernames -cannot be changed after registration at this time. Will fail if the user is -already registered. -Identity does not go over cmix, it occurs over normal communications - */ -- (BOOL)register:(NSString* _Nullable)username error:(NSError* _Nullable* _Nullable)error; -/** - * RemoveFact removes a previously confirmed fact. Will fail if the passed fact string is -not well-formed or if the fact is not associated with this client. -Users cannot remove username facts and must instead remove the user. - */ -- (BOOL)removeFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * RemoveUser deletes a user. The fact sent must be the username. -This function preserves the username forever and makes it -unusable. - */ -- (BOOL)removeUser:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * Search for the passed Facts. The factList is the stringification of a -fact list object, look at /bindings/list.go for more on that object. -This will reject if that object is malformed. The SearchCallback will return -a list of contacts, each having the facts it hit against. -This is NOT intended to be used to search for multiple users at once, that -can have a privacy reduction. Instead, it is intended to be used to search -for a user where multiple pieces of information is known. - */ -- (BOOL)search:(NSString* _Nullable)fl callback:(id<BindingsSearchCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * SearchSingle searches for the passed Facts. The fact is the stringification of a -fact object, look at /bindings/contact.go for more on that object. -This will reject if that object is malformed. The SearchCallback will return -a list of contacts, each having the facts it hit against. -This only searches for a single fact at a time. It is intended to make some -simple use cases of the API easier. - */ -- (BOOL)searchSingle:(NSString* _Nullable)f callback:(id<BindingsSingleSearchCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * SetAlternativeUserDiscovery sets the alternativeUd object within manager. -Once set, any user discovery operation will go through the alternative -user discovery service. -To undo this operation, use UnsetAlternativeUserDiscovery. -The contact file is the already read in bytes, not the file path for the contact file. - */ -- (BOOL)setAlternativeUserDiscovery:(NSData* _Nullable)address cert:(NSData* _Nullable)cert contactFile:(NSData* _Nullable)contactFile error:(NSError* _Nullable* _Nullable)error; -/** - * UnsetAlternativeUserDiscovery clears out the information from -the Manager object. - */ -- (BOOL)unsetAlternativeUserDiscovery:(NSError* _Nullable* _Nullable)error; -@end - -/** - * Error codes - */ -FOUNDATION_EXPORT NSString* _Nonnull const BindingsUnrecognizedCode; -FOUNDATION_EXPORT NSString* _Nonnull const BindingsUnrecognizedMessage; - -/** - * CompressJpeg takes a JPEG image in byte format -and compresses it based on desired output size - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsCompressJpeg(NSData* _Nullable imgBytes, NSError* _Nullable* _Nullable error); - -/** - * CompressJpegForPreview takes a JPEG image in byte format -and compresses it based on desired output size - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsCompressJpegForPreview(NSData* _Nullable imgBytes, NSError* _Nullable* _Nullable error); - -/** - * DownloadAndVerifySignedNdfWithUrl retrieves the NDF from a specified URL. -The NDF is processed into a protobuf containing a signature which -is verified using the cert string passed in. The NDF is returned as marshaled -byte data which may be used to start a client. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadAndVerifySignedNdfWithUrl(NSString* _Nullable url, NSString* _Nullable cert, NSError* _Nullable* _Nullable error); - -/** - * DownloadDAppRegistrationDB returns a []byte containing -the JSON data describing registered dApps. -See https://git.xx.network/elixxir/registered-dapps - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadDAppRegistrationDB(NSError* _Nullable* _Nullable error); - -/** - * DownloadErrorDB returns a []byte containing the JSON data -describing client errors. -See https://git.xx.network/elixxir/client-error-database/ - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadErrorDB(NSError* _Nullable* _Nullable error); - -/** - * DumpStack returns a string with the stack trace of every running thread. - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsDumpStack(NSError* _Nullable* _Nullable error); - -/** - * EnableGrpcLogs sets GRPC trace logging - */ -FOUNDATION_EXPORT void BindingsEnableGrpcLogs(id<BindingsLogWriter> _Nullable writer); - -/** - * ErrorStringToUserFriendlyMessage takes a passed in errStr which will be -a backend generated error. These may be error specifically written by -the backend team or lower level errors gotten from low level dependencies. -This function will parse the error string for common errors provided from -errToUserErr to provide a more user-friendly error message for the front end. -If the error is not common, some simple parsing is done on the error message -to make it more user-accessible, removing backend specific jargon. - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsErrorStringToUserFriendlyMessage(NSString* _Nullable errStr); - -/** - * GenerateSecret creates a secret password using a system-based -pseudorandom number generator. It takes 1 parameter, `numBytes`, -which should be set to 32, but can be set higher in certain cases. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsGenerateSecret(long numBytes); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetCMIXParams(NSError* _Nullable* _Nullable error); - -/** - * returns a previously created client. IF be used if the garbage collector -removes the client instance on the app side. Is NOT thread safe relative to -login, newClient, or newPrecannedClient - */ -FOUNDATION_EXPORT BindingsClient* _Nullable BindingsGetClientSingleton(void); - -/** - * GetDependencies returns the api DEPENDENCIES - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetDependencies(void); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetE2EParams(NSError* _Nullable* _Nullable error); - -/** - * GetGitVersion rturns the api GITVERSION - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetGitVersion(void); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetNetworkParams(NSError* _Nullable* _Nullable error); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetUnsafeParams(NSError* _Nullable* _Nullable error); - -/** - * GetVersion returns the api SEMVER - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetVersion(void); - -/** - * InitializeBackup starts the backup processes that returns backup updates when -they occur. Any time an event occurs that changes the contents of the backup, -such as adding or deleting a contact, the backup is triggered and an -encrypted backup is generated and returned on the updateBackupCb callback. -Call this function only when enabling backup if it has not already been -initialized or when the user wants to change their password. -To resume backup process on app recovery, use ResumeBackup. - */ -FOUNDATION_EXPORT BindingsBackup* _Nullable BindingsInitializeBackup(NSString* _Nullable password, id<BindingsUpdateBackupFunc> _Nullable updateBackupCb, BindingsClient* _Nullable c, NSError* _Nullable* _Nullable error); - -/** - * LoadSecretWithMnemonic loads the secret stored from the call to -StoreSecretWithMnemonic. The path given should be the same filepath -as the path given in StoreSecretWithMnemonic. There should be a file -in this path called ".recovery". This operation is not tied -to client operations, as the user will not have a client when trying to -recover their account. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsLoadSecretWithMnemonic(NSString* _Nullable mnemonic, NSString* _Nullable path, NSError* _Nullable* _Nullable error); - -/** - * sets level of logging. All logs the set level and above will be displayed -options are: - TRACE - 0 - DEBUG - 1 - INFO - 2 - WARN - 3 - ERROR - 4 - CRITICAL - 5 - FATAL - 6 -The default state without updates is: INFO - */ -FOUNDATION_EXPORT BOOL BindingsLogLevel(long level, NSError* _Nullable* _Nullable error); - -/** - * Login will load an existing client from the storageDir -using the password. This will fail if the client doesn't exist or -the password is incorrect. -The password is passed as a byte array so that it can be cleared from -memory and stored as securely as possible using the memguard library. -Login does not block on network connection, and instead loads and -starts subprocesses to perform network operations. - */ -FOUNDATION_EXPORT BindingsClient* _Nullable BindingsLogin(NSString* _Nullable storageDir, NSData* _Nullable password, NSString* _Nullable parameters, NSError* _Nullable* _Nullable error); - -/** - * MakeIdList creates a new empty IdList. - */ -FOUNDATION_EXPORT BindingsIdList* _Nullable BindingsMakeIdList(void); - -FOUNDATION_EXPORT BindingsIntList* _Nullable BindingsMakeIntList(void); - -/** - * NewClient creates client storage, generates keys, connects, and registers -with the network. Note that this does not register a username/identity, but -merely creates a new cryptographic identity for adding such information -at a later date. - -Users of this function should delete the storage directory on error. - */ -FOUNDATION_EXPORT BOOL BindingsNewClient(NSString* _Nullable network, NSString* _Nullable storageDir, NSData* _Nullable password, NSString* _Nullable regCode, NSError* _Nullable* _Nullable error); - -/** - * NewClientFromBackup constructs a new Client from an encrypted backup. The backup -is decrypted using the backupPassphrase. On success a successful client creation, -the function will return a JSON encoded list of the E2E partners -contained in the backup and a json-encoded string of the parameters stored in the backup - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsNewClientFromBackup(NSString* _Nullable ndfJSON, NSString* _Nullable storageDir, NSData* _Nullable sessionPassword, NSData* _Nullable backupPassphrase, NSData* _Nullable backupFileContents, NSError* _Nullable* _Nullable error); - -/** - * NewDummyTrafficManager creates a DummyTraffic manager and initialises the -dummy traffic send thread. Note that the manager does not start sending dummy -traffic until its status is set to true using DummyTraffic.SetStatus. -The maxNumMessages is the upper bound of the random number of messages sent -each send. avgSendDeltaMS is the average duration, in milliseconds, to wait -between sends. Sends occur every avgSendDeltaMS +/- a random duration with an -upper bound of randomRangeMS. - */ -FOUNDATION_EXPORT BindingsDummyTraffic* _Nullable BindingsNewDummyTrafficManager(BindingsClient* _Nullable client, long maxNumMessages, long avgSendDeltaMS, long randomRangeMS, NSError* _Nullable* _Nullable error); - -/** - * fact object -creates a new fact. The factType must be either: - 0 - Username - 1 - Email - 2 - Phone Number -The fact must be well formed for the type and must not include commas or -semicolons. If it is not well formed, it will be rejected. Phone numbers -must have the two letter country codes appended. For the complete set of -validation, see /elixxir/primitives/fact/fact.go - */ -FOUNDATION_EXPORT BindingsFact* _Nullable BindingsNewFact(long factType, NSString* _Nullable factStr, NSError* _Nullable* _Nullable error); - -/** - * FactList - */ -FOUNDATION_EXPORT BindingsFactList* _Nullable BindingsNewFactList(void); - -/** - * NewFileTransferManager creates a new file transfer manager and starts the -sending and receiving threads. The receiveFunc is called everytime a new file -transfer is received. -The parameters string contains file transfer network configuration options -and is a JSON formatted string of the fileTransfer.Params object. If it is -left empty, then defaults are used. It is highly recommended that defaults -are used. If it is set, it must match the following format: - {"MaxThroughput":150000,"SendTimeout":500000000} -MaxThroughput is in bytes/sec and SendTimeout is in nanoseconds. - */ -FOUNDATION_EXPORT BindingsFileTransfer* _Nullable BindingsNewFileTransferManager(BindingsClient* _Nullable client, id<BindingsFileTransferReceiveFunc> _Nullable receiveFunc, NSString* _Nullable parameters, NSError* _Nullable* _Nullable error); - -/** - * NewGroupManager creates a new group chat manager. - */ -FOUNDATION_EXPORT BindingsGroupChat* _Nullable BindingsNewGroupManager(BindingsClient* _Nullable client, id<BindingsGroupRequestFunc> _Nullable requestFunc, id<BindingsGroupReceiveFunc> _Nullable receiveFunc, NSError* _Nullable* _Nullable error); - -/** - * NewPrecannedClient creates an insecure user with predetermined keys with nodes -It creates client storage, generates keys, connects, and registers -with the network. Note that this does not register a username/identity, but -merely creates a new cryptographic identity for adding such information -at a later date. - -Users of this function should delete the storage directory on error. - */ -FOUNDATION_EXPORT BOOL BindingsNewPrecannedClient(long precannedID, NSString* _Nullable network, NSString* _Nullable storageDir, NSData* _Nullable password, NSError* _Nullable* _Nullable error); - -/** - * NewUserDiscovery returns a new user discovery object. Only call this once. It must be called -after StartNetworkFollower is called and will fail if the network has never -been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscovery(BindingsClient* _Nullable client, NSError* _Nullable* _Nullable error); - -/** - * NewUserDiscoveryFromBackup returns a new user discovery object. It -wil set up the manager with the backup data. Pass into it the backed up -facts, one email and phone number each. This will add the registered facts -to the backed Store. Any one of these fields may be empty, -however both fields being empty will cause an error. Any other fact that is not -an email or phone number will return an error. You may only add a fact for the -accepted types once each. If you attempt to back up a fact type that has already -been backed up, an error will be returned. Anytime an error is returned, it means -the backup was not successful. -NOTE: Do not use this as a direct store operation. This feature is intended to add facts -to a backend store that have ALREADY BEEN REGISTERED on the account. -THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend. -Only call this once. It must be called after StartNetworkFollower -is called and will fail if the network has never been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscoveryFromBackup(BindingsClient* _Nullable client, NSString* _Nullable email, NSString* _Nullable phone, NSError* _Nullable* _Nullable error); - -/** - * NotificationsForMe Check if a notification received is for me -It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller, -a Type, and a source. These are as follows: - TYPE SOURCE DESCRIPTION - "default" recipient user ID A message with no association - "request" sender user ID A channel request has been received - "reset" sender user ID A channel reset has been received - "confirm" sender user ID A channel request has been accepted - "silent" sender user ID A message which should not be notified on - "e2e" sender user ID reception of an E2E message - "group" group ID reception of a group chat message - "endFT" sender user ID Last message sent confirming end of file transfer - "groupRQ" sender user ID Request from sender to join a group chat - */ -FOUNDATION_EXPORT BindingsManyNotificationForMeReport* _Nullable BindingsNotificationsForMe(NSString* _Nullable notifCSV, NSString* _Nullable preimages, NSError* _Nullable* _Nullable error); - -/** - * RegisterLogWriter registers a callback on which logs are written. - */ -FOUNDATION_EXPORT void BindingsRegisterLogWriter(id<BindingsLogWriter> _Nullable writer); - -/** - * RestoreContactsFromBackup takes as input the jason output of the -`NewClientFromBackup` function, unmarshals it into IDs, looks up -each ID in user discovery, and initiates a session reset request. -This function will not return until every id in the list has been sent a -request. It should be called again and again until it completes. -xxDK users should not use this function. This function is used by -the mobile phone apps and are not intended to be part of the xxDK. It -should be treated as internal functions specific to the phone apps. - */ -FOUNDATION_EXPORT BindingsRestoreContactsReport* _Nullable BindingsRestoreContactsFromBackup(NSData* _Nullable backupPartnerIDs, BindingsClient* _Nullable client, BindingsUserDiscovery* _Nullable udManager, id<BindingsLookupCallback> _Nullable lookupCB, id<BindingsRestoreContactsUpdater> _Nullable updatesCb); - -/** - * ResumeBackup starts the backup processes back up with a new callback after it -has been initialized. -Call this function only when resuming a backup that has already been -initialized or to replace the callback. -To start the backup for the first time or to use a new password, use -InitializeBackup. - */ -FOUNDATION_EXPORT BindingsBackup* _Nullable BindingsResumeBackup(id<BindingsUpdateBackupFunc> _Nullable cb, BindingsClient* _Nullable c, NSError* _Nullable* _Nullable error); - -/** - * SetTimeSource sets the network time to a custom source. - */ -FOUNDATION_EXPORT void BindingsSetTimeSource(id<BindingsTimeSource> _Nullable timeNow); - -/** - * StoreSecretWithMnemonic stores the secret tied with the mnemonic to storage. -Unlike other storage operations, this does not use EKV, as that is -intrinsically tied to client operations, which the user will not have while -trying to recover their account. As such, we store the encrypted data -directly, with a specified path. Path will be a valid filepath in which the -recover file will be stored as ".recovery". - -As an example, given "home/user/xxmessenger/storagePath", -the recovery file will be stored at -"home/user/xxmessenger/storagePath/.recovery" - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsStoreSecretWithMnemonic(NSData* _Nullable secret, NSString* _Nullable path, NSError* _Nullable* _Nullable error); - -/** - * Unmarshals a marshaled contact object, returns an error if it fails - */ -FOUNDATION_EXPORT BindingsContact* _Nullable BindingsUnmarshalContact(NSData* _Nullable b, NSError* _Nullable* _Nullable error); - -/** - * Unmarshals a marshaled send report object, returns an error if it fails - */ -FOUNDATION_EXPORT BindingsSendReport* _Nullable BindingsUnmarshalSendReport(NSData* _Nullable b, NSError* _Nullable* _Nullable error); - -/** - * UpdateCommonErrors takes the passed in contents of a JSON file and updates the -errToUserErr map with the contents of the json file. The JSON's expected format -conform with the commented examples provides in errToUserErr above. -NOTE that you should not pass in a file path, but a preloaded JSON file - */ -FOUNDATION_EXPORT BOOL BindingsUpdateCommonErrors(NSString* _Nullable jsonFile, NSError* _Nullable* _Nullable error); - -// skipped function WrapAPIClient with unsupported parameter or return types - - -// skipped function WrapUserDiscovery with unsupported parameter or return types - - -@class BindingsAuthConfirmCallback; - -@class BindingsAuthRequestCallback; - -@class BindingsAuthResetNotificationCallback; - -@class BindingsClientError; - -@class BindingsEventCallbackFunctionObject; - -@class BindingsFileTransferReceiveFunc; - -@class BindingsFileTransferReceivedProgressFunc; - -@class BindingsFileTransferSentProgressFunc; - -@class BindingsGroupReceiveFunc; - -@class BindingsGroupRequestFunc; - -@class BindingsListener; - -@class BindingsLogWriter; - -@class BindingsLookupCallback; - -@class BindingsMessageDeliveryCallback; - -@class BindingsMultiLookupCallback; - -@class BindingsNetworkHealthCallback; - -@class BindingsPreimageNotification; - -@class BindingsRestoreContactsUpdater; - -@class BindingsRoundCompletionCallback; - -@class BindingsRoundEventCallback; - -@class BindingsSearchCallback; - -@class BindingsSingleSearchCallback; - -@class BindingsTimeSource; - -@class BindingsUpdateBackupFunc; - -/** - * AuthConfirmCallback notifies the register whenever they receive an auth -request confirmation - */ -@interface BindingsAuthConfirmCallback : NSObject <goSeqRefInterface, BindingsAuthConfirmCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)partner; -@end - -/** - * AuthRequestCallback notifies the register whenever they receive an auth -request - */ -@interface BindingsAuthRequestCallback : NSObject <goSeqRefInterface, BindingsAuthRequestCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -/** - * AuthRequestCallback notifies the register whenever they receive an auth -request - */ -@interface BindingsAuthResetNotificationCallback : NSObject <goSeqRefInterface, BindingsAuthResetNotificationCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@interface BindingsClientError : NSObject <goSeqRefInterface, BindingsClientError> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)report:(NSString* _Nullable)source message:(NSString* _Nullable)message trace:(NSString* _Nullable)trace; -@end - -/** - * EventCallbackFunctionObject bindings interface which contains function -that implements the EventCallbackFunction - */ -@interface BindingsEventCallbackFunctionObject : NSObject <goSeqRefInterface, BindingsEventCallbackFunctionObject> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)reportEvent:(long)priority category:(NSString* _Nullable)category evtType:(NSString* _Nullable)evtType details:(NSString* _Nullable)details; -@end - -/** - * FileTransferReceiveFunc contains a function callback that notifies the -receiver of an incoming file transfer. It is called on the reception of the -initial file transfer message. - */ -@interface BindingsFileTransferReceiveFunc : NSObject <goSeqRefInterface, BindingsFileTransferReceiveFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)receiveCallback:(NSData* _Nullable)tid fileName:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType sender:(NSData* _Nullable)sender size:(long)size preview:(NSData* _Nullable)preview; -@end - -/** - * FileTransferReceivedProgressFunc contains a function callback that tracks the -progress of receiving a file. It is called when a file part is received, the -transfer completes, or on error. - */ -@interface BindingsFileTransferReceivedProgressFunc : NSObject <goSeqRefInterface, BindingsFileTransferReceivedProgressFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)receivedProgressCallback:(BOOL)completed received:(long)received total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -/** - * FileTransferSentProgressFunc contains a function callback that tracks the -progress of sending a file. It is called when a file part is sent, a file -part arrives, the transfer completes, or on error. - */ -@interface BindingsFileTransferSentProgressFunc : NSObject <goSeqRefInterface, BindingsFileTransferSentProgressFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)sentProgressCallback:(BOOL)completed sent:(long)sent arrived:(long)arrived total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -/** - * GroupReceiveFunc contains a function callback that is called when a group -message is received. - */ -@interface BindingsGroupReceiveFunc : NSObject <goSeqRefInterface, BindingsGroupReceiveFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)groupReceiveCallback:(BindingsGroupMessageReceive* _Nullable)msg; -@end - -/** - * GroupRequestFunc contains a function callback that is called when a group -request is received. - */ -@interface BindingsGroupRequestFunc : NSObject <goSeqRefInterface, BindingsGroupRequestFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)groupRequestCallback:(BindingsGroup* _Nullable)g; -@end - -/** - * Listener provides a callback to hear a message -An object implementing this interface can be called back when the client -gets a message of the type that the registerer specified at registration -time. - */ -@interface BindingsListener : NSObject <goSeqRefInterface, BindingsListener> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * Hear is called to receive a message in the UI - */ -- (void)hear:(BindingsMessage* _Nullable)message; -/** - * Returns a name, used for debugging - */ -- (NSString* _Nonnull)name; -@end - -@interface BindingsLogWriter : NSObject <goSeqRefInterface, BindingsLogWriter> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)log:(NSString* _Nullable)p0; -@end - -/** - * LookupCallback returns the result of a single lookup - */ -@interface BindingsLookupCallback : NSObject <goSeqRefInterface, BindingsLookupCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -/** - * MessageDeliveryCallback gets called on the determination if all events -related to a message send were successful. - */ -@interface BindingsMessageDeliveryCallback : NSObject <goSeqRefInterface, BindingsMessageDeliveryCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(NSData* _Nullable)msgID delivered:(BOOL)delivered timedOut:(BOOL)timedOut roundResults:(NSData* _Nullable)roundResults; -@end - -/** - * MultiLookupCallback returns the result of many parallel lookups - */ -@interface BindingsMultiLookupCallback : NSObject <goSeqRefInterface, BindingsMultiLookupCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContactList* _Nullable)Succeeded failed:(BindingsIdList* _Nullable)failed errors:(NSString* _Nullable)errors; -@end - -/** - * A callback when which is used to receive notification if network health -changes - */ -@interface BindingsNetworkHealthCallback : NSObject <goSeqRefInterface, BindingsNetworkHealthCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BOOL)p0; -@end - -@interface BindingsPreimageNotification : NSObject <goSeqRefInterface, BindingsPreimageNotification> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)notify:(NSData* _Nullable)identity deleted:(BOOL)deleted; -@end - -/** - * RestoreContactsUpdater interface provides a callback function -for receiving update information from RestoreContactsFromBackup. - */ -@interface BindingsRestoreContactsUpdater : NSObject <goSeqRefInterface, BindingsRestoreContactsUpdater> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * RestoreContactsCallback is called to report the current # of contacts -that have been found and how many have been restored -against the total number that need to be -processed. If an error occurs it it set on the err variable as a -plain string. - */ -- (void)restoreContactsCallback:(long)numFound numRestored:(long)numRestored total:(long)total err:(NSString* _Nullable)err; -@end - -/** - * RoundCompletionCallback is returned when the completion of a round is known. - */ -@interface BindingsRoundCompletionCallback : NSObject <goSeqRefInterface, BindingsRoundCompletionCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(long)rid success:(BOOL)success timedOut:(BOOL)timedOut; -@end - -/** - * RoundEventCallback handles waiting on the exact state of a round on -the cMix network. - */ -@interface BindingsRoundEventCallback : NSObject <goSeqRefInterface, BindingsRoundEventCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(long)rid state:(long)state timedOut:(BOOL)timedOut; -@end - -/** - * SearchCallback returns the result of a search - */ -@interface BindingsSearchCallback : NSObject <goSeqRefInterface, BindingsSearchCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContactList* _Nullable)contacts error:(NSString* _Nullable)error; -@end - -/** - * SingleSearchCallback returns the result of a single search - */ -@interface BindingsSingleSearchCallback : NSObject <goSeqRefInterface, BindingsSingleSearchCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@interface BindingsTimeSource : NSObject <goSeqRefInterface, BindingsTimeSource> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (int64_t)nowMs; -@end - -/** - * UpdateBackupFunc contains a function callback that returns new backups. - */ -@interface BindingsUpdateBackupFunc : NSObject <goSeqRefInterface, BindingsUpdateBackupFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)updateBackup:(NSData* _Nullable)encryptedBackup; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/Universe.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/Universe.objc.h deleted file mode 100644 index 019e7502d581983722a15bf30799e85cbc5dd766..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/Universe.objc.h +++ /dev/null @@ -1,29 +0,0 @@ -// Objective-C API for talking to Go package. -// gobind -lang=objc -// -// File is generated by gobind. Do not edit. - -#ifndef __Universe_H__ -#define __Universe_H__ - -@import Foundation; -#include "ref.h" - -@protocol Universeerror; -@class Universeerror; - -@protocol Universeerror <NSObject> -- (NSString* _Nonnull)error; -@end - -@class Universeerror; - -@interface Universeerror : NSError <goSeqRefInterface, Universeerror> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (NSString* _Nonnull)error; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/ref.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/ref.h deleted file mode 100644 index b8036a4d85c7387f3def61473a071b5d8c4c8208..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/ref.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef __GO_REF_HDR__ -#define __GO_REF_HDR__ - -#include <Foundation/Foundation.h> - -// GoSeqRef is an object tagged with an integer for passing back and -// forth across the language boundary. A GoSeqRef may represent either -// an instance of a Go object, or an Objective-C object passed to Go. -// The explicit allocation of a GoSeqRef is used to pin a Go object -// when it is passed to Objective-C. The Go seq package maintains a -// reference to the Go object in a map keyed by the refnum along with -// a reference count. When the reference count reaches zero, the Go -// seq package will clear the corresponding entry in the map. -@interface GoSeqRef : NSObject { -} -@property(readonly) int32_t refnum; -@property(strong) id obj; // NULL when representing a Go object. - -// new GoSeqRef object to proxy a Go object. The refnum must be -// provided from Go side. -- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj; - -- (int32_t)incNum; - -@end - -@protocol goSeqRefInterface --(GoSeqRef*) _ref; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Modules/module.modulemap b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Modules/module.modulemap deleted file mode 100644 index 4316a5b24058edfc18ffb2dc7f7a982e8353441a..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Modules/module.modulemap +++ /dev/null @@ -1,8 +0,0 @@ -framework module "Bindings" { - header "ref.h" - header "Bindings.objc.h" - header "Universe.objc.h" - header "Bindings.h" - - export * -} \ No newline at end of file diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Resources/Info.plist b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Resources/Info.plist deleted file mode 100644 index 0d1a4b8ab9b1fc8e9357197398f73353470cb636..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Resources/Info.plist +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> - <plist version="1.0"> - <dict> - </dict> - </plist> diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Bindings b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Bindings deleted file mode 100644 index 1e3b1dc25b4d65bf1e21a5d169dc3ae4de6c4fd7..0000000000000000000000000000000000000000 Binary files a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Bindings and /dev/null differ diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Bindings.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Bindings.h deleted file mode 100644 index 8906a7da239705b790cb2bb64de92f806640cb38..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Bindings.h +++ /dev/null @@ -1,13 +0,0 @@ - -// Objective-C API for talking to the following Go packages -// -// gitlab.com/elixxir/client/bindings -// -// File is generated by gomobile bind. Do not edit. -#ifndef __Bindings_FRAMEWORK_H__ -#define __Bindings_FRAMEWORK_H__ - -#include "Bindings.objc.h" -#include "Universe.objc.h" - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Bindings.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Bindings.objc.h deleted file mode 100644 index 32bf6d116888f787ced27b01b95cb4e1b2c1138b..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Bindings.objc.h +++ /dev/null @@ -1,2083 +0,0 @@ -// Objective-C API for talking to gitlab.com/elixxir/client/bindings Go package. -// gobind -lang=objc gitlab.com/elixxir/client/bindings -// -// File is generated by gobind. Do not edit. - -#ifndef __Bindings_H__ -#define __Bindings_H__ - -@import Foundation; -#include "ref.h" -#include "Universe.objc.h" - - -@class BindingsBackup; -@class BindingsBackupReport; -@class BindingsClient; -@class BindingsContact; -@class BindingsContactList; -@class BindingsDummyTraffic; -@class BindingsFact; -@class BindingsFactList; -@class BindingsFilePartTracker; -@class BindingsFileTransfer; -@class BindingsGroup; -@class BindingsGroupChat; -@class BindingsGroupMember; -@class BindingsGroupMembership; -@class BindingsGroupMessageReceive; -@class BindingsGroupReportDisk; -@class BindingsGroupSendReport; -@class BindingsIdList; -@class BindingsIntList; -@class BindingsManyNotificationForMeReport; -@class BindingsMessage; -@class BindingsNewGroupReport; -@class BindingsNodeRegistrationsStatus; -@class BindingsNotificationForMeReport; -@class BindingsRestoreContactsReport; -@class BindingsRoundList; -@class BindingsSendReport; -@class BindingsSendReportDisk; -@class BindingsUnregister; -@class BindingsUser; -@class BindingsUserDiscovery; -@protocol BindingsAuthConfirmCallback; -@class BindingsAuthConfirmCallback; -@protocol BindingsAuthRequestCallback; -@class BindingsAuthRequestCallback; -@protocol BindingsAuthResetNotificationCallback; -@class BindingsAuthResetNotificationCallback; -@protocol BindingsClientError; -@class BindingsClientError; -@protocol BindingsEventCallbackFunctionObject; -@class BindingsEventCallbackFunctionObject; -@protocol BindingsFileTransferReceiveFunc; -@class BindingsFileTransferReceiveFunc; -@protocol BindingsFileTransferReceivedProgressFunc; -@class BindingsFileTransferReceivedProgressFunc; -@protocol BindingsFileTransferSentProgressFunc; -@class BindingsFileTransferSentProgressFunc; -@protocol BindingsGroupReceiveFunc; -@class BindingsGroupReceiveFunc; -@protocol BindingsGroupRequestFunc; -@class BindingsGroupRequestFunc; -@protocol BindingsListener; -@class BindingsListener; -@protocol BindingsLogWriter; -@class BindingsLogWriter; -@protocol BindingsLookupCallback; -@class BindingsLookupCallback; -@protocol BindingsMessageDeliveryCallback; -@class BindingsMessageDeliveryCallback; -@protocol BindingsMultiLookupCallback; -@class BindingsMultiLookupCallback; -@protocol BindingsNetworkHealthCallback; -@class BindingsNetworkHealthCallback; -@protocol BindingsPreimageNotification; -@class BindingsPreimageNotification; -@protocol BindingsRestoreContactsUpdater; -@class BindingsRestoreContactsUpdater; -@protocol BindingsRoundCompletionCallback; -@class BindingsRoundCompletionCallback; -@protocol BindingsRoundEventCallback; -@class BindingsRoundEventCallback; -@protocol BindingsSearchCallback; -@class BindingsSearchCallback; -@protocol BindingsSingleSearchCallback; -@class BindingsSingleSearchCallback; -@protocol BindingsTimeSource; -@class BindingsTimeSource; -@protocol BindingsUpdateBackupFunc; -@class BindingsUpdateBackupFunc; - -@protocol BindingsAuthConfirmCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)partner; -@end - -@protocol BindingsAuthRequestCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@protocol BindingsAuthResetNotificationCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@protocol BindingsClientError <NSObject> -- (void)report:(NSString* _Nullable)source message:(NSString* _Nullable)message trace:(NSString* _Nullable)trace; -@end - -@protocol BindingsEventCallbackFunctionObject <NSObject> -- (void)reportEvent:(long)priority category:(NSString* _Nullable)category evtType:(NSString* _Nullable)evtType details:(NSString* _Nullable)details; -@end - -@protocol BindingsFileTransferReceiveFunc <NSObject> -- (void)receiveCallback:(NSData* _Nullable)tid fileName:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType sender:(NSData* _Nullable)sender size:(long)size preview:(NSData* _Nullable)preview; -@end - -@protocol BindingsFileTransferReceivedProgressFunc <NSObject> -- (void)receivedProgressCallback:(BOOL)completed received:(long)received total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -@protocol BindingsFileTransferSentProgressFunc <NSObject> -- (void)sentProgressCallback:(BOOL)completed sent:(long)sent arrived:(long)arrived total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -@protocol BindingsGroupReceiveFunc <NSObject> -- (void)groupReceiveCallback:(BindingsGroupMessageReceive* _Nullable)msg; -@end - -@protocol BindingsGroupRequestFunc <NSObject> -- (void)groupRequestCallback:(BindingsGroup* _Nullable)g; -@end - -@protocol BindingsListener <NSObject> -/** - * Hear is called to receive a message in the UI - */ -- (void)hear:(BindingsMessage* _Nullable)message; -/** - * Returns a name, used for debugging - */ -- (NSString* _Nonnull)name; -@end - -@protocol BindingsLogWriter <NSObject> -- (void)log:(NSString* _Nullable)p0; -@end - -@protocol BindingsLookupCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@protocol BindingsMessageDeliveryCallback <NSObject> -- (void)eventCallback:(NSData* _Nullable)msgID delivered:(BOOL)delivered timedOut:(BOOL)timedOut roundResults:(NSData* _Nullable)roundResults; -@end - -@protocol BindingsMultiLookupCallback <NSObject> -- (void)callback:(BindingsContactList* _Nullable)Succeeded failed:(BindingsIdList* _Nullable)failed errors:(NSString* _Nullable)errors; -@end - -@protocol BindingsNetworkHealthCallback <NSObject> -- (void)callback:(BOOL)p0; -@end - -@protocol BindingsPreimageNotification <NSObject> -- (void)notify:(NSData* _Nullable)identity deleted:(BOOL)deleted; -@end - -@protocol BindingsRestoreContactsUpdater <NSObject> -/** - * RestoreContactsCallback is called to report the current # of contacts -that have been found and how many have been restored -against the total number that need to be -processed. If an error occurs it it set on the err variable as a -plain string. - */ -- (void)restoreContactsCallback:(long)numFound numRestored:(long)numRestored total:(long)total err:(NSString* _Nullable)err; -@end - -@protocol BindingsRoundCompletionCallback <NSObject> -- (void)eventCallback:(long)rid success:(BOOL)success timedOut:(BOOL)timedOut; -@end - -@protocol BindingsRoundEventCallback <NSObject> -- (void)eventCallback:(long)rid state:(long)state timedOut:(BOOL)timedOut; -@end - -@protocol BindingsSearchCallback <NSObject> -- (void)callback:(BindingsContactList* _Nullable)contacts error:(NSString* _Nullable)error; -@end - -@protocol BindingsSingleSearchCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@protocol BindingsTimeSource <NSObject> -- (int64_t)nowMs; -@end - -@protocol BindingsUpdateBackupFunc <NSObject> -- (void)updateBackup:(NSData* _Nullable)encryptedBackup; -@end - -@interface BindingsBackup : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * AddJson stores a passed in json string in the backup structure - */ -- (void)addJson:(NSString* _Nullable)json; -/** - * IsBackupRunning returns true if the backup has been initialized and is -running. Returns false if it has been stopped. - */ -- (BOOL)isBackupRunning; -/** - * StopBackup stops the backup processes and deletes the user's password from -storage. To enable backups again, call InitializeBackup. - */ -- (BOOL)stopBackup:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsBackupReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field BackupReport.RestoredContacts with unsupported type: []*gitlab.com/xx_network/primitives/id.ID - -@property (nonatomic) NSString* _Nonnull params; -@end - -/** - * BindingsClient wraps the api.Client, implementing additional functions -to support the gomobile Client interface - */ -@interface BindingsClient : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BOOL)confirmAuthenticatedChannel:(NSData* _Nullable)recipientMarshaled ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteAllRequests clears all requests from Client's auth storage. - */ -- (BOOL)deleteAllRequests:(NSError* _Nullable* _Nullable)error; -/** - * DeleteContact is a function which removes a contact from Client's storage - */ -- (BOOL)deleteContact:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteReceiveRequests clears receive requests from Client's auth storage. - */ -- (BOOL)deleteReceiveRequests:(NSError* _Nullable* _Nullable)error; -/** - * DeleteRequest will delete a request, agnostic of request type -for the given partner ID. If no request exists for this -partner ID an error will be returned. - */ -- (BOOL)deleteRequest:(NSData* _Nullable)requesterUserId error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteSentRequests clears sent requests from Client's auth storage. - */ -- (BOOL)deleteSentRequests:(NSError* _Nullable* _Nullable)error; -// skipped method Client.GetInternalClient with unsupported parameter or return types - -/** - * GetNodeRegistrationStatus returns a struct with the number of nodes the -client is registered with and the number total. - */ -- (BindingsNodeRegistrationsStatus* _Nullable)getNodeRegistrationStatus:(NSError* _Nullable* _Nullable)error; -/** - * GetPartners returns a list of - */ -- (NSData* _Nullable)getPartners:(NSError* _Nullable* _Nullable)error; -/** - * GetPreferredBins returns the geographic bin or bins that the provided two -character country code is a part of. The bins are returned as CSV. - */ -- (NSString* _Nonnull)getPreferredBins:(NSString* _Nullable)countryCode error:(NSError* _Nullable* _Nullable)error; -- (NSString* _Nonnull)getPreimages:(NSData* _Nullable)identity; -// skipped method Client.GetRateLimitParams with unsupported parameter or return types - -- (NSString* _Nonnull)getRelationshipFingerprint:(NSData* _Nullable)partnerID error:(NSError* _Nullable* _Nullable)error; -/** - * Returns a user object from which all information about the current user -can be gleaned - */ -- (BindingsUser* _Nullable)getUser; -/** - * HasRunningProcessies checks if any background threads are running. -returns true if none are running. This is meant to be -used when NetworkFollowerStatus() returns Stopping. -Due to the handling of comms on iOS, where the OS can -block indefiently, it may not enter the stopped -state apropreatly. This can be used instead. - */ -- (BOOL)hasRunningProcessies; -/** - * returns true if the network is read to be in a healthy state where -messages can be sent - */ -- (BOOL)isNetworkHealthy; -- (BindingsContact* _Nullable)makePrecannedAuthenticatedChannel:(long)precannedID error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the state of the network follower. Returns: -Stopped - 0 -Starting - 1000 -Running - 2000 -Stopping - 3000 - */ -- (long)networkFollowerStatus; -- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetNotificationCallback> _Nullable)reset; -/** - * RegisterClientErrorCallback registers the callback to handle errors from the -long running threads controlled by StartNetworkFollower and StopNetworkFollower - */ -- (void)registerClientErrorCallback:(id<BindingsClientError> _Nullable)clientError; -/** - * RegisterEventCallback records the given function to receive -ReportableEvent objects. It returns the internal index -of the callback so that it can be deleted later. - */ -- (BOOL)registerEventCallback:(NSString* _Nullable)name myObj:(id<BindingsEventCallbackFunctionObject> _Nullable)myObj error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterForNotifications accepts firebase messaging token - */ -- (BOOL)registerForNotifications:(NSString* _Nullable)token error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterListener records and installs a listener for messages -matching specific uid, msgType, and/or username -Returns a ListenerUnregister interface which can be - -to register for any userID, pass in an id with length 0 or an id with -all zeroes - -to register for any message type, pass in a message type of 0 - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types - */ -- (BindingsUnregister* _Nullable)registerListener:(NSData* _Nullable)uid msgType:(long)msgType listener:(id<BindingsListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterNetworkHealthCB registers the network health callback to be called -any time the network health changes. Returns a unique ID that can be used to -unregister the network health callback. - */ -- (int64_t)registerNetworkHealthCB:(id<BindingsNetworkHealthCallback> _Nullable)nhc; -- (void)registerPreimageCallback:(NSData* _Nullable)identity pin:(id<BindingsPreimageNotification> _Nullable)pin; -/** - * RegisterRoundEventsHandler registers a callback interface for round -events. -The rid is the round the event attaches to -The timeoutMS is the number of milliseconds until the event fails, and the -validStates are a list of states (one per byte) on which the event gets -triggered -States: - 0x00 - PENDING (Never seen by client) - 0x01 - PRECOMPUTING - 0x02 - STANDBY - 0x03 - QUEUED - 0x04 - REALTIME - 0x05 - COMPLETED - 0x06 - FAILED -These states are defined in elixxir/primitives/states/state.go - */ -- (BindingsUnregister* _Nullable)registerRoundEventsHandler:(long)rid cb:(id<BindingsRoundEventCallback> _Nullable)cb timeoutMS:(long)timeoutMS il:(BindingsIntList* _Nullable)il; -- (void)replayRequests; -- (BOOL)requestAuthenticatedChannel:(NSData* _Nullable)recipientMarshaled meMarshaled:(NSData* _Nullable)meMarshaled message:(NSString* _Nullable)message ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -- (BOOL)resetSession:(NSData* _Nullable)recipientMarshaled meMarshaled:(NSData* _Nullable)meMarshaled message:(NSString* _Nullable)message ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * This will return the round the message was sent on if it is successfully sent -This can be used to register a round event to learn about message delivery. -on failure a round id of -1 is returned - */ -- (BOOL)sendCmix:(NSData* _Nullable)recipient contents:(NSData* _Nullable)contents parameters:(NSString* _Nullable)parameters ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * SendE2E sends an end-to-end payload to the provided recipient with -the provided msgType. Returns the list of rounds in which parts of -the message were sent or an error if it fails. - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types - */ -- (BindingsSendReport* _Nullable)sendE2E:(NSData* _Nullable)recipient payload:(NSData* _Nullable)payload messageType:(long)messageType parameters:(NSString* _Nullable)parameters error:(NSError* _Nullable* _Nullable)error; -/** - * SendUnsafe sends an unencrypted payload to the provided recipient -with the provided msgType. Returns the list of rounds in which parts -of the message were sent or an error if it fails. -NOTE: Do not use this function unless you know what you are doing. -This function always produces an error message in client logging. - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types with custom types - */ -- (BindingsRoundList* _Nullable)sendUnsafe:(NSData* _Nullable)recipient payload:(NSData* _Nullable)payload messageType:(long)messageType parameters:(NSString* _Nullable)parameters error:(NSError* _Nullable* _Nullable)error; -/** - * SetProxiedBins updates the host pool filter that filters out gateways that -are not in one of the specified bins. The provided bins should be CSV. - */ -- (BOOL)setProxiedBins:(NSString* _Nullable)binStringsCSV error:(NSError* _Nullable* _Nullable)error; -/** - * StartNetworkFollower kicks off the tracking of the network. It starts -long running network client threads and returns an object for checking -state and stopping those threads. -Call this when returning from sleep and close when going back to -sleep. -These threads may become a significant drain on battery when offline, ensure -they are stopped if there is no internet access -Threads Started: - - Network Follower (/network/follow.go) - tracks the network events and hands them off to workers for handling - - Historical Round Retrieval (/network/rounds/historical.go) - Retrieves data about rounds which are too old to be stored by the client - - Message Retrieval Worker Group (/network/rounds/retrieve.go) - Requests all messages in a given round from the gateway of the last node - - Message Handling Worker Group (/network/message/handle.go) - Decrypts and partitions messages when signals via the Switchboard - - Health Tracker (/network/health) - Via the network instance tracks the state of the network - - Garbled Messages (/network/message/garbled.go) - Can be signaled to check all recent messages which could be be decoded - Uses a message store on disk for persistence - - Critical Messages (/network/message/critical.go) - Ensures all protocol layer mandatory messages are sent - Uses a message store on disk for persistence - - KeyExchange Trigger (/keyExchange/trigger.go) - Responds to sent rekeys and executes them - - KeyExchange Confirm (/keyExchange/confirm.go) - Responds to confirmations of successful rekey operations - */ -- (BOOL)startNetworkFollower:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * StopNetworkFollower stops the network follower if it is running. -It returns errors if the Follower is in the wrong status to stop or if it -fails to stop it. -if the network follower is running and this fails, the client object will -most likely be in an unrecoverable state and need to be trashed. - */ -- (BOOL)stopNetworkFollower:(NSError* _Nullable* _Nullable)error; -/** - * UnregisterEventCallback deletes the callback identified by the -index. It returns an error if it fails. - */ -- (void)unregisterEventCallback:(NSString* _Nullable)name; -/** - * UnregisterForNotifications unregister user for notifications - */ -- (BOOL)unregisterForNotifications:(NSError* _Nullable* _Nullable)error; -- (void)unregisterNetworkHealthCB:(int64_t)funcID; -- (BOOL)verifyOwnership:(NSData* _Nullable)receivedMarshaled verifiedMarshaled:(NSData* _Nullable)verifiedMarshaled ret0_:(BOOL* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * WaitForMessageDelivery allows the caller to get notified if the rounds a -message was sent in successfully completed. Under the hood, this uses an API -which uses the internal round data, network historical round lookup, and -waiting on network events to determine what has (or will) occur. - -The callbacks will return at timeoutMS if no state update occurs - -This function takes the marshaled send report to ensure a memory leak does -not occur as a result of both sides of the bindings holding a reference to -the same pointer. - */ -- (BOOL)waitForMessageDelivery:(NSData* _Nullable)marshaledSendReport mdc:(id<BindingsMessageDeliveryCallback> _Nullable)mdc timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * WaitForNewtwork will block until either the network is healthy or the -passed timeout. It will return true if the network is healthy - */ -- (BOOL)waitForNetwork:(long)timeoutMS; -/** - * WaitForRoundCompletion allows the caller to get notified if a round -has completed (or failed). Under the hood, this uses an API which uses the internal -round data, network historical round lookup, and waiting on network events -to determine what has (or will) occur. - -The callbacks will return at timeoutMS if no state update occurs - */ -- (BOOL)waitForRoundCompletion:(long)roundID rec:(id<BindingsRoundCompletionCallback> _Nullable)rec timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * contact object - */ -@interface BindingsContact : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped method Contact.GetAPIContact with unsupported parameter or return types - -/** - * GetDHPublicKey returns the public key associated with the Contact. - */ -- (NSData* _Nullable)getDHPublicKey; -/** - * Returns a fact list for adding and getting facts to and from the contact - */ -- (BindingsFactList* _Nullable)getFactList; -/** - * GetID returns the user ID for this user. - */ -- (NSData* _Nullable)getID; -/** - * GetDHPublicKey returns hash of a DH proof of key ownership. - */ -- (NSData* _Nullable)getOwnershipProof; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsContactList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Gets a stored round ID at the given index - */ -- (BindingsContact* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the number of round IDs stored - */ -- (long)len; -@end - -/** - * DummyTraffic contains the file dummy traffic manager. The manager can be used -to set and get the status of the send thread. - */ -@interface BindingsDummyTraffic : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewDummyTrafficManager creates a DummyTraffic manager and initialises the -dummy traffic send thread. Note that the manager does not start sending dummy -traffic until its status is set to true using DummyTraffic.SetStatus. -The maxNumMessages is the upper bound of the random number of messages sent -each send. avgSendDeltaMS is the average duration, in milliseconds, to wait -between sends. Sends occur every avgSendDeltaMS +/- a random duration with an -upper bound of randomRangeMS. - */ -- (nullable instancetype)initManager:(BindingsClient* _Nullable)client maxNumMessages:(long)maxNumMessages avgSendDeltaMS:(long)avgSendDeltaMS randomRangeMS:(long)randomRangeMS; -/** - * GetStatus returns the current state of the dummy traffic send thread. It has -the following return values: - true = send thread is sending dummy messages - false = send thread is paused/stopped and not sending dummy messages -Note that this function does not return the status set by SetStatus directly; -it returns the current status of the send thread, which means any call to -SetStatus will have a small delay before it is returned by GetStatus. - */ -- (BOOL)getStatus; -/** - * SetStatus sets the state of the dummy traffic send thread, which determines -if the thread is running or paused. The possible statuses are: - true = send thread is sending dummy messages - false = send thread is paused/stopped and not sending dummy messages -Returns an error if the channel is full. -Note that this function cannot change the status of the send thread if it has -yet to be started or stopped. - */ -- (BOOL)setStatus:(BOOL)status error:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsFact : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * fact object -creates a new fact. The factType must be either: - 0 - Username - 1 - Email - 2 - Phone Number -The fact must be well formed for the type and must not include commas or -semicolons. If it is not well formed, it will be rejected. Phone numbers -must have the two letter country codes appended. For the complete set of -validation, see /elixxir/primitives/fact/fact.go - */ -- (nullable instancetype)init:(long)factType factStr:(NSString* _Nullable)factStr; -- (NSString* _Nonnull)get; -- (NSString* _Nonnull)stringify; -- (long)type; -@end - -@interface BindingsFactList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * FactList - */ -- (nullable instancetype)init; -- (BOOL)add:(NSString* _Nullable)factData factType:(long)factType error:(NSError* _Nullable* _Nullable)error; -- (BindingsFact* _Nullable)get:(long)i; -- (long)num; -- (NSString* _Nonnull)stringify:(NSError* _Nullable* _Nullable)error; -@end - -/** - * FilePartTracker contains the interfaces.FilePartTracker. - */ -@interface BindingsFilePartTracker : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetNumParts returns the total number of file parts in the transfer. - */ -- (long)getNumParts; -/** - * GetPartStatus returns the status of the file part with the given part number. -The possible values for the status are: -0 = unsent -1 = sent (sender has sent a part, but it has not arrived) -2 = arrived (sender has sent a part, and it has arrived) -3 = received (receiver has received a part) - */ -- (long)getPartStatus:(long)partNum; -@end - -/** - * FileTransfer contains the file transfer manager. - */ -@interface BindingsFileTransfer : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewFileTransferManager creates a new file transfer manager and starts the -sending and receiving threads. The receiveFunc is called everytime a new file -transfer is received. -The parameters string contains file transfer network configuration options -and is a JSON formatted string of the fileTransfer.Params object. If it is -left empty, then defaults are used. It is highly recommended that defaults -are used. If it is set, it must match the following format: - {"MaxThroughput":150000,"SendTimeout":500000000} -MaxThroughput is in bytes/sec and SendTimeout is in nanoseconds. - */ -- (nullable instancetype)initManager:(BindingsClient* _Nullable)client receiveFunc:(id<BindingsFileTransferReceiveFunc> _Nullable)receiveFunc parameters:(NSString* _Nullable)parameters; -/** - * CloseSend deletes a sent file transfer from the sent transfer map and from -storage once a transfer has completed or reached the retry limit. Returns an -error if the transfer has not run out of retries. - */ -- (BOOL)closeSend:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error; -/** - * GetMaxFileNameByteLength returns the maximum length, in bytes, allowed for a -file name. - */ -- (long)getMaxFileNameByteLength; -/** - * GetMaxFilePreviewSize returns the maximum file preview size, in bytes. - */ -- (long)getMaxFilePreviewSize; -/** - * GetMaxFileSize returns the maximum file size, in bytes, allowed to be -transferred. - */ -- (long)getMaxFileSize; -/** - * GetMaxFileTypeByteLength returns the maximum length, in bytes, allowed for a -file type. - */ -- (long)getMaxFileTypeByteLength; -/** - * Receive returns the fully assembled file on the completion of the transfer. -It deletes the transfer from the received transfer map and from storage. -Returns an error if the transfer is not complete, the full file cannot be -verified, or if the transfer cannot be found. - */ -- (NSData* _Nullable)receive:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterReceiveProgressCallback allows for the registration of a callback to -track the progress of an individual received file transfer. The callback will -be called immediately when added to report the current status of the -transfer. It will then call every time a file part is received, the transfer -completes, or an error occurs. It is called at most once ever period, which -means if events occur faster than the period, then they will not be reported -and instead, the progress will be reported once at the end of the period. -Once the callback reports that the transfer has completed, the recipient -can get the full file by calling Receive. -The period is specified in milliseconds. - */ -- (BOOL)registerReceiveProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferReceivedProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterSendProgressCallback allows for the registration of a callback to -track the progress of an individual sent file transfer. The callback will be -called immediately when added to report the current status of the transfer. -It will then call every time a file part is sent, a file part arrives, the -transfer completes, or an error occurs. It is called at most once every -period, which means if events occur faster than the period, then they will -not be reported and instead, the progress will be reported once at the end of -the period. -The period is specified in milliseconds. - */ -- (BOOL)registerSendProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -/** - * Send sends a file to the recipient. The sender must have an E2E relationship -with the recipient. -The file name is the name of the file to show a user. It has a max length of -48 bytes. -The file type identifies what type of file is being sent. It has a max length -of 8 bytes. -The file data cannot be larger than 256 kB -The retry float is the total amount of data to send relative to the data -size. Data will be resent on error and will resend up to [(1 + retry) * -fileSize]. -The preview stores a preview of the data (such as a thumbnail) and is -capped at 4 kB in size. -Returns a unique transfer ID used to identify the transfer. -PeriodMS is the duration, in milliseconds, to wait between progress callback -calls. Set this large enough to prevent spamming. - */ -- (NSData* _Nullable)send:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType fileData:(NSData* _Nullable)fileData recipientID:(NSData* _Nullable)recipientID retry:(float)retry preview:(NSData* _Nullable)preview progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * Group structure contains the identifying and membership information of a -group chat. - */ -@interface BindingsGroup : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetCreatedMS returns the time the group was created in milliseconds. This is -also the time the group requests were sent. - */ -- (int64_t)getCreatedMS; -/** - * GetCreatedNano returns the time the group was created in nanoseconds. This is -also the time the group requests were sent. - */ -- (int64_t)getCreatedNano; -/** - * GetID return the 33-byte unique group ID. - */ -- (NSData* _Nullable)getID; -/** - * GetInitMessage returns initial message sent with the group request. - */ -- (NSData* _Nullable)getInitMessage; -/** - * GetMembership returns a list of contacts, one for each member in the group. -The list is in order; the first contact is the leader/creator of the group. -All subsequent members are ordered by their ID. - */ -- (BindingsGroupMembership* _Nullable)getMembership; -/** - * GetName returns the name set by the user for the group. - */ -- (NSData* _Nullable)getName; -/** - * Serialize serializes the Group. - */ -- (NSData* _Nullable)serialize; -@end - -/** - * GroupChat object contains the group chat manager. - */ -@interface BindingsGroupChat : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetGroup returns the group with the group ID. If no group exists, then the -error "failed to find group" is returned. - */ -- (BindingsGroup* _Nullable)getGroup:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * GetGroups returns an IdList containing a list of group IDs that the user is a -part of. - */ -- (BindingsIdList* _Nullable)getGroups; -/** - * JoinGroup allows a user to join a group when they receive a request. The -caller must pass in the serialized bytes of a Group. - */ -- (BOOL)joinGroup:(NSData* _Nullable)serializedGroupData error:(NSError* _Nullable* _Nullable)error; -/** - * LeaveGroup deletes a group so a user no longer has access. - */ -- (BOOL)leaveGroup:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * MakeGroup creates a new group and sends a group request to all members in the -group. The ID of the new group, the rounds the requests were sent on, and the -status of the send are contained in NewGroupReport. - */ -- (BindingsNewGroupReport* _Nullable)makeGroup:(BindingsIdList* _Nullable)membership name:(NSData* _Nullable)name message:(NSData* _Nullable)message; -/** - * NumGroups returns the number of groups the user is a part of. - */ -- (long)numGroups; -/** - * ResendRequest resends a group request to all members in the group. The rounds -they were sent on and the status of the send are contained in NewGroupReport. - */ -- (BindingsNewGroupReport* _Nullable)resendRequest:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * Send sends the message to the specified group. Returns the round the messages -were sent on. - */ -- (BindingsGroupSendReport* _Nullable)send:(NSData* _Nullable)groupIdBytes message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * // -Member Structure -// -GroupMember represents a member in the group membership list. - */ -@interface BindingsGroupMember : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupMember.Member with unsupported type: gitlab.com/elixxir/crypto/group.Member - -// skipped method GroupMember.DeepCopy with unsupported parameter or return types - -// skipped method GroupMember.Equal with unsupported parameter or return types - -/** - * GetDhKey returns the byte representation of the public Diffie–Hellman key of -the member. - */ -- (NSData* _Nullable)getDhKey; -/** - * GetID returns the 33-byte user ID of the member. - */ -- (NSData* _Nullable)getID; -- (NSString* _Nonnull)goString; -- (NSData* _Nullable)serialize; -- (NSString* _Nonnull)string; -@end - -/** - * GroupMembership structure contains a list of members that are part of a -group. The first member is the group leader. - */ -@interface BindingsGroupMembership : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Get returns the member at the index. The member at index 0 is always the -group leader. An error is returned if the index is out of range. - */ -- (BindingsGroupMember* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Len returns the number of members in the group membership. - */ -- (long)len; -@end - -/** - * GroupMessageReceive contains a group message, its ID, and its data that a -user receives. - */ -@interface BindingsGroupMessageReceive : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupMessageReceive.MessageReceive with unsupported type: gitlab.com/elixxir/client/groupChat.MessageReceive - -/** - * GetEphemeralID returns the ephemeral ID of the recipient. - */ -- (int64_t)getEphemeralID; -/** - * GetGroupID returns the 33-byte group ID. - */ -- (NSData* _Nullable)getGroupID; -/** - * GetMessageID returns the message ID. - */ -- (NSData* _Nullable)getMessageID; -/** - * GetPayload returns the message payload. - */ -- (NSData* _Nullable)getPayload; -/** - * GetRecipientID returns the 33-byte user ID of the recipient. - */ -- (NSData* _Nullable)getRecipientID; -/** - * GetRoundID returns the ID of the round the message was sent on. - */ -- (int64_t)getRoundID; -/** - * GetRoundTimestampMS returns the timestamp, in milliseconds, of the round the -message was sent on. - */ -- (int64_t)getRoundTimestampMS; -/** - * GetRoundTimestampNano returns the timestamp, in nanoseconds, of the round the -message was sent on. - */ -- (int64_t)getRoundTimestampNano; -/** - * GetRoundURL returns the ID of the round the message was sent on. - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetSenderID returns the 33-byte user ID of the sender. - */ -- (NSData* _Nullable)getSenderID; -/** - * GetTimestampMS returns the message timestamp in milliseconds. - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message timestamp in nanoseconds. - */ -- (int64_t)getTimestampNano; -- (NSString* _Nonnull)string; -@end - -@interface BindingsGroupReportDisk : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupReportDisk.List with unsupported type: []gitlab.com/xx_network/primitives/id.Round - -@property (nonatomic) NSData* _Nullable grpId; -@property (nonatomic) long status; -@end - -/** - * GroupSendReport is returned when sending a group message. It contains the -round ID sent on and the timestamp of the send. - */ -@interface BindingsGroupSendReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetMessageID returns the ID of the round that the send occurred on. - */ -- (NSData* _Nullable)getMessageID; -/** - * GetRoundID returns the ID of the round that the send occurred on. - */ -- (int64_t)getRoundID; -/** - * GetRoundURL returns the URL of the round that the send occurred on. - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetTimestampMS returns the timestamp of the send in milliseconds. - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the timestamp of the send in nanoseconds. - */ -- (int64_t)getTimestampNano; -@end - -/** - * ID list -IdList contains a list of IDs. - */ -@interface BindingsIdList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Add appends the ID bytes to the end of the list. - */ -- (BOOL)add:(NSData* _Nullable)idBytes error:(NSError* _Nullable* _Nullable)error; -/** - * Get returns the ID at the index. An error is returned if the index is out of -range. - */ -- (NSData* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Len returns the number of IDs in the list. - */ -- (long)len; -@end - -@interface BindingsIntList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (void)add:(long)i; -- (BOOL)get:(long)i ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -- (long)len; -@end - -@interface BindingsManyNotificationForMeReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BindingsNotificationForMeReport* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -- (long)len; -@end - -/** - * Message is a message received from the cMix network in the clear -or that has been decrypted using established E2E keys. - */ -@interface BindingsMessage : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetID returns the id of the message - */ -- (NSData* _Nullable)getID; -/** - * GetMessageType returns the message's type - */ -- (long)getMessageType; -/** - * GetPayload returns the message's payload/contents - */ -- (NSData* _Nullable)getPayload; -/** - * GetRoundId returns the message's round ID - */ -- (int64_t)getRoundId; -/** - * GetRoundTimestampMS returns the message's round timestamp in milliseconds - */ -- (int64_t)getRoundTimestampMS; -/** - * GetRoundTimestampNano returns the message's round timestamp in nanoseconds - */ -- (int64_t)getRoundTimestampNano; -/** - * GetRoundURL returns the message's round URL - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetSender returns the message's sender ID, if available - */ -- (NSData* _Nullable)getSender; -/** - * GetTimestampMS returns the message's timestamp in milliseconds - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message's timestamp in nanoseconds - */ -- (int64_t)getTimestampNano; -@end - -/** - * NewGroupReport is returned when creating a new group and contains the ID of -the group, a list of rounds that the group requests were sent on, and the -status of the send. - */ -@interface BindingsNewGroupReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetError returns the string of an error. -Will be an empty string if no error occured - */ -- (NSString* _Nonnull)getError; -/** - * GetGroup returns the Group. - */ -- (BindingsGroup* _Nullable)getGroup; -/** - * GetRoundList returns the RoundList containing a list of rounds requests were -sent on. - */ -- (BindingsRoundList* _Nullable)getRoundList; -/** - * GetStatus returns the status of the requests sent when creating a new group. -status = 0 an error occurred before any requests could be sent - 1 all requests failed to send (call Resend Group) - 2 some request failed and some succeeded (call Resend Group) - 3, all requests sent successfully (call Resend Group) - */ -- (long)getStatus; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -- (BOOL)unmarshal:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * NodeRegistrationsStatus structure for returning node registration statuses -for bindings. - */ -@interface BindingsNodeRegistrationsStatus : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetRegistered returns the number of nodes registered with the client. - */ -- (long)getRegistered; -/** - * GetTotal return the total of nodes currently in the network. - */ -- (long)getTotal; -@end - -@interface BindingsNotificationForMeReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BOOL)forMe; -- (NSData* _Nullable)source; -- (NSString* _Nonnull)type; -@end - -/** - * RestoreContactsReport is a gomobile friendly report structure -for determining which IDs restored, which failed, and why. - */ -@interface BindingsRestoreContactsReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetErrorAt returns the error string at index - */ -- (NSString* _Nonnull)getErrorAt:(long)index; -/** - * GetFailedAt returns the failed ID at index - */ -- (NSData* _Nullable)getFailedAt:(long)index; -/** - * GetRestoreContactsError returns an error string. Empty if no error. - */ -- (NSString* _Nonnull)getRestoreContactsError; -/** - * GetRestoredAt returns the restored ID at index - */ -- (NSData* _Nullable)getRestoredAt:(long)index; -/** - * LenFailed returns the length of the ID's failed. - */ -- (long)lenFailed; -/** - * LenRestored returns the length of ID's restored. - */ -- (long)lenRestored; -@end - -@interface BindingsRoundList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Gets a stored round ID at the given index - */ -- (BOOL)get:(long)i ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the number of round IDs stored - */ -- (long)len; -@end - -/** - * the send report is the mechanisim by which sendE2E returns a single - */ -@interface BindingsSendReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (NSData* _Nullable)getMessageID; -- (BindingsRoundList* _Nullable)getRoundList; -- (NSString* _Nonnull)getRoundURL; -/** - * GetTimestampMS returns the message's timestamp in milliseconds - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message's timestamp in nanoseconds - */ -- (int64_t)getTimestampNano; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -- (BOOL)unmarshal:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsSendReportDisk : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field SendReportDisk.List with unsupported type: []gitlab.com/xx_network/primitives/id.Round - -@property (nonatomic) NSData* _Nullable mid; -@property (nonatomic) int64_t ts; -@end - -/** - * Generic Unregister - a generic return used for all callbacks which can be -unregistered -Interface which allows the un-registration of a listener - */ -@interface BindingsUnregister : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Call unregisters a callback - */ -- (void)unregister; -@end - -@interface BindingsUser : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BindingsContact* _Nullable)getContact; -- (NSData* _Nullable)getE2EDhPrivateKey; -- (NSData* _Nullable)getE2EDhPublicKey; -- (NSData* _Nullable)getReceptionID; -- (NSData* _Nullable)getReceptionRSAPrivateKeyPem; -- (NSData* _Nullable)getReceptionRSAPublicKeyPem; -- (NSData* _Nullable)getReceptionSalt; -- (NSData* _Nullable)getTransmissionID; -- (NSData* _Nullable)getTransmissionRSAPrivateKeyPem; -- (NSData* _Nullable)getTransmissionRSAPublicKeyPem; -- (NSData* _Nullable)getTransmissionSalt; -- (BOOL)isPrecanned; -@end - -@interface BindingsUserDiscovery : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewUserDiscovery returns a new user discovery object. Only call this once. It must be called -after StartNetworkFollower is called and will fail if the network has never -been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -- (nullable instancetype)init:(BindingsClient* _Nullable)client; -/** - * NewUserDiscoveryFromBackup returns a new user discovery object. It -wil set up the manager with the backup data. Pass into it the backed up -facts, one email and phone number each. This will add the registered facts -to the backed Store. Any one of these fields may be empty, -however both fields being empty will cause an error. Any other fact that is not -an email or phone number will return an error. You may only add a fact for the -accepted types once each. If you attempt to back up a fact type that has already -been backed up, an error will be returned. Anytime an error is returned, it means -the backup was not successful. -NOTE: Do not use this as a direct store operation. This feature is intended to add facts -to a backend store that have ALREADY BEEN REGISTERED on the account. -THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend. -Only call this once. It must be called after StartNetworkFollower -is called and will fail if the network has never been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -- (nullable instancetype)initFromBackup:(BindingsClient* _Nullable)client email:(NSString* _Nullable)email phone:(NSString* _Nullable)phone; -/** - * AddFact adds a fact for the user to user discovery. Will only succeed if the -user is already registered and the system does not have the fact currently -registered for any user. -Will fail if the fact string is not well formed. -This does not complete the fact registration process, it returns a -confirmation id instead. Over the communications system the fact is -associated with, a code will be sent. This confirmation ID needs to be -called along with the code to finalize the fact. - */ -- (NSString* _Nonnull)addFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * ConfirmFact confirms a fact first registered via AddFact. The confirmation ID comes from -AddFact while the code will come over the associated communications system - */ -- (BOOL)confirmFact:(NSString* _Nullable)confirmationID code:(NSString* _Nullable)code error:(NSError* _Nullable* _Nullable)error; -/** - * Lookup the contact object associated with the given userID. The -id is the byte representation of an id. -This will reject if that id is malformed. The LookupCallback will return -the associated contact if it exists. - */ -- (BOOL)lookup:(NSData* _Nullable)idBytes callback:(id<BindingsLookupCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * MultiLookup Looks for the contact object associated with all given userIDs. -The ids are the byte representation of an id stored in an IDList object. -This will reject if that id is malformed or if the indexing on the IDList -object is wrong. The MultiLookupCallback will return with all contacts -returned within the timeout. - */ -- (BOOL)multiLookup:(BindingsIdList* _Nullable)ids callback:(id<BindingsMultiLookupCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * Register registers a user with user discovery. Will return an error if the -network signatures are malformed or if the username is taken. Usernames -cannot be changed after registration at this time. Will fail if the user is -already registered. -Identity does not go over cmix, it occurs over normal communications - */ -- (BOOL)register:(NSString* _Nullable)username error:(NSError* _Nullable* _Nullable)error; -/** - * RemoveFact removes a previously confirmed fact. Will fail if the passed fact string is -not well-formed or if the fact is not associated with this client. -Users cannot remove username facts and must instead remove the user. - */ -- (BOOL)removeFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * RemoveUser deletes a user. The fact sent must be the username. -This function preserves the username forever and makes it -unusable. - */ -- (BOOL)removeUser:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * Search for the passed Facts. The factList is the stringification of a -fact list object, look at /bindings/list.go for more on that object. -This will reject if that object is malformed. The SearchCallback will return -a list of contacts, each having the facts it hit against. -This is NOT intended to be used to search for multiple users at once, that -can have a privacy reduction. Instead, it is intended to be used to search -for a user where multiple pieces of information is known. - */ -- (BOOL)search:(NSString* _Nullable)fl callback:(id<BindingsSearchCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * SearchSingle searches for the passed Facts. The fact is the stringification of a -fact object, look at /bindings/contact.go for more on that object. -This will reject if that object is malformed. The SearchCallback will return -a list of contacts, each having the facts it hit against. -This only searches for a single fact at a time. It is intended to make some -simple use cases of the API easier. - */ -- (BOOL)searchSingle:(NSString* _Nullable)f callback:(id<BindingsSingleSearchCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * SetAlternativeUserDiscovery sets the alternativeUd object within manager. -Once set, any user discovery operation will go through the alternative -user discovery service. -To undo this operation, use UnsetAlternativeUserDiscovery. -The contact file is the already read in bytes, not the file path for the contact file. - */ -- (BOOL)setAlternativeUserDiscovery:(NSData* _Nullable)address cert:(NSData* _Nullable)cert contactFile:(NSData* _Nullable)contactFile error:(NSError* _Nullable* _Nullable)error; -/** - * UnsetAlternativeUserDiscovery clears out the information from -the Manager object. - */ -- (BOOL)unsetAlternativeUserDiscovery:(NSError* _Nullable* _Nullable)error; -@end - -/** - * Error codes - */ -FOUNDATION_EXPORT NSString* _Nonnull const BindingsUnrecognizedCode; -FOUNDATION_EXPORT NSString* _Nonnull const BindingsUnrecognizedMessage; - -/** - * CompressJpeg takes a JPEG image in byte format -and compresses it based on desired output size - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsCompressJpeg(NSData* _Nullable imgBytes, NSError* _Nullable* _Nullable error); - -/** - * CompressJpegForPreview takes a JPEG image in byte format -and compresses it based on desired output size - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsCompressJpegForPreview(NSData* _Nullable imgBytes, NSError* _Nullable* _Nullable error); - -/** - * DownloadAndVerifySignedNdfWithUrl retrieves the NDF from a specified URL. -The NDF is processed into a protobuf containing a signature which -is verified using the cert string passed in. The NDF is returned as marshaled -byte data which may be used to start a client. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadAndVerifySignedNdfWithUrl(NSString* _Nullable url, NSString* _Nullable cert, NSError* _Nullable* _Nullable error); - -/** - * DownloadDAppRegistrationDB returns a []byte containing -the JSON data describing registered dApps. -See https://git.xx.network/elixxir/registered-dapps - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadDAppRegistrationDB(NSError* _Nullable* _Nullable error); - -/** - * DownloadErrorDB returns a []byte containing the JSON data -describing client errors. -See https://git.xx.network/elixxir/client-error-database/ - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadErrorDB(NSError* _Nullable* _Nullable error); - -/** - * DumpStack returns a string with the stack trace of every running thread. - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsDumpStack(NSError* _Nullable* _Nullable error); - -/** - * EnableGrpcLogs sets GRPC trace logging - */ -FOUNDATION_EXPORT void BindingsEnableGrpcLogs(id<BindingsLogWriter> _Nullable writer); - -/** - * ErrorStringToUserFriendlyMessage takes a passed in errStr which will be -a backend generated error. These may be error specifically written by -the backend team or lower level errors gotten from low level dependencies. -This function will parse the error string for common errors provided from -errToUserErr to provide a more user-friendly error message for the front end. -If the error is not common, some simple parsing is done on the error message -to make it more user-accessible, removing backend specific jargon. - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsErrorStringToUserFriendlyMessage(NSString* _Nullable errStr); - -/** - * GenerateSecret creates a secret password using a system-based -pseudorandom number generator. It takes 1 parameter, `numBytes`, -which should be set to 32, but can be set higher in certain cases. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsGenerateSecret(long numBytes); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetCMIXParams(NSError* _Nullable* _Nullable error); - -/** - * returns a previously created client. IF be used if the garbage collector -removes the client instance on the app side. Is NOT thread safe relative to -login, newClient, or newPrecannedClient - */ -FOUNDATION_EXPORT BindingsClient* _Nullable BindingsGetClientSingleton(void); - -/** - * GetDependencies returns the api DEPENDENCIES - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetDependencies(void); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetE2EParams(NSError* _Nullable* _Nullable error); - -/** - * GetGitVersion rturns the api GITVERSION - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetGitVersion(void); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetNetworkParams(NSError* _Nullable* _Nullable error); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetUnsafeParams(NSError* _Nullable* _Nullable error); - -/** - * GetVersion returns the api SEMVER - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetVersion(void); - -/** - * InitializeBackup starts the backup processes that returns backup updates when -they occur. Any time an event occurs that changes the contents of the backup, -such as adding or deleting a contact, the backup is triggered and an -encrypted backup is generated and returned on the updateBackupCb callback. -Call this function only when enabling backup if it has not already been -initialized or when the user wants to change their password. -To resume backup process on app recovery, use ResumeBackup. - */ -FOUNDATION_EXPORT BindingsBackup* _Nullable BindingsInitializeBackup(NSString* _Nullable password, id<BindingsUpdateBackupFunc> _Nullable updateBackupCb, BindingsClient* _Nullable c, NSError* _Nullable* _Nullable error); - -/** - * LoadSecretWithMnemonic loads the secret stored from the call to -StoreSecretWithMnemonic. The path given should be the same filepath -as the path given in StoreSecretWithMnemonic. There should be a file -in this path called ".recovery". This operation is not tied -to client operations, as the user will not have a client when trying to -recover their account. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsLoadSecretWithMnemonic(NSString* _Nullable mnemonic, NSString* _Nullable path, NSError* _Nullable* _Nullable error); - -/** - * sets level of logging. All logs the set level and above will be displayed -options are: - TRACE - 0 - DEBUG - 1 - INFO - 2 - WARN - 3 - ERROR - 4 - CRITICAL - 5 - FATAL - 6 -The default state without updates is: INFO - */ -FOUNDATION_EXPORT BOOL BindingsLogLevel(long level, NSError* _Nullable* _Nullable error); - -/** - * Login will load an existing client from the storageDir -using the password. This will fail if the client doesn't exist or -the password is incorrect. -The password is passed as a byte array so that it can be cleared from -memory and stored as securely as possible using the memguard library. -Login does not block on network connection, and instead loads and -starts subprocesses to perform network operations. - */ -FOUNDATION_EXPORT BindingsClient* _Nullable BindingsLogin(NSString* _Nullable storageDir, NSData* _Nullable password, NSString* _Nullable parameters, NSError* _Nullable* _Nullable error); - -/** - * MakeIdList creates a new empty IdList. - */ -FOUNDATION_EXPORT BindingsIdList* _Nullable BindingsMakeIdList(void); - -FOUNDATION_EXPORT BindingsIntList* _Nullable BindingsMakeIntList(void); - -/** - * NewClient creates client storage, generates keys, connects, and registers -with the network. Note that this does not register a username/identity, but -merely creates a new cryptographic identity for adding such information -at a later date. - -Users of this function should delete the storage directory on error. - */ -FOUNDATION_EXPORT BOOL BindingsNewClient(NSString* _Nullable network, NSString* _Nullable storageDir, NSData* _Nullable password, NSString* _Nullable regCode, NSError* _Nullable* _Nullable error); - -/** - * NewClientFromBackup constructs a new Client from an encrypted backup. The backup -is decrypted using the backupPassphrase. On success a successful client creation, -the function will return a JSON encoded list of the E2E partners -contained in the backup and a json-encoded string of the parameters stored in the backup - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsNewClientFromBackup(NSString* _Nullable ndfJSON, NSString* _Nullable storageDir, NSData* _Nullable sessionPassword, NSData* _Nullable backupPassphrase, NSData* _Nullable backupFileContents, NSError* _Nullable* _Nullable error); - -/** - * NewDummyTrafficManager creates a DummyTraffic manager and initialises the -dummy traffic send thread. Note that the manager does not start sending dummy -traffic until its status is set to true using DummyTraffic.SetStatus. -The maxNumMessages is the upper bound of the random number of messages sent -each send. avgSendDeltaMS is the average duration, in milliseconds, to wait -between sends. Sends occur every avgSendDeltaMS +/- a random duration with an -upper bound of randomRangeMS. - */ -FOUNDATION_EXPORT BindingsDummyTraffic* _Nullable BindingsNewDummyTrafficManager(BindingsClient* _Nullable client, long maxNumMessages, long avgSendDeltaMS, long randomRangeMS, NSError* _Nullable* _Nullable error); - -/** - * fact object -creates a new fact. The factType must be either: - 0 - Username - 1 - Email - 2 - Phone Number -The fact must be well formed for the type and must not include commas or -semicolons. If it is not well formed, it will be rejected. Phone numbers -must have the two letter country codes appended. For the complete set of -validation, see /elixxir/primitives/fact/fact.go - */ -FOUNDATION_EXPORT BindingsFact* _Nullable BindingsNewFact(long factType, NSString* _Nullable factStr, NSError* _Nullable* _Nullable error); - -/** - * FactList - */ -FOUNDATION_EXPORT BindingsFactList* _Nullable BindingsNewFactList(void); - -/** - * NewFileTransferManager creates a new file transfer manager and starts the -sending and receiving threads. The receiveFunc is called everytime a new file -transfer is received. -The parameters string contains file transfer network configuration options -and is a JSON formatted string of the fileTransfer.Params object. If it is -left empty, then defaults are used. It is highly recommended that defaults -are used. If it is set, it must match the following format: - {"MaxThroughput":150000,"SendTimeout":500000000} -MaxThroughput is in bytes/sec and SendTimeout is in nanoseconds. - */ -FOUNDATION_EXPORT BindingsFileTransfer* _Nullable BindingsNewFileTransferManager(BindingsClient* _Nullable client, id<BindingsFileTransferReceiveFunc> _Nullable receiveFunc, NSString* _Nullable parameters, NSError* _Nullable* _Nullable error); - -/** - * NewGroupManager creates a new group chat manager. - */ -FOUNDATION_EXPORT BindingsGroupChat* _Nullable BindingsNewGroupManager(BindingsClient* _Nullable client, id<BindingsGroupRequestFunc> _Nullable requestFunc, id<BindingsGroupReceiveFunc> _Nullable receiveFunc, NSError* _Nullable* _Nullable error); - -/** - * NewPrecannedClient creates an insecure user with predetermined keys with nodes -It creates client storage, generates keys, connects, and registers -with the network. Note that this does not register a username/identity, but -merely creates a new cryptographic identity for adding such information -at a later date. - -Users of this function should delete the storage directory on error. - */ -FOUNDATION_EXPORT BOOL BindingsNewPrecannedClient(long precannedID, NSString* _Nullable network, NSString* _Nullable storageDir, NSData* _Nullable password, NSError* _Nullable* _Nullable error); - -/** - * NewUserDiscovery returns a new user discovery object. Only call this once. It must be called -after StartNetworkFollower is called and will fail if the network has never -been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscovery(BindingsClient* _Nullable client, NSError* _Nullable* _Nullable error); - -/** - * NewUserDiscoveryFromBackup returns a new user discovery object. It -wil set up the manager with the backup data. Pass into it the backed up -facts, one email and phone number each. This will add the registered facts -to the backed Store. Any one of these fields may be empty, -however both fields being empty will cause an error. Any other fact that is not -an email or phone number will return an error. You may only add a fact for the -accepted types once each. If you attempt to back up a fact type that has already -been backed up, an error will be returned. Anytime an error is returned, it means -the backup was not successful. -NOTE: Do not use this as a direct store operation. This feature is intended to add facts -to a backend store that have ALREADY BEEN REGISTERED on the account. -THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend. -Only call this once. It must be called after StartNetworkFollower -is called and will fail if the network has never been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscoveryFromBackup(BindingsClient* _Nullable client, NSString* _Nullable email, NSString* _Nullable phone, NSError* _Nullable* _Nullable error); - -/** - * NotificationsForMe Check if a notification received is for me -It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller, -a Type, and a source. These are as follows: - TYPE SOURCE DESCRIPTION - "default" recipient user ID A message with no association - "request" sender user ID A channel request has been received - "reset" sender user ID A channel reset has been received - "confirm" sender user ID A channel request has been accepted - "silent" sender user ID A message which should not be notified on - "e2e" sender user ID reception of an E2E message - "group" group ID reception of a group chat message - "endFT" sender user ID Last message sent confirming end of file transfer - "groupRQ" sender user ID Request from sender to join a group chat - */ -FOUNDATION_EXPORT BindingsManyNotificationForMeReport* _Nullable BindingsNotificationsForMe(NSString* _Nullable notifCSV, NSString* _Nullable preimages, NSError* _Nullable* _Nullable error); - -/** - * RegisterLogWriter registers a callback on which logs are written. - */ -FOUNDATION_EXPORT void BindingsRegisterLogWriter(id<BindingsLogWriter> _Nullable writer); - -/** - * RestoreContactsFromBackup takes as input the jason output of the -`NewClientFromBackup` function, unmarshals it into IDs, looks up -each ID in user discovery, and initiates a session reset request. -This function will not return until every id in the list has been sent a -request. It should be called again and again until it completes. -xxDK users should not use this function. This function is used by -the mobile phone apps and are not intended to be part of the xxDK. It -should be treated as internal functions specific to the phone apps. - */ -FOUNDATION_EXPORT BindingsRestoreContactsReport* _Nullable BindingsRestoreContactsFromBackup(NSData* _Nullable backupPartnerIDs, BindingsClient* _Nullable client, BindingsUserDiscovery* _Nullable udManager, id<BindingsLookupCallback> _Nullable lookupCB, id<BindingsRestoreContactsUpdater> _Nullable updatesCb); - -/** - * ResumeBackup starts the backup processes back up with a new callback after it -has been initialized. -Call this function only when resuming a backup that has already been -initialized or to replace the callback. -To start the backup for the first time or to use a new password, use -InitializeBackup. - */ -FOUNDATION_EXPORT BindingsBackup* _Nullable BindingsResumeBackup(id<BindingsUpdateBackupFunc> _Nullable cb, BindingsClient* _Nullable c, NSError* _Nullable* _Nullable error); - -/** - * SetTimeSource sets the network time to a custom source. - */ -FOUNDATION_EXPORT void BindingsSetTimeSource(id<BindingsTimeSource> _Nullable timeNow); - -/** - * StoreSecretWithMnemonic stores the secret tied with the mnemonic to storage. -Unlike other storage operations, this does not use EKV, as that is -intrinsically tied to client operations, which the user will not have while -trying to recover their account. As such, we store the encrypted data -directly, with a specified path. Path will be a valid filepath in which the -recover file will be stored as ".recovery". - -As an example, given "home/user/xxmessenger/storagePath", -the recovery file will be stored at -"home/user/xxmessenger/storagePath/.recovery" - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsStoreSecretWithMnemonic(NSData* _Nullable secret, NSString* _Nullable path, NSError* _Nullable* _Nullable error); - -/** - * Unmarshals a marshaled contact object, returns an error if it fails - */ -FOUNDATION_EXPORT BindingsContact* _Nullable BindingsUnmarshalContact(NSData* _Nullable b, NSError* _Nullable* _Nullable error); - -/** - * Unmarshals a marshaled send report object, returns an error if it fails - */ -FOUNDATION_EXPORT BindingsSendReport* _Nullable BindingsUnmarshalSendReport(NSData* _Nullable b, NSError* _Nullable* _Nullable error); - -/** - * UpdateCommonErrors takes the passed in contents of a JSON file and updates the -errToUserErr map with the contents of the json file. The JSON's expected format -conform with the commented examples provides in errToUserErr above. -NOTE that you should not pass in a file path, but a preloaded JSON file - */ -FOUNDATION_EXPORT BOOL BindingsUpdateCommonErrors(NSString* _Nullable jsonFile, NSError* _Nullable* _Nullable error); - -// skipped function WrapAPIClient with unsupported parameter or return types - - -// skipped function WrapUserDiscovery with unsupported parameter or return types - - -@class BindingsAuthConfirmCallback; - -@class BindingsAuthRequestCallback; - -@class BindingsAuthResetNotificationCallback; - -@class BindingsClientError; - -@class BindingsEventCallbackFunctionObject; - -@class BindingsFileTransferReceiveFunc; - -@class BindingsFileTransferReceivedProgressFunc; - -@class BindingsFileTransferSentProgressFunc; - -@class BindingsGroupReceiveFunc; - -@class BindingsGroupRequestFunc; - -@class BindingsListener; - -@class BindingsLogWriter; - -@class BindingsLookupCallback; - -@class BindingsMessageDeliveryCallback; - -@class BindingsMultiLookupCallback; - -@class BindingsNetworkHealthCallback; - -@class BindingsPreimageNotification; - -@class BindingsRestoreContactsUpdater; - -@class BindingsRoundCompletionCallback; - -@class BindingsRoundEventCallback; - -@class BindingsSearchCallback; - -@class BindingsSingleSearchCallback; - -@class BindingsTimeSource; - -@class BindingsUpdateBackupFunc; - -/** - * AuthConfirmCallback notifies the register whenever they receive an auth -request confirmation - */ -@interface BindingsAuthConfirmCallback : NSObject <goSeqRefInterface, BindingsAuthConfirmCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)partner; -@end - -/** - * AuthRequestCallback notifies the register whenever they receive an auth -request - */ -@interface BindingsAuthRequestCallback : NSObject <goSeqRefInterface, BindingsAuthRequestCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -/** - * AuthRequestCallback notifies the register whenever they receive an auth -request - */ -@interface BindingsAuthResetNotificationCallback : NSObject <goSeqRefInterface, BindingsAuthResetNotificationCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@interface BindingsClientError : NSObject <goSeqRefInterface, BindingsClientError> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)report:(NSString* _Nullable)source message:(NSString* _Nullable)message trace:(NSString* _Nullable)trace; -@end - -/** - * EventCallbackFunctionObject bindings interface which contains function -that implements the EventCallbackFunction - */ -@interface BindingsEventCallbackFunctionObject : NSObject <goSeqRefInterface, BindingsEventCallbackFunctionObject> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)reportEvent:(long)priority category:(NSString* _Nullable)category evtType:(NSString* _Nullable)evtType details:(NSString* _Nullable)details; -@end - -/** - * FileTransferReceiveFunc contains a function callback that notifies the -receiver of an incoming file transfer. It is called on the reception of the -initial file transfer message. - */ -@interface BindingsFileTransferReceiveFunc : NSObject <goSeqRefInterface, BindingsFileTransferReceiveFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)receiveCallback:(NSData* _Nullable)tid fileName:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType sender:(NSData* _Nullable)sender size:(long)size preview:(NSData* _Nullable)preview; -@end - -/** - * FileTransferReceivedProgressFunc contains a function callback that tracks the -progress of receiving a file. It is called when a file part is received, the -transfer completes, or on error. - */ -@interface BindingsFileTransferReceivedProgressFunc : NSObject <goSeqRefInterface, BindingsFileTransferReceivedProgressFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)receivedProgressCallback:(BOOL)completed received:(long)received total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -/** - * FileTransferSentProgressFunc contains a function callback that tracks the -progress of sending a file. It is called when a file part is sent, a file -part arrives, the transfer completes, or on error. - */ -@interface BindingsFileTransferSentProgressFunc : NSObject <goSeqRefInterface, BindingsFileTransferSentProgressFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)sentProgressCallback:(BOOL)completed sent:(long)sent arrived:(long)arrived total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -/** - * GroupReceiveFunc contains a function callback that is called when a group -message is received. - */ -@interface BindingsGroupReceiveFunc : NSObject <goSeqRefInterface, BindingsGroupReceiveFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)groupReceiveCallback:(BindingsGroupMessageReceive* _Nullable)msg; -@end - -/** - * GroupRequestFunc contains a function callback that is called when a group -request is received. - */ -@interface BindingsGroupRequestFunc : NSObject <goSeqRefInterface, BindingsGroupRequestFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)groupRequestCallback:(BindingsGroup* _Nullable)g; -@end - -/** - * Listener provides a callback to hear a message -An object implementing this interface can be called back when the client -gets a message of the type that the registerer specified at registration -time. - */ -@interface BindingsListener : NSObject <goSeqRefInterface, BindingsListener> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * Hear is called to receive a message in the UI - */ -- (void)hear:(BindingsMessage* _Nullable)message; -/** - * Returns a name, used for debugging - */ -- (NSString* _Nonnull)name; -@end - -@interface BindingsLogWriter : NSObject <goSeqRefInterface, BindingsLogWriter> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)log:(NSString* _Nullable)p0; -@end - -/** - * LookupCallback returns the result of a single lookup - */ -@interface BindingsLookupCallback : NSObject <goSeqRefInterface, BindingsLookupCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -/** - * MessageDeliveryCallback gets called on the determination if all events -related to a message send were successful. - */ -@interface BindingsMessageDeliveryCallback : NSObject <goSeqRefInterface, BindingsMessageDeliveryCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(NSData* _Nullable)msgID delivered:(BOOL)delivered timedOut:(BOOL)timedOut roundResults:(NSData* _Nullable)roundResults; -@end - -/** - * MultiLookupCallback returns the result of many parallel lookups - */ -@interface BindingsMultiLookupCallback : NSObject <goSeqRefInterface, BindingsMultiLookupCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContactList* _Nullable)Succeeded failed:(BindingsIdList* _Nullable)failed errors:(NSString* _Nullable)errors; -@end - -/** - * A callback when which is used to receive notification if network health -changes - */ -@interface BindingsNetworkHealthCallback : NSObject <goSeqRefInterface, BindingsNetworkHealthCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BOOL)p0; -@end - -@interface BindingsPreimageNotification : NSObject <goSeqRefInterface, BindingsPreimageNotification> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)notify:(NSData* _Nullable)identity deleted:(BOOL)deleted; -@end - -/** - * RestoreContactsUpdater interface provides a callback function -for receiving update information from RestoreContactsFromBackup. - */ -@interface BindingsRestoreContactsUpdater : NSObject <goSeqRefInterface, BindingsRestoreContactsUpdater> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * RestoreContactsCallback is called to report the current # of contacts -that have been found and how many have been restored -against the total number that need to be -processed. If an error occurs it it set on the err variable as a -plain string. - */ -- (void)restoreContactsCallback:(long)numFound numRestored:(long)numRestored total:(long)total err:(NSString* _Nullable)err; -@end - -/** - * RoundCompletionCallback is returned when the completion of a round is known. - */ -@interface BindingsRoundCompletionCallback : NSObject <goSeqRefInterface, BindingsRoundCompletionCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(long)rid success:(BOOL)success timedOut:(BOOL)timedOut; -@end - -/** - * RoundEventCallback handles waiting on the exact state of a round on -the cMix network. - */ -@interface BindingsRoundEventCallback : NSObject <goSeqRefInterface, BindingsRoundEventCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(long)rid state:(long)state timedOut:(BOOL)timedOut; -@end - -/** - * SearchCallback returns the result of a search - */ -@interface BindingsSearchCallback : NSObject <goSeqRefInterface, BindingsSearchCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContactList* _Nullable)contacts error:(NSString* _Nullable)error; -@end - -/** - * SingleSearchCallback returns the result of a single search - */ -@interface BindingsSingleSearchCallback : NSObject <goSeqRefInterface, BindingsSingleSearchCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@interface BindingsTimeSource : NSObject <goSeqRefInterface, BindingsTimeSource> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (int64_t)nowMs; -@end - -/** - * UpdateBackupFunc contains a function callback that returns new backups. - */ -@interface BindingsUpdateBackupFunc : NSObject <goSeqRefInterface, BindingsUpdateBackupFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)updateBackup:(NSData* _Nullable)encryptedBackup; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Universe.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Universe.objc.h deleted file mode 100644 index 019e7502d581983722a15bf30799e85cbc5dd766..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Universe.objc.h +++ /dev/null @@ -1,29 +0,0 @@ -// Objective-C API for talking to Go package. -// gobind -lang=objc -// -// File is generated by gobind. Do not edit. - -#ifndef __Universe_H__ -#define __Universe_H__ - -@import Foundation; -#include "ref.h" - -@protocol Universeerror; -@class Universeerror; - -@protocol Universeerror <NSObject> -- (NSString* _Nonnull)error; -@end - -@class Universeerror; - -@interface Universeerror : NSError <goSeqRefInterface, Universeerror> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (NSString* _Nonnull)error; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/ref.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/ref.h deleted file mode 100644 index b8036a4d85c7387f3def61473a071b5d8c4c8208..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/ref.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef __GO_REF_HDR__ -#define __GO_REF_HDR__ - -#include <Foundation/Foundation.h> - -// GoSeqRef is an object tagged with an integer for passing back and -// forth across the language boundary. A GoSeqRef may represent either -// an instance of a Go object, or an Objective-C object passed to Go. -// The explicit allocation of a GoSeqRef is used to pin a Go object -// when it is passed to Objective-C. The Go seq package maintains a -// reference to the Go object in a map keyed by the refnum along with -// a reference count. When the reference count reaches zero, the Go -// seq package will clear the corresponding entry in the map. -@interface GoSeqRef : NSObject { -} -@property(readonly) int32_t refnum; -@property(strong) id obj; // NULL when representing a Go object. - -// new GoSeqRef object to proxy a Go object. The refnum must be -// provided from Go side. -- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj; - -- (int32_t)incNum; - -@end - -@protocol goSeqRefInterface --(GoSeqRef*) _ref; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Modules/module.modulemap b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Modules/module.modulemap deleted file mode 100644 index 4316a5b24058edfc18ffb2dc7f7a982e8353441a..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Modules/module.modulemap +++ /dev/null @@ -1,8 +0,0 @@ -framework module "Bindings" { - header "ref.h" - header "Bindings.objc.h" - header "Universe.objc.h" - header "Bindings.h" - - export * -} \ No newline at end of file diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Resources/Info.plist b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Resources/Info.plist deleted file mode 100644 index 0d1a4b8ab9b1fc8e9357197398f73353470cb636..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Resources/Info.plist +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> - <plist version="1.0"> - <dict> - </dict> - </plist> diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Bindings b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Bindings deleted file mode 100644 index 1e3b1dc25b4d65bf1e21a5d169dc3ae4de6c4fd7..0000000000000000000000000000000000000000 Binary files a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Bindings and /dev/null differ diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/Bindings.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/Bindings.h deleted file mode 100644 index 8906a7da239705b790cb2bb64de92f806640cb38..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/Bindings.h +++ /dev/null @@ -1,13 +0,0 @@ - -// Objective-C API for talking to the following Go packages -// -// gitlab.com/elixxir/client/bindings -// -// File is generated by gomobile bind. Do not edit. -#ifndef __Bindings_FRAMEWORK_H__ -#define __Bindings_FRAMEWORK_H__ - -#include "Bindings.objc.h" -#include "Universe.objc.h" - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/Bindings.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/Bindings.objc.h deleted file mode 100644 index 32bf6d116888f787ced27b01b95cb4e1b2c1138b..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/Bindings.objc.h +++ /dev/null @@ -1,2083 +0,0 @@ -// Objective-C API for talking to gitlab.com/elixxir/client/bindings Go package. -// gobind -lang=objc gitlab.com/elixxir/client/bindings -// -// File is generated by gobind. Do not edit. - -#ifndef __Bindings_H__ -#define __Bindings_H__ - -@import Foundation; -#include "ref.h" -#include "Universe.objc.h" - - -@class BindingsBackup; -@class BindingsBackupReport; -@class BindingsClient; -@class BindingsContact; -@class BindingsContactList; -@class BindingsDummyTraffic; -@class BindingsFact; -@class BindingsFactList; -@class BindingsFilePartTracker; -@class BindingsFileTransfer; -@class BindingsGroup; -@class BindingsGroupChat; -@class BindingsGroupMember; -@class BindingsGroupMembership; -@class BindingsGroupMessageReceive; -@class BindingsGroupReportDisk; -@class BindingsGroupSendReport; -@class BindingsIdList; -@class BindingsIntList; -@class BindingsManyNotificationForMeReport; -@class BindingsMessage; -@class BindingsNewGroupReport; -@class BindingsNodeRegistrationsStatus; -@class BindingsNotificationForMeReport; -@class BindingsRestoreContactsReport; -@class BindingsRoundList; -@class BindingsSendReport; -@class BindingsSendReportDisk; -@class BindingsUnregister; -@class BindingsUser; -@class BindingsUserDiscovery; -@protocol BindingsAuthConfirmCallback; -@class BindingsAuthConfirmCallback; -@protocol BindingsAuthRequestCallback; -@class BindingsAuthRequestCallback; -@protocol BindingsAuthResetNotificationCallback; -@class BindingsAuthResetNotificationCallback; -@protocol BindingsClientError; -@class BindingsClientError; -@protocol BindingsEventCallbackFunctionObject; -@class BindingsEventCallbackFunctionObject; -@protocol BindingsFileTransferReceiveFunc; -@class BindingsFileTransferReceiveFunc; -@protocol BindingsFileTransferReceivedProgressFunc; -@class BindingsFileTransferReceivedProgressFunc; -@protocol BindingsFileTransferSentProgressFunc; -@class BindingsFileTransferSentProgressFunc; -@protocol BindingsGroupReceiveFunc; -@class BindingsGroupReceiveFunc; -@protocol BindingsGroupRequestFunc; -@class BindingsGroupRequestFunc; -@protocol BindingsListener; -@class BindingsListener; -@protocol BindingsLogWriter; -@class BindingsLogWriter; -@protocol BindingsLookupCallback; -@class BindingsLookupCallback; -@protocol BindingsMessageDeliveryCallback; -@class BindingsMessageDeliveryCallback; -@protocol BindingsMultiLookupCallback; -@class BindingsMultiLookupCallback; -@protocol BindingsNetworkHealthCallback; -@class BindingsNetworkHealthCallback; -@protocol BindingsPreimageNotification; -@class BindingsPreimageNotification; -@protocol BindingsRestoreContactsUpdater; -@class BindingsRestoreContactsUpdater; -@protocol BindingsRoundCompletionCallback; -@class BindingsRoundCompletionCallback; -@protocol BindingsRoundEventCallback; -@class BindingsRoundEventCallback; -@protocol BindingsSearchCallback; -@class BindingsSearchCallback; -@protocol BindingsSingleSearchCallback; -@class BindingsSingleSearchCallback; -@protocol BindingsTimeSource; -@class BindingsTimeSource; -@protocol BindingsUpdateBackupFunc; -@class BindingsUpdateBackupFunc; - -@protocol BindingsAuthConfirmCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)partner; -@end - -@protocol BindingsAuthRequestCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@protocol BindingsAuthResetNotificationCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@protocol BindingsClientError <NSObject> -- (void)report:(NSString* _Nullable)source message:(NSString* _Nullable)message trace:(NSString* _Nullable)trace; -@end - -@protocol BindingsEventCallbackFunctionObject <NSObject> -- (void)reportEvent:(long)priority category:(NSString* _Nullable)category evtType:(NSString* _Nullable)evtType details:(NSString* _Nullable)details; -@end - -@protocol BindingsFileTransferReceiveFunc <NSObject> -- (void)receiveCallback:(NSData* _Nullable)tid fileName:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType sender:(NSData* _Nullable)sender size:(long)size preview:(NSData* _Nullable)preview; -@end - -@protocol BindingsFileTransferReceivedProgressFunc <NSObject> -- (void)receivedProgressCallback:(BOOL)completed received:(long)received total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -@protocol BindingsFileTransferSentProgressFunc <NSObject> -- (void)sentProgressCallback:(BOOL)completed sent:(long)sent arrived:(long)arrived total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -@protocol BindingsGroupReceiveFunc <NSObject> -- (void)groupReceiveCallback:(BindingsGroupMessageReceive* _Nullable)msg; -@end - -@protocol BindingsGroupRequestFunc <NSObject> -- (void)groupRequestCallback:(BindingsGroup* _Nullable)g; -@end - -@protocol BindingsListener <NSObject> -/** - * Hear is called to receive a message in the UI - */ -- (void)hear:(BindingsMessage* _Nullable)message; -/** - * Returns a name, used for debugging - */ -- (NSString* _Nonnull)name; -@end - -@protocol BindingsLogWriter <NSObject> -- (void)log:(NSString* _Nullable)p0; -@end - -@protocol BindingsLookupCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@protocol BindingsMessageDeliveryCallback <NSObject> -- (void)eventCallback:(NSData* _Nullable)msgID delivered:(BOOL)delivered timedOut:(BOOL)timedOut roundResults:(NSData* _Nullable)roundResults; -@end - -@protocol BindingsMultiLookupCallback <NSObject> -- (void)callback:(BindingsContactList* _Nullable)Succeeded failed:(BindingsIdList* _Nullable)failed errors:(NSString* _Nullable)errors; -@end - -@protocol BindingsNetworkHealthCallback <NSObject> -- (void)callback:(BOOL)p0; -@end - -@protocol BindingsPreimageNotification <NSObject> -- (void)notify:(NSData* _Nullable)identity deleted:(BOOL)deleted; -@end - -@protocol BindingsRestoreContactsUpdater <NSObject> -/** - * RestoreContactsCallback is called to report the current # of contacts -that have been found and how many have been restored -against the total number that need to be -processed. If an error occurs it it set on the err variable as a -plain string. - */ -- (void)restoreContactsCallback:(long)numFound numRestored:(long)numRestored total:(long)total err:(NSString* _Nullable)err; -@end - -@protocol BindingsRoundCompletionCallback <NSObject> -- (void)eventCallback:(long)rid success:(BOOL)success timedOut:(BOOL)timedOut; -@end - -@protocol BindingsRoundEventCallback <NSObject> -- (void)eventCallback:(long)rid state:(long)state timedOut:(BOOL)timedOut; -@end - -@protocol BindingsSearchCallback <NSObject> -- (void)callback:(BindingsContactList* _Nullable)contacts error:(NSString* _Nullable)error; -@end - -@protocol BindingsSingleSearchCallback <NSObject> -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@protocol BindingsTimeSource <NSObject> -- (int64_t)nowMs; -@end - -@protocol BindingsUpdateBackupFunc <NSObject> -- (void)updateBackup:(NSData* _Nullable)encryptedBackup; -@end - -@interface BindingsBackup : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * AddJson stores a passed in json string in the backup structure - */ -- (void)addJson:(NSString* _Nullable)json; -/** - * IsBackupRunning returns true if the backup has been initialized and is -running. Returns false if it has been stopped. - */ -- (BOOL)isBackupRunning; -/** - * StopBackup stops the backup processes and deletes the user's password from -storage. To enable backups again, call InitializeBackup. - */ -- (BOOL)stopBackup:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsBackupReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field BackupReport.RestoredContacts with unsupported type: []*gitlab.com/xx_network/primitives/id.ID - -@property (nonatomic) NSString* _Nonnull params; -@end - -/** - * BindingsClient wraps the api.Client, implementing additional functions -to support the gomobile Client interface - */ -@interface BindingsClient : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BOOL)confirmAuthenticatedChannel:(NSData* _Nullable)recipientMarshaled ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteAllRequests clears all requests from Client's auth storage. - */ -- (BOOL)deleteAllRequests:(NSError* _Nullable* _Nullable)error; -/** - * DeleteContact is a function which removes a contact from Client's storage - */ -- (BOOL)deleteContact:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteReceiveRequests clears receive requests from Client's auth storage. - */ -- (BOOL)deleteReceiveRequests:(NSError* _Nullable* _Nullable)error; -/** - * DeleteRequest will delete a request, agnostic of request type -for the given partner ID. If no request exists for this -partner ID an error will be returned. - */ -- (BOOL)deleteRequest:(NSData* _Nullable)requesterUserId error:(NSError* _Nullable* _Nullable)error; -/** - * DeleteSentRequests clears sent requests from Client's auth storage. - */ -- (BOOL)deleteSentRequests:(NSError* _Nullable* _Nullable)error; -// skipped method Client.GetInternalClient with unsupported parameter or return types - -/** - * GetNodeRegistrationStatus returns a struct with the number of nodes the -client is registered with and the number total. - */ -- (BindingsNodeRegistrationsStatus* _Nullable)getNodeRegistrationStatus:(NSError* _Nullable* _Nullable)error; -/** - * GetPartners returns a list of - */ -- (NSData* _Nullable)getPartners:(NSError* _Nullable* _Nullable)error; -/** - * GetPreferredBins returns the geographic bin or bins that the provided two -character country code is a part of. The bins are returned as CSV. - */ -- (NSString* _Nonnull)getPreferredBins:(NSString* _Nullable)countryCode error:(NSError* _Nullable* _Nullable)error; -- (NSString* _Nonnull)getPreimages:(NSData* _Nullable)identity; -// skipped method Client.GetRateLimitParams with unsupported parameter or return types - -- (NSString* _Nonnull)getRelationshipFingerprint:(NSData* _Nullable)partnerID error:(NSError* _Nullable* _Nullable)error; -/** - * Returns a user object from which all information about the current user -can be gleaned - */ -- (BindingsUser* _Nullable)getUser; -/** - * HasRunningProcessies checks if any background threads are running. -returns true if none are running. This is meant to be -used when NetworkFollowerStatus() returns Stopping. -Due to the handling of comms on iOS, where the OS can -block indefiently, it may not enter the stopped -state apropreatly. This can be used instead. - */ -- (BOOL)hasRunningProcessies; -/** - * returns true if the network is read to be in a healthy state where -messages can be sent - */ -- (BOOL)isNetworkHealthy; -- (BindingsContact* _Nullable)makePrecannedAuthenticatedChannel:(long)precannedID error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the state of the network follower. Returns: -Stopped - 0 -Starting - 1000 -Running - 2000 -Stopping - 3000 - */ -- (long)networkFollowerStatus; -- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetNotificationCallback> _Nullable)reset; -/** - * RegisterClientErrorCallback registers the callback to handle errors from the -long running threads controlled by StartNetworkFollower and StopNetworkFollower - */ -- (void)registerClientErrorCallback:(id<BindingsClientError> _Nullable)clientError; -/** - * RegisterEventCallback records the given function to receive -ReportableEvent objects. It returns the internal index -of the callback so that it can be deleted later. - */ -- (BOOL)registerEventCallback:(NSString* _Nullable)name myObj:(id<BindingsEventCallbackFunctionObject> _Nullable)myObj error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterForNotifications accepts firebase messaging token - */ -- (BOOL)registerForNotifications:(NSString* _Nullable)token error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterListener records and installs a listener for messages -matching specific uid, msgType, and/or username -Returns a ListenerUnregister interface which can be - -to register for any userID, pass in an id with length 0 or an id with -all zeroes - -to register for any message type, pass in a message type of 0 - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types - */ -- (BindingsUnregister* _Nullable)registerListener:(NSData* _Nullable)uid msgType:(long)msgType listener:(id<BindingsListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterNetworkHealthCB registers the network health callback to be called -any time the network health changes. Returns a unique ID that can be used to -unregister the network health callback. - */ -- (int64_t)registerNetworkHealthCB:(id<BindingsNetworkHealthCallback> _Nullable)nhc; -- (void)registerPreimageCallback:(NSData* _Nullable)identity pin:(id<BindingsPreimageNotification> _Nullable)pin; -/** - * RegisterRoundEventsHandler registers a callback interface for round -events. -The rid is the round the event attaches to -The timeoutMS is the number of milliseconds until the event fails, and the -validStates are a list of states (one per byte) on which the event gets -triggered -States: - 0x00 - PENDING (Never seen by client) - 0x01 - PRECOMPUTING - 0x02 - STANDBY - 0x03 - QUEUED - 0x04 - REALTIME - 0x05 - COMPLETED - 0x06 - FAILED -These states are defined in elixxir/primitives/states/state.go - */ -- (BindingsUnregister* _Nullable)registerRoundEventsHandler:(long)rid cb:(id<BindingsRoundEventCallback> _Nullable)cb timeoutMS:(long)timeoutMS il:(BindingsIntList* _Nullable)il; -- (void)replayRequests; -- (BOOL)requestAuthenticatedChannel:(NSData* _Nullable)recipientMarshaled meMarshaled:(NSData* _Nullable)meMarshaled message:(NSString* _Nullable)message ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -- (BOOL)resetSession:(NSData* _Nullable)recipientMarshaled meMarshaled:(NSData* _Nullable)meMarshaled message:(NSString* _Nullable)message ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * This will return the round the message was sent on if it is successfully sent -This can be used to register a round event to learn about message delivery. -on failure a round id of -1 is returned - */ -- (BOOL)sendCmix:(NSData* _Nullable)recipient contents:(NSData* _Nullable)contents parameters:(NSString* _Nullable)parameters ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * SendE2E sends an end-to-end payload to the provided recipient with -the provided msgType. Returns the list of rounds in which parts of -the message were sent or an error if it fails. - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types - */ -- (BindingsSendReport* _Nullable)sendE2E:(NSData* _Nullable)recipient payload:(NSData* _Nullable)payload messageType:(long)messageType parameters:(NSString* _Nullable)parameters error:(NSError* _Nullable* _Nullable)error; -/** - * SendUnsafe sends an unencrypted payload to the provided recipient -with the provided msgType. Returns the list of rounds in which parts -of the message were sent or an error if it fails. -NOTE: Do not use this function unless you know what you are doing. -This function always produces an error message in client logging. - -Message Types can be found in client/interfaces/message/type.go -Make sure to not conflict with ANY default message types with custom types - */ -- (BindingsRoundList* _Nullable)sendUnsafe:(NSData* _Nullable)recipient payload:(NSData* _Nullable)payload messageType:(long)messageType parameters:(NSString* _Nullable)parameters error:(NSError* _Nullable* _Nullable)error; -/** - * SetProxiedBins updates the host pool filter that filters out gateways that -are not in one of the specified bins. The provided bins should be CSV. - */ -- (BOOL)setProxiedBins:(NSString* _Nullable)binStringsCSV error:(NSError* _Nullable* _Nullable)error; -/** - * StartNetworkFollower kicks off the tracking of the network. It starts -long running network client threads and returns an object for checking -state and stopping those threads. -Call this when returning from sleep and close when going back to -sleep. -These threads may become a significant drain on battery when offline, ensure -they are stopped if there is no internet access -Threads Started: - - Network Follower (/network/follow.go) - tracks the network events and hands them off to workers for handling - - Historical Round Retrieval (/network/rounds/historical.go) - Retrieves data about rounds which are too old to be stored by the client - - Message Retrieval Worker Group (/network/rounds/retrieve.go) - Requests all messages in a given round from the gateway of the last node - - Message Handling Worker Group (/network/message/handle.go) - Decrypts and partitions messages when signals via the Switchboard - - Health Tracker (/network/health) - Via the network instance tracks the state of the network - - Garbled Messages (/network/message/garbled.go) - Can be signaled to check all recent messages which could be be decoded - Uses a message store on disk for persistence - - Critical Messages (/network/message/critical.go) - Ensures all protocol layer mandatory messages are sent - Uses a message store on disk for persistence - - KeyExchange Trigger (/keyExchange/trigger.go) - Responds to sent rekeys and executes them - - KeyExchange Confirm (/keyExchange/confirm.go) - Responds to confirmations of successful rekey operations - */ -- (BOOL)startNetworkFollower:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * StopNetworkFollower stops the network follower if it is running. -It returns errors if the Follower is in the wrong status to stop or if it -fails to stop it. -if the network follower is running and this fails, the client object will -most likely be in an unrecoverable state and need to be trashed. - */ -- (BOOL)stopNetworkFollower:(NSError* _Nullable* _Nullable)error; -/** - * UnregisterEventCallback deletes the callback identified by the -index. It returns an error if it fails. - */ -- (void)unregisterEventCallback:(NSString* _Nullable)name; -/** - * UnregisterForNotifications unregister user for notifications - */ -- (BOOL)unregisterForNotifications:(NSError* _Nullable* _Nullable)error; -- (void)unregisterNetworkHealthCB:(int64_t)funcID; -- (BOOL)verifyOwnership:(NSData* _Nullable)receivedMarshaled verifiedMarshaled:(NSData* _Nullable)verifiedMarshaled ret0_:(BOOL* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * WaitForMessageDelivery allows the caller to get notified if the rounds a -message was sent in successfully completed. Under the hood, this uses an API -which uses the internal round data, network historical round lookup, and -waiting on network events to determine what has (or will) occur. - -The callbacks will return at timeoutMS if no state update occurs - -This function takes the marshaled send report to ensure a memory leak does -not occur as a result of both sides of the bindings holding a reference to -the same pointer. - */ -- (BOOL)waitForMessageDelivery:(NSData* _Nullable)marshaledSendReport mdc:(id<BindingsMessageDeliveryCallback> _Nullable)mdc timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * WaitForNewtwork will block until either the network is healthy or the -passed timeout. It will return true if the network is healthy - */ -- (BOOL)waitForNetwork:(long)timeoutMS; -/** - * WaitForRoundCompletion allows the caller to get notified if a round -has completed (or failed). Under the hood, this uses an API which uses the internal -round data, network historical round lookup, and waiting on network events -to determine what has (or will) occur. - -The callbacks will return at timeoutMS if no state update occurs - */ -- (BOOL)waitForRoundCompletion:(long)roundID rec:(id<BindingsRoundCompletionCallback> _Nullable)rec timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * contact object - */ -@interface BindingsContact : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped method Contact.GetAPIContact with unsupported parameter or return types - -/** - * GetDHPublicKey returns the public key associated with the Contact. - */ -- (NSData* _Nullable)getDHPublicKey; -/** - * Returns a fact list for adding and getting facts to and from the contact - */ -- (BindingsFactList* _Nullable)getFactList; -/** - * GetID returns the user ID for this user. - */ -- (NSData* _Nullable)getID; -/** - * GetDHPublicKey returns hash of a DH proof of key ownership. - */ -- (NSData* _Nullable)getOwnershipProof; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsContactList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Gets a stored round ID at the given index - */ -- (BindingsContact* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the number of round IDs stored - */ -- (long)len; -@end - -/** - * DummyTraffic contains the file dummy traffic manager. The manager can be used -to set and get the status of the send thread. - */ -@interface BindingsDummyTraffic : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewDummyTrafficManager creates a DummyTraffic manager and initialises the -dummy traffic send thread. Note that the manager does not start sending dummy -traffic until its status is set to true using DummyTraffic.SetStatus. -The maxNumMessages is the upper bound of the random number of messages sent -each send. avgSendDeltaMS is the average duration, in milliseconds, to wait -between sends. Sends occur every avgSendDeltaMS +/- a random duration with an -upper bound of randomRangeMS. - */ -- (nullable instancetype)initManager:(BindingsClient* _Nullable)client maxNumMessages:(long)maxNumMessages avgSendDeltaMS:(long)avgSendDeltaMS randomRangeMS:(long)randomRangeMS; -/** - * GetStatus returns the current state of the dummy traffic send thread. It has -the following return values: - true = send thread is sending dummy messages - false = send thread is paused/stopped and not sending dummy messages -Note that this function does not return the status set by SetStatus directly; -it returns the current status of the send thread, which means any call to -SetStatus will have a small delay before it is returned by GetStatus. - */ -- (BOOL)getStatus; -/** - * SetStatus sets the state of the dummy traffic send thread, which determines -if the thread is running or paused. The possible statuses are: - true = send thread is sending dummy messages - false = send thread is paused/stopped and not sending dummy messages -Returns an error if the channel is full. -Note that this function cannot change the status of the send thread if it has -yet to be started or stopped. - */ -- (BOOL)setStatus:(BOOL)status error:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsFact : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * fact object -creates a new fact. The factType must be either: - 0 - Username - 1 - Email - 2 - Phone Number -The fact must be well formed for the type and must not include commas or -semicolons. If it is not well formed, it will be rejected. Phone numbers -must have the two letter country codes appended. For the complete set of -validation, see /elixxir/primitives/fact/fact.go - */ -- (nullable instancetype)init:(long)factType factStr:(NSString* _Nullable)factStr; -- (NSString* _Nonnull)get; -- (NSString* _Nonnull)stringify; -- (long)type; -@end - -@interface BindingsFactList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * FactList - */ -- (nullable instancetype)init; -- (BOOL)add:(NSString* _Nullable)factData factType:(long)factType error:(NSError* _Nullable* _Nullable)error; -- (BindingsFact* _Nullable)get:(long)i; -- (long)num; -- (NSString* _Nonnull)stringify:(NSError* _Nullable* _Nullable)error; -@end - -/** - * FilePartTracker contains the interfaces.FilePartTracker. - */ -@interface BindingsFilePartTracker : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetNumParts returns the total number of file parts in the transfer. - */ -- (long)getNumParts; -/** - * GetPartStatus returns the status of the file part with the given part number. -The possible values for the status are: -0 = unsent -1 = sent (sender has sent a part, but it has not arrived) -2 = arrived (sender has sent a part, and it has arrived) -3 = received (receiver has received a part) - */ -- (long)getPartStatus:(long)partNum; -@end - -/** - * FileTransfer contains the file transfer manager. - */ -@interface BindingsFileTransfer : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewFileTransferManager creates a new file transfer manager and starts the -sending and receiving threads. The receiveFunc is called everytime a new file -transfer is received. -The parameters string contains file transfer network configuration options -and is a JSON formatted string of the fileTransfer.Params object. If it is -left empty, then defaults are used. It is highly recommended that defaults -are used. If it is set, it must match the following format: - {"MaxThroughput":150000,"SendTimeout":500000000} -MaxThroughput is in bytes/sec and SendTimeout is in nanoseconds. - */ -- (nullable instancetype)initManager:(BindingsClient* _Nullable)client receiveFunc:(id<BindingsFileTransferReceiveFunc> _Nullable)receiveFunc parameters:(NSString* _Nullable)parameters; -/** - * CloseSend deletes a sent file transfer from the sent transfer map and from -storage once a transfer has completed or reached the retry limit. Returns an -error if the transfer has not run out of retries. - */ -- (BOOL)closeSend:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error; -/** - * GetMaxFileNameByteLength returns the maximum length, in bytes, allowed for a -file name. - */ -- (long)getMaxFileNameByteLength; -/** - * GetMaxFilePreviewSize returns the maximum file preview size, in bytes. - */ -- (long)getMaxFilePreviewSize; -/** - * GetMaxFileSize returns the maximum file size, in bytes, allowed to be -transferred. - */ -- (long)getMaxFileSize; -/** - * GetMaxFileTypeByteLength returns the maximum length, in bytes, allowed for a -file type. - */ -- (long)getMaxFileTypeByteLength; -/** - * Receive returns the fully assembled file on the completion of the transfer. -It deletes the transfer from the received transfer map and from storage. -Returns an error if the transfer is not complete, the full file cannot be -verified, or if the transfer cannot be found. - */ -- (NSData* _Nullable)receive:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterReceiveProgressCallback allows for the registration of a callback to -track the progress of an individual received file transfer. The callback will -be called immediately when added to report the current status of the -transfer. It will then call every time a file part is received, the transfer -completes, or an error occurs. It is called at most once ever period, which -means if events occur faster than the period, then they will not be reported -and instead, the progress will be reported once at the end of the period. -Once the callback reports that the transfer has completed, the recipient -can get the full file by calling Receive. -The period is specified in milliseconds. - */ -- (BOOL)registerReceiveProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferReceivedProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -/** - * RegisterSendProgressCallback allows for the registration of a callback to -track the progress of an individual sent file transfer. The callback will be -called immediately when added to report the current status of the transfer. -It will then call every time a file part is sent, a file part arrives, the -transfer completes, or an error occurs. It is called at most once every -period, which means if events occur faster than the period, then they will -not be reported and instead, the progress will be reported once at the end of -the period. -The period is specified in milliseconds. - */ -- (BOOL)registerSendProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -/** - * Send sends a file to the recipient. The sender must have an E2E relationship -with the recipient. -The file name is the name of the file to show a user. It has a max length of -48 bytes. -The file type identifies what type of file is being sent. It has a max length -of 8 bytes. -The file data cannot be larger than 256 kB -The retry float is the total amount of data to send relative to the data -size. Data will be resent on error and will resend up to [(1 + retry) * -fileSize]. -The preview stores a preview of the data (such as a thumbnail) and is -capped at 4 kB in size. -Returns a unique transfer ID used to identify the transfer. -PeriodMS is the duration, in milliseconds, to wait between progress callback -calls. Set this large enough to prevent spamming. - */ -- (NSData* _Nullable)send:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType fileData:(NSData* _Nullable)fileData recipientID:(NSData* _Nullable)recipientID retry:(float)retry preview:(NSData* _Nullable)preview progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * Group structure contains the identifying and membership information of a -group chat. - */ -@interface BindingsGroup : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetCreatedMS returns the time the group was created in milliseconds. This is -also the time the group requests were sent. - */ -- (int64_t)getCreatedMS; -/** - * GetCreatedNano returns the time the group was created in nanoseconds. This is -also the time the group requests were sent. - */ -- (int64_t)getCreatedNano; -/** - * GetID return the 33-byte unique group ID. - */ -- (NSData* _Nullable)getID; -/** - * GetInitMessage returns initial message sent with the group request. - */ -- (NSData* _Nullable)getInitMessage; -/** - * GetMembership returns a list of contacts, one for each member in the group. -The list is in order; the first contact is the leader/creator of the group. -All subsequent members are ordered by their ID. - */ -- (BindingsGroupMembership* _Nullable)getMembership; -/** - * GetName returns the name set by the user for the group. - */ -- (NSData* _Nullable)getName; -/** - * Serialize serializes the Group. - */ -- (NSData* _Nullable)serialize; -@end - -/** - * GroupChat object contains the group chat manager. - */ -@interface BindingsGroupChat : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetGroup returns the group with the group ID. If no group exists, then the -error "failed to find group" is returned. - */ -- (BindingsGroup* _Nullable)getGroup:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * GetGroups returns an IdList containing a list of group IDs that the user is a -part of. - */ -- (BindingsIdList* _Nullable)getGroups; -/** - * JoinGroup allows a user to join a group when they receive a request. The -caller must pass in the serialized bytes of a Group. - */ -- (BOOL)joinGroup:(NSData* _Nullable)serializedGroupData error:(NSError* _Nullable* _Nullable)error; -/** - * LeaveGroup deletes a group so a user no longer has access. - */ -- (BOOL)leaveGroup:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * MakeGroup creates a new group and sends a group request to all members in the -group. The ID of the new group, the rounds the requests were sent on, and the -status of the send are contained in NewGroupReport. - */ -- (BindingsNewGroupReport* _Nullable)makeGroup:(BindingsIdList* _Nullable)membership name:(NSData* _Nullable)name message:(NSData* _Nullable)message; -/** - * NumGroups returns the number of groups the user is a part of. - */ -- (long)numGroups; -/** - * ResendRequest resends a group request to all members in the group. The rounds -they were sent on and the status of the send are contained in NewGroupReport. - */ -- (BindingsNewGroupReport* _Nullable)resendRequest:(NSData* _Nullable)groupIdBytes error:(NSError* _Nullable* _Nullable)error; -/** - * Send sends the message to the specified group. Returns the round the messages -were sent on. - */ -- (BindingsGroupSendReport* _Nullable)send:(NSData* _Nullable)groupIdBytes message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * // -Member Structure -// -GroupMember represents a member in the group membership list. - */ -@interface BindingsGroupMember : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupMember.Member with unsupported type: gitlab.com/elixxir/crypto/group.Member - -// skipped method GroupMember.DeepCopy with unsupported parameter or return types - -// skipped method GroupMember.Equal with unsupported parameter or return types - -/** - * GetDhKey returns the byte representation of the public Diffie–Hellman key of -the member. - */ -- (NSData* _Nullable)getDhKey; -/** - * GetID returns the 33-byte user ID of the member. - */ -- (NSData* _Nullable)getID; -- (NSString* _Nonnull)goString; -- (NSData* _Nullable)serialize; -- (NSString* _Nonnull)string; -@end - -/** - * GroupMembership structure contains a list of members that are part of a -group. The first member is the group leader. - */ -@interface BindingsGroupMembership : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Get returns the member at the index. The member at index 0 is always the -group leader. An error is returned if the index is out of range. - */ -- (BindingsGroupMember* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Len returns the number of members in the group membership. - */ -- (long)len; -@end - -/** - * GroupMessageReceive contains a group message, its ID, and its data that a -user receives. - */ -@interface BindingsGroupMessageReceive : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupMessageReceive.MessageReceive with unsupported type: gitlab.com/elixxir/client/groupChat.MessageReceive - -/** - * GetEphemeralID returns the ephemeral ID of the recipient. - */ -- (int64_t)getEphemeralID; -/** - * GetGroupID returns the 33-byte group ID. - */ -- (NSData* _Nullable)getGroupID; -/** - * GetMessageID returns the message ID. - */ -- (NSData* _Nullable)getMessageID; -/** - * GetPayload returns the message payload. - */ -- (NSData* _Nullable)getPayload; -/** - * GetRecipientID returns the 33-byte user ID of the recipient. - */ -- (NSData* _Nullable)getRecipientID; -/** - * GetRoundID returns the ID of the round the message was sent on. - */ -- (int64_t)getRoundID; -/** - * GetRoundTimestampMS returns the timestamp, in milliseconds, of the round the -message was sent on. - */ -- (int64_t)getRoundTimestampMS; -/** - * GetRoundTimestampNano returns the timestamp, in nanoseconds, of the round the -message was sent on. - */ -- (int64_t)getRoundTimestampNano; -/** - * GetRoundURL returns the ID of the round the message was sent on. - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetSenderID returns the 33-byte user ID of the sender. - */ -- (NSData* _Nullable)getSenderID; -/** - * GetTimestampMS returns the message timestamp in milliseconds. - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message timestamp in nanoseconds. - */ -- (int64_t)getTimestampNano; -- (NSString* _Nonnull)string; -@end - -@interface BindingsGroupReportDisk : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field GroupReportDisk.List with unsupported type: []gitlab.com/xx_network/primitives/id.Round - -@property (nonatomic) NSData* _Nullable grpId; -@property (nonatomic) long status; -@end - -/** - * GroupSendReport is returned when sending a group message. It contains the -round ID sent on and the timestamp of the send. - */ -@interface BindingsGroupSendReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetMessageID returns the ID of the round that the send occurred on. - */ -- (NSData* _Nullable)getMessageID; -/** - * GetRoundID returns the ID of the round that the send occurred on. - */ -- (int64_t)getRoundID; -/** - * GetRoundURL returns the URL of the round that the send occurred on. - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetTimestampMS returns the timestamp of the send in milliseconds. - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the timestamp of the send in nanoseconds. - */ -- (int64_t)getTimestampNano; -@end - -/** - * ID list -IdList contains a list of IDs. - */ -@interface BindingsIdList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Add appends the ID bytes to the end of the list. - */ -- (BOOL)add:(NSData* _Nullable)idBytes error:(NSError* _Nullable* _Nullable)error; -/** - * Get returns the ID at the index. An error is returned if the index is out of -range. - */ -- (NSData* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -/** - * Len returns the number of IDs in the list. - */ -- (long)len; -@end - -@interface BindingsIntList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (void)add:(long)i; -- (BOOL)get:(long)i ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -- (long)len; -@end - -@interface BindingsManyNotificationForMeReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BindingsNotificationForMeReport* _Nullable)get:(long)i error:(NSError* _Nullable* _Nullable)error; -- (long)len; -@end - -/** - * Message is a message received from the cMix network in the clear -or that has been decrypted using established E2E keys. - */ -@interface BindingsMessage : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetID returns the id of the message - */ -- (NSData* _Nullable)getID; -/** - * GetMessageType returns the message's type - */ -- (long)getMessageType; -/** - * GetPayload returns the message's payload/contents - */ -- (NSData* _Nullable)getPayload; -/** - * GetRoundId returns the message's round ID - */ -- (int64_t)getRoundId; -/** - * GetRoundTimestampMS returns the message's round timestamp in milliseconds - */ -- (int64_t)getRoundTimestampMS; -/** - * GetRoundTimestampNano returns the message's round timestamp in nanoseconds - */ -- (int64_t)getRoundTimestampNano; -/** - * GetRoundURL returns the message's round URL - */ -- (NSString* _Nonnull)getRoundURL; -/** - * GetSender returns the message's sender ID, if available - */ -- (NSData* _Nullable)getSender; -/** - * GetTimestampMS returns the message's timestamp in milliseconds - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message's timestamp in nanoseconds - */ -- (int64_t)getTimestampNano; -@end - -/** - * NewGroupReport is returned when creating a new group and contains the ID of -the group, a list of rounds that the group requests were sent on, and the -status of the send. - */ -@interface BindingsNewGroupReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetError returns the string of an error. -Will be an empty string if no error occured - */ -- (NSString* _Nonnull)getError; -/** - * GetGroup returns the Group. - */ -- (BindingsGroup* _Nullable)getGroup; -/** - * GetRoundList returns the RoundList containing a list of rounds requests were -sent on. - */ -- (BindingsRoundList* _Nullable)getRoundList; -/** - * GetStatus returns the status of the requests sent when creating a new group. -status = 0 an error occurred before any requests could be sent - 1 all requests failed to send (call Resend Group) - 2 some request failed and some succeeded (call Resend Group) - 3, all requests sent successfully (call Resend Group) - */ -- (long)getStatus; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -- (BOOL)unmarshal:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -@end - -/** - * NodeRegistrationsStatus structure for returning node registration statuses -for bindings. - */ -@interface BindingsNodeRegistrationsStatus : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetRegistered returns the number of nodes registered with the client. - */ -- (long)getRegistered; -/** - * GetTotal return the total of nodes currently in the network. - */ -- (long)getTotal; -@end - -@interface BindingsNotificationForMeReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BOOL)forMe; -- (NSData* _Nullable)source; -- (NSString* _Nonnull)type; -@end - -/** - * RestoreContactsReport is a gomobile friendly report structure -for determining which IDs restored, which failed, and why. - */ -@interface BindingsRestoreContactsReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * GetErrorAt returns the error string at index - */ -- (NSString* _Nonnull)getErrorAt:(long)index; -/** - * GetFailedAt returns the failed ID at index - */ -- (NSData* _Nullable)getFailedAt:(long)index; -/** - * GetRestoreContactsError returns an error string. Empty if no error. - */ -- (NSString* _Nonnull)getRestoreContactsError; -/** - * GetRestoredAt returns the restored ID at index - */ -- (NSData* _Nullable)getRestoredAt:(long)index; -/** - * LenFailed returns the length of the ID's failed. - */ -- (long)lenFailed; -/** - * LenRestored returns the length of ID's restored. - */ -- (long)lenRestored; -@end - -@interface BindingsRoundList : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Gets a stored round ID at the given index - */ -- (BOOL)get:(long)i ret0_:(long* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; -/** - * Gets the number of round IDs stored - */ -- (long)len; -@end - -/** - * the send report is the mechanisim by which sendE2E returns a single - */ -@interface BindingsSendReport : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (NSData* _Nullable)getMessageID; -- (BindingsRoundList* _Nullable)getRoundList; -- (NSString* _Nonnull)getRoundURL; -/** - * GetTimestampMS returns the message's timestamp in milliseconds - */ -- (int64_t)getTimestampMS; -/** - * GetTimestampNano returns the message's timestamp in nanoseconds - */ -- (int64_t)getTimestampNano; -- (NSData* _Nullable)marshal:(NSError* _Nullable* _Nullable)error; -- (BOOL)unmarshal:(NSData* _Nullable)b error:(NSError* _Nullable* _Nullable)error; -@end - -@interface BindingsSendReportDisk : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -// skipped field SendReportDisk.List with unsupported type: []gitlab.com/xx_network/primitives/id.Round - -@property (nonatomic) NSData* _Nullable mid; -@property (nonatomic) int64_t ts; -@end - -/** - * Generic Unregister - a generic return used for all callbacks which can be -unregistered -Interface which allows the un-registration of a listener - */ -@interface BindingsUnregister : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -/** - * Call unregisters a callback - */ -- (void)unregister; -@end - -@interface BindingsUser : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (nonnull instancetype)init; -- (BindingsContact* _Nullable)getContact; -- (NSData* _Nullable)getE2EDhPrivateKey; -- (NSData* _Nullable)getE2EDhPublicKey; -- (NSData* _Nullable)getReceptionID; -- (NSData* _Nullable)getReceptionRSAPrivateKeyPem; -- (NSData* _Nullable)getReceptionRSAPublicKeyPem; -- (NSData* _Nullable)getReceptionSalt; -- (NSData* _Nullable)getTransmissionID; -- (NSData* _Nullable)getTransmissionRSAPrivateKeyPem; -- (NSData* _Nullable)getTransmissionRSAPublicKeyPem; -- (NSData* _Nullable)getTransmissionSalt; -- (BOOL)isPrecanned; -@end - -@interface BindingsUserDiscovery : NSObject <goSeqRefInterface> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * NewUserDiscovery returns a new user discovery object. Only call this once. It must be called -after StartNetworkFollower is called and will fail if the network has never -been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -- (nullable instancetype)init:(BindingsClient* _Nullable)client; -/** - * NewUserDiscoveryFromBackup returns a new user discovery object. It -wil set up the manager with the backup data. Pass into it the backed up -facts, one email and phone number each. This will add the registered facts -to the backed Store. Any one of these fields may be empty, -however both fields being empty will cause an error. Any other fact that is not -an email or phone number will return an error. You may only add a fact for the -accepted types once each. If you attempt to back up a fact type that has already -been backed up, an error will be returned. Anytime an error is returned, it means -the backup was not successful. -NOTE: Do not use this as a direct store operation. This feature is intended to add facts -to a backend store that have ALREADY BEEN REGISTERED on the account. -THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend. -Only call this once. It must be called after StartNetworkFollower -is called and will fail if the network has never been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -- (nullable instancetype)initFromBackup:(BindingsClient* _Nullable)client email:(NSString* _Nullable)email phone:(NSString* _Nullable)phone; -/** - * AddFact adds a fact for the user to user discovery. Will only succeed if the -user is already registered and the system does not have the fact currently -registered for any user. -Will fail if the fact string is not well formed. -This does not complete the fact registration process, it returns a -confirmation id instead. Over the communications system the fact is -associated with, a code will be sent. This confirmation ID needs to be -called along with the code to finalize the fact. - */ -- (NSString* _Nonnull)addFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * ConfirmFact confirms a fact first registered via AddFact. The confirmation ID comes from -AddFact while the code will come over the associated communications system - */ -- (BOOL)confirmFact:(NSString* _Nullable)confirmationID code:(NSString* _Nullable)code error:(NSError* _Nullable* _Nullable)error; -/** - * Lookup the contact object associated with the given userID. The -id is the byte representation of an id. -This will reject if that id is malformed. The LookupCallback will return -the associated contact if it exists. - */ -- (BOOL)lookup:(NSData* _Nullable)idBytes callback:(id<BindingsLookupCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * MultiLookup Looks for the contact object associated with all given userIDs. -The ids are the byte representation of an id stored in an IDList object. -This will reject if that id is malformed or if the indexing on the IDList -object is wrong. The MultiLookupCallback will return with all contacts -returned within the timeout. - */ -- (BOOL)multiLookup:(BindingsIdList* _Nullable)ids callback:(id<BindingsMultiLookupCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * Register registers a user with user discovery. Will return an error if the -network signatures are malformed or if the username is taken. Usernames -cannot be changed after registration at this time. Will fail if the user is -already registered. -Identity does not go over cmix, it occurs over normal communications - */ -- (BOOL)register:(NSString* _Nullable)username error:(NSError* _Nullable* _Nullable)error; -/** - * RemoveFact removes a previously confirmed fact. Will fail if the passed fact string is -not well-formed or if the fact is not associated with this client. -Users cannot remove username facts and must instead remove the user. - */ -- (BOOL)removeFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * RemoveUser deletes a user. The fact sent must be the username. -This function preserves the username forever and makes it -unusable. - */ -- (BOOL)removeUser:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error; -/** - * Search for the passed Facts. The factList is the stringification of a -fact list object, look at /bindings/list.go for more on that object. -This will reject if that object is malformed. The SearchCallback will return -a list of contacts, each having the facts it hit against. -This is NOT intended to be used to search for multiple users at once, that -can have a privacy reduction. Instead, it is intended to be used to search -for a user where multiple pieces of information is known. - */ -- (BOOL)search:(NSString* _Nullable)fl callback:(id<BindingsSearchCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * SearchSingle searches for the passed Facts. The fact is the stringification of a -fact object, look at /bindings/contact.go for more on that object. -This will reject if that object is malformed. The SearchCallback will return -a list of contacts, each having the facts it hit against. -This only searches for a single fact at a time. It is intended to make some -simple use cases of the API easier. - */ -- (BOOL)searchSingle:(NSString* _Nullable)f callback:(id<BindingsSingleSearchCallback> _Nullable)callback timeoutMS:(long)timeoutMS error:(NSError* _Nullable* _Nullable)error; -/** - * SetAlternativeUserDiscovery sets the alternativeUd object within manager. -Once set, any user discovery operation will go through the alternative -user discovery service. -To undo this operation, use UnsetAlternativeUserDiscovery. -The contact file is the already read in bytes, not the file path for the contact file. - */ -- (BOOL)setAlternativeUserDiscovery:(NSData* _Nullable)address cert:(NSData* _Nullable)cert contactFile:(NSData* _Nullable)contactFile error:(NSError* _Nullable* _Nullable)error; -/** - * UnsetAlternativeUserDiscovery clears out the information from -the Manager object. - */ -- (BOOL)unsetAlternativeUserDiscovery:(NSError* _Nullable* _Nullable)error; -@end - -/** - * Error codes - */ -FOUNDATION_EXPORT NSString* _Nonnull const BindingsUnrecognizedCode; -FOUNDATION_EXPORT NSString* _Nonnull const BindingsUnrecognizedMessage; - -/** - * CompressJpeg takes a JPEG image in byte format -and compresses it based on desired output size - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsCompressJpeg(NSData* _Nullable imgBytes, NSError* _Nullable* _Nullable error); - -/** - * CompressJpegForPreview takes a JPEG image in byte format -and compresses it based on desired output size - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsCompressJpegForPreview(NSData* _Nullable imgBytes, NSError* _Nullable* _Nullable error); - -/** - * DownloadAndVerifySignedNdfWithUrl retrieves the NDF from a specified URL. -The NDF is processed into a protobuf containing a signature which -is verified using the cert string passed in. The NDF is returned as marshaled -byte data which may be used to start a client. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadAndVerifySignedNdfWithUrl(NSString* _Nullable url, NSString* _Nullable cert, NSError* _Nullable* _Nullable error); - -/** - * DownloadDAppRegistrationDB returns a []byte containing -the JSON data describing registered dApps. -See https://git.xx.network/elixxir/registered-dapps - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadDAppRegistrationDB(NSError* _Nullable* _Nullable error); - -/** - * DownloadErrorDB returns a []byte containing the JSON data -describing client errors. -See https://git.xx.network/elixxir/client-error-database/ - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsDownloadErrorDB(NSError* _Nullable* _Nullable error); - -/** - * DumpStack returns a string with the stack trace of every running thread. - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsDumpStack(NSError* _Nullable* _Nullable error); - -/** - * EnableGrpcLogs sets GRPC trace logging - */ -FOUNDATION_EXPORT void BindingsEnableGrpcLogs(id<BindingsLogWriter> _Nullable writer); - -/** - * ErrorStringToUserFriendlyMessage takes a passed in errStr which will be -a backend generated error. These may be error specifically written by -the backend team or lower level errors gotten from low level dependencies. -This function will parse the error string for common errors provided from -errToUserErr to provide a more user-friendly error message for the front end. -If the error is not common, some simple parsing is done on the error message -to make it more user-accessible, removing backend specific jargon. - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsErrorStringToUserFriendlyMessage(NSString* _Nullable errStr); - -/** - * GenerateSecret creates a secret password using a system-based -pseudorandom number generator. It takes 1 parameter, `numBytes`, -which should be set to 32, but can be set higher in certain cases. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsGenerateSecret(long numBytes); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetCMIXParams(NSError* _Nullable* _Nullable error); - -/** - * returns a previously created client. IF be used if the garbage collector -removes the client instance on the app side. Is NOT thread safe relative to -login, newClient, or newPrecannedClient - */ -FOUNDATION_EXPORT BindingsClient* _Nullable BindingsGetClientSingleton(void); - -/** - * GetDependencies returns the api DEPENDENCIES - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetDependencies(void); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetE2EParams(NSError* _Nullable* _Nullable error); - -/** - * GetGitVersion rturns the api GITVERSION - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetGitVersion(void); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetNetworkParams(NSError* _Nullable* _Nullable error); - -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetUnsafeParams(NSError* _Nullable* _Nullable error); - -/** - * GetVersion returns the api SEMVER - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsGetVersion(void); - -/** - * InitializeBackup starts the backup processes that returns backup updates when -they occur. Any time an event occurs that changes the contents of the backup, -such as adding or deleting a contact, the backup is triggered and an -encrypted backup is generated and returned on the updateBackupCb callback. -Call this function only when enabling backup if it has not already been -initialized or when the user wants to change their password. -To resume backup process on app recovery, use ResumeBackup. - */ -FOUNDATION_EXPORT BindingsBackup* _Nullable BindingsInitializeBackup(NSString* _Nullable password, id<BindingsUpdateBackupFunc> _Nullable updateBackupCb, BindingsClient* _Nullable c, NSError* _Nullable* _Nullable error); - -/** - * LoadSecretWithMnemonic loads the secret stored from the call to -StoreSecretWithMnemonic. The path given should be the same filepath -as the path given in StoreSecretWithMnemonic. There should be a file -in this path called ".recovery". This operation is not tied -to client operations, as the user will not have a client when trying to -recover their account. - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsLoadSecretWithMnemonic(NSString* _Nullable mnemonic, NSString* _Nullable path, NSError* _Nullable* _Nullable error); - -/** - * sets level of logging. All logs the set level and above will be displayed -options are: - TRACE - 0 - DEBUG - 1 - INFO - 2 - WARN - 3 - ERROR - 4 - CRITICAL - 5 - FATAL - 6 -The default state without updates is: INFO - */ -FOUNDATION_EXPORT BOOL BindingsLogLevel(long level, NSError* _Nullable* _Nullable error); - -/** - * Login will load an existing client from the storageDir -using the password. This will fail if the client doesn't exist or -the password is incorrect. -The password is passed as a byte array so that it can be cleared from -memory and stored as securely as possible using the memguard library. -Login does not block on network connection, and instead loads and -starts subprocesses to perform network operations. - */ -FOUNDATION_EXPORT BindingsClient* _Nullable BindingsLogin(NSString* _Nullable storageDir, NSData* _Nullable password, NSString* _Nullable parameters, NSError* _Nullable* _Nullable error); - -/** - * MakeIdList creates a new empty IdList. - */ -FOUNDATION_EXPORT BindingsIdList* _Nullable BindingsMakeIdList(void); - -FOUNDATION_EXPORT BindingsIntList* _Nullable BindingsMakeIntList(void); - -/** - * NewClient creates client storage, generates keys, connects, and registers -with the network. Note that this does not register a username/identity, but -merely creates a new cryptographic identity for adding such information -at a later date. - -Users of this function should delete the storage directory on error. - */ -FOUNDATION_EXPORT BOOL BindingsNewClient(NSString* _Nullable network, NSString* _Nullable storageDir, NSData* _Nullable password, NSString* _Nullable regCode, NSError* _Nullable* _Nullable error); - -/** - * NewClientFromBackup constructs a new Client from an encrypted backup. The backup -is decrypted using the backupPassphrase. On success a successful client creation, -the function will return a JSON encoded list of the E2E partners -contained in the backup and a json-encoded string of the parameters stored in the backup - */ -FOUNDATION_EXPORT NSData* _Nullable BindingsNewClientFromBackup(NSString* _Nullable ndfJSON, NSString* _Nullable storageDir, NSData* _Nullable sessionPassword, NSData* _Nullable backupPassphrase, NSData* _Nullable backupFileContents, NSError* _Nullable* _Nullable error); - -/** - * NewDummyTrafficManager creates a DummyTraffic manager and initialises the -dummy traffic send thread. Note that the manager does not start sending dummy -traffic until its status is set to true using DummyTraffic.SetStatus. -The maxNumMessages is the upper bound of the random number of messages sent -each send. avgSendDeltaMS is the average duration, in milliseconds, to wait -between sends. Sends occur every avgSendDeltaMS +/- a random duration with an -upper bound of randomRangeMS. - */ -FOUNDATION_EXPORT BindingsDummyTraffic* _Nullable BindingsNewDummyTrafficManager(BindingsClient* _Nullable client, long maxNumMessages, long avgSendDeltaMS, long randomRangeMS, NSError* _Nullable* _Nullable error); - -/** - * fact object -creates a new fact. The factType must be either: - 0 - Username - 1 - Email - 2 - Phone Number -The fact must be well formed for the type and must not include commas or -semicolons. If it is not well formed, it will be rejected. Phone numbers -must have the two letter country codes appended. For the complete set of -validation, see /elixxir/primitives/fact/fact.go - */ -FOUNDATION_EXPORT BindingsFact* _Nullable BindingsNewFact(long factType, NSString* _Nullable factStr, NSError* _Nullable* _Nullable error); - -/** - * FactList - */ -FOUNDATION_EXPORT BindingsFactList* _Nullable BindingsNewFactList(void); - -/** - * NewFileTransferManager creates a new file transfer manager and starts the -sending and receiving threads. The receiveFunc is called everytime a new file -transfer is received. -The parameters string contains file transfer network configuration options -and is a JSON formatted string of the fileTransfer.Params object. If it is -left empty, then defaults are used. It is highly recommended that defaults -are used. If it is set, it must match the following format: - {"MaxThroughput":150000,"SendTimeout":500000000} -MaxThroughput is in bytes/sec and SendTimeout is in nanoseconds. - */ -FOUNDATION_EXPORT BindingsFileTransfer* _Nullable BindingsNewFileTransferManager(BindingsClient* _Nullable client, id<BindingsFileTransferReceiveFunc> _Nullable receiveFunc, NSString* _Nullable parameters, NSError* _Nullable* _Nullable error); - -/** - * NewGroupManager creates a new group chat manager. - */ -FOUNDATION_EXPORT BindingsGroupChat* _Nullable BindingsNewGroupManager(BindingsClient* _Nullable client, id<BindingsGroupRequestFunc> _Nullable requestFunc, id<BindingsGroupReceiveFunc> _Nullable receiveFunc, NSError* _Nullable* _Nullable error); - -/** - * NewPrecannedClient creates an insecure user with predetermined keys with nodes -It creates client storage, generates keys, connects, and registers -with the network. Note that this does not register a username/identity, but -merely creates a new cryptographic identity for adding such information -at a later date. - -Users of this function should delete the storage directory on error. - */ -FOUNDATION_EXPORT BOOL BindingsNewPrecannedClient(long precannedID, NSString* _Nullable network, NSString* _Nullable storageDir, NSData* _Nullable password, NSError* _Nullable* _Nullable error); - -/** - * NewUserDiscovery returns a new user discovery object. Only call this once. It must be called -after StartNetworkFollower is called and will fail if the network has never -been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscovery(BindingsClient* _Nullable client, NSError* _Nullable* _Nullable error); - -/** - * NewUserDiscoveryFromBackup returns a new user discovery object. It -wil set up the manager with the backup data. Pass into it the backed up -facts, one email and phone number each. This will add the registered facts -to the backed Store. Any one of these fields may be empty, -however both fields being empty will cause an error. Any other fact that is not -an email or phone number will return an error. You may only add a fact for the -accepted types once each. If you attempt to back up a fact type that has already -been backed up, an error will be returned. Anytime an error is returned, it means -the backup was not successful. -NOTE: Do not use this as a direct store operation. This feature is intended to add facts -to a backend store that have ALREADY BEEN REGISTERED on the account. -THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend. -Only call this once. It must be called after StartNetworkFollower -is called and will fail if the network has never been contacted. -This function technically has a memory leak because it causes both sides of -the bindings to think the other is in charge of the client object. -In general this is not an issue because the client object should exist -for the life of the program. -This must be called while start network follower is running. - */ -FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscoveryFromBackup(BindingsClient* _Nullable client, NSString* _Nullable email, NSString* _Nullable phone, NSError* _Nullable* _Nullable error); - -/** - * NotificationsForMe Check if a notification received is for me -It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller, -a Type, and a source. These are as follows: - TYPE SOURCE DESCRIPTION - "default" recipient user ID A message with no association - "request" sender user ID A channel request has been received - "reset" sender user ID A channel reset has been received - "confirm" sender user ID A channel request has been accepted - "silent" sender user ID A message which should not be notified on - "e2e" sender user ID reception of an E2E message - "group" group ID reception of a group chat message - "endFT" sender user ID Last message sent confirming end of file transfer - "groupRQ" sender user ID Request from sender to join a group chat - */ -FOUNDATION_EXPORT BindingsManyNotificationForMeReport* _Nullable BindingsNotificationsForMe(NSString* _Nullable notifCSV, NSString* _Nullable preimages, NSError* _Nullable* _Nullable error); - -/** - * RegisterLogWriter registers a callback on which logs are written. - */ -FOUNDATION_EXPORT void BindingsRegisterLogWriter(id<BindingsLogWriter> _Nullable writer); - -/** - * RestoreContactsFromBackup takes as input the jason output of the -`NewClientFromBackup` function, unmarshals it into IDs, looks up -each ID in user discovery, and initiates a session reset request. -This function will not return until every id in the list has been sent a -request. It should be called again and again until it completes. -xxDK users should not use this function. This function is used by -the mobile phone apps and are not intended to be part of the xxDK. It -should be treated as internal functions specific to the phone apps. - */ -FOUNDATION_EXPORT BindingsRestoreContactsReport* _Nullable BindingsRestoreContactsFromBackup(NSData* _Nullable backupPartnerIDs, BindingsClient* _Nullable client, BindingsUserDiscovery* _Nullable udManager, id<BindingsLookupCallback> _Nullable lookupCB, id<BindingsRestoreContactsUpdater> _Nullable updatesCb); - -/** - * ResumeBackup starts the backup processes back up with a new callback after it -has been initialized. -Call this function only when resuming a backup that has already been -initialized or to replace the callback. -To start the backup for the first time or to use a new password, use -InitializeBackup. - */ -FOUNDATION_EXPORT BindingsBackup* _Nullable BindingsResumeBackup(id<BindingsUpdateBackupFunc> _Nullable cb, BindingsClient* _Nullable c, NSError* _Nullable* _Nullable error); - -/** - * SetTimeSource sets the network time to a custom source. - */ -FOUNDATION_EXPORT void BindingsSetTimeSource(id<BindingsTimeSource> _Nullable timeNow); - -/** - * StoreSecretWithMnemonic stores the secret tied with the mnemonic to storage. -Unlike other storage operations, this does not use EKV, as that is -intrinsically tied to client operations, which the user will not have while -trying to recover their account. As such, we store the encrypted data -directly, with a specified path. Path will be a valid filepath in which the -recover file will be stored as ".recovery". - -As an example, given "home/user/xxmessenger/storagePath", -the recovery file will be stored at -"home/user/xxmessenger/storagePath/.recovery" - */ -FOUNDATION_EXPORT NSString* _Nonnull BindingsStoreSecretWithMnemonic(NSData* _Nullable secret, NSString* _Nullable path, NSError* _Nullable* _Nullable error); - -/** - * Unmarshals a marshaled contact object, returns an error if it fails - */ -FOUNDATION_EXPORT BindingsContact* _Nullable BindingsUnmarshalContact(NSData* _Nullable b, NSError* _Nullable* _Nullable error); - -/** - * Unmarshals a marshaled send report object, returns an error if it fails - */ -FOUNDATION_EXPORT BindingsSendReport* _Nullable BindingsUnmarshalSendReport(NSData* _Nullable b, NSError* _Nullable* _Nullable error); - -/** - * UpdateCommonErrors takes the passed in contents of a JSON file and updates the -errToUserErr map with the contents of the json file. The JSON's expected format -conform with the commented examples provides in errToUserErr above. -NOTE that you should not pass in a file path, but a preloaded JSON file - */ -FOUNDATION_EXPORT BOOL BindingsUpdateCommonErrors(NSString* _Nullable jsonFile, NSError* _Nullable* _Nullable error); - -// skipped function WrapAPIClient with unsupported parameter or return types - - -// skipped function WrapUserDiscovery with unsupported parameter or return types - - -@class BindingsAuthConfirmCallback; - -@class BindingsAuthRequestCallback; - -@class BindingsAuthResetNotificationCallback; - -@class BindingsClientError; - -@class BindingsEventCallbackFunctionObject; - -@class BindingsFileTransferReceiveFunc; - -@class BindingsFileTransferReceivedProgressFunc; - -@class BindingsFileTransferSentProgressFunc; - -@class BindingsGroupReceiveFunc; - -@class BindingsGroupRequestFunc; - -@class BindingsListener; - -@class BindingsLogWriter; - -@class BindingsLookupCallback; - -@class BindingsMessageDeliveryCallback; - -@class BindingsMultiLookupCallback; - -@class BindingsNetworkHealthCallback; - -@class BindingsPreimageNotification; - -@class BindingsRestoreContactsUpdater; - -@class BindingsRoundCompletionCallback; - -@class BindingsRoundEventCallback; - -@class BindingsSearchCallback; - -@class BindingsSingleSearchCallback; - -@class BindingsTimeSource; - -@class BindingsUpdateBackupFunc; - -/** - * AuthConfirmCallback notifies the register whenever they receive an auth -request confirmation - */ -@interface BindingsAuthConfirmCallback : NSObject <goSeqRefInterface, BindingsAuthConfirmCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)partner; -@end - -/** - * AuthRequestCallback notifies the register whenever they receive an auth -request - */ -@interface BindingsAuthRequestCallback : NSObject <goSeqRefInterface, BindingsAuthRequestCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -/** - * AuthRequestCallback notifies the register whenever they receive an auth -request - */ -@interface BindingsAuthResetNotificationCallback : NSObject <goSeqRefInterface, BindingsAuthResetNotificationCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)requestor; -@end - -@interface BindingsClientError : NSObject <goSeqRefInterface, BindingsClientError> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)report:(NSString* _Nullable)source message:(NSString* _Nullable)message trace:(NSString* _Nullable)trace; -@end - -/** - * EventCallbackFunctionObject bindings interface which contains function -that implements the EventCallbackFunction - */ -@interface BindingsEventCallbackFunctionObject : NSObject <goSeqRefInterface, BindingsEventCallbackFunctionObject> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)reportEvent:(long)priority category:(NSString* _Nullable)category evtType:(NSString* _Nullable)evtType details:(NSString* _Nullable)details; -@end - -/** - * FileTransferReceiveFunc contains a function callback that notifies the -receiver of an incoming file transfer. It is called on the reception of the -initial file transfer message. - */ -@interface BindingsFileTransferReceiveFunc : NSObject <goSeqRefInterface, BindingsFileTransferReceiveFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)receiveCallback:(NSData* _Nullable)tid fileName:(NSString* _Nullable)fileName fileType:(NSString* _Nullable)fileType sender:(NSData* _Nullable)sender size:(long)size preview:(NSData* _Nullable)preview; -@end - -/** - * FileTransferReceivedProgressFunc contains a function callback that tracks the -progress of receiving a file. It is called when a file part is received, the -transfer completes, or on error. - */ -@interface BindingsFileTransferReceivedProgressFunc : NSObject <goSeqRefInterface, BindingsFileTransferReceivedProgressFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)receivedProgressCallback:(BOOL)completed received:(long)received total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -/** - * FileTransferSentProgressFunc contains a function callback that tracks the -progress of sending a file. It is called when a file part is sent, a file -part arrives, the transfer completes, or on error. - */ -@interface BindingsFileTransferSentProgressFunc : NSObject <goSeqRefInterface, BindingsFileTransferSentProgressFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)sentProgressCallback:(BOOL)completed sent:(long)sent arrived:(long)arrived total:(long)total t:(BindingsFilePartTracker* _Nullable)t err:(NSError* _Nullable)err; -@end - -/** - * GroupReceiveFunc contains a function callback that is called when a group -message is received. - */ -@interface BindingsGroupReceiveFunc : NSObject <goSeqRefInterface, BindingsGroupReceiveFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)groupReceiveCallback:(BindingsGroupMessageReceive* _Nullable)msg; -@end - -/** - * GroupRequestFunc contains a function callback that is called when a group -request is received. - */ -@interface BindingsGroupRequestFunc : NSObject <goSeqRefInterface, BindingsGroupRequestFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)groupRequestCallback:(BindingsGroup* _Nullable)g; -@end - -/** - * Listener provides a callback to hear a message -An object implementing this interface can be called back when the client -gets a message of the type that the registerer specified at registration -time. - */ -@interface BindingsListener : NSObject <goSeqRefInterface, BindingsListener> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * Hear is called to receive a message in the UI - */ -- (void)hear:(BindingsMessage* _Nullable)message; -/** - * Returns a name, used for debugging - */ -- (NSString* _Nonnull)name; -@end - -@interface BindingsLogWriter : NSObject <goSeqRefInterface, BindingsLogWriter> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)log:(NSString* _Nullable)p0; -@end - -/** - * LookupCallback returns the result of a single lookup - */ -@interface BindingsLookupCallback : NSObject <goSeqRefInterface, BindingsLookupCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -/** - * MessageDeliveryCallback gets called on the determination if all events -related to a message send were successful. - */ -@interface BindingsMessageDeliveryCallback : NSObject <goSeqRefInterface, BindingsMessageDeliveryCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(NSData* _Nullable)msgID delivered:(BOOL)delivered timedOut:(BOOL)timedOut roundResults:(NSData* _Nullable)roundResults; -@end - -/** - * MultiLookupCallback returns the result of many parallel lookups - */ -@interface BindingsMultiLookupCallback : NSObject <goSeqRefInterface, BindingsMultiLookupCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContactList* _Nullable)Succeeded failed:(BindingsIdList* _Nullable)failed errors:(NSString* _Nullable)errors; -@end - -/** - * A callback when which is used to receive notification if network health -changes - */ -@interface BindingsNetworkHealthCallback : NSObject <goSeqRefInterface, BindingsNetworkHealthCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BOOL)p0; -@end - -@interface BindingsPreimageNotification : NSObject <goSeqRefInterface, BindingsPreimageNotification> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)notify:(NSData* _Nullable)identity deleted:(BOOL)deleted; -@end - -/** - * RestoreContactsUpdater interface provides a callback function -for receiving update information from RestoreContactsFromBackup. - */ -@interface BindingsRestoreContactsUpdater : NSObject <goSeqRefInterface, BindingsRestoreContactsUpdater> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -/** - * RestoreContactsCallback is called to report the current # of contacts -that have been found and how many have been restored -against the total number that need to be -processed. If an error occurs it it set on the err variable as a -plain string. - */ -- (void)restoreContactsCallback:(long)numFound numRestored:(long)numRestored total:(long)total err:(NSString* _Nullable)err; -@end - -/** - * RoundCompletionCallback is returned when the completion of a round is known. - */ -@interface BindingsRoundCompletionCallback : NSObject <goSeqRefInterface, BindingsRoundCompletionCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(long)rid success:(BOOL)success timedOut:(BOOL)timedOut; -@end - -/** - * RoundEventCallback handles waiting on the exact state of a round on -the cMix network. - */ -@interface BindingsRoundEventCallback : NSObject <goSeqRefInterface, BindingsRoundEventCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)eventCallback:(long)rid state:(long)state timedOut:(BOOL)timedOut; -@end - -/** - * SearchCallback returns the result of a search - */ -@interface BindingsSearchCallback : NSObject <goSeqRefInterface, BindingsSearchCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContactList* _Nullable)contacts error:(NSString* _Nullable)error; -@end - -/** - * SingleSearchCallback returns the result of a single search - */ -@interface BindingsSingleSearchCallback : NSObject <goSeqRefInterface, BindingsSingleSearchCallback> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)callback:(BindingsContact* _Nullable)contact error:(NSString* _Nullable)error; -@end - -@interface BindingsTimeSource : NSObject <goSeqRefInterface, BindingsTimeSource> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (int64_t)nowMs; -@end - -/** - * UpdateBackupFunc contains a function callback that returns new backups. - */ -@interface BindingsUpdateBackupFunc : NSObject <goSeqRefInterface, BindingsUpdateBackupFunc> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (void)updateBackup:(NSData* _Nullable)encryptedBackup; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/Universe.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/Universe.objc.h deleted file mode 100644 index 019e7502d581983722a15bf30799e85cbc5dd766..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/Universe.objc.h +++ /dev/null @@ -1,29 +0,0 @@ -// Objective-C API for talking to Go package. -// gobind -lang=objc -// -// File is generated by gobind. Do not edit. - -#ifndef __Universe_H__ -#define __Universe_H__ - -@import Foundation; -#include "ref.h" - -@protocol Universeerror; -@class Universeerror; - -@protocol Universeerror <NSObject> -- (NSString* _Nonnull)error; -@end - -@class Universeerror; - -@interface Universeerror : NSError <goSeqRefInterface, Universeerror> { -} -@property(strong, readonly) _Nonnull id _ref; - -- (nonnull instancetype)initWithRef:(_Nonnull id)ref; -- (NSString* _Nonnull)error; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/ref.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/ref.h deleted file mode 100644 index b8036a4d85c7387f3def61473a071b5d8c4c8208..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/ref.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef __GO_REF_HDR__ -#define __GO_REF_HDR__ - -#include <Foundation/Foundation.h> - -// GoSeqRef is an object tagged with an integer for passing back and -// forth across the language boundary. A GoSeqRef may represent either -// an instance of a Go object, or an Objective-C object passed to Go. -// The explicit allocation of a GoSeqRef is used to pin a Go object -// when it is passed to Objective-C. The Go seq package maintains a -// reference to the Go object in a map keyed by the refnum along with -// a reference count. When the reference count reaches zero, the Go -// seq package will clear the corresponding entry in the map. -@interface GoSeqRef : NSObject { -} -@property(readonly) int32_t refnum; -@property(strong) id obj; // NULL when representing a Go object. - -// new GoSeqRef object to proxy a Go object. The refnum must be -// provided from Go side. -- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj; - -- (int32_t)incNum; - -@end - -@protocol goSeqRefInterface --(GoSeqRef*) _ref; -@end - -#endif diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Modules/module.modulemap b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Modules/module.modulemap deleted file mode 100644 index 4316a5b24058edfc18ffb2dc7f7a982e8353441a..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Modules/module.modulemap +++ /dev/null @@ -1,8 +0,0 @@ -framework module "Bindings" { - header "ref.h" - header "Bindings.objc.h" - header "Universe.objc.h" - header "Bindings.h" - - export * -} \ No newline at end of file diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Resources/Info.plist b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Resources/Info.plist deleted file mode 100644 index 0d1a4b8ab9b1fc8e9357197398f73353470cb636..0000000000000000000000000000000000000000 --- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Resources/Info.plist +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> - <plist version="1.0"> - <dict> - </dict> - </plist> diff --git a/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved b/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved index fa732be43c432b37cc0f8bd9f88a1e9c9d2f4592..a54280f2babcb8bf2fd3df02e19585ae88ae4eb1 100644 --- a/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -386,15 +386,6 @@ "revision" : "16e6409ee82e1b81390bdffbf217b9c08ab32784", "version" : "0.5.0" } - }, - { - "identity" : "xxm-di", - "kind" : "remoteSourceControl", - "location" : "https://git.xx.network/elixxir/xxm-di", - "state" : { - "revision" : "43b1e12c32109f1753fcc62e5b0b21e479ee27e3", - "version" : "1.0.0" - } } ], "version" : 2 diff --git a/run_swiftgen.sh b/run_swiftgen.sh index 63ba2cb744a4016ea02e72cff976714ccc44f1dc..adfb40051d165d4ae1211553b486d776c6e4bd49 100755 --- a/run_swiftgen.sh +++ b/run_swiftgen.sh @@ -1,2 +1,2 @@ #!/bin/bash -swiftgen config run --config Sources/Shared/swiftgen.yml +swiftgen config run --config Sources/AppResources/swiftgen.yml