Add sign-in to server
This commit is contained in:
parent
ed8fa69d9a
commit
37c6dc0699
|
@ -11,13 +11,20 @@
|
|||
F8341F92295C63BB009C8EE6 /* ImageStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8341F91295C63BB009C8EE6 /* ImageStatus.swift */; };
|
||||
F83901A4295D864D00456AE2 /* TagView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F83901A3295D864D00456AE2 /* TagView.swift */; };
|
||||
F83901A6295D8EC000456AE2 /* LabelIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F83901A5295D8EC000456AE2 /* LabelIconView.swift */; };
|
||||
F866F6A0296040A8002E8F88 /* ApplicationSettings+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = F866F69E296040A8002E8F88 /* ApplicationSettings+CoreDataClass.swift */; };
|
||||
F866F6A1296040A8002E8F88 /* ApplicationSettings+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = F866F69F296040A8002E8F88 /* ApplicationSettings+CoreDataProperties.swift */; };
|
||||
F866F6A329604161002E8F88 /* AccountDataHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F866F6A229604161002E8F88 /* AccountDataHandler.swift */; };
|
||||
F866F6A529604194002E8F88 /* ApplicationSettingsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F866F6A429604194002E8F88 /* ApplicationSettingsHandler.swift */; };
|
||||
F866F6A729604629002E8F88 /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F866F6A629604629002E8F88 /* SignInView.swift */; };
|
||||
F866F6AA29605AFA002E8F88 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F866F6A929605AFA002E8F88 /* SceneDelegate.swift */; };
|
||||
F866F6AE29606367002E8F88 /* ApplicationViewMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F866F6AD29606367002E8F88 /* ApplicationViewMode.swift */; };
|
||||
F866F6B729608467002E8F88 /* MastodonSwift in Frameworks */ = {isa = PBXBuildFile; productRef = F866F6B629608467002E8F88 /* MastodonSwift */; };
|
||||
F88C246C295C37B80006098B /* VernissageApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C246B295C37B80006098B /* VernissageApp.swift */; };
|
||||
F88C246E295C37B80006098B /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C246D295C37B80006098B /* MainView.swift */; };
|
||||
F88C2470295C37BB0006098B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F88C246F295C37BB0006098B /* Assets.xcassets */; };
|
||||
F88C2473295C37BB0006098B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F88C2472295C37BB0006098B /* Preview Assets.xcassets */; };
|
||||
F88C2475295C37BB0006098B /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2474295C37BB0006098B /* Persistence.swift */; };
|
||||
F88C2475295C37BB0006098B /* CoreDataHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2474295C37BB0006098B /* CoreDataHandler.swift */; };
|
||||
F88C2478295C37BB0006098B /* Vernissage.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = F88C2476295C37BB0006098B /* Vernissage.xcdatamodeld */; };
|
||||
F88C2480295C38400006098B /* MastodonSwift in Frameworks */ = {isa = PBXBuildFile; productRef = F88C247F295C38400006098B /* MastodonSwift */; };
|
||||
F88C2482295C3A4F0006098B /* DetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2481295C3A4F0006098B /* DetailsView.swift */; };
|
||||
F88C2486295C48030006098B /* HTMLFotmattedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2485295C48030006098B /* HTMLFotmattedText.swift */; };
|
||||
F88FAD21295F3944009B20C9 /* HomeFeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD20295F3944009B20C9 /* HomeFeedView.swift */; };
|
||||
|
@ -27,7 +34,6 @@
|
|||
F88FAD2A295F43B8009B20C9 /* AccountData+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD28295F43B8009B20C9 /* AccountData+CoreDataClass.swift */; };
|
||||
F88FAD2B295F43B8009B20C9 /* AccountData+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD29295F43B8009B20C9 /* AccountData+CoreDataProperties.swift */; };
|
||||
F88FAD2D295F4AD7009B20C9 /* ApplicationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD2C295F4AD7009B20C9 /* ApplicationState.swift */; };
|
||||
F88FAD2F295F4D3C009B20C9 /* AccountData+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD2E295F4D3C009B20C9 /* AccountData+CoreDataProperties.swift */; };
|
||||
F88FAD32295F5029009B20C9 /* RemoteFileService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD31295F5029009B20C9 /* RemoteFileService.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
|
@ -36,12 +42,20 @@
|
|||
F8341F91295C63BB009C8EE6 /* ImageStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageStatus.swift; sourceTree = "<group>"; };
|
||||
F83901A3295D864D00456AE2 /* TagView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagView.swift; sourceTree = "<group>"; };
|
||||
F83901A5295D8EC000456AE2 /* LabelIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelIconView.swift; sourceTree = "<group>"; };
|
||||
F866F69E296040A8002E8F88 /* ApplicationSettings+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ApplicationSettings+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
F866F69F296040A8002E8F88 /* ApplicationSettings+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ApplicationSettings+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
F866F6A229604161002E8F88 /* AccountDataHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDataHandler.swift; sourceTree = "<group>"; };
|
||||
F866F6A429604194002E8F88 /* ApplicationSettingsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationSettingsHandler.swift; sourceTree = "<group>"; };
|
||||
F866F6A629604629002E8F88 /* SignInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInView.swift; sourceTree = "<group>"; };
|
||||
F866F6A829604FFF002E8F88 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
F866F6A929605AFA002E8F88 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
F866F6AD29606367002E8F88 /* ApplicationViewMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationViewMode.swift; sourceTree = "<group>"; };
|
||||
F88C2468295C37B80006098B /* Vernissage.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Vernissage.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F88C246B295C37B80006098B /* VernissageApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VernissageApp.swift; sourceTree = "<group>"; };
|
||||
F88C246D295C37B80006098B /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
|
||||
F88C246F295C37BB0006098B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
F88C2472295C37BB0006098B /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
F88C2474295C37BB0006098B /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
|
||||
F88C2474295C37BB0006098B /* CoreDataHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataHandler.swift; sourceTree = "<group>"; };
|
||||
F88C2477295C37BB0006098B /* Vernissage.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Vernissage.xcdatamodel; sourceTree = "<group>"; };
|
||||
F88C2481295C3A4F0006098B /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = "<group>"; };
|
||||
F88C2485295C48030006098B /* HTMLFotmattedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTMLFotmattedText.swift; sourceTree = "<group>"; };
|
||||
|
@ -52,7 +66,6 @@
|
|||
F88FAD28295F43B8009B20C9 /* AccountData+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountData+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
F88FAD29295F43B8009B20C9 /* AccountData+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountData+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
F88FAD2C295F4AD7009B20C9 /* ApplicationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationState.swift; sourceTree = "<group>"; };
|
||||
F88FAD2E295F4D3C009B20C9 /* AccountData+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "AccountData+CoreDataProperties.swift"; path = "Vernissage/CoreData/AccountData+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
F88FAD31295F5029009B20C9 /* RemoteFileService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteFileService.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
|
@ -61,7 +74,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F88C2480295C38400006098B /* MastodonSwift in Frameworks */,
|
||||
F866F6B729608467002E8F88 /* MastodonSwift in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -77,6 +90,7 @@
|
|||
F88FAD22295F3FC4009B20C9 /* LocalFeedView.swift */,
|
||||
F88FAD24295F3FF7009B20C9 /* FederatedFeedView.swift */,
|
||||
F88FAD26295F400E009B20C9 /* NotificationsView.swift */,
|
||||
F866F6A629604629002E8F88 /* SignInView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
|
@ -94,6 +108,7 @@
|
|||
children = (
|
||||
F8341F91295C63BB009C8EE6 /* ImageStatus.swift */,
|
||||
F88FAD2C295F4AD7009B20C9 /* ApplicationState.swift */,
|
||||
F866F6AD29606367002E8F88 /* ApplicationViewMode.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
|
@ -101,9 +116,13 @@
|
|||
F8341F96295C6427009C8EE6 /* CoreData */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F866F69E296040A8002E8F88 /* ApplicationSettings+CoreDataClass.swift */,
|
||||
F866F69F296040A8002E8F88 /* ApplicationSettings+CoreDataProperties.swift */,
|
||||
F88FAD28295F43B8009B20C9 /* AccountData+CoreDataClass.swift */,
|
||||
F88FAD29295F43B8009B20C9 /* AccountData+CoreDataProperties.swift */,
|
||||
F88C2474295C37BB0006098B /* Persistence.swift */,
|
||||
F88C2474295C37BB0006098B /* CoreDataHandler.swift */,
|
||||
F866F6A229604161002E8F88 /* AccountDataHandler.swift */,
|
||||
F866F6A429604194002E8F88 /* ApplicationSettingsHandler.swift */,
|
||||
);
|
||||
path = CoreData;
|
||||
sourceTree = "<group>";
|
||||
|
@ -128,7 +147,6 @@
|
|||
F88C245F295C37B80006098B = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F88FAD2E295F4D3C009B20C9 /* AccountData+CoreDataProperties.swift */,
|
||||
F88C246A295C37B80006098B /* Vernissage */,
|
||||
F88C2469295C37B80006098B /* Products */,
|
||||
);
|
||||
|
@ -145,6 +163,7 @@
|
|||
F88C246A295C37B80006098B /* Vernissage */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F866F6A829604FFF002E8F88 /* Info.plist */,
|
||||
F88FAD30295F5010009B20C9 /* Services */,
|
||||
F83901A2295D863B00456AE2 /* Widgets */,
|
||||
F8341F97295C6434009C8EE6 /* Formatters */,
|
||||
|
@ -153,6 +172,7 @@
|
|||
F8341F94295C63FE009C8EE6 /* Extensions */,
|
||||
F8341F93295C63E2009C8EE6 /* Views */,
|
||||
F88C246B295C37B80006098B /* VernissageApp.swift */,
|
||||
F866F6A929605AFA002E8F88 /* SceneDelegate.swift */,
|
||||
F88C246F295C37BB0006098B /* Assets.xcassets */,
|
||||
F88C2476295C37BB0006098B /* Vernissage.xcdatamodeld */,
|
||||
F88C2471295C37BB0006098B /* Preview Content */,
|
||||
|
@ -193,7 +213,7 @@
|
|||
);
|
||||
name = Vernissage;
|
||||
packageProductDependencies = (
|
||||
F88C247F295C38400006098B /* MastodonSwift */,
|
||||
F866F6B629608467002E8F88 /* MastodonSwift */,
|
||||
);
|
||||
productName = Vernissage;
|
||||
productReference = F88C2468295C37B80006098B /* Vernissage.app */;
|
||||
|
@ -224,7 +244,7 @@
|
|||
);
|
||||
mainGroup = F88C245F295C37B80006098B;
|
||||
packageReferences = (
|
||||
F88C247E295C38400006098B /* XCRemoteSwiftPackageReference "Mastodon" */,
|
||||
F866F6B529608467002E8F88 /* XCRemoteSwiftPackageReference "Mastodon" */,
|
||||
);
|
||||
productRefGroup = F88C2469295C37B80006098B /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -252,11 +272,11 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F866F6A0296040A8002E8F88 /* ApplicationSettings+CoreDataClass.swift in Sources */,
|
||||
F88FAD23295F3FC4009B20C9 /* LocalFeedView.swift in Sources */,
|
||||
F88FAD2B295F43B8009B20C9 /* AccountData+CoreDataProperties.swift in Sources */,
|
||||
F88FAD21295F3944009B20C9 /* HomeFeedView.swift in Sources */,
|
||||
F88FAD2F295F4D3C009B20C9 /* AccountData+CoreDataProperties.swift in Sources */,
|
||||
F88C2475295C37BB0006098B /* Persistence.swift in Sources */,
|
||||
F88C2475295C37BB0006098B /* CoreDataHandler.swift in Sources */,
|
||||
F88FAD2A295F43B8009B20C9 /* AccountData+CoreDataClass.swift in Sources */,
|
||||
F8341F92295C63BB009C8EE6 /* ImageStatus.swift in Sources */,
|
||||
F83901A6295D8EC000456AE2 /* LabelIconView.swift in Sources */,
|
||||
|
@ -264,13 +284,19 @@
|
|||
F88C246E295C37B80006098B /* MainView.swift in Sources */,
|
||||
F88C2478295C37BB0006098B /* Vernissage.xcdatamodeld in Sources */,
|
||||
F88C2482295C3A4F0006098B /* DetailsView.swift in Sources */,
|
||||
F866F6A329604161002E8F88 /* AccountDataHandler.swift in Sources */,
|
||||
F88C2486295C48030006098B /* HTMLFotmattedText.swift in Sources */,
|
||||
F866F6A529604194002E8F88 /* ApplicationSettingsHandler.swift in Sources */,
|
||||
F866F6A729604629002E8F88 /* SignInView.swift in Sources */,
|
||||
F88C246C295C37B80006098B /* VernissageApp.swift in Sources */,
|
||||
F83901A4295D864D00456AE2 /* TagView.swift in Sources */,
|
||||
F88FAD25295F3FF7009B20C9 /* FederatedFeedView.swift in Sources */,
|
||||
F88FAD32295F5029009B20C9 /* RemoteFileService.swift in Sources */,
|
||||
F88FAD27295F400E009B20C9 /* NotificationsView.swift in Sources */,
|
||||
F88FAD2D295F4AD7009B20C9 /* ApplicationState.swift in Sources */,
|
||||
F866F6A1296040A8002E8F88 /* ApplicationSettings+CoreDataProperties.swift in Sources */,
|
||||
F866F6AE29606367002E8F88 /* ApplicationViewMode.swift in Sources */,
|
||||
F866F6AA29605AFA002E8F88 /* SceneDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -402,6 +428,7 @@
|
|||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = Vernissage/Info.plist;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
|
@ -432,6 +459,7 @@
|
|||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = Vernissage/Info.plist;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
|
@ -475,9 +503,9 @@
|
|||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
F88C247E295C38400006098B /* XCRemoteSwiftPackageReference "Mastodon" */ = {
|
||||
F866F6B529608467002E8F88 /* XCRemoteSwiftPackageReference "Mastodon" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/Swiftodon/Mastodon.swift";
|
||||
repositoryURL = "https://github.com/mczachurski/Mastodon.swift";
|
||||
requirement = {
|
||||
branch = main;
|
||||
kind = branch;
|
||||
|
@ -486,9 +514,9 @@
|
|||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
F88C247F295C38400006098B /* MastodonSwift */ = {
|
||||
F866F6B629608467002E8F88 /* MastodonSwift */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = F88C247E295C38400006098B /* XCRemoteSwiftPackageReference "Mastodon" */;
|
||||
package = F866F6B529608467002E8F88 /* XCRemoteSwiftPackageReference "Mastodon" */;
|
||||
productName = MastodonSwift;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
|
|
|
@ -16,21 +16,25 @@ extension AccountData {
|
|||
return NSFetchRequest<AccountData>(entityName: "AccountData")
|
||||
}
|
||||
|
||||
@NSManaged public var id: String?
|
||||
@NSManaged public var username: String?
|
||||
@NSManaged public var accessToken: String?
|
||||
@NSManaged public var acct: String?
|
||||
@NSManaged public var displayName: String?
|
||||
@NSManaged public var note: String?
|
||||
@NSManaged public var url: URL?
|
||||
@NSManaged public var avatar: URL?
|
||||
@NSManaged public var header: URL?
|
||||
@NSManaged public var locked: Bool
|
||||
@NSManaged public var avatarData: Data?
|
||||
@NSManaged public var createdAt: String?
|
||||
@NSManaged public var displayName: String?
|
||||
@NSManaged public var followersCount: Int32
|
||||
@NSManaged public var followingCount: Int32
|
||||
@NSManaged public var header: URL?
|
||||
@NSManaged public var id: String?
|
||||
@NSManaged public var locked: Bool
|
||||
@NSManaged public var note: String?
|
||||
@NSManaged public var statusesCount: Int32
|
||||
@NSManaged public var accessToken: String?
|
||||
@NSManaged public var avatarData: Data?
|
||||
@NSManaged public var url: URL?
|
||||
@NSManaged public var username: String?
|
||||
@NSManaged public var clientId: String
|
||||
@NSManaged public var clientSecret: String
|
||||
@NSManaged public var clientVapidKey: String
|
||||
@NSManaged public var serverUrl: URL
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
class AccountDataHandler {
|
||||
func getAccountsData() -> [AccountData] {
|
||||
let context = CoreDataHandler.shared.container.viewContext
|
||||
let fetchRequest = AccountData.fetchRequest()
|
||||
do {
|
||||
return try context.fetch(fetchRequest)
|
||||
} catch {
|
||||
print("Error during fetching accounts")
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
func createAccountDataEntity() -> AccountData {
|
||||
let context = CoreDataHandler.shared.container.viewContext
|
||||
return AccountData(context: context)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
@objc(ApplicationSettings)
|
||||
public class ApplicationSettings: NSManagedObject {
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
extension ApplicationSettings {
|
||||
|
||||
@nonobjc public class func fetchRequest() -> NSFetchRequest<ApplicationSettings> {
|
||||
return NSFetchRequest<ApplicationSettings>(entityName: "ApplicationSettings")
|
||||
}
|
||||
|
||||
@NSManaged public var currentAccount: String?
|
||||
|
||||
}
|
||||
|
||||
extension ApplicationSettings : Identifiable {
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
class ApplicationSettingsHandler {
|
||||
func getDefaultSettings() -> ApplicationSettings {
|
||||
var settingsList: [ApplicationSettings] = []
|
||||
|
||||
let context = CoreDataHandler.shared.container.viewContext
|
||||
let fetchRequest = ApplicationSettings.fetchRequest()
|
||||
do {
|
||||
settingsList = try context.fetch(fetchRequest)
|
||||
} catch {
|
||||
print("Error during fetching application settings")
|
||||
}
|
||||
|
||||
if let settings = settingsList.first {
|
||||
return settings
|
||||
} else {
|
||||
let settings = self.createApplicationSettingsEntity()
|
||||
CoreDataHandler.shared.save()
|
||||
|
||||
return settings
|
||||
}
|
||||
}
|
||||
|
||||
private func createApplicationSettingsEntity() -> ApplicationSettings {
|
||||
let context = CoreDataHandler.shared.container.viewContext
|
||||
return ApplicationSettings(context: context)
|
||||
}
|
||||
}
|
|
@ -7,28 +7,9 @@
|
|||
|
||||
import CoreData
|
||||
|
||||
struct PersistenceController {
|
||||
static let shared = PersistenceController()
|
||||
|
||||
static var preview: PersistenceController = {
|
||||
let result = PersistenceController(inMemory: true)
|
||||
let viewContext = result.container.viewContext
|
||||
for _ in 0..<10 {
|
||||
let newItem = AccountData(context: viewContext)
|
||||
newItem.id = "123"
|
||||
}
|
||||
do {
|
||||
try viewContext.save()
|
||||
} catch {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
let nsError = error as NSError
|
||||
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||
}
|
||||
return result
|
||||
}()
|
||||
|
||||
let container: NSPersistentContainer
|
||||
public class CoreDataHandler {
|
||||
public static let shared = CoreDataHandler()
|
||||
public let container: NSPersistentContainer
|
||||
|
||||
init(inMemory: Bool = false) {
|
||||
container = NSPersistentContainer(name: "Vernissage")
|
||||
|
@ -53,4 +34,40 @@ struct PersistenceController {
|
|||
})
|
||||
container.viewContext.automaticallyMergesChangesFromParent = true
|
||||
}
|
||||
|
||||
public func save() {
|
||||
let context = self.container.viewContext
|
||||
if context.hasChanges {
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// fatalError() causes the application to generate a crash log and terminate.
|
||||
// You should not use this function in a shipping application, although it may be useful during development.
|
||||
|
||||
let nserror = error as NSError
|
||||
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CoreDataHandler {
|
||||
public static var preview: CoreDataHandler = {
|
||||
let result = CoreDataHandler(inMemory: true)
|
||||
let viewContext = result.container.viewContext
|
||||
for _ in 0..<10 {
|
||||
let newItem = AccountData(context: viewContext)
|
||||
newItem.id = "123"
|
||||
}
|
||||
do {
|
||||
try viewContext.save()
|
||||
} catch {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
let nsError = error as NSError
|
||||
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||
}
|
||||
return result
|
||||
}()
|
||||
}
|
|
@ -3,7 +3,6 @@
|
|||
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
|
@ -38,7 +37,7 @@ struct HTMLFormattedText: UIViewRepresentable {
|
|||
}
|
||||
|
||||
private func converHTML(text: String) -> NSAttributedString?{
|
||||
guard let data = text.data(using: .utf8) else {
|
||||
guard let data = text.data(using: .utf16) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?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>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>oauth-vernissage</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,12 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
enum ApplicationViewMode {
|
||||
case loading, signIn, mainView
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
|
||||
import SwiftUI
|
||||
import MastodonSwift
|
||||
import OAuthSwift
|
||||
|
||||
class SceneDelegate: NSObject, UISceneDelegate {
|
||||
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
|
||||
guard let url = URLContexts.first?.url else {
|
||||
return
|
||||
}
|
||||
if url.host == "oauth-callback" {
|
||||
OAuthSwift.handle(url: url)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,9 @@
|
|||
<attribute name="acct" attributeType="String"/>
|
||||
<attribute name="avatar" optional="YES" attributeType="URI"/>
|
||||
<attribute name="avatarData" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="clientId" attributeType="String"/>
|
||||
<attribute name="clientSecret" attributeType="String"/>
|
||||
<attribute name="clientVapidKey" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="String"/>
|
||||
<attribute name="displayName" optional="YES" attributeType="String"/>
|
||||
<attribute name="followersCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
|
@ -13,8 +16,12 @@
|
|||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="locked" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="note" optional="YES" attributeType="String"/>
|
||||
<attribute name="serverUrl" attributeType="URI"/>
|
||||
<attribute name="statusesCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="url" optional="YES" attributeType="URI"/>
|
||||
<attribute name="username" attributeType="String"/>
|
||||
</entity>
|
||||
<entity name="ApplicationSettings" representedClassName="ApplicationSettings" syncable="YES">
|
||||
<attribute name="currentAccount" optional="YES" attributeType="String"/>
|
||||
</entity>
|
||||
</model>
|
|
@ -5,19 +5,130 @@
|
|||
//
|
||||
|
||||
import SwiftUI
|
||||
import MastodonSwift
|
||||
|
||||
@main
|
||||
struct VernissageApp: App {
|
||||
let persistenceController = PersistenceController.shared
|
||||
struct VernissageApp: SwiftUI.App {
|
||||
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
|
||||
let coreDataHandler = CoreDataHandler.shared
|
||||
let applicationState = ApplicationState.shared
|
||||
|
||||
@State var applicationViewMode: ApplicationViewMode = .loading
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
NavigationStack {
|
||||
MainView()
|
||||
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
||||
.environmentObject(ApplicationState.shared)
|
||||
switch applicationViewMode {
|
||||
case .loading:
|
||||
Text("Loading")
|
||||
case .signIn:
|
||||
SignInView { viewMode in
|
||||
applicationViewMode = viewMode
|
||||
}
|
||||
.environment(\.managedObjectContext, coreDataHandler.container.viewContext)
|
||||
.environmentObject(applicationState)
|
||||
case .mainView:
|
||||
MainView()
|
||||
.environment(\.managedObjectContext, coreDataHandler.container.viewContext)
|
||||
.environmentObject(applicationState)
|
||||
}
|
||||
}
|
||||
.task {
|
||||
let accountDataHandler = AccountDataHandler()
|
||||
let accounts = accountDataHandler.getAccountsData()
|
||||
|
||||
// When we dont have even one account stored in database then we have to ask user to enter server and sign in.
|
||||
guard let accountData = accounts.first, let accessToken = accountData.accessToken else {
|
||||
self.applicationViewMode = .signIn
|
||||
return
|
||||
}
|
||||
|
||||
// When we have at least one account then we have to verify access token.
|
||||
let client = MastodonClient(baseURL: accountData.serverUrl).getAuthenticated(token: accessToken)
|
||||
do {
|
||||
let account = try await client.verifyCredentials()
|
||||
try await self.updateAccount(accountData: accountData, account: account)
|
||||
|
||||
self.applicationViewMode = .mainView
|
||||
self.applicationState.accountData = accountData
|
||||
} catch {
|
||||
do {
|
||||
try await self.refreshCredentials(accountData: accountData)
|
||||
|
||||
self.applicationViewMode = .mainView
|
||||
self.applicationState.accountData = accountData
|
||||
} catch {
|
||||
// TODO: show information to the user.
|
||||
print("Cannot refresh credentials!!!")
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
}
|
||||
}
|
||||
|
||||
private func refreshCredentials(accountData: AccountData) async throws {
|
||||
let client = MastodonClient(baseURL: accountData.serverUrl)
|
||||
|
||||
// Create application (we will get clientId amd clientSecret).
|
||||
let oAuthApp = App(clientId: accountData.clientId, clientSecret: accountData.clientSecret)
|
||||
|
||||
// Authorize a user (browser, we will get clientCode).
|
||||
let oAuthSwiftCredential = try await client.authenticate(app: oAuthApp, scope: Scopes(["read", "write", "follow", "push"]))
|
||||
|
||||
// Get authenticated client.
|
||||
let authenticatedClient = client.getAuthenticated(token: oAuthSwiftCredential.oauthToken)
|
||||
|
||||
// Get account information from server.
|
||||
let account = try await authenticatedClient.verifyCredentials()
|
||||
try await self.updateAccount(accountData: accountData, account: account, accessToken: oAuthSwiftCredential.oauthToken)
|
||||
|
||||
self.applicationState.accountData = accountData
|
||||
self.applicationViewMode = .mainView
|
||||
}
|
||||
|
||||
private func updateAccount(accountData: AccountData, account: Account, accessToken: String? = nil) async throws {
|
||||
accountData.username = account.username
|
||||
accountData.acct = account.acct
|
||||
accountData.displayName = account.displayName
|
||||
accountData.note = account.note
|
||||
accountData.url = account.url
|
||||
accountData.avatar = account.avatar
|
||||
accountData.header = account.header
|
||||
accountData.locked = account.locked
|
||||
accountData.createdAt = account.createdAt
|
||||
accountData.followersCount = Int32(account.followersCount)
|
||||
accountData.followingCount = Int32(account.followingCount)
|
||||
accountData.statusesCount = Int32(account.statusesCount)
|
||||
|
||||
if accessToken != nil {
|
||||
accountData.accessToken = accessToken
|
||||
}
|
||||
|
||||
// Download avatar image.
|
||||
if let avatarUrl = account.avatar {
|
||||
do {
|
||||
let avatarData = try await RemoteFileService.shared.fetchData(url: avatarUrl)
|
||||
accountData.avatarData = avatarData
|
||||
}
|
||||
catch {
|
||||
print("Avatar has not been downloaded")
|
||||
}
|
||||
}
|
||||
|
||||
// Save account data in database and in application state.
|
||||
try self.coreDataHandler.container.viewContext.save()
|
||||
}
|
||||
}
|
||||
|
||||
class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
func application(_ application: UIApplication,
|
||||
configurationForConnecting connectingSceneSession: UISceneSession,
|
||||
options: UIScene.ConnectionOptions
|
||||
) -> UISceneConfiguration {
|
||||
let sceneConfig: UISceneConfiguration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
|
||||
sceneConfig.delegateClass = SceneDelegate.self
|
||||
return sceneConfig
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,14 +24,13 @@ struct DetailsView: View {
|
|||
image
|
||||
.resizable()
|
||||
.clipShape(Circle())
|
||||
.shadow(radius: 10)
|
||||
.aspectRatio(contentMode: .fit)
|
||||
} placeholder: {
|
||||
Image(systemName: "person.circle")
|
||||
.resizable()
|
||||
.foregroundColor(Color("mainTextColor"))
|
||||
}
|
||||
.frame(height: 48)
|
||||
.frame(width: 48)
|
||||
.frame(width: 48.0, height: 48.0)
|
||||
|
||||
VStack (alignment: .leading) {
|
||||
Text(current.status.account?.displayName ?? current.status.account?.username ?? "")
|
||||
|
|
|
@ -10,6 +10,7 @@ import MastodonSwift
|
|||
import UIKit
|
||||
|
||||
struct HomeFeedView: View {
|
||||
@EnvironmentObject var applicationState: ApplicationState
|
||||
|
||||
@State private var statuses: [Status] = []
|
||||
@State private var images: [ImageStatus] = []
|
||||
|
@ -61,24 +62,22 @@ struct HomeFeedView: View {
|
|||
}
|
||||
.task {
|
||||
do {
|
||||
defer {
|
||||
self.showLoading = false
|
||||
}
|
||||
|
||||
self.showLoading = true
|
||||
try await loadData()
|
||||
self.showLoading = false
|
||||
} catch {
|
||||
self.showLoading = false
|
||||
print("Error", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadData() async throws {
|
||||
let accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiI2MTQwOCIsImp0aSI6IjZjMDg4N2ZlZThjZjBmZjQ1N2RjZDQ5MTU2YjE2NzYyYmQ2MDQ1NDQ1MmEwYzEyMzVmNDA3YjY2YjFhYjU3ZjBjMTY2YjFmZmIyNjJlZDg2IiwiaWF0IjoxNjcyMDU5MDYwLjI3NDgyMSwibmJmIjoxNjcyMDU5MDYwLjI3NDgyNCwiZXhwIjoxNzAzNTk1MDYwLjI1MzM1Nywic3ViIjoiNjc4MjMiLCJzY29wZXMiOlsicmVhZCIsIndyaXRlIiwiZm9sbG93Il19.kGvg3lW8lF1X1mOTdgGgoXNyzwUIJz5hz5RJKK_WiSoBWDQNadhZDty7XMNF0IAPjxOSi6UaIx2av7_eH_65aNlKFw89bkm8bT_zFQW2V0KbADJ-NmE6X0B_NgU2CNoF5IPn6bhCFHCKMtV6MWAQ_db6DT-LXaGemMY3QimcJzCqQuXI_1ouiZ235T297uEPNTrLwtLq-x_UoO-wx254LStBalDIGDVHAa4by9IT-mvu-QXz7k8pH2NHKoX-9Ql_Y3G9RJJNqoOmWMU45Dyo2HaJKKEb1tkeJ9tA3LIYgbwnEbG2PJ7CE8CXxtakiCIflJZpzzOmq1jXLAsCJ1mHnc77o7NfMaB_hY-f8PEI6d2ttOdH8bNlreF2avznNAIVHg_bf-yv_4wKUCUe0QZMG_yWqOwOk6lyruvboSGKuI5RnYsJbXBoJTGMLON6jVmtiKPbHy-9jNcfFgShAc3D5kTO-8Avj9_RquqEh1TQF_S4ljmganxKzMihyMDLK1OVcXzCFO6FKlCw7YKvbfJk1Qrn9kPBrVDM5jzIyXAmqRd1ivcE9nAdYb2l7KnxW_pi31uT0IdJMpTkZrUQSDMyEnj0HgV6Yd5BDlLG6Cnk8GXATTcU-a1pgE13OtWsCpD2cZQm-tOsFHWBDvY-BA0RtTvQAyEUxRIP9NjHe8rSR90"
|
||||
|
||||
let client = MastodonClient(baseURL: URL(string: "https://pixelfed.social")!)
|
||||
.getAuthenticated(token: accessToken)
|
||||
|
||||
guard let accessData = self.applicationState.accountData, let accessToken = accessData.accessToken else {
|
||||
return
|
||||
}
|
||||
|
||||
let client = MastodonClient(baseURL: accessData.serverUrl).getAuthenticated(token: accessToken)
|
||||
self.statuses = try await client.getHomeTimeline(limit: 40)
|
||||
|
||||
var imagesCache: [ImageStatus] = []
|
||||
|
|
|
@ -11,22 +11,12 @@ import MastodonSwift
|
|||
|
||||
struct MainView: View {
|
||||
@Environment(\.managedObjectContext) private var viewContext
|
||||
|
||||
@EnvironmentObject var applicationState: ApplicationState
|
||||
|
||||
@State private var navBarTitle: String = "Home"
|
||||
@State private var viewMode: ViewMode = .home {
|
||||
didSet {
|
||||
switch viewMode {
|
||||
case .home:
|
||||
self.navBarTitle = "Home"
|
||||
case .local:
|
||||
self.navBarTitle = "Local"
|
||||
case .federated:
|
||||
self.navBarTitle = "Federated"
|
||||
case .notifications:
|
||||
self.navBarTitle = "Notifications"
|
||||
}
|
||||
self.navBarTitle = self.getViewTitle(viewMode: viewMode)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,13 +32,6 @@ struct MainView: View {
|
|||
self.getLeadingToolbar()
|
||||
self.getPrincipalToolbar()
|
||||
}
|
||||
.task {
|
||||
do {
|
||||
try await loadData()
|
||||
} catch {
|
||||
print("Error", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
@ -73,7 +56,7 @@ struct MainView: View {
|
|||
viewMode = .home
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Home")
|
||||
Text(self.getViewTitle(viewMode: .home))
|
||||
Image(systemName: "house")
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +65,7 @@ struct MainView: View {
|
|||
viewMode = .local
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Local")
|
||||
Text(self.getViewTitle(viewMode: .local))
|
||||
Image(systemName: "text.redaction")
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +74,7 @@ struct MainView: View {
|
|||
viewMode = .federated
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Global")
|
||||
Text(self.getViewTitle(viewMode: .federated))
|
||||
Image(systemName: "globe.europe.africa")
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +83,7 @@ struct MainView: View {
|
|||
viewMode = .notifications
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Notifications")
|
||||
Text(self.getViewTitle(viewMode: .notifications))
|
||||
Image(systemName: "bell.badge")
|
||||
}
|
||||
}
|
||||
|
@ -112,7 +95,7 @@ struct MainView: View {
|
|||
.font(.subheadline)
|
||||
}
|
||||
.frame(width: 150)
|
||||
.foregroundColor(Color.white)
|
||||
.foregroundColor(Color("mainTextColor"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,71 +110,27 @@ struct MainView: View {
|
|||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
.clipShape(Circle())
|
||||
.shadow(radius: 10)
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(height: 32)
|
||||
.frame(width: 32)
|
||||
.frame(width: 32.0, height: 32.0)
|
||||
} else {
|
||||
Image(systemName: "person.circle")
|
||||
.resizable()
|
||||
.frame(width: 32.0, height: 32.0)
|
||||
.foregroundColor(Color("mainTextColor"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadData() async throws {
|
||||
|
||||
// Set account data from database.
|
||||
let accountDataFromDb = self.getAccountData()
|
||||
if let accountDataFromDb {
|
||||
self.applicationState.accountData = accountDataFromDb
|
||||
return
|
||||
}
|
||||
|
||||
// Retrieve account data from API.
|
||||
let accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiI2MTQwOCIsImp0aSI6IjZjMDg4N2ZlZThjZjBmZjQ1N2RjZDQ5MTU2YjE2NzYyYmQ2MDQ1NDQ1MmEwYzEyMzVmNDA3YjY2YjFhYjU3ZjBjMTY2YjFmZmIyNjJlZDg2IiwiaWF0IjoxNjcyMDU5MDYwLjI3NDgyMSwibmJmIjoxNjcyMDU5MDYwLjI3NDgyNCwiZXhwIjoxNzAzNTk1MDYwLjI1MzM1Nywic3ViIjoiNjc4MjMiLCJzY29wZXMiOlsicmVhZCIsIndyaXRlIiwiZm9sbG93Il19.kGvg3lW8lF1X1mOTdgGgoXNyzwUIJz5hz5RJKK_WiSoBWDQNadhZDty7XMNF0IAPjxOSi6UaIx2av7_eH_65aNlKFw89bkm8bT_zFQW2V0KbADJ-NmE6X0B_NgU2CNoF5IPn6bhCFHCKMtV6MWAQ_db6DT-LXaGemMY3QimcJzCqQuXI_1ouiZ235T297uEPNTrLwtLq-x_UoO-wx254LStBalDIGDVHAa4by9IT-mvu-QXz7k8pH2NHKoX-9Ql_Y3G9RJJNqoOmWMU45Dyo2HaJKKEb1tkeJ9tA3LIYgbwnEbG2PJ7CE8CXxtakiCIflJZpzzOmq1jXLAsCJ1mHnc77o7NfMaB_hY-f8PEI6d2ttOdH8bNlreF2avznNAIVHg_bf-yv_4wKUCUe0QZMG_yWqOwOk6lyruvboSGKuI5RnYsJbXBoJTGMLON6jVmtiKPbHy-9jNcfFgShAc3D5kTO-8Avj9_RquqEh1TQF_S4ljmganxKzMihyMDLK1OVcXzCFO6FKlCw7YKvbfJk1Qrn9kPBrVDM5jzIyXAmqRd1ivcE9nAdYb2l7KnxW_pi31uT0IdJMpTkZrUQSDMyEnj0HgV6Yd5BDlLG6Cnk8GXATTcU-a1pgE13OtWsCpD2cZQm-tOsFHWBDvY-BA0RtTvQAyEUxRIP9NjHe8rSR90"
|
||||
|
||||
let client = MastodonClient(baseURL: URL(string: "https://pixelfed.social")!)
|
||||
.getAuthenticated(token: accessToken)
|
||||
|
||||
// Get account information from server.
|
||||
let account = try await client.verifyCredentials()
|
||||
|
||||
// Create account object in database.
|
||||
let accountData = AccountData(context: viewContext)
|
||||
accountData.id = account.id
|
||||
accountData.username = account.username
|
||||
accountData.acct = account.acct
|
||||
accountData.displayName = account.displayName
|
||||
accountData.note = account.note
|
||||
accountData.url = account.url
|
||||
accountData.avatar = account.avatar
|
||||
accountData.header = account.header
|
||||
accountData.locked = account.locked
|
||||
accountData.createdAt = account.createdAt
|
||||
accountData.followersCount = Int32(account.followersCount)
|
||||
accountData.followingCount = Int32(account.followingCount)
|
||||
accountData.statusesCount = Int32(account.statusesCount)
|
||||
accountData.accessToken = accessToken
|
||||
|
||||
// Download avatar image.
|
||||
if let avatarUrl = account.avatar {
|
||||
let avatarData = try await RemoteFileService.shared.fetchData(url: avatarUrl)
|
||||
accountData.avatarData = avatarData
|
||||
}
|
||||
|
||||
// Save account data in database and in application state.
|
||||
try self.viewContext.save()
|
||||
self.applicationState.accountData = accountData
|
||||
}
|
||||
|
||||
private func getAccountData() -> AccountData? {
|
||||
let fetchRequest: NSFetchRequest<AccountData> = AccountData.fetchRequest()
|
||||
|
||||
do {
|
||||
return try self.viewContext.fetch(fetchRequest).first
|
||||
}
|
||||
catch {
|
||||
return nil
|
||||
private func getViewTitle(viewMode: ViewMode) -> String {
|
||||
switch viewMode {
|
||||
case .home:
|
||||
return "Home"
|
||||
case .local:
|
||||
return "Local"
|
||||
case .federated:
|
||||
return "Federated"
|
||||
case .notifications:
|
||||
return "Notifications"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -199,6 +138,6 @@ struct MainView: View {
|
|||
|
||||
struct MainView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
MainView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
|
||||
MainView().environment(\.managedObjectContext, CoreDataHandler.preview.container.viewContext)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import MastodonSwift
|
||||
|
||||
struct SignInView: View {
|
||||
@Environment(\.managedObjectContext) private var viewContext
|
||||
@EnvironmentObject var applicationState: ApplicationState
|
||||
|
||||
@State private var serverAddress: String = ""
|
||||
|
||||
var onSignInStateChenge: (_ applicationViewMode: ApplicationViewMode) -> Void?
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
TextField(
|
||||
"Server address",
|
||||
text: $serverAddress
|
||||
)
|
||||
.onSubmit {
|
||||
}
|
||||
.textInputAutocapitalization(.never)
|
||||
.disableAutocorrection(true)
|
||||
|
||||
Button("Go") {
|
||||
Task {
|
||||
let baseUrl = URL(string: serverAddress)!
|
||||
let client = MastodonClient(baseURL: baseUrl)
|
||||
|
||||
// Verify address.
|
||||
let instanceInformation = try await client.readInstanceInformation()
|
||||
print(instanceInformation)
|
||||
|
||||
// Create application (we will get clientId amd clientSecret).
|
||||
let oAuthApp = try await client.createApp(named: "Photofed",
|
||||
redirectUri: "oauth-vernissage://oauth-callback/mastodon",
|
||||
scopes: Scopes(["read", "write", "follow", "push"]),
|
||||
website: baseUrl)
|
||||
|
||||
// Authorize a user (browser, we will get clientCode).
|
||||
let oAuthSwiftCredential = try await client.authenticate(app: oAuthApp, scope: Scopes(["read", "write", "follow", "push"]))
|
||||
|
||||
// Get authenticated client.
|
||||
let authenticatedClient = client.getAuthenticated(token: oAuthSwiftCredential.oauthToken)
|
||||
|
||||
// Get account information from server.
|
||||
let account = try await authenticatedClient.verifyCredentials()
|
||||
|
||||
// Create account object in database.
|
||||
let accountDataHandler = AccountDataHandler()
|
||||
let accountData = accountDataHandler.createAccountDataEntity()
|
||||
|
||||
accountData.id = account.id
|
||||
accountData.username = account.username
|
||||
accountData.acct = account.acct
|
||||
accountData.displayName = account.displayName
|
||||
accountData.note = account.note
|
||||
accountData.url = account.url
|
||||
accountData.avatar = account.avatar
|
||||
accountData.header = account.header
|
||||
accountData.locked = account.locked
|
||||
accountData.createdAt = account.createdAt
|
||||
accountData.followersCount = Int32(account.followersCount)
|
||||
accountData.followingCount = Int32(account.followingCount)
|
||||
accountData.statusesCount = Int32(account.statusesCount)
|
||||
|
||||
accountData.serverUrl = baseUrl
|
||||
accountData.clientId = oAuthApp.clientId
|
||||
accountData.clientSecret = oAuthApp.clientSecret
|
||||
accountData.clientVapidKey = oAuthApp.vapidKey ?? ""
|
||||
accountData.accessToken = oAuthSwiftCredential.oauthToken
|
||||
|
||||
// Download avatar image.
|
||||
if let avatarUrl = account.avatar {
|
||||
do {
|
||||
let avatarData = try await RemoteFileService.shared.fetchData(url: avatarUrl)
|
||||
accountData.avatarData = avatarData
|
||||
}
|
||||
catch {
|
||||
print("Avatar has not been downloaded")
|
||||
}
|
||||
}
|
||||
|
||||
// Save account data in database and in application state.
|
||||
try self.viewContext.save()
|
||||
self.applicationState.accountData = accountData
|
||||
self.onSignInStateChenge(.mainView)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.navigationBarTitle("Sign in to Pixelfed")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
|
||||
struct SignInView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
SignInView { applicationViewMode in
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue