mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-26 18:21:00 +00:00
Migrate TagGroup to SwiftData
This commit is contained in:
parent
527d982dce
commit
4870b202d6
10 changed files with 120 additions and 96 deletions
|
@ -31,9 +31,15 @@ extension View {
|
||||||
case let .conversationDetail(conversation):
|
case let .conversationDetail(conversation):
|
||||||
ConversationDetailView(conversation: conversation)
|
ConversationDetailView(conversation: conversation)
|
||||||
case let .hashTag(tag, accountId):
|
case let .hashTag(tag, accountId):
|
||||||
TimelineView(timeline: .constant(.hashtag(tag: tag, accountId: accountId)), scrollToTopSignal: .constant(0), canFilterTimeline: false)
|
TimelineView(timeline: .constant(.hashtag(tag: tag, accountId: accountId)),
|
||||||
|
selectedTagGroup: .constant(nil),
|
||||||
|
scrollToTopSignal: .constant(0),
|
||||||
|
canFilterTimeline: false)
|
||||||
case let .list(list):
|
case let .list(list):
|
||||||
TimelineView(timeline: .constant(.list(list: list)), scrollToTopSignal: .constant(0), canFilterTimeline: false)
|
TimelineView(timeline: .constant(.list(list: list)),
|
||||||
|
selectedTagGroup: .constant(nil),
|
||||||
|
scrollToTopSignal: .constant(0),
|
||||||
|
canFilterTimeline: false)
|
||||||
case let .following(id):
|
case let .following(id):
|
||||||
AccountsListView(mode: .following(accountId: id))
|
AccountsListView(mode: .following(accountId: id))
|
||||||
case let .followers(id):
|
case let .followers(id):
|
||||||
|
@ -45,7 +51,10 @@ extension View {
|
||||||
case let .accountsList(accounts):
|
case let .accountsList(accounts):
|
||||||
AccountsListView(mode: .accountsList(accounts: accounts))
|
AccountsListView(mode: .accountsList(accounts: accounts))
|
||||||
case .trendingTimeline:
|
case .trendingTimeline:
|
||||||
TimelineView(timeline: .constant(.trending), scrollToTopSignal: .constant(0), canFilterTimeline: false)
|
TimelineView(timeline: .constant(.trending),
|
||||||
|
selectedTagGroup: .constant(nil),
|
||||||
|
scrollToTopSignal: .constant(0),
|
||||||
|
canFilterTimeline: false)
|
||||||
case let .tagsList(tags):
|
case let .tagsList(tags):
|
||||||
TagsListView(tags: tags)
|
TagsListView(tags: tags)
|
||||||
}
|
}
|
||||||
|
@ -125,6 +134,7 @@ extension View {
|
||||||
modelContainer(for: [
|
modelContainer(for: [
|
||||||
Draft.self,
|
Draft.self,
|
||||||
LocalTimeline.self,
|
LocalTimeline.self,
|
||||||
|
TagGroup.self
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ struct SettingsTabs: View {
|
||||||
@Binding var popToRootTab: Tab
|
@Binding var popToRootTab: Tab
|
||||||
|
|
||||||
@Query(sort: \LocalTimeline.creationDate, order: .reverse) var localTimelines: [LocalTimeline]
|
@Query(sort: \LocalTimeline.creationDate, order: .reverse) var localTimelines: [LocalTimeline]
|
||||||
|
@Query(sort: \TagGroup.creationDate, order: .reverse) var tagGroups: [TagGroup]
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack(path: $routerPath.path) {
|
NavigationStack(path: $routerPath.path) {
|
||||||
|
@ -274,20 +275,17 @@ struct SettingsTabs: View {
|
||||||
|
|
||||||
private var tagGroupsView: some View {
|
private var tagGroupsView: some View {
|
||||||
Form {
|
Form {
|
||||||
ForEach(preferences.tagGroups, id: \.self) { group in
|
ForEach(tagGroups) { group in
|
||||||
Label(group.title, systemImage: group.sfSymbolName)
|
Label(group.title, systemImage: group.symbolName)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
routerPath.presentedSheet = .editTagGroup(tagGroup: group, onSaved: nil)
|
routerPath.presentedSheet = .editTagGroup(tagGroup: group, onSaved: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDelete { indexes in
|
.onDelete { indexes in
|
||||||
if let index = indexes.first {
|
if let index = indexes.first {
|
||||||
_ = preferences.tagGroups.remove(at: index)
|
context.delete(tagGroups[index])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onMove { source, destination in
|
|
||||||
preferences.tagGroups.move(fromOffsets: source, toOffset: destination)
|
|
||||||
}
|
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
|
|
|
@ -10,8 +10,8 @@ import SwiftUI
|
||||||
@MainActor
|
@MainActor
|
||||||
struct EditTagGroupView: View {
|
struct EditTagGroupView: View {
|
||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
@Environment(\.modelContext) private var context
|
||||||
|
|
||||||
@Environment(UserPreferences.self) private var preferences
|
|
||||||
@Environment(Theme.self) private var theme
|
@Environment(Theme.self) private var theme
|
||||||
|
|
||||||
@State private var title: String = ""
|
@State private var title: String = ""
|
||||||
|
@ -22,7 +22,7 @@ struct EditTagGroupView: View {
|
||||||
|
|
||||||
private var editingTagGroup: TagGroup?
|
private var editingTagGroup: TagGroup?
|
||||||
private var onSaved: ((TagGroup) -> Void)?
|
private var onSaved: ((TagGroup) -> Void)?
|
||||||
|
|
||||||
private var canSave: Bool {
|
private var canSave: Bool {
|
||||||
!title.isEmpty &&
|
!title.isEmpty &&
|
||||||
// At least have 2 tags, one main and one additional.
|
// At least have 2 tags, one main and one additional.
|
||||||
|
@ -70,7 +70,7 @@ struct EditTagGroupView: View {
|
||||||
focusedField = .title
|
focusedField = .title
|
||||||
if let editingTagGroup {
|
if let editingTagGroup {
|
||||||
title = editingTagGroup.title
|
title = editingTagGroup.title
|
||||||
sfSymbolName = editingTagGroup.sfSymbolName
|
sfSymbolName = editingTagGroup.symbolName
|
||||||
tags = editingTagGroup.tags
|
tags = editingTagGroup.tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,25 +162,20 @@ struct EditTagGroupView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func save() {
|
private func save() {
|
||||||
var toSave = tags
|
if let editingTagGroup {
|
||||||
let main = toSave.removeFirst()
|
editingTagGroup.title = title
|
||||||
|
editingTagGroup.symbolName = sfSymbolName
|
||||||
let tagGroup: TagGroup = .init(
|
editingTagGroup.tags = tags
|
||||||
title: title.trimmingCharacters(in: .whitespaces),
|
onSaved?(editingTagGroup)
|
||||||
sfSymbolName: sfSymbolName,
|
|
||||||
main: main,
|
|
||||||
additional: toSave
|
|
||||||
)
|
|
||||||
if let editingTagGroup,
|
|
||||||
let index = preferences.tagGroups.firstIndex(of: editingTagGroup)
|
|
||||||
{
|
|
||||||
preferences.tagGroups[index] = tagGroup
|
|
||||||
} else {
|
} else {
|
||||||
preferences.tagGroups.append(tagGroup)
|
let tagGroup = TagGroup(title: title.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||||
|
symbolName: sfSymbolName,
|
||||||
|
tags: tags)
|
||||||
|
context.insert(tagGroup)
|
||||||
|
onSaved?(tagGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
dismiss()
|
dismiss()
|
||||||
onSaved?(tagGroup)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
|
|
|
@ -22,12 +22,15 @@ struct TimelineTab: View {
|
||||||
|
|
||||||
@State private var didAppear: Bool = false
|
@State private var didAppear: Bool = false
|
||||||
@State private var timeline: TimelineFilter = .home
|
@State private var timeline: TimelineFilter = .home
|
||||||
|
@State private var selectedTagGroup: TagGroup?
|
||||||
@State private var scrollToTopSignal: Int = 0
|
@State private var scrollToTopSignal: Int = 0
|
||||||
|
|
||||||
@Query(sort: \LocalTimeline.creationDate, order: .reverse) var localTimelines: [LocalTimeline]
|
@Query(sort: \LocalTimeline.creationDate, order: .reverse) var localTimelines: [LocalTimeline]
|
||||||
|
@Query(sort: \TagGroup.creationDate, order: .reverse) var tagGroups: [TagGroup]
|
||||||
@AppStorage("remote_local_timeline") var legacyLocalTimelines: [String] = []
|
@AppStorage("remote_local_timeline") var legacyLocalTimelines: [String] = []
|
||||||
@AppStorage("last_timeline_filter") var lastTimelineFilter: TimelineFilter = .home
|
@AppStorage("tag_groups") var legacyTagGroups: [LegacyTagGroup] = []
|
||||||
|
|
||||||
|
@AppStorage("last_timeline_filter") var lastTimelineFilter: TimelineFilter = .home
|
||||||
|
|
||||||
private let canFilterTimeline: Bool
|
private let canFilterTimeline: Bool
|
||||||
|
|
||||||
|
@ -39,7 +42,10 @@ struct TimelineTab: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack(path: $routerPath.path) {
|
NavigationStack(path: $routerPath.path) {
|
||||||
TimelineView(timeline: $timeline, scrollToTopSignal: $scrollToTopSignal, canFilterTimeline: canFilterTimeline)
|
TimelineView(timeline: $timeline,
|
||||||
|
selectedTagGroup: $selectedTagGroup,
|
||||||
|
scrollToTopSignal: $scrollToTopSignal,
|
||||||
|
canFilterTimeline: canFilterTimeline)
|
||||||
.withAppRouter()
|
.withAppRouter()
|
||||||
.withSheetDestinations(sheetDestinations: $routerPath.presentedSheet)
|
.withSheetDestinations(sheetDestinations: $routerPath.presentedSheet)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
|
@ -50,6 +56,7 @@ struct TimelineTab: View {
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
migrateUserPreferencesTimeline()
|
migrateUserPreferencesTimeline()
|
||||||
|
migrateUserPreferencesTagGroups()
|
||||||
routerPath.client = client
|
routerPath.client = client
|
||||||
if !didAppear, canFilterTimeline {
|
if !didAppear, canFilterTimeline {
|
||||||
didAppear = true
|
didAppear = true
|
||||||
|
@ -153,12 +160,13 @@ struct TimelineTab: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu("timeline.filter.tag-groups") {
|
Menu("timeline.filter.tag-groups") {
|
||||||
ForEach(preferences.tagGroups, id: \.self) { group in
|
ForEach(tagGroups) { group in
|
||||||
Button {
|
Button {
|
||||||
timeline = .tagGroup(group)
|
selectedTagGroup = group
|
||||||
|
timeline = .tagGroup(title: group.title, tags: group.tags)
|
||||||
} label: {
|
} label: {
|
||||||
VStack {
|
VStack {
|
||||||
let icon = group.sfSymbolName.isEmpty ? "number" : group.sfSymbolName
|
let icon = group.symbolName.isEmpty ? "number" : group.symbolName
|
||||||
Label(group.title, systemImage: icon)
|
Label(group.title, systemImage: icon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,4 +257,11 @@ struct TimelineTab: View {
|
||||||
}
|
}
|
||||||
legacyLocalTimelines = []
|
legacyLocalTimelines = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func migrateUserPreferencesTagGroups() {
|
||||||
|
for group in legacyTagGroups {
|
||||||
|
context.insert(TagGroup(title: group.title, symbolName: group.sfSymbolName, tags: group.tags))
|
||||||
|
}
|
||||||
|
legacyTagGroups = []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import SwiftUI
|
||||||
@MainActor
|
@MainActor
|
||||||
@Observable public class UserPreferences {
|
@Observable public class UserPreferences {
|
||||||
class Storage {
|
class Storage {
|
||||||
@AppStorage("tag_groups") public var tagGroups: [TagGroup] = []
|
|
||||||
@AppStorage("preferred_browser") public var preferredBrowser: PreferredBrowser = .inAppSafari
|
@AppStorage("preferred_browser") public var preferredBrowser: PreferredBrowser = .inAppSafari
|
||||||
@AppStorage("show_translate_button_inline") public var showTranslateButton: Bool = true
|
@AppStorage("show_translate_button_inline") public var showTranslateButton: Bool = true
|
||||||
@AppStorage("is_open_ai_enabled") public var isOpenAIEnabled: Bool = true
|
@AppStorage("is_open_ai_enabled") public var isOpenAIEnabled: Bool = true
|
||||||
|
@ -62,11 +61,6 @@ import SwiftUI
|
||||||
|
|
||||||
private var client: Client?
|
private var client: Client?
|
||||||
|
|
||||||
public var tagGroups: [TagGroup] {
|
|
||||||
didSet {
|
|
||||||
storage.tagGroups = tagGroups
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public var preferredBrowser: PreferredBrowser {
|
public var preferredBrowser: PreferredBrowser {
|
||||||
didSet {
|
didSet {
|
||||||
storage.preferredBrowser = preferredBrowser
|
storage.preferredBrowser = preferredBrowser
|
||||||
|
@ -365,7 +359,6 @@ import SwiftUI
|
||||||
}
|
}
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
tagGroups = storage.tagGroups
|
|
||||||
preferredBrowser = storage.preferredBrowser
|
preferredBrowser = storage.preferredBrowser
|
||||||
showTranslateButton = storage.showTranslateButton
|
showTranslateButton = storage.showTranslateButton
|
||||||
isOpenAIEnabled = storage.isOpenAIEnabled
|
isOpenAIEnabled = storage.isOpenAIEnabled
|
||||||
|
|
28
Packages/Models/Sources/Models/SwiftData/TagGroup.swift
Normal file
28
Packages/Models/Sources/Models/SwiftData/TagGroup.swift
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import SwiftData
|
||||||
|
import SwiftUI
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@Model public class TagGroup: Equatable {
|
||||||
|
public var title: String
|
||||||
|
public var symbolName: String
|
||||||
|
public var tags: [String]
|
||||||
|
public var creationDate: Date
|
||||||
|
|
||||||
|
public init(title: String, symbolName: String, tags: [String]) {
|
||||||
|
self.title = title
|
||||||
|
self.symbolName = symbolName
|
||||||
|
self.tags = tags
|
||||||
|
self.creationDate = Date()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct LegacyTagGroup: Codable, Equatable, Hashable {
|
||||||
|
public let title: String
|
||||||
|
public let sfSymbolName: String
|
||||||
|
public let main: String
|
||||||
|
public let additional: [String]
|
||||||
|
|
||||||
|
public var tags: [String] {
|
||||||
|
[main] + additional
|
||||||
|
}
|
||||||
|
}
|
|
@ -63,20 +63,3 @@ extension Tag: Sendable {}
|
||||||
extension Tag.History: Sendable {}
|
extension Tag.History: Sendable {}
|
||||||
extension FeaturedTag: Sendable {}
|
extension FeaturedTag: Sendable {}
|
||||||
|
|
||||||
public struct TagGroup: Codable, Equatable, Hashable {
|
|
||||||
public init(title: String, sfSymbolName: String, main: String, additional: [String]) {
|
|
||||||
self.title = title
|
|
||||||
self.sfSymbolName = sfSymbolName
|
|
||||||
self.main = main
|
|
||||||
self.additional = additional
|
|
||||||
}
|
|
||||||
|
|
||||||
public let title: String
|
|
||||||
public let sfSymbolName: String
|
|
||||||
public let main: String
|
|
||||||
public let additional: [String]
|
|
||||||
|
|
||||||
public var tags: [String] {
|
|
||||||
[main] + additional
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ public enum RemoteTimelineFilter: String, CaseIterable, Hashable, Equatable {
|
||||||
public enum TimelineFilter: Hashable, Equatable {
|
public enum TimelineFilter: Hashable, Equatable {
|
||||||
case home, local, federated, trending
|
case home, local, federated, trending
|
||||||
case hashtag(tag: String, accountId: String?)
|
case hashtag(tag: String, accountId: String?)
|
||||||
case tagGroup(TagGroup)
|
case tagGroup(title: String, tags: [String])
|
||||||
case list(list: Models.List)
|
case list(list: Models.List)
|
||||||
case remoteLocal(server: String, filter: RemoteTimelineFilter)
|
case remoteLocal(server: String, filter: RemoteTimelineFilter)
|
||||||
case latest
|
case latest
|
||||||
|
@ -73,8 +73,8 @@ public enum TimelineFilter: Hashable, Equatable {
|
||||||
"Home"
|
"Home"
|
||||||
case let .hashtag(tag, _):
|
case let .hashtag(tag, _):
|
||||||
"#\(tag)"
|
"#\(tag)"
|
||||||
case let .tagGroup(group):
|
case let .tagGroup(title, _):
|
||||||
group.title
|
title
|
||||||
case let .list(list):
|
case let .list(list):
|
||||||
list.title
|
list.title
|
||||||
case let .remoteLocal(server, _):
|
case let .remoteLocal(server, _):
|
||||||
|
@ -96,8 +96,8 @@ public enum TimelineFilter: Hashable, Equatable {
|
||||||
"timeline.home"
|
"timeline.home"
|
||||||
case let .hashtag(tag, _):
|
case let .hashtag(tag, _):
|
||||||
"#\(tag)"
|
"#\(tag)"
|
||||||
case let .tagGroup(group):
|
case let .tagGroup(title, _):
|
||||||
LocalizedStringKey(group.title) // ?? not sure since this can't be localized.
|
LocalizedStringKey(title) // ?? not sure since this can't be localized.
|
||||||
case let .list(list):
|
case let .list(list):
|
||||||
LocalizedStringKey(list.title)
|
LocalizedStringKey(list.title)
|
||||||
case let .remoteLocal(server, _):
|
case let .remoteLocal(server, _):
|
||||||
|
@ -128,29 +128,31 @@ public enum TimelineFilter: Hashable, Equatable {
|
||||||
|
|
||||||
public func endpoint(sinceId: String?, maxId: String?, minId: String?, offset: Int?) -> Endpoint {
|
public func endpoint(sinceId: String?, maxId: String?, minId: String?, offset: Int?) -> Endpoint {
|
||||||
switch self {
|
switch self {
|
||||||
case .federated: Timelines.pub(sinceId: sinceId, maxId: maxId, minId: minId, local: false)
|
case .federated: return Timelines.pub(sinceId: sinceId, maxId: maxId, minId: minId, local: false)
|
||||||
case .local: Timelines.pub(sinceId: sinceId, maxId: maxId, minId: minId, local: true)
|
case .local: return Timelines.pub(sinceId: sinceId, maxId: maxId, minId: minId, local: true)
|
||||||
case let .remoteLocal(_, filter):
|
case let .remoteLocal(_, filter):
|
||||||
switch filter {
|
switch filter {
|
||||||
case .local:
|
case .local:
|
||||||
Timelines.pub(sinceId: sinceId, maxId: maxId, minId: minId, local: true)
|
return Timelines.pub(sinceId: sinceId, maxId: maxId, minId: minId, local: true)
|
||||||
case .federated:
|
case .federated:
|
||||||
Timelines.pub(sinceId: sinceId, maxId: maxId, minId: minId, local: false)
|
return Timelines.pub(sinceId: sinceId, maxId: maxId, minId: minId, local: false)
|
||||||
case .trending:
|
case .trending:
|
||||||
Trends.statuses(offset: offset)
|
return Trends.statuses(offset: offset)
|
||||||
}
|
}
|
||||||
case .latest: Timelines.home(sinceId: nil, maxId: nil, minId: nil)
|
case .latest: return Timelines.home(sinceId: nil, maxId: nil, minId: nil)
|
||||||
case .home: Timelines.home(sinceId: sinceId, maxId: maxId, minId: minId)
|
case .home: return Timelines.home(sinceId: sinceId, maxId: maxId, minId: minId)
|
||||||
case .trending: Trends.statuses(offset: offset)
|
case .trending: return Trends.statuses(offset: offset)
|
||||||
case let .list(list): Timelines.list(listId: list.id, sinceId: sinceId, maxId: maxId, minId: minId)
|
case let .list(list): return Timelines.list(listId: list.id, sinceId: sinceId, maxId: maxId, minId: minId)
|
||||||
case let .hashtag(tag, accountId):
|
case let .hashtag(tag, accountId):
|
||||||
if let accountId {
|
if let accountId {
|
||||||
Accounts.statuses(id: accountId, sinceId: nil, tag: tag, onlyMedia: nil, excludeReplies: nil, pinned: nil)
|
return Accounts.statuses(id: accountId, sinceId: nil, tag: tag, onlyMedia: nil, excludeReplies: nil, pinned: nil)
|
||||||
} else {
|
} else {
|
||||||
Timelines.hashtag(tag: tag, additional: nil, maxId: maxId)
|
return Timelines.hashtag(tag: tag, additional: nil, maxId: maxId)
|
||||||
}
|
}
|
||||||
case let .tagGroup(group):
|
case let .tagGroup(_, tags):
|
||||||
Timelines.hashtag(tag: group.main, additional: group.additional, maxId: maxId)
|
var tags = tags
|
||||||
|
tags.removeFirst()
|
||||||
|
return Timelines.hashtag(tag: tags.first ?? "", additional: tags, maxId: maxId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,8 +191,13 @@ extension TimelineFilter: Codable {
|
||||||
accountId: accountId
|
accountId: accountId
|
||||||
)
|
)
|
||||||
case .tagGroup:
|
case .tagGroup:
|
||||||
let group = try container.decode(TagGroup.self, forKey: .tagGroup)
|
var nestedContainer = try container.nestedUnkeyedContainer(forKey: .hashtag)
|
||||||
self = .tagGroup(group)
|
let title = try nestedContainer.decode(String.self)
|
||||||
|
let tags = try nestedContainer.decode([String].self)
|
||||||
|
self = .tagGroup(
|
||||||
|
title: title,
|
||||||
|
tags: tags
|
||||||
|
)
|
||||||
case .list:
|
case .list:
|
||||||
let list = try container.decode(
|
let list = try container.decode(
|
||||||
Models.List.self,
|
Models.List.self,
|
||||||
|
@ -232,8 +239,10 @@ extension TimelineFilter: Codable {
|
||||||
var nestedContainer = container.nestedUnkeyedContainer(forKey: .hashtag)
|
var nestedContainer = container.nestedUnkeyedContainer(forKey: .hashtag)
|
||||||
try nestedContainer.encode(tag)
|
try nestedContainer.encode(tag)
|
||||||
try nestedContainer.encode(accountId)
|
try nestedContainer.encode(accountId)
|
||||||
case let .tagGroup(group):
|
case let .tagGroup(title, tags):
|
||||||
try container.encode(group, forKey: .tagGroup)
|
var nestedContainer = container.nestedUnkeyedContainer(forKey: .tagGroup)
|
||||||
|
try nestedContainer.encode(title)
|
||||||
|
try nestedContainer.encode(tags)
|
||||||
case let .list(list):
|
case let .list(list):
|
||||||
try container.encode(list, forKey: .list)
|
try container.encode(list, forKey: .list)
|
||||||
case let .remoteLocal(server, filter):
|
case let .remoteLocal(server, filter):
|
||||||
|
|
|
@ -27,11 +27,15 @@ public struct TimelineView: View {
|
||||||
@State private var collectionView: UICollectionView?
|
@State private var collectionView: UICollectionView?
|
||||||
|
|
||||||
@Binding var timeline: TimelineFilter
|
@Binding var timeline: TimelineFilter
|
||||||
|
@Binding var selectedTagGroup: TagGroup?
|
||||||
@Binding var scrollToTopSignal: Int
|
@Binding var scrollToTopSignal: Int
|
||||||
private let canFilterTimeline: Bool
|
private let canFilterTimeline: Bool
|
||||||
|
|
||||||
public init(timeline: Binding<TimelineFilter>, scrollToTopSignal: Binding<Int>, canFilterTimeline: Bool) {
|
public init(timeline: Binding<TimelineFilter>,
|
||||||
|
selectedTagGroup: Binding<TagGroup?>,
|
||||||
|
scrollToTopSignal: Binding<Int>, canFilterTimeline: Bool) {
|
||||||
_timeline = timeline
|
_timeline = timeline
|
||||||
|
_selectedTagGroup = selectedTagGroup
|
||||||
_scrollToTopSignal = scrollToTopSignal
|
_scrollToTopSignal = scrollToTopSignal
|
||||||
self.canFilterTimeline = canFilterTimeline
|
self.canFilterTimeline = canFilterTimeline
|
||||||
}
|
}
|
||||||
|
@ -40,13 +44,9 @@ public struct TimelineView: View {
|
||||||
ScrollViewReader { proxy in
|
ScrollViewReader { proxy in
|
||||||
ZStack(alignment: .top) {
|
ZStack(alignment: .top) {
|
||||||
List {
|
List {
|
||||||
if viewModel.tagGroup != nil {
|
scrollToTopView
|
||||||
tagGroupHeaderView
|
tagGroupHeaderView
|
||||||
} else if viewModel.tag == nil {
|
tagHeaderView
|
||||||
scrollToTopView
|
|
||||||
} else {
|
|
||||||
tagHeaderView
|
|
||||||
}
|
|
||||||
switch viewModel.timeline {
|
switch viewModel.timeline {
|
||||||
case .remoteLocal:
|
case .remoteLocal:
|
||||||
StatusesListView(fetcher: viewModel, client: client, routerPath: routerPath, isRemote: true)
|
StatusesListView(fetcher: viewModel, client: client, routerPath: routerPath, isRemote: true)
|
||||||
|
@ -211,7 +211,7 @@ public struct TimelineView: View {
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var tagGroupHeaderView: some View {
|
private var tagGroupHeaderView: some View {
|
||||||
if let group = viewModel.tagGroup {
|
if let group = selectedTagGroup {
|
||||||
headerView {
|
headerView {
|
||||||
HStack {
|
HStack {
|
||||||
ScrollView(.horizontal) {
|
ScrollView(.horizontal) {
|
||||||
|
@ -230,7 +230,7 @@ public struct TimelineView: View {
|
||||||
.scrollIndicators(.hidden)
|
.scrollIndicators(.hidden)
|
||||||
Button("status.action.edit") {
|
Button("status.action.edit") {
|
||||||
routerPath.presentedSheet = .editTagGroup(tagGroup: group, onSaved: { group in
|
routerPath.presentedSheet = .editTagGroup(tagGroup: group, onSaved: { group in
|
||||||
viewModel.timeline = .tagGroup(group)
|
viewModel.timeline = .tagGroup(title: group.title, tags: group.tags)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
.buttonStyle(.bordered)
|
.buttonStyle(.bordered)
|
||||||
|
|
|
@ -42,13 +42,6 @@ import SwiftUI
|
||||||
|
|
||||||
var tag: Tag?
|
var tag: Tag?
|
||||||
|
|
||||||
var tagGroup: TagGroup? {
|
|
||||||
if case let .tagGroup(group) = timeline {
|
|
||||||
return group
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal source of truth for a timeline.
|
// Internal source of truth for a timeline.
|
||||||
private var datasource = TimelineDatasource()
|
private var datasource = TimelineDatasource()
|
||||||
private let cache = TimelineCache()
|
private let cache = TimelineCache()
|
||||||
|
|
Loading…
Reference in a new issue