mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-25 09:41:02 +00:00
Followed Tags + Lists tab. + sidebar customization
This commit is contained in:
parent
6246d7b0a5
commit
0da8228e61
10 changed files with 329 additions and 18 deletions
|
@ -83,6 +83,7 @@
|
|||
9FB143D22983104A00A27BB1 /* glass.wav in Resources */ = {isa = PBXBuildFile; fileRef = 9F2A542D296B1CC0009B2D7C /* glass.wav */; };
|
||||
9FB183222AE9268800BBB692 /* IceCubesApp+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB183212AE9268800BBB692 /* IceCubesApp+Menu.swift */; };
|
||||
9FB183292AE9449100BBB692 /* IceCubesApp+Scene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB183282AE9449100BBB692 /* IceCubesApp+Scene.swift */; };
|
||||
9FBA1D352B4E9B7E00ADB568 /* SidebarEntriesSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBA1D342B4E9B7E00ADB568 /* SidebarEntriesSettingsView.swift */; };
|
||||
9FBFE63D292A715500C250E9 /* IceCubesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBFE63C292A715500C250E9 /* IceCubesApp.swift */; };
|
||||
9FBFE64E292A72BD00C250E9 /* Network in Frameworks */ = {isa = PBXBuildFile; productRef = 9FBFE64D292A72BD00C250E9 /* Network */; };
|
||||
9FC14EF22B494D180006CEE1 /* TagsGroupSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC14EF12B494D180006CEE1 /* TagsGroupSettingView.swift */; };
|
||||
|
@ -227,6 +228,7 @@
|
|||
9FAE4ACA293783B000772766 /* SettingsTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTab.swift; sourceTree = "<group>"; };
|
||||
9FB183212AE9268800BBB692 /* IceCubesApp+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IceCubesApp+Menu.swift"; sourceTree = "<group>"; };
|
||||
9FB183282AE9449100BBB692 /* IceCubesApp+Scene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IceCubesApp+Scene.swift"; sourceTree = "<group>"; };
|
||||
9FBA1D342B4E9B7E00ADB568 /* SidebarEntriesSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarEntriesSettingsView.swift; sourceTree = "<group>"; };
|
||||
9FBFE639292A715500C250E9 /* Ice Cubes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Ice Cubes.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9FBFE63C292A715500C250E9 /* IceCubesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IceCubesApp.swift; sourceTree = "<group>"; };
|
||||
9FC14EF12B494D180006CEE1 /* TagsGroupSettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsGroupSettingView.swift; sourceTree = "<group>"; };
|
||||
|
@ -531,6 +533,7 @@
|
|||
9FC14EF12B494D180006CEE1 /* TagsGroupSettingView.swift */,
|
||||
9FC14EF32B494D940006CEE1 /* RemoteTimelinesSettingView.swift */,
|
||||
9FC14EF52B494DFF0006CEE1 /* RecenTagsSettingView.swift */,
|
||||
9FBA1D342B4E9B7E00ADB568 /* SidebarEntriesSettingsView.swift */,
|
||||
);
|
||||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
|
@ -852,6 +855,7 @@
|
|||
9F6028562B3F36AE00476078 /* AppView.swift in Sources */,
|
||||
9F55C68D2955968700F94077 /* ExploreTab.swift in Sources */,
|
||||
9F1E8B47298EBCBB00609F80 /* HapticSettingsView.swift in Sources */,
|
||||
9FBA1D352B4E9B7E00ADB568 /* SidebarEntriesSettingsView.swift in Sources */,
|
||||
9F2A5411296A1429009B2D7C /* PushNotificationsView.swift in Sources */,
|
||||
9F6028582B3F3B7600476078 /* ToolbarTab.swift in Sources */,
|
||||
);
|
||||
|
|
|
@ -26,6 +26,7 @@ struct AppView: View {
|
|||
|
||||
@State var popToRootTab: Tab = .other
|
||||
@State var iosTabs = iOSTabs.shared
|
||||
@State var sidebarTabs = SidebarTabs.shared
|
||||
|
||||
var body: some View {
|
||||
#if os(visionOS)
|
||||
|
@ -40,10 +41,15 @@ struct AppView: View {
|
|||
}
|
||||
|
||||
var availableTabs: [Tab] {
|
||||
if UIDevice.current.userInterfaceIdiom == .phone || horizontalSizeClass == .compact {
|
||||
return appAccountsManager.currentClient.isAuth ? iosTabs.tabs : Tab.loggedOutTab()
|
||||
guard appAccountsManager.currentClient.isAuth else {
|
||||
return Tab.loggedOutTab()
|
||||
}
|
||||
return appAccountsManager.currentClient.isAuth ? Tab.loggedInTabs() : Tab.loggedOutTab()
|
||||
if UIDevice.current.userInterfaceIdiom == .phone || horizontalSizeClass == .compact {
|
||||
return iosTabs.tabs
|
||||
} else if UIDevice.current.userInterfaceIdiom == .vision {
|
||||
return Tab.visionOSTab()
|
||||
}
|
||||
return sidebarTabs.tabs.map{ $0.tab }
|
||||
}
|
||||
|
||||
var tabBarView: some View {
|
||||
|
|
|
@ -17,11 +17,13 @@ struct SideBarView<Content: View>: View {
|
|||
@Environment(StreamWatcher.self) private var watcher
|
||||
@Environment(UserPreferences.self) private var userPreferences
|
||||
@Environment(RouterPath.self) private var routerPath
|
||||
|
||||
|
||||
@Binding var selectedTab: Tab
|
||||
@Binding var popToRootTab: Tab
|
||||
var tabs: [Tab]
|
||||
@ViewBuilder var content: () -> Content
|
||||
|
||||
@State private var sidebarTabs = SidebarTabs.shared
|
||||
|
||||
private func badgeFor(tab: Tab) -> Int {
|
||||
if tab == .notifications, selectedTab != tab,
|
||||
|
@ -107,7 +109,7 @@ struct SideBarView<Content: View>: View {
|
|||
|
||||
private var tabsView: some View {
|
||||
ForEach(tabs) { tab in
|
||||
if tab != .profile {
|
||||
if tab != .profile && sidebarTabs.isEnabled(tab) {
|
||||
Button {
|
||||
// ensure keyboard is always dismissed when selecting a tab
|
||||
hideKeyboard()
|
||||
|
|
|
@ -164,6 +164,10 @@ struct SettingsTabs: View {
|
|||
NavigationLink(destination: TabbarEntriesSettingsView()) {
|
||||
Label("settings.general.tabbarEntries", systemImage: "platter.filled.bottom.iphone")
|
||||
}
|
||||
} else if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
NavigationLink(destination: SidebarEntriesSettingsView()) {
|
||||
Label("settings.general.sidebarEntries", systemImage: "sidebar.squares.leading")
|
||||
}
|
||||
}
|
||||
NavigationLink(destination: TranslationSettingsView()) {
|
||||
Label("settings.general.translate", systemImage: "captions.bubble")
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import DesignSystem
|
||||
import Env
|
||||
import SwiftUI
|
||||
|
||||
struct SidebarEntriesSettingsView: View {
|
||||
@Environment(Theme.self) private var theme
|
||||
@Environment(UserPreferences.self) private var userPreferences
|
||||
|
||||
@State private var sidebarTabs = SidebarTabs.shared
|
||||
|
||||
var body: some View {
|
||||
@Bindable var userPreferences = userPreferences
|
||||
Form {
|
||||
Section {
|
||||
ForEach($sidebarTabs.tabs, id: \.tab) { $tab in
|
||||
if tab.tab != .profile && tab.tab != .settings {
|
||||
Toggle(isOn: $tab.enabled) {
|
||||
tab.tab.label
|
||||
}
|
||||
}
|
||||
}
|
||||
.onMove(perform: move)
|
||||
}
|
||||
#if !os(visionOS)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
#endif
|
||||
}
|
||||
.environment(\.editMode, .constant(.active))
|
||||
.navigationTitle("settings.general.sidebarEntries")
|
||||
#if !os(visionOS)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(theme.secondaryBackgroundColor)
|
||||
#endif
|
||||
}
|
||||
|
||||
func move(from source: IndexSet, to destination: Int) {
|
||||
sidebarTabs.tabs.move(fromOffsets: source, toOffset: destination)
|
||||
}
|
||||
}
|
|
@ -6,13 +6,15 @@ import StatusKit
|
|||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
enum Tab: Int, Identifiable, Hashable, CaseIterable {
|
||||
enum Tab: Int, Identifiable, Hashable, CaseIterable, Codable {
|
||||
case timeline, notifications, mentions, explore, messages, settings, other
|
||||
case trending, federated, local
|
||||
case profile
|
||||
case bookmarks
|
||||
case favorites
|
||||
case post
|
||||
case followedTags
|
||||
case lists
|
||||
|
||||
nonisolated var id: Int {
|
||||
rawValue
|
||||
|
@ -21,16 +23,9 @@ enum Tab: Int, Identifiable, Hashable, CaseIterable {
|
|||
static func loggedOutTab() -> [Tab] {
|
||||
[.timeline, .settings]
|
||||
}
|
||||
|
||||
static func loggedInTabs() -> [Tab] {
|
||||
if UIDevice.current.userInterfaceIdiom == .pad ||
|
||||
UIDevice.current.userInterfaceIdiom == .mac {
|
||||
[.timeline, .trending, .federated, .local, .notifications, .mentions, .explore, .messages, .bookmarks, .favorites, .profile, .settings]
|
||||
} else if UIDevice.current.userInterfaceIdiom == .vision {
|
||||
[.profile, .timeline, .notifications, .mentions, .explore, .messages, .settings]
|
||||
} else {
|
||||
[.timeline, .notifications, .explore, .messages, .profile]
|
||||
}
|
||||
|
||||
static func visionOSTab() -> [Tab] {
|
||||
[.profile, .timeline, .notifications, .mentions, .explore, .messages, .settings]
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
@ -64,6 +59,14 @@ enum Tab: Int, Identifiable, Hashable, CaseIterable {
|
|||
NavigationTab {
|
||||
AccountStatusesListView(mode: .favorites)
|
||||
}
|
||||
case .followedTags:
|
||||
NavigationTab {
|
||||
FollowedTagsListView()
|
||||
}
|
||||
case .lists:
|
||||
NavigationTab {
|
||||
ListsListView()
|
||||
}
|
||||
case .post:
|
||||
VStack { }
|
||||
case .other:
|
||||
|
@ -100,6 +103,10 @@ enum Tab: Int, Identifiable, Hashable, CaseIterable {
|
|||
Label("accessibility.tabs.profile.picker.favorites", systemImage: iconName)
|
||||
case .post:
|
||||
Label("menu.new-post", systemImage: iconName)
|
||||
case .followedTags:
|
||||
Label("timeline.filter.tags", systemImage: iconName)
|
||||
case .lists:
|
||||
Label("timeline.filter.lists", systemImage: iconName)
|
||||
case .other:
|
||||
EmptyView()
|
||||
|
||||
|
@ -134,12 +141,61 @@ enum Tab: Int, Identifiable, Hashable, CaseIterable {
|
|||
"star"
|
||||
case .post:
|
||||
"square.and.pencil"
|
||||
case .followedTags:
|
||||
"tag"
|
||||
case .lists:
|
||||
"list.bullet"
|
||||
case .other:
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Observable
|
||||
class SidebarTabs {
|
||||
struct SidedebarTab: Hashable, Codable {
|
||||
let tab: Tab
|
||||
var enabled: Bool
|
||||
}
|
||||
|
||||
class Storage {
|
||||
@AppStorage("sidebar_tabs") var tabs: [SidedebarTab] = [
|
||||
.init(tab: .timeline, enabled: true),
|
||||
.init(tab: .trending, enabled: true),
|
||||
.init(tab: .federated, enabled: true),
|
||||
.init(tab: .local, enabled: true),
|
||||
.init(tab: .notifications, enabled: true),
|
||||
.init(tab: .mentions, enabled: true),
|
||||
.init(tab: .messages, enabled: true),
|
||||
.init(tab: .explore, enabled: true),
|
||||
.init(tab: .bookmarks, enabled: true),
|
||||
.init(tab: .favorites, enabled: true),
|
||||
.init(tab: .followedTags, enabled: true),
|
||||
.init(tab: .lists, enabled: true),
|
||||
|
||||
.init(tab: .settings, enabled: true),
|
||||
.init(tab: .profile, enabled: true),
|
||||
]
|
||||
}
|
||||
|
||||
private let storage = Storage()
|
||||
public static let shared = SidebarTabs()
|
||||
|
||||
var tabs: [SidedebarTab] {
|
||||
didSet {
|
||||
storage.tabs = tabs
|
||||
}
|
||||
}
|
||||
|
||||
func isEnabled(_ tab: Tab) -> Bool {
|
||||
tabs.first(where: { $0.tab.id == tab.id })?.enabled == true
|
||||
}
|
||||
|
||||
private init() {
|
||||
tabs = storage.tabs
|
||||
}
|
||||
}
|
||||
|
||||
@Observable
|
||||
class iOSTabs {
|
||||
enum TabEntries: String {
|
||||
|
|
|
@ -47558,6 +47558,125 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"settings.general.sidebarEntries" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"be" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"en-GB" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"eu" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"nb" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Sidebar Customizations"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings.general.swipeactions" : {
|
||||
"localizations" : {
|
||||
"be" : {
|
||||
|
@ -75118,4 +75237,4 @@
|
|||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
}
|
46
Packages/Account/Sources/Account/Lists/ListsListView.swift
Normal file
46
Packages/Account/Sources/Account/Lists/ListsListView.swift
Normal file
|
@ -0,0 +1,46 @@
|
|||
import DesignSystem
|
||||
import Models
|
||||
import SwiftUI
|
||||
import Env
|
||||
|
||||
public struct ListsListView: View {
|
||||
@Environment(CurrentAccount.self) private var currentAccount
|
||||
@Environment(Theme.self) private var theme
|
||||
|
||||
public init() {}
|
||||
|
||||
public var body: some View {
|
||||
List {
|
||||
ForEach(currentAccount.lists) { list in
|
||||
NavigationLink(value: RouterDestination.list(list: list)) {
|
||||
Text(list.title)
|
||||
.font(.scaledHeadline)
|
||||
}
|
||||
#if !os(visionOS)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
#endif
|
||||
}
|
||||
.onDelete { index in
|
||||
if let index = index.first {
|
||||
Task {
|
||||
await currentAccount.deleteList(currentAccount.lists[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.task {
|
||||
await currentAccount.fetchLists()
|
||||
}
|
||||
.refreshable {
|
||||
await currentAccount.fetchLists()
|
||||
}
|
||||
#if !os(visionOS)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(theme.secondaryBackgroundColor)
|
||||
#endif
|
||||
.listStyle(.plain)
|
||||
.navigationTitle("timeline.filter.lists")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import DesignSystem
|
||||
import Models
|
||||
import SwiftUI
|
||||
import Env
|
||||
|
||||
public struct FollowedTagsListView: View {
|
||||
@Environment(CurrentAccount.self) private var currentAccount
|
||||
@Environment(Theme.self) private var theme
|
||||
|
||||
public init() {}
|
||||
|
||||
public var body: some View {
|
||||
List(currentAccount.tags) { tag in
|
||||
TagRowView(tag: tag)
|
||||
#if !os(visionOS)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
#endif
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
.task {
|
||||
await currentAccount.fetchFollowedTags()
|
||||
}
|
||||
.refreshable {
|
||||
await currentAccount.fetchFollowedTags()
|
||||
}
|
||||
#if !os(visionOS)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(theme.secondaryBackgroundColor)
|
||||
#endif
|
||||
.listStyle(.plain)
|
||||
.navigationTitle("timeline.filter.tags")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
|
|
@ -88,7 +88,7 @@ import Observation
|
|||
}
|
||||
}
|
||||
|
||||
public func deleteList(list: Models.List) async {
|
||||
public func deleteList(_ list: Models.List) async {
|
||||
guard let client else { return }
|
||||
lists.removeAll(where: { $0.id == list.id })
|
||||
let response = try? await client.delete(endpoint: Lists.list(id: list.id))
|
||||
|
|
Loading…
Reference in a new issue