mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-23 00:30:59 +00:00
Refactoring/preferences
This commit is contained in:
parent
c39f2d94d3
commit
2df47efdc9
21 changed files with 280 additions and 83 deletions
|
@ -118,12 +118,16 @@ extension AppEnvironment {
|
||||||
webAuthSessionType: SuccessfulStubbingWebAuthSession.self)
|
webAuthSessionType: SuccessfulStubbingWebAuthSession.self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension IdentifiedEnvironment {
|
||||||
|
static let development = try! IdentifiedEnvironment(identityID: devIdentityID, appEnvironment: .development)
|
||||||
|
}
|
||||||
|
|
||||||
extension RootViewModel {
|
extension RootViewModel {
|
||||||
static let development = RootViewModel(environment: .development)
|
static let development = RootViewModel(environment: .development)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MainNavigationViewModel {
|
extension MainNavigationViewModel {
|
||||||
static let development = try! MainNavigationViewModel(identityID: devIdentityID, environment: .development)
|
static let development = MainNavigationViewModel(environment: .development)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SettingsViewModel {
|
extension SettingsViewModel {
|
||||||
|
|
|
@ -7,6 +7,10 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
D0091B6824DC10B30040E8D2 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B6724DC10B30040E8D2 /* PreferencesView.swift */; };
|
||||||
|
D0091B6924DC10B30040E8D2 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B6724DC10B30040E8D2 /* PreferencesView.swift */; };
|
||||||
|
D0091B6B24DC10CE0040E8D2 /* PreferencesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B6A24DC10CE0040E8D2 /* PreferencesViewModel.swift */; };
|
||||||
|
D0091B6C24DC10CE0040E8D2 /* PreferencesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B6A24DC10CE0040E8D2 /* PreferencesViewModel.swift */; };
|
||||||
D047FAAE24C3E21200AF17C5 /* MetatextApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D047FA8524C3E21000AF17C5 /* MetatextApp.swift */; };
|
D047FAAE24C3E21200AF17C5 /* MetatextApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D047FA8524C3E21000AF17C5 /* MetatextApp.swift */; };
|
||||||
D047FAAF24C3E21200AF17C5 /* MetatextApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D047FA8524C3E21000AF17C5 /* MetatextApp.swift */; };
|
D047FAAF24C3E21200AF17C5 /* MetatextApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D047FA8524C3E21000AF17C5 /* MetatextApp.swift */; };
|
||||||
D047FAB224C3E21200AF17C5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D047FA8724C3E21200AF17C5 /* Assets.xcassets */; };
|
D047FAB224C3E21200AF17C5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D047FA8724C3E21200AF17C5 /* Assets.xcassets */; };
|
||||||
|
@ -91,6 +95,14 @@
|
||||||
D0C963FC24CC359D003BD330 /* AlertItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C963FA24CC359D003BD330 /* AlertItem.swift */; };
|
D0C963FC24CC359D003BD330 /* AlertItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C963FA24CC359D003BD330 /* AlertItem.swift */; };
|
||||||
D0C963FE24CC3812003BD330 /* Publisher+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C963FD24CC3812003BD330 /* Publisher+Extensions.swift */; };
|
D0C963FE24CC3812003BD330 /* Publisher+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C963FD24CC3812003BD330 /* Publisher+Extensions.swift */; };
|
||||||
D0C963FF24CC3812003BD330 /* Publisher+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C963FD24CC3812003BD330 /* Publisher+Extensions.swift */; };
|
D0C963FF24CC3812003BD330 /* Publisher+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C963FD24CC3812003BD330 /* Publisher+Extensions.swift */; };
|
||||||
|
D0CD847324DBDEC700CF380C /* MastodonPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CD847224DBDEC700CF380C /* MastodonPreferences.swift */; };
|
||||||
|
D0CD847424DBDEC700CF380C /* MastodonPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CD847224DBDEC700CF380C /* MastodonPreferences.swift */; };
|
||||||
|
D0CD847624DBDF3C00CF380C /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CD847524DBDF3C00CF380C /* Status.swift */; };
|
||||||
|
D0CD847724DBDF3C00CF380C /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CD847524DBDF3C00CF380C /* Status.swift */; };
|
||||||
|
D0CD847C24DBEA9F00CF380C /* Unknowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CD847B24DBEA9F00CF380C /* Unknowable.swift */; };
|
||||||
|
D0CD847D24DBEA9F00CF380C /* Unknowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CD847B24DBEA9F00CF380C /* Unknowable.swift */; };
|
||||||
|
D0CD847F24DBF1BB00CF380C /* PreferencesEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CD847E24DBF1BB00CF380C /* PreferencesEndpoint.swift */; };
|
||||||
|
D0CD848024DBF1BB00CF380C /* PreferencesEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CD847E24DBF1BB00CF380C /* PreferencesEndpoint.swift */; };
|
||||||
D0DB6EF424C5228A00D965FE /* AddIdentityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DB6EF324C5228A00D965FE /* AddIdentityView.swift */; };
|
D0DB6EF424C5228A00D965FE /* AddIdentityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DB6EF324C5228A00D965FE /* AddIdentityView.swift */; };
|
||||||
D0DB6EF524C5233E00D965FE /* AddIdentityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DB6EF324C5228A00D965FE /* AddIdentityView.swift */; };
|
D0DB6EF524C5233E00D965FE /* AddIdentityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DB6EF324C5228A00D965FE /* AddIdentityView.swift */; };
|
||||||
D0DB6F0924C65AC000D965FE /* AddIdentityViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DB6F0824C65AC000D965FE /* AddIdentityViewModel.swift */; };
|
D0DB6F0924C65AC000D965FE /* AddIdentityViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DB6F0824C65AC000D965FE /* AddIdentityViewModel.swift */; };
|
||||||
|
@ -151,6 +163,8 @@
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
D0091B6724DC10B30040E8D2 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; };
|
||||||
|
D0091B6A24DC10CE0040E8D2 /* PreferencesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewModel.swift; sourceTree = "<group>"; };
|
||||||
D047FA8524C3E21000AF17C5 /* MetatextApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetatextApp.swift; sourceTree = "<group>"; };
|
D047FA8524C3E21000AF17C5 /* MetatextApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetatextApp.swift; sourceTree = "<group>"; };
|
||||||
D047FA8724C3E21200AF17C5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
D047FA8724C3E21200AF17C5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
D047FA8C24C3E21200AF17C5 /* Metatext.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Metatext.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
D047FA8C24C3E21200AF17C5 /* Metatext.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Metatext.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
@ -199,6 +213,10 @@
|
||||||
D0BEC95024CA2B7E00E864C4 /* TabNavigation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabNavigation.swift; sourceTree = "<group>"; };
|
D0BEC95024CA2B7E00E864C4 /* TabNavigation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabNavigation.swift; sourceTree = "<group>"; };
|
||||||
D0C963FA24CC359D003BD330 /* AlertItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertItem.swift; sourceTree = "<group>"; };
|
D0C963FA24CC359D003BD330 /* AlertItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertItem.swift; sourceTree = "<group>"; };
|
||||||
D0C963FD24CC3812003BD330 /* Publisher+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Publisher+Extensions.swift"; sourceTree = "<group>"; };
|
D0C963FD24CC3812003BD330 /* Publisher+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Publisher+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
|
D0CD847224DBDEC700CF380C /* MastodonPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPreferences.swift; sourceTree = "<group>"; };
|
||||||
|
D0CD847524DBDF3C00CF380C /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = "<group>"; };
|
||||||
|
D0CD847B24DBEA9F00CF380C /* Unknowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Unknowable.swift; sourceTree = "<group>"; };
|
||||||
|
D0CD847E24DBF1BB00CF380C /* PreferencesEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesEndpoint.swift; sourceTree = "<group>"; };
|
||||||
D0DB6EF324C5228A00D965FE /* AddIdentityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddIdentityView.swift; sourceTree = "<group>"; };
|
D0DB6EF324C5228A00D965FE /* AddIdentityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddIdentityView.swift; sourceTree = "<group>"; };
|
||||||
D0DB6F0824C65AC000D965FE /* AddIdentityViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddIdentityViewModel.swift; sourceTree = "<group>"; };
|
D0DB6F0824C65AC000D965FE /* AddIdentityViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddIdentityViewModel.swift; sourceTree = "<group>"; };
|
||||||
D0DC174524CFEC2000A75C65 /* StubbingURLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StubbingURLProtocol.swift; sourceTree = "<group>"; };
|
D0DC174524CFEC2000A75C65 /* StubbingURLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StubbingURLProtocol.swift; sourceTree = "<group>"; };
|
||||||
|
@ -332,14 +350,17 @@
|
||||||
D052BBCC24D750A100A80A7A /* AppEnvironment.swift */,
|
D052BBCC24D750A100A80A7A /* AppEnvironment.swift */,
|
||||||
D0ED1BD624CF94B200B4899C /* Application.swift */,
|
D0ED1BD624CF94B200B4899C /* Application.swift */,
|
||||||
D0666A4424C6BC0A00F3F04B /* DatabaseError.swift */,
|
D0666A4424C6BC0A00F3F04B /* DatabaseError.swift */,
|
||||||
|
D052BBCE24D750C000A80A7A /* Defaults.swift */,
|
||||||
D0666A5324C6C3E500F3F04B /* Emoji.swift */,
|
D0666A5324C6C3E500F3F04B /* Emoji.swift */,
|
||||||
D0666A4A24C6C37700F3F04B /* Identity.swift */,
|
D0666A4A24C6C37700F3F04B /* Identity.swift */,
|
||||||
D0666A4124C6BB7B00F3F04B /* IdentityDatabase.swift */,
|
D0666A4124C6BB7B00F3F04B /* IdentityDatabase.swift */,
|
||||||
D0666A4D24C6C39600F3F04B /* Instance.swift */,
|
D0666A4D24C6C39600F3F04B /* Instance.swift */,
|
||||||
D0DC177324D0B58800A75C65 /* Keychain.swift */,
|
D0DC177324D0B58800A75C65 /* Keychain.swift */,
|
||||||
D0ED1BE224CFA84400B4899C /* MastodonError.swift */,
|
D0ED1BE224CFA84400B4899C /* MastodonError.swift */,
|
||||||
D052BBCE24D750C000A80A7A /* Defaults.swift */,
|
D0CD847224DBDEC700CF380C /* MastodonPreferences.swift */,
|
||||||
D0666A7124C6E0D300F3F04B /* Secrets.swift */,
|
D0666A7124C6E0D300F3F04B /* Secrets.swift */,
|
||||||
|
D0CD847524DBDF3C00CF380C /* Status.swift */,
|
||||||
|
D0CD847B24DBEA9F00CF380C /* Unknowable.swift */,
|
||||||
);
|
);
|
||||||
path = Model;
|
path = Model;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -364,6 +385,7 @@
|
||||||
children = (
|
children = (
|
||||||
D0DB6EF324C5228A00D965FE /* AddIdentityView.swift */,
|
D0DB6EF324C5228A00D965FE /* AddIdentityView.swift */,
|
||||||
D06BAB5024D942CF0081B8FD /* IdentitiesView.swift */,
|
D06BAB5024D942CF0081B8FD /* IdentitiesView.swift */,
|
||||||
|
D0091B6724DC10B30040E8D2 /* PreferencesView.swift */,
|
||||||
D0BEC93A24C96FD500E864C4 /* RootView.swift */,
|
D0BEC93A24C96FD500E864C4 /* RootView.swift */,
|
||||||
D04FD73224D48F37007D572D /* SettingsView.swift */,
|
D04FD73224D48F37007D572D /* SettingsView.swift */,
|
||||||
D0BEC94924CA231200E864C4 /* TimelineView.swift */,
|
D0BEC94924CA231200E864C4 /* TimelineView.swift */,
|
||||||
|
@ -390,6 +412,7 @@
|
||||||
D0DB6F0824C65AC000D965FE /* AddIdentityViewModel.swift */,
|
D0DB6F0824C65AC000D965FE /* AddIdentityViewModel.swift */,
|
||||||
D06BAB4D24D942BC0081B8FD /* IdentitiesViewModel.swift */,
|
D06BAB4D24D942BC0081B8FD /* IdentitiesViewModel.swift */,
|
||||||
D052BBDF24D805E300A80A7A /* MainNavigationViewModel.swift */,
|
D052BBDF24D805E300A80A7A /* MainNavigationViewModel.swift */,
|
||||||
|
D0091B6A24DC10CE0040E8D2 /* PreferencesViewModel.swift */,
|
||||||
D0BEC93724C9632800E864C4 /* RootViewModel.swift */,
|
D0BEC93724C9632800E864C4 /* RootViewModel.swift */,
|
||||||
D04FD73524D49506007D572D /* SettingsViewModel.swift */,
|
D04FD73524D49506007D572D /* SettingsViewModel.swift */,
|
||||||
D0BEC94624CA22C400E864C4 /* TimelineViewModel.swift */,
|
D0BEC94624CA22C400E864C4 /* TimelineViewModel.swift */,
|
||||||
|
@ -457,6 +480,7 @@
|
||||||
D0ED1BCA24CF744200B4899C /* MastodonClient.swift */,
|
D0ED1BCA24CF744200B4899C /* MastodonClient.swift */,
|
||||||
D0ED1BCD24CF768200B4899C /* MastodonEndpoint.swift */,
|
D0ED1BCD24CF768200B4899C /* MastodonEndpoint.swift */,
|
||||||
D0ED1BD024CF779B00B4899C /* MastodonTarget.swift */,
|
D0ED1BD024CF779B00B4899C /* MastodonTarget.swift */,
|
||||||
|
D0CD847E24DBF1BB00CF380C /* PreferencesEndpoint.swift */,
|
||||||
);
|
);
|
||||||
path = "Mastodon API";
|
path = "Mastodon API";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -652,6 +676,7 @@
|
||||||
files = (
|
files = (
|
||||||
D04FD73924D4A7B4007D572D /* AccountEndpoint+Stubbing.swift in Sources */,
|
D04FD73924D4A7B4007D572D /* AccountEndpoint+Stubbing.swift in Sources */,
|
||||||
D0DB6F0924C65AC000D965FE /* AddIdentityViewModel.swift in Sources */,
|
D0DB6F0924C65AC000D965FE /* AddIdentityViewModel.swift in Sources */,
|
||||||
|
D0CD847324DBDEC700CF380C /* MastodonPreferences.swift in Sources */,
|
||||||
D0ED1BD724CF94B200B4899C /* Application.swift in Sources */,
|
D0ED1BD724CF94B200B4899C /* Application.swift in Sources */,
|
||||||
D047FAAE24C3E21200AF17C5 /* MetatextApp.swift in Sources */,
|
D047FAAE24C3E21200AF17C5 /* MetatextApp.swift in Sources */,
|
||||||
D0BEC94724CA22C400E864C4 /* TimelineViewModel.swift in Sources */,
|
D0BEC94724CA22C400E864C4 /* TimelineViewModel.swift in Sources */,
|
||||||
|
@ -672,6 +697,7 @@
|
||||||
D0ED1BC124CED48800B4899C /* HTTPClient.swift in Sources */,
|
D0ED1BC124CED48800B4899C /* HTTPClient.swift in Sources */,
|
||||||
D0666A4524C6BC0A00F3F04B /* DatabaseError.swift in Sources */,
|
D0666A4524C6BC0A00F3F04B /* DatabaseError.swift in Sources */,
|
||||||
D0ED1BDD24CF982600B4899C /* AccessTokenEndpoint.swift in Sources */,
|
D0ED1BDD24CF982600B4899C /* AccessTokenEndpoint.swift in Sources */,
|
||||||
|
D0CD847F24DBF1BB00CF380C /* PreferencesEndpoint.swift in Sources */,
|
||||||
D0666A4B24C6C37700F3F04B /* Identity.swift in Sources */,
|
D0666A4B24C6C37700F3F04B /* Identity.swift in Sources */,
|
||||||
D0666A5424C6C3E500F3F04B /* Emoji.swift in Sources */,
|
D0666A5424C6C3E500F3F04B /* Emoji.swift in Sources */,
|
||||||
D0DC175524D00F0A00A75C65 /* AccessTokenEndpoint+Stubbing.swift in Sources */,
|
D0DC175524D00F0A00A75C65 /* AccessTokenEndpoint+Stubbing.swift in Sources */,
|
||||||
|
@ -689,6 +715,7 @@
|
||||||
D0C963FB24CC359D003BD330 /* AlertItem.swift in Sources */,
|
D0C963FB24CC359D003BD330 /* AlertItem.swift in Sources */,
|
||||||
D0DC174624CFEC2000A75C65 /* StubbingURLProtocol.swift in Sources */,
|
D0DC174624CFEC2000A75C65 /* StubbingURLProtocol.swift in Sources */,
|
||||||
D0DC174D24CFF1F100A75C65 /* Stubbing.swift in Sources */,
|
D0DC174D24CFF1F100A75C65 /* Stubbing.swift in Sources */,
|
||||||
|
D0091B6B24DC10CE0040E8D2 /* PreferencesViewModel.swift in Sources */,
|
||||||
D0666A5724C6C63400F3F04B /* MastodonDecoder.swift in Sources */,
|
D0666A5724C6C63400F3F04B /* MastodonDecoder.swift in Sources */,
|
||||||
D0DB6EF424C5228A00D965FE /* AddIdentityView.swift in Sources */,
|
D0DB6EF424C5228A00D965FE /* AddIdentityView.swift in Sources */,
|
||||||
D0DC177424D0B58800A75C65 /* Keychain.swift in Sources */,
|
D0DC177424D0B58800A75C65 /* Keychain.swift in Sources */,
|
||||||
|
@ -706,8 +733,11 @@
|
||||||
D0DC175B24D0154F00A75C65 /* MastodonAPI.swift in Sources */,
|
D0DC175B24D0154F00A75C65 /* MastodonAPI.swift in Sources */,
|
||||||
D0ED1BD124CF779B00B4899C /* MastodonTarget.swift in Sources */,
|
D0ED1BD124CF779B00B4899C /* MastodonTarget.swift in Sources */,
|
||||||
D065F53E24D3D20300741304 /* InstanceEndpoint.swift in Sources */,
|
D065F53E24D3D20300741304 /* InstanceEndpoint.swift in Sources */,
|
||||||
|
D0CD847C24DBEA9F00CF380C /* Unknowable.swift in Sources */,
|
||||||
D0666A6F24C6DFB300F3F04B /* AccessToken.swift in Sources */,
|
D0666A6F24C6DFB300F3F04B /* AccessToken.swift in Sources */,
|
||||||
D0ED1BCB24CF744200B4899C /* MastodonClient.swift in Sources */,
|
D0ED1BCB24CF744200B4899C /* MastodonClient.swift in Sources */,
|
||||||
|
D0091B6824DC10B30040E8D2 /* PreferencesView.swift in Sources */,
|
||||||
|
D0CD847624DBDF3C00CF380C /* Status.swift in Sources */,
|
||||||
D052BBE024D805E300A80A7A /* MainNavigationViewModel.swift in Sources */,
|
D052BBE024D805E300A80A7A /* MainNavigationViewModel.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -718,6 +748,7 @@
|
||||||
files = (
|
files = (
|
||||||
D04FD73A24D4A7B4007D572D /* AccountEndpoint+Stubbing.swift in Sources */,
|
D04FD73A24D4A7B4007D572D /* AccountEndpoint+Stubbing.swift in Sources */,
|
||||||
D0DB6F0A24C65AC000D965FE /* AddIdentityViewModel.swift in Sources */,
|
D0DB6F0A24C65AC000D965FE /* AddIdentityViewModel.swift in Sources */,
|
||||||
|
D0CD847424DBDEC700CF380C /* MastodonPreferences.swift in Sources */,
|
||||||
D0ED1BD824CF94B200B4899C /* Application.swift in Sources */,
|
D0ED1BD824CF94B200B4899C /* Application.swift in Sources */,
|
||||||
D047FAAF24C3E21200AF17C5 /* MetatextApp.swift in Sources */,
|
D047FAAF24C3E21200AF17C5 /* MetatextApp.swift in Sources */,
|
||||||
D0BEC94824CA22C400E864C4 /* TimelineViewModel.swift in Sources */,
|
D0BEC94824CA22C400E864C4 /* TimelineViewModel.swift in Sources */,
|
||||||
|
@ -738,6 +769,7 @@
|
||||||
D0ED1BC224CED48800B4899C /* HTTPClient.swift in Sources */,
|
D0ED1BC224CED48800B4899C /* HTTPClient.swift in Sources */,
|
||||||
D0666A4624C6BC0A00F3F04B /* DatabaseError.swift in Sources */,
|
D0666A4624C6BC0A00F3F04B /* DatabaseError.swift in Sources */,
|
||||||
D0ED1BDE24CF982600B4899C /* AccessTokenEndpoint.swift in Sources */,
|
D0ED1BDE24CF982600B4899C /* AccessTokenEndpoint.swift in Sources */,
|
||||||
|
D0CD848024DBF1BB00CF380C /* PreferencesEndpoint.swift in Sources */,
|
||||||
D0666A4C24C6C37700F3F04B /* Identity.swift in Sources */,
|
D0666A4C24C6C37700F3F04B /* Identity.swift in Sources */,
|
||||||
D0666A5524C6C3E500F3F04B /* Emoji.swift in Sources */,
|
D0666A5524C6C3E500F3F04B /* Emoji.swift in Sources */,
|
||||||
D0DC175624D00F0A00A75C65 /* AccessTokenEndpoint+Stubbing.swift in Sources */,
|
D0DC175624D00F0A00A75C65 /* AccessTokenEndpoint+Stubbing.swift in Sources */,
|
||||||
|
@ -755,6 +787,7 @@
|
||||||
D0C963FC24CC359D003BD330 /* AlertItem.swift in Sources */,
|
D0C963FC24CC359D003BD330 /* AlertItem.swift in Sources */,
|
||||||
D0DC174724CFEC2000A75C65 /* StubbingURLProtocol.swift in Sources */,
|
D0DC174724CFEC2000A75C65 /* StubbingURLProtocol.swift in Sources */,
|
||||||
D0DC174E24CFF1F100A75C65 /* Stubbing.swift in Sources */,
|
D0DC174E24CFF1F100A75C65 /* Stubbing.swift in Sources */,
|
||||||
|
D0091B6C24DC10CE0040E8D2 /* PreferencesViewModel.swift in Sources */,
|
||||||
D0666A5824C6C63400F3F04B /* MastodonDecoder.swift in Sources */,
|
D0666A5824C6C63400F3F04B /* MastodonDecoder.swift in Sources */,
|
||||||
D0DB6EF524C5233E00D965FE /* AddIdentityView.swift in Sources */,
|
D0DB6EF524C5233E00D965FE /* AddIdentityView.swift in Sources */,
|
||||||
D0DC177524D0B58800A75C65 /* Keychain.swift in Sources */,
|
D0DC177524D0B58800A75C65 /* Keychain.swift in Sources */,
|
||||||
|
@ -772,8 +805,11 @@
|
||||||
D0DC175C24D0154F00A75C65 /* MastodonAPI.swift in Sources */,
|
D0DC175C24D0154F00A75C65 /* MastodonAPI.swift in Sources */,
|
||||||
D0ED1BD224CF779B00B4899C /* MastodonTarget.swift in Sources */,
|
D0ED1BD224CF779B00B4899C /* MastodonTarget.swift in Sources */,
|
||||||
D065F53F24D3D20300741304 /* InstanceEndpoint.swift in Sources */,
|
D065F53F24D3D20300741304 /* InstanceEndpoint.swift in Sources */,
|
||||||
|
D0CD847D24DBEA9F00CF380C /* Unknowable.swift in Sources */,
|
||||||
D0666A7024C6DFB300F3F04B /* AccessToken.swift in Sources */,
|
D0666A7024C6DFB300F3F04B /* AccessToken.swift in Sources */,
|
||||||
D0ED1BCC24CF744200B4899C /* MastodonClient.swift in Sources */,
|
D0ED1BCC24CF744200B4899C /* MastodonClient.swift in Sources */,
|
||||||
|
D0091B6924DC10B30040E8D2 /* PreferencesView.swift in Sources */,
|
||||||
|
D0CD847724DBDF3C00CF380C /* Status.swift in Sources */,
|
||||||
D052BBE124D805E300A80A7A /* MainNavigationViewModel.swift in Sources */,
|
D052BBE124D805E300A80A7A /* MainNavigationViewModel.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
|
@ -7,12 +7,12 @@ extension Publisher {
|
||||||
func assignErrorsToAlertItem<Root: AnyObject>(
|
func assignErrorsToAlertItem<Root: AnyObject>(
|
||||||
to keyPath: ReferenceWritableKeyPath<Root, AlertItem?>,
|
to keyPath: ReferenceWritableKeyPath<Root, AlertItem?>,
|
||||||
on object: Root) -> AnyPublisher<Output, Never> {
|
on object: Root) -> AnyPublisher<Output, Never> {
|
||||||
self.catch { [weak object] error -> AnyPublisher<Output, Never> in
|
self.catch { [weak object] error -> Empty<Output, Never> in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
object?[keyPath: keyPath] = AlertItem(error: error)
|
object?[keyPath: keyPath] = AlertItem(error: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Empty().eraseToAnyPublisher()
|
return Empty()
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
|
||||||
struct AppEnvironment {
|
struct AppEnvironment {
|
||||||
let URLSessionConfiguration: URLSessionConfiguration
|
let URLSessionConfiguration: URLSessionConfiguration
|
||||||
|
@ -9,3 +10,41 @@ struct AppEnvironment {
|
||||||
let secrets: Secrets
|
let secrets: Secrets
|
||||||
let webAuthSessionType: WebAuthSession.Type
|
let webAuthSessionType: WebAuthSession.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class IdentifiedEnvironment {
|
||||||
|
@Published var identity: Identity
|
||||||
|
let observationErrors: AnyPublisher<Error, Never>
|
||||||
|
let networkClient: MastodonClient
|
||||||
|
let appEnvironment: AppEnvironment
|
||||||
|
|
||||||
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
private let observationErrorsInput = PassthroughSubject<Error, Never>()
|
||||||
|
|
||||||
|
init(identityID: String, appEnvironment: AppEnvironment) throws {
|
||||||
|
self.appEnvironment = appEnvironment
|
||||||
|
observationErrors = observationErrorsInput.eraseToAnyPublisher()
|
||||||
|
networkClient = MastodonClient(configuration: appEnvironment.URLSessionConfiguration)
|
||||||
|
networkClient.accessToken = try appEnvironment.secrets.item(.accessToken, forIdentityID: identityID)
|
||||||
|
|
||||||
|
let observation = appEnvironment.identityDatabase.identityObservation(id: identityID).share()
|
||||||
|
|
||||||
|
var initialIdentity: Identity?
|
||||||
|
|
||||||
|
observation.first().sink(
|
||||||
|
receiveCompletion: { _ in },
|
||||||
|
receiveValue: { initialIdentity = $0 })
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
guard let identity = initialIdentity else { throw IdentityDatabaseError.identityNotFound }
|
||||||
|
|
||||||
|
self.identity = identity
|
||||||
|
networkClient.instanceURL = identity.url
|
||||||
|
|
||||||
|
observation.catch { [weak self] error -> Empty<Identity, Never> in
|
||||||
|
self?.observationErrorsInput.send(error)
|
||||||
|
|
||||||
|
return Empty()
|
||||||
|
}
|
||||||
|
.assign(to: &$identity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ struct Identity: Codable, Hashable, Identifiable {
|
||||||
let id: String
|
let id: String
|
||||||
let url: URL
|
let url: URL
|
||||||
let lastUsedAt: Date
|
let lastUsedAt: Date
|
||||||
|
let preferences: Identity.Preferences
|
||||||
let instance: Identity.Instance?
|
let instance: Identity.Instance?
|
||||||
let account: Identity.Account?
|
let account: Identity.Account?
|
||||||
}
|
}
|
||||||
|
@ -28,6 +29,16 @@ extension Identity {
|
||||||
let header: URL
|
let header: URL
|
||||||
let headerStatic: URL
|
let headerStatic: URL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Preferences: Codable, Hashable {
|
||||||
|
var useServerPostingPreferences = true
|
||||||
|
var postingDefaultVisibility = Status.Visibility.public
|
||||||
|
var postingDefaultSensitive = false
|
||||||
|
var postingDefaultLanguage: String?
|
||||||
|
var useServerReadingPreferences = true
|
||||||
|
var readingExpandMedia = MastodonPreferences.ExpandMedia.default
|
||||||
|
var readingExpandSpoilers = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Identity {
|
extension Identity {
|
||||||
|
|
|
@ -36,6 +36,7 @@ extension IdentityDatabase {
|
||||||
id: id,
|
id: id,
|
||||||
url: url,
|
url: url,
|
||||||
lastUsedAt: Date(),
|
lastUsedAt: Date(),
|
||||||
|
preferences: Identity.Preferences(),
|
||||||
instanceURI: nil).save)
|
instanceURI: nil).save)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
@ -150,6 +151,7 @@ private extension IdentityDatabase {
|
||||||
t.column("instanceURI", .text)
|
t.column("instanceURI", .text)
|
||||||
.indexed()
|
.indexed()
|
||||||
.references("instance", column: "uri")
|
.references("instance", column: "uri")
|
||||||
|
t.column("preferences", .blob).notNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
try db.create(table: "account", ifNotExists: true) { t in
|
try db.create(table: "account", ifNotExists: true) { t in
|
||||||
|
@ -175,6 +177,7 @@ private struct StoredIdentity: Codable, Hashable, TableRecord, FetchableRecord,
|
||||||
let id: String
|
let id: String
|
||||||
let url: URL
|
let url: URL
|
||||||
let lastUsedAt: Date
|
let lastUsedAt: Date
|
||||||
|
let preferences: Identity.Preferences
|
||||||
let instanceURI: String?
|
let instanceURI: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,6 +206,7 @@ private extension Identity {
|
||||||
id: result.identity.id,
|
id: result.identity.id,
|
||||||
url: result.identity.url,
|
url: result.identity.url,
|
||||||
lastUsedAt: result.identity.lastUsedAt,
|
lastUsedAt: result.identity.lastUsedAt,
|
||||||
|
preferences: result.identity.preferences,
|
||||||
instance: result.instance,
|
instance: result.instance,
|
||||||
account: result.account)
|
account: result.account)
|
||||||
}
|
}
|
||||||
|
|
28
Shared/Model/MastodonPreferences.swift
Normal file
28
Shared/Model/MastodonPreferences.swift
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct MastodonPreferences: Codable {
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case postingDefaultVisibility = "posting:default:visibility"
|
||||||
|
case postingDefaultSensitive = "posting:default:sensitive"
|
||||||
|
case postingDefaultLanguage = "posting:default:language"
|
||||||
|
case readingExpandMedia = "reading:expand:media"
|
||||||
|
case readingExpandSpoilers = "reading:expand:spoilers"
|
||||||
|
}
|
||||||
|
|
||||||
|
let postingDefaultVisibility: Status.Visibility
|
||||||
|
let postingDefaultSensitive: Bool
|
||||||
|
let postingDefaultLanguage: String?
|
||||||
|
let readingExpandMedia: ExpandMedia
|
||||||
|
let readingExpandSpoilers: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MastodonPreferences {
|
||||||
|
enum ExpandMedia: String, Codable, Unknowable {
|
||||||
|
case `default`
|
||||||
|
case showAll
|
||||||
|
case hideAll
|
||||||
|
case unknown
|
||||||
|
}
|
||||||
|
}
|
13
Shared/Model/Status.swift
Normal file
13
Shared/Model/Status.swift
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct Status {
|
||||||
|
enum Visibility: String, Codable, Unknowable {
|
||||||
|
case `public`
|
||||||
|
case unlisted
|
||||||
|
case `private`
|
||||||
|
case direct
|
||||||
|
case unknown
|
||||||
|
}
|
||||||
|
}
|
17
Shared/Model/Unknowable.swift
Normal file
17
Shared/Model/Unknowable.swift
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol Unknowable: RawRepresentable, CaseIterable where RawValue: Equatable {
|
||||||
|
static var unknown: RawValue { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Unknowable {
|
||||||
|
init(rawValue: RawValue) {
|
||||||
|
self = Self.allCases.first { $0.rawValue == rawValue } ?? Self(rawValue: Self.unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Unknowable where RawValue == String {
|
||||||
|
static var unknown: String { "unknown" }
|
||||||
|
}
|
23
Shared/Networking/Mastodon API/PreferencesEndpoint.swift
Normal file
23
Shared/Networking/Mastodon API/PreferencesEndpoint.swift
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum PreferencesEndpoint {
|
||||||
|
case preferences
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PreferencesEndpoint: MastodonEndpoint {
|
||||||
|
typealias ResultType = MastodonPreferences
|
||||||
|
|
||||||
|
var pathComponentsInContext: [String] {
|
||||||
|
switch self {
|
||||||
|
case .preferences: return ["instance"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var method: HTTPMethod {
|
||||||
|
switch self {
|
||||||
|
case .preferences: return .get
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,24 +8,15 @@ class IdentitiesViewModel: ObservableObject {
|
||||||
@Published var identities = [Identity]()
|
@Published var identities = [Identity]()
|
||||||
@Published var alertItem: AlertItem?
|
@Published var alertItem: AlertItem?
|
||||||
|
|
||||||
private let environment: AppEnvironment
|
private let environment: IdentifiedEnvironment
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
init(identity: Published<Identity>, environment: AppEnvironment) {
|
init(environment: IdentifiedEnvironment) {
|
||||||
_identity = identity
|
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
|
identity = environment.identity
|
||||||
|
|
||||||
environment.identityDatabase.identitiesObservation()
|
environment.appEnvironment.identityDatabase.identitiesObservation()
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
.assign(to: &$identities)
|
.assign(to: &$identities)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension IdentitiesViewModel {
|
|
||||||
func identitySelected(id: String) {
|
|
||||||
environment.identityDatabase.updateLastUsedAt(identityID: id)
|
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
|
||||||
.sink(receiveValue: {})
|
|
||||||
.store(in: &cancellables)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,39 +10,21 @@ class MainNavigationViewModel: ObservableObject {
|
||||||
@Published var alertItem: AlertItem?
|
@Published var alertItem: AlertItem?
|
||||||
var selectedTab: Tab? = .timelines
|
var selectedTab: Tab? = .timelines
|
||||||
|
|
||||||
private let environment: AppEnvironment
|
private let environment: IdentifiedEnvironment
|
||||||
private let networkClient: MastodonClient
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
init(identityID: String, environment: AppEnvironment) throws {
|
init(environment: IdentifiedEnvironment) {
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
networkClient = MastodonClient(configuration: environment.URLSessionConfiguration)
|
identity = environment.identity
|
||||||
|
environment.$identity.dropFirst().assign(to: &$identity)
|
||||||
|
|
||||||
let observation = environment.identityDatabase.identityObservation(id: identityID).share()
|
environment.appEnvironment.identityDatabase
|
||||||
var initialIdentity: Identity?
|
.recentIdentitiesObservation(excluding: environment.identity.id)
|
||||||
|
|
||||||
observation.first().sink(
|
|
||||||
receiveCompletion: { _ in },
|
|
||||||
receiveValue: { initialIdentity = $0 })
|
|
||||||
.store(in: &cancellables)
|
|
||||||
|
|
||||||
guard let identity = initialIdentity else { throw IdentityDatabaseError.identityNotFound }
|
|
||||||
|
|
||||||
self.identity = identity
|
|
||||||
networkClient.instanceURL = identity.url
|
|
||||||
|
|
||||||
do {
|
|
||||||
networkClient.accessToken = try environment.secrets.item(.accessToken, forIdentityID: identity.id)
|
|
||||||
} catch {
|
|
||||||
alertItem = AlertItem(error: error)
|
|
||||||
}
|
|
||||||
|
|
||||||
observation.assignErrorsToAlertItem(to: \.alertItem, on: self).assign(to: &$identity)
|
|
||||||
environment.identityDatabase.recentIdentitiesObservation(excluding: identityID)
|
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
.assign(to: &$recentIdentities)
|
.assign(to: &$recentIdentities)
|
||||||
|
|
||||||
environment.identityDatabase.updateLastUsedAt(identityID: identityID)
|
environment.appEnvironment.identityDatabase
|
||||||
|
.updateLastUsedAt(identityID: environment.identity.id)
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
.sink(receiveValue: {})
|
.sink(receiveValue: {})
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
@ -53,25 +35,25 @@ extension MainNavigationViewModel {
|
||||||
func refreshIdentity() {
|
func refreshIdentity() {
|
||||||
let id = identity.id
|
let id = identity.id
|
||||||
|
|
||||||
if networkClient.accessToken != nil {
|
if environment.networkClient.accessToken != nil {
|
||||||
networkClient.request(AccountEndpoint.verifyCredentials)
|
environment.networkClient.request(AccountEndpoint.verifyCredentials)
|
||||||
.map { ($0, id) }
|
.map { ($0, id) }
|
||||||
.flatMap(environment.identityDatabase.updateAccount)
|
.flatMap(environment.appEnvironment.identityDatabase.updateAccount)
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
.sink(receiveValue: {})
|
.sink(receiveValue: {})
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
networkClient.request(InstanceEndpoint.instance)
|
environment.networkClient.request(InstanceEndpoint.instance)
|
||||||
.map { ($0, id) }
|
.map { ($0, id) }
|
||||||
.flatMap(environment.identityDatabase.updateInstance)
|
.flatMap(environment.appEnvironment.identityDatabase.updateInstance)
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
.sink(receiveValue: {})
|
.sink(receiveValue: {})
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
func settingsViewModel() -> SettingsViewModel {
|
func settingsViewModel() -> SettingsViewModel {
|
||||||
SettingsViewModel(identity: _identity, environment: environment)
|
SettingsViewModel(environment: environment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
Shared/View Models/PreferencesViewModel.swift
Normal file
15
Shared/View Models/PreferencesViewModel.swift
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class PreferencesViewModel: ObservableObject {
|
||||||
|
@Published var preferences: Identity.Preferences
|
||||||
|
|
||||||
|
private let environment: IdentifiedEnvironment
|
||||||
|
|
||||||
|
init(environment: IdentifiedEnvironment) {
|
||||||
|
self.environment = environment
|
||||||
|
preferences = environment.identity.preferences
|
||||||
|
environment.$identity.map(\.preferences).assign(to: &$preferences)
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,24 +4,13 @@ import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
class RootViewModel: ObservableObject {
|
class RootViewModel: ObservableObject {
|
||||||
@Published private(set) var mainNavigationViewModel: MainNavigationViewModel?
|
@Published private(set) var identityID: String?
|
||||||
|
|
||||||
@Published private var identityID: String?
|
|
||||||
private let environment: AppEnvironment
|
private let environment: AppEnvironment
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
init(environment: AppEnvironment) {
|
init(environment: AppEnvironment) {
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
identityID = environment.identityDatabase.mostRecentlyUsedIdentityID
|
identityID = environment.identityDatabase.mostRecentlyUsedIdentityID
|
||||||
|
|
||||||
$identityID
|
|
||||||
.tryMap {
|
|
||||||
guard let id = $0 else { return nil }
|
|
||||||
|
|
||||||
return try MainNavigationViewModel(identityID: id, environment: environment)
|
|
||||||
}
|
|
||||||
.replaceError(with: nil)
|
|
||||||
.assign(to: &$mainNavigationViewModel)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,12 +20,23 @@ extension RootViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
func addIdentityViewModel() -> AddIdentityViewModel {
|
func addIdentityViewModel() -> AddIdentityViewModel {
|
||||||
let addAccountViewModel = AddIdentityViewModel(environment: environment)
|
AddIdentityViewModel(environment: environment)
|
||||||
|
}
|
||||||
|
|
||||||
addAccountViewModel.addedIdentityID
|
func mainNavigationViewModel(identityID: String) -> MainNavigationViewModel? {
|
||||||
.sink(receiveValue: newIdentitySelected(id:))
|
let identifiedEnvironment: IdentifiedEnvironment
|
||||||
.store(in: &cancellables)
|
|
||||||
|
|
||||||
return addAccountViewModel
|
do {
|
||||||
|
identifiedEnvironment = try IdentifiedEnvironment(identityID: identityID, appEnvironment: environment)
|
||||||
|
} catch {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
identifiedEnvironment.observationErrors
|
||||||
|
.receive(on: RunLoop.main)
|
||||||
|
.map { [weak self] _ in self?.environment.identityDatabase.mostRecentlyUsedIdentityID }
|
||||||
|
.assign(to: &$identityID)
|
||||||
|
|
||||||
|
return MainNavigationViewModel(environment: identifiedEnvironment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,16 +4,17 @@ import Foundation
|
||||||
|
|
||||||
class SettingsViewModel: ObservableObject {
|
class SettingsViewModel: ObservableObject {
|
||||||
@Published private(set) var identity: Identity
|
@Published private(set) var identity: Identity
|
||||||
private let environment: AppEnvironment
|
private let environment: IdentifiedEnvironment
|
||||||
|
|
||||||
init(identity: Published<Identity>, environment: AppEnvironment) {
|
init(environment: IdentifiedEnvironment) {
|
||||||
_identity = identity
|
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
|
identity = environment.identity
|
||||||
|
environment.$identity.dropFirst().assign(to: &$identity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SettingsViewModel {
|
extension SettingsViewModel {
|
||||||
func identitiesViewModel() -> IdentitiesViewModel {
|
func identitiesViewModel() -> IdentitiesViewModel {
|
||||||
IdentitiesViewModel(identity: _identity, environment: environment)
|
IdentitiesViewModel(environment: environment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import SwiftUI
|
||||||
|
|
||||||
struct AddIdentityView: View {
|
struct AddIdentityView: View {
|
||||||
@StateObject var viewModel: AddIdentityViewModel
|
@StateObject var viewModel: AddIdentityViewModel
|
||||||
|
@EnvironmentObject var rootViewModel: RootViewModel
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
|
@ -32,6 +33,11 @@ struct AddIdentityView: View {
|
||||||
}
|
}
|
||||||
.paddingIfMac()
|
.paddingIfMac()
|
||||||
.alertItem($viewModel.alertItem)
|
.alertItem($viewModel.alertItem)
|
||||||
|
.onReceive(viewModel.addedIdentityID) { id in
|
||||||
|
withAnimation {
|
||||||
|
rootViewModel.newIdentitySelected(id: id)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,9 @@ struct IdentitiesView: View {
|
||||||
Section {
|
Section {
|
||||||
List(viewModel.identities) { identity in
|
List(viewModel.identities) { identity in
|
||||||
Button(identity.handle) {
|
Button(identity.handle) {
|
||||||
rootViewModel.newIdentitySelected(id: identity.id)
|
withAnimation {
|
||||||
|
rootViewModel.newIdentitySelected(id: identity.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
15
Shared/Views/PreferencesView.swift
Normal file
15
Shared/Views/PreferencesView.swift
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PreferencesView: View {
|
||||||
|
var body: some View {
|
||||||
|
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PreferencesView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
PreferencesView()
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,13 +6,16 @@ struct RootView: View {
|
||||||
@StateObject var viewModel: RootViewModel
|
@StateObject var viewModel: RootViewModel
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
if let id = viewModel.identityID,
|
||||||
if let mainNavigationViewModel = viewModel.mainNavigationViewModel {
|
let mainNavigationViewModel = viewModel.mainNavigationViewModel(identityID: id) {
|
||||||
Self.mainNavigation(mainNavigationViewModel: mainNavigationViewModel)
|
Self.mainNavigation(mainNavigationViewModel: mainNavigationViewModel)
|
||||||
.environmentObject(viewModel)
|
.id(id)
|
||||||
} else {
|
.environmentObject(viewModel)
|
||||||
AddIdentityView(viewModel: viewModel.addIdentityViewModel())
|
.transition(.opacity)
|
||||||
}
|
} else {
|
||||||
|
AddIdentityView(viewModel: viewModel.addIdentityViewModel())
|
||||||
|
.environmentObject(viewModel)
|
||||||
|
.transition(.opacity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,19 +6,26 @@ import CombineExpectations
|
||||||
@testable import Metatext
|
@testable import Metatext
|
||||||
|
|
||||||
class RootViewModelTests: XCTestCase {
|
class RootViewModelTests: XCTestCase {
|
||||||
|
var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
func testAddIdentity() throws {
|
func testAddIdentity() throws {
|
||||||
let sut = RootViewModel(environment: .fresh())
|
let sut = RootViewModel(environment: .fresh())
|
||||||
let recorder = sut.$mainNavigationViewModel.record()
|
let recorder = sut.$identityID.record()
|
||||||
|
|
||||||
XCTAssertNil(try wait(for: recorder.next(), timeout: 1))
|
XCTAssertNil(try wait(for: recorder.next(), timeout: 1))
|
||||||
|
|
||||||
let addIdentityViewModel = sut.addIdentityViewModel()
|
let addIdentityViewModel = sut.addIdentityViewModel()
|
||||||
|
|
||||||
|
addIdentityViewModel.addedIdentityID
|
||||||
|
.sink(receiveValue: sut.newIdentitySelected(id:))
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
addIdentityViewModel.urlFieldText = "https://mastodon.social"
|
addIdentityViewModel.urlFieldText = "https://mastodon.social"
|
||||||
addIdentityViewModel.goTapped()
|
addIdentityViewModel.goTapped()
|
||||||
|
|
||||||
let mainNavigationViewModel = try wait(for: recorder.next(), timeout: 1)!
|
let identityID = try wait(for: recorder.next(), timeout: 1)!
|
||||||
|
|
||||||
XCTAssertNotNil(mainNavigationViewModel)
|
XCTAssertNotNil(identityID)
|
||||||
|
XCTAssertNotNil(sut.mainNavigationViewModel(identityID: identityID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,8 @@ struct TabNavigation: View {
|
||||||
SettingsView(viewModel: viewModel.settingsViewModel())
|
SettingsView(viewModel: viewModel.settingsViewModel())
|
||||||
.environmentObject(rootViewModel)
|
.environmentObject(rootViewModel)
|
||||||
}
|
}
|
||||||
.onReceive(rootViewModel.$mainNavigationViewModel.map { _ in ()},
|
.alertItem($viewModel.alertItem)
|
||||||
perform: viewModel.refreshIdentity)
|
.onAppear(perform: viewModel.refreshIdentity)
|
||||||
.onReceive(NotificationCenter.default
|
.onReceive(NotificationCenter.default
|
||||||
.publisher(for: UIScene.willEnterForegroundNotification)
|
.publisher(for: UIScene.willEnterForegroundNotification)
|
||||||
.map { _ in () },
|
.map { _ in () },
|
||||||
|
|
Loading…
Reference in a new issue