mirror of
https://github.com/yattee/yattee.git
synced 2024-11-09 15:58:20 +00:00
Use separate defaults keys for instances and accounts
This commit is contained in:
parent
a0f74a5899
commit
3d35110c67
@ -1,11 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
extension String {
|
||||
var serializationSafe: String {
|
||||
let serializationUnsafe = ":;"
|
||||
let forbidden = CharacterSet(charactersIn: serializationUnsafe)
|
||||
let result = unicodeScalars.filter { !forbidden.contains($0) }
|
||||
|
||||
return String(String.UnicodeScalarView(result))
|
||||
}
|
||||
}
|
@ -2,9 +2,6 @@ import Foundation
|
||||
|
||||
extension Instance {
|
||||
static var fixture: Instance {
|
||||
Instance(name: "Home", url: "https://invidious.home.net", accounts: [
|
||||
.init(id: UUID(), name: "Evelyn", url: "https://invidious.home.net", sid: "abc"),
|
||||
.init(id: UUID(), name: "Jake", url: "https://invidious.home.net", sid: "xyz")
|
||||
])
|
||||
Instance(name: "Home", url: "https://invidious.home.net")
|
||||
}
|
||||
}
|
||||
|
@ -5,13 +5,15 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
|
||||
struct Account: Defaults.Serializable, Hashable, Identifiable {
|
||||
static var bridge = AccountsBridge()
|
||||
|
||||
let id: UUID?
|
||||
let id: UUID
|
||||
let instanceID: UUID
|
||||
var name: String?
|
||||
let url: String
|
||||
let sid: String
|
||||
|
||||
init(id: UUID? = nil, name: String? = nil, url: String, sid: String) {
|
||||
init(id: UUID? = nil, instanceID: UUID, name: String? = nil, url: String, sid: String) {
|
||||
self.id = id ?? UUID()
|
||||
self.instanceID = instanceID
|
||||
self.name = name
|
||||
self.url = url
|
||||
self.sid = sid
|
||||
@ -45,7 +47,8 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
|
||||
}
|
||||
|
||||
return [
|
||||
"id": value.id?.uuidString ?? "",
|
||||
"id": value.id.uuidString,
|
||||
"instanceID": value.instanceID.uuidString,
|
||||
"name": value.name ?? "",
|
||||
"url": value.url,
|
||||
"sid": value.sid
|
||||
@ -55,30 +58,32 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
|
||||
func deserialize(_ object: Serializable?) -> Value? {
|
||||
guard
|
||||
let object = object,
|
||||
let id = object["id"],
|
||||
let instanceID = object["instanceID"],
|
||||
let url = object["url"],
|
||||
let sid = object["sid"]
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let uuid = UUID(uuidString: id)
|
||||
let instanceUUID = UUID(uuidString: instanceID)!
|
||||
let name = object["name"] ?? ""
|
||||
|
||||
return Account(name: name, url: url, sid: sid)
|
||||
return Account(id: uuid, instanceID: instanceUUID, name: name, url: url, sid: sid)
|
||||
}
|
||||
}
|
||||
|
||||
static var bridge = InstancesBridge()
|
||||
|
||||
let id: UUID?
|
||||
let id: UUID
|
||||
let name: String
|
||||
let url: String
|
||||
var accounts = [Account]()
|
||||
|
||||
init(id: UUID? = nil, name: String, url: String, accounts: [Account] = []) {
|
||||
init(id: UUID? = nil, name: String, url: String) {
|
||||
self.id = id ?? UUID()
|
||||
self.name = name
|
||||
self.url = url
|
||||
self.accounts = accounts
|
||||
}
|
||||
|
||||
var description: String {
|
||||
@ -90,7 +95,7 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
|
||||
}
|
||||
|
||||
var anonymousAccount: Account {
|
||||
Account(name: "Anonymous", url: url, sid: "")
|
||||
Account(instanceID: id, name: "Anonymous", url: url, sid: "")
|
||||
}
|
||||
|
||||
struct InstancesBridge: Defaults.Bridge {
|
||||
@ -103,10 +108,9 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
|
||||
}
|
||||
|
||||
return [
|
||||
"id": value.id?.uuidString ?? "",
|
||||
"id": value.id.uuidString,
|
||||
"name": value.name,
|
||||
"url": value.url,
|
||||
"accounts": value.accounts.map { "\($0.id!):\($0.name ?? ""):\($0.sid)" }.joined(separator: ";")
|
||||
"url": value.url
|
||||
]
|
||||
}
|
||||
|
||||
@ -119,24 +123,10 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
|
||||
return nil
|
||||
}
|
||||
|
||||
let name = object["name"] ?? ""
|
||||
let accounts = object["accounts"] ?? ""
|
||||
let uuid = UUID(uuidString: id)
|
||||
let name = object["name"] ?? ""
|
||||
|
||||
var instance = Instance(id: uuid, name: name, url: url)
|
||||
|
||||
accounts.split(separator: ";").forEach { sid in
|
||||
let components = sid.components(separatedBy: ":")
|
||||
|
||||
let id = components[0]
|
||||
let name = components[1]
|
||||
let sid = components[2]
|
||||
|
||||
let uuid = UUID(uuidString: id)
|
||||
instance.accounts.append(Account(id: uuid, name: name, url: instance.url, sid: sid))
|
||||
}
|
||||
|
||||
return instance
|
||||
return Instance(id: uuid, name: name, url: url)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ import Foundation
|
||||
|
||||
final class InstancesModel: ObservableObject {
|
||||
var defaultAccount: Instance.Account! {
|
||||
Defaults[.instances].first?.accounts.first
|
||||
Defaults[.accounts].first
|
||||
}
|
||||
|
||||
func find(_ id: Instance.ID?) -> Instance? {
|
||||
@ -15,7 +15,7 @@ final class InstancesModel: ObservableObject {
|
||||
}
|
||||
|
||||
func accounts(_ id: Instance.ID?) -> [Instance.Account] {
|
||||
find(id)?.accounts ?? []
|
||||
Defaults[.accounts].filter { $0.instanceID == id }
|
||||
}
|
||||
|
||||
func add(name: String, url: String) -> Instance {
|
||||
@ -32,20 +32,15 @@ final class InstancesModel: ObservableObject {
|
||||
}
|
||||
|
||||
func addAccount(instance: Instance, name: String, sid: String) -> Instance.Account {
|
||||
let account = Instance.Account(name: name, url: instance.url, sid: sid)
|
||||
|
||||
if let index = Defaults[.instances].firstIndex(where: { $0.id == instance.id }) {
|
||||
Defaults[.instances][index].accounts.append(account)
|
||||
}
|
||||
let account = Instance.Account(instanceID: instance.id, name: name, url: instance.url, sid: sid)
|
||||
Defaults[.accounts].append(account)
|
||||
|
||||
return account
|
||||
}
|
||||
|
||||
func removeAccount(instance: Instance, account: Instance.Account) {
|
||||
if let instanceIndex = Defaults[.instances].firstIndex(where: { $0.id == instance.id }) {
|
||||
if let accountIndex = Defaults[.instances][instanceIndex].accounts.firstIndex(where: { $0.id == account.id }) {
|
||||
Defaults[.instances][instanceIndex].accounts.remove(at: accountIndex)
|
||||
}
|
||||
func removeAccount(_ account: Instance.Account) {
|
||||
if let accountIndex = Defaults[.accounts].firstIndex(where: { $0.id == account.id }) {
|
||||
Defaults[.accounts].remove(at: accountIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,9 +80,6 @@
|
||||
375168D62700FAFF008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
|
||||
375168D72700FDB8008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
|
||||
375168D82700FDB9008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
|
||||
375168DB27010806008F96A6 /* String+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D92701070E008F96A6 /* String+Format.swift */; };
|
||||
375168DC27010807008F96A6 /* String+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D92701070E008F96A6 /* String+Format.swift */; };
|
||||
375168DD27010808008F96A6 /* String+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D92701070E008F96A6 /* String+Format.swift */; };
|
||||
375DFB5826F9DA010013F468 /* InstancesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375DFB5726F9DA010013F468 /* InstancesModel.swift */; };
|
||||
375DFB5926F9DA010013F468 /* InstancesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375DFB5726F9DA010013F468 /* InstancesModel.swift */; };
|
||||
375DFB5A26F9DA010013F468 /* InstancesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375DFB5726F9DA010013F468 /* InstancesModel.swift */; };
|
||||
@ -332,7 +329,6 @@
|
||||
37484C2C26FC844700287258 /* InstanceDetailsSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceDetailsSettingsView.swift; sourceTree = "<group>"; };
|
||||
37484C3026FCB8F900287258 /* AccountValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountValidator.swift; sourceTree = "<group>"; };
|
||||
375168D52700FAFF008F96A6 /* Debounce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debounce.swift; sourceTree = "<group>"; };
|
||||
375168D92701070E008F96A6 /* String+Format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Format.swift"; sourceTree = "<group>"; };
|
||||
375DFB5726F9DA010013F468 /* InstancesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesModel.swift; sourceTree = "<group>"; };
|
||||
3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnvironmentValues.swift; sourceTree = "<group>"; };
|
||||
3761AC0E26F0F9A600AA496F /* UnsubscribeAlertModifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnsubscribeAlertModifier.swift; sourceTree = "<group>"; };
|
||||
@ -648,7 +644,6 @@
|
||||
379775922689365600DD52A8 /* Array+Next.swift */,
|
||||
376578842685429C00D4EA09 /* CaseIterable+Next.swift */,
|
||||
37BA794E26DC3E0E002A0235 /* Int+Format.swift */,
|
||||
375168D92701070E008F96A6 /* String+Format.swift */,
|
||||
377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
@ -1079,7 +1074,6 @@
|
||||
37BA793F26DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
|
||||
37754C9D26B7500000DBD602 /* VideosView.swift in Sources */,
|
||||
37C194C726F6A9C8005D3B96 /* RecentsModel.swift in Sources */,
|
||||
375168DD27010808008F96A6 /* String+Format.swift in Sources */,
|
||||
37484C1926FC837400287258 /* PlaybackSettingsView.swift in Sources */,
|
||||
3711403F26B206A6005B3555 /* SearchModel.swift in Sources */,
|
||||
37F64FE426FE70A60081B69E /* RedrawOnViewModifier.swift in Sources */,
|
||||
@ -1214,7 +1208,6 @@
|
||||
37754C9E26B7500000DBD602 /* VideosView.swift in Sources */,
|
||||
3797758C2689345500DD52A8 /* Store.swift in Sources */,
|
||||
37141674267A8E10006CA35D /* Country.swift in Sources */,
|
||||
375168DC27010807008F96A6 /* String+Format.swift in Sources */,
|
||||
37AAF2A126741C97007FC770 /* SubscriptionsView.swift in Sources */,
|
||||
37BA794C26DC30EC002A0235 /* AppSidebarPlaylists.swift in Sources */,
|
||||
37D4B19826717E1500C925CA /* Video.swift in Sources */,
|
||||
@ -1295,7 +1288,6 @@
|
||||
37AAF27E26737323007FC770 /* PopularView.swift in Sources */,
|
||||
37A9966026D6F9B9006E3224 /* WatchNowView.swift in Sources */,
|
||||
37484C1F26FC83A400287258 /* InstancesSettingsView.swift in Sources */,
|
||||
375168DB27010806008F96A6 /* String+Format.swift in Sources */,
|
||||
37AAF29A26740A01007FC770 /* VideosListView.swift in Sources */,
|
||||
37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
|
||||
376578932685490700D4EA09 /* PlaylistsView.swift in Sources */,
|
||||
|
@ -6,6 +6,7 @@ extension Defaults.Keys {
|
||||
#endif
|
||||
|
||||
static let instances = Key<[Instance]>("instances", default: [])
|
||||
static let accounts = Key<[Instance.Account]>("accounts", default: [])
|
||||
|
||||
static let selectedPlaylistID = Key<String?>("selectedPlaylistID")
|
||||
static let showingAddToPlaylist = Key<Bool>("showingAddToPlaylist", default: false)
|
||||
|
@ -2,6 +2,7 @@ import Defaults
|
||||
import SwiftUI
|
||||
|
||||
struct AccountsMenuView: View {
|
||||
@EnvironmentObject<InstancesModel> private var instancesModel
|
||||
@EnvironmentObject<InvidiousAPI> private var api
|
||||
|
||||
@Default(.instances) private var instances
|
||||
@ -13,7 +14,7 @@ struct AccountsMenuView: View {
|
||||
api.setAccount(instance.anonymousAccount)
|
||||
}
|
||||
|
||||
ForEach(instance.accounts) { account in
|
||||
ForEach(instancesModel.accounts(instance.id)) { account in
|
||||
Button(accountButtonTitle(instance: instance, account: account)) {
|
||||
api.setAccount(account)
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ struct AccountFormView: View {
|
||||
return
|
||||
}
|
||||
|
||||
let account = instances.addAccount(instance: instance, name: name.serializationSafe, sid: sid)
|
||||
let account = instances.addAccount(instance: instance, name: name, sid: sid)
|
||||
selectedAccount?.wrappedValue = account
|
||||
|
||||
dismiss()
|
||||
@ -120,7 +120,7 @@ struct AccountFormView: View {
|
||||
private var validator: AccountValidator {
|
||||
AccountValidator(
|
||||
url: instance.url,
|
||||
account: Instance.Account(url: instance.url, sid: sid),
|
||||
account: Instance.Account(instanceID: instance.id, url: instance.url, sid: sid),
|
||||
id: $sid,
|
||||
valid: $valid,
|
||||
validated: $validated
|
||||
|
@ -12,6 +12,7 @@ struct AccountSettingsView: View {
|
||||
var body: some View {
|
||||
HStack {
|
||||
Text(account.description)
|
||||
|
||||
Spacer()
|
||||
|
||||
HStack {
|
||||
@ -23,7 +24,7 @@ struct AccountSettingsView: View {
|
||||
isPresented: $presentingRemovalConfirmationDialog
|
||||
) {
|
||||
Button("Remove", role: .destructive) {
|
||||
instances.removeAccount(instance: instance, account: account)
|
||||
instances.removeAccount(account)
|
||||
}
|
||||
}
|
||||
#if os(macOS)
|
||||
|
@ -15,12 +15,12 @@ struct InstanceDetailsSettingsView: View {
|
||||
var body: some View {
|
||||
List {
|
||||
Section(header: Text("Accounts")) {
|
||||
ForEach(instance.accounts) { account in
|
||||
ForEach(instances.accounts(instanceID)) { account in
|
||||
Text(account.description)
|
||||
#if !os(tvOS)
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
|
||||
Button("Remove", role: .destructive) {
|
||||
instances.removeAccount(instance: instance, account: account)
|
||||
instances.removeAccount(account)
|
||||
accountsChanged.toggle()
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,14 @@ struct InstancesSettingsView: View {
|
||||
instancesModel.find(selectedInstanceID)
|
||||
}
|
||||
|
||||
var accounts: [Instance.Account] {
|
||||
guard selectedInstance != nil else {
|
||||
return []
|
||||
}
|
||||
|
||||
return instancesModel.accounts(selectedInstanceID)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
#if os(iOS)
|
||||
@ -74,16 +82,17 @@ struct InstancesSettingsView: View {
|
||||
}
|
||||
|
||||
if let instance = selectedInstance {
|
||||
if instance.accounts.isEmpty {
|
||||
if accounts.isEmpty {
|
||||
Text("You have no accounts for this instance")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
} else {
|
||||
Text("Accounts")
|
||||
List(selection: $selectedAccount) {
|
||||
ForEach(instance.accounts) { account in
|
||||
ForEach(accounts) { account in
|
||||
AccountSettingsView(instance: instance, account: account,
|
||||
selectedAccount: $selectedAccount)
|
||||
.tag(account)
|
||||
}
|
||||
}
|
||||
#if os(macOS)
|
||||
|
Loading…
Reference in New Issue
Block a user