mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-01-11 16:45:27 +00:00
Tabbar fix + bump to iOS 18
This commit is contained in:
parent
1008008b9b
commit
3ec5c8c676
23 changed files with 118 additions and 86 deletions
|
@ -1187,7 +1187,7 @@
|
||||||
INFOPLIST_FILE = IceCubesNotifications/Info.plist;
|
INFOPLIST_FILE = IceCubesNotifications/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = IceCubesNotifications;
|
INFOPLIST_KEY_CFBundleDisplayName = IceCubesNotifications;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
@ -1222,7 +1222,7 @@
|
||||||
INFOPLIST_FILE = IceCubesNotifications/Info.plist;
|
INFOPLIST_FILE = IceCubesNotifications/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = IceCubesNotifications;
|
INFOPLIST_KEY_CFBundleDisplayName = IceCubesNotifications;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
@ -1261,7 +1261,7 @@
|
||||||
INFOPLIST_FILE = IceCubesAppWidgetsExtension/Info.plist;
|
INFOPLIST_FILE = IceCubesAppWidgetsExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = IceCubesAppWidgetsExtension;
|
INFOPLIST_KEY_CFBundleDisplayName = IceCubesAppWidgetsExtension;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.4;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
@ -1297,7 +1297,7 @@
|
||||||
INFOPLIST_FILE = IceCubesAppWidgetsExtension/Info.plist;
|
INFOPLIST_FILE = IceCubesAppWidgetsExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = IceCubesAppWidgetsExtension;
|
INFOPLIST_KEY_CFBundleDisplayName = IceCubesAppWidgetsExtension;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.4;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
@ -1330,7 +1330,7 @@
|
||||||
INFOPLIST_FILE = IceCubesShareExtension/Info.plist;
|
INFOPLIST_FILE = IceCubesShareExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Ice Cubes";
|
INFOPLIST_KEY_CFBundleDisplayName = "Ice Cubes";
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
@ -1364,7 +1364,7 @@
|
||||||
INFOPLIST_FILE = IceCubesShareExtension/Info.plist;
|
INFOPLIST_FILE = IceCubesShareExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Ice Cubes";
|
INFOPLIST_KEY_CFBundleDisplayName = "Ice Cubes";
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
@ -1548,7 +1548,7 @@
|
||||||
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
|
@ -1573,7 +1573,7 @@
|
||||||
SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES;
|
SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2,7";
|
TARGETED_DEVICE_FAMILY = "1,2,7";
|
||||||
_EXPERIMENTAL_SWIFT_EXPLICIT_MODULES = YES;
|
_EXPERIMENTAL_SWIFT_EXPLICIT_MODULES = NO;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
|
@ -1615,7 +1615,7 @@
|
||||||
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
|
@ -1640,7 +1640,7 @@
|
||||||
SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES;
|
SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2,7";
|
TARGETED_DEVICE_FAMILY = "1,2,7";
|
||||||
_EXPERIMENTAL_SWIFT_EXPLICIT_MODULES = YES;
|
_EXPERIMENTAL_SWIFT_EXPLICIT_MODULES = NO;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
@ -1659,7 +1659,7 @@
|
||||||
INFOPLIST_FILE = IceCubesActionExtension/Info.plist;
|
INFOPLIST_FILE = IceCubesActionExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Open in Ice Cube";
|
INFOPLIST_KEY_CFBundleDisplayName = "Open in Ice Cube";
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
@ -1694,7 +1694,7 @@
|
||||||
INFOPLIST_FILE = IceCubesActionExtension/Info.plist;
|
INFOPLIST_FILE = IceCubesActionExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Open in Ice Cube";
|
INFOPLIST_KEY_CFBundleDisplayName = "Open in Ice Cube";
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
|
|
@ -21,10 +21,10 @@ struct AppView: View {
|
||||||
@Environment(\.openWindow) var openWindow
|
@Environment(\.openWindow) var openWindow
|
||||||
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||||
|
|
||||||
@Binding var selectedTab: Tab
|
@Binding var selectedTab: AppTab
|
||||||
@Binding var appRouterPath: RouterPath
|
@Binding var appRouterPath: RouterPath
|
||||||
|
|
||||||
@State var popToRootTab: Tab = .other
|
@State var popToRootTab: AppTab = .other
|
||||||
@State var iosTabs = iOSTabs.shared
|
@State var iosTabs = iOSTabs.shared
|
||||||
@State var sidebarTabs = SidebarTabs.shared
|
@State var sidebarTabs = SidebarTabs.shared
|
||||||
|
|
||||||
|
@ -40,14 +40,14 @@ struct AppView: View {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
var availableTabs: [Tab] {
|
var availableTabs: [AppTab] {
|
||||||
guard appAccountsManager.currentClient.isAuth else {
|
guard appAccountsManager.currentClient.isAuth else {
|
||||||
return Tab.loggedOutTab()
|
return AppTab.loggedOutTab()
|
||||||
}
|
}
|
||||||
if UIDevice.current.userInterfaceIdiom == .phone || horizontalSizeClass == .compact {
|
if UIDevice.current.userInterfaceIdiom == .phone || horizontalSizeClass == .compact {
|
||||||
return iosTabs.tabs
|
return iosTabs.tabs
|
||||||
} else if UIDevice.current.userInterfaceIdiom == .vision {
|
} else if UIDevice.current.userInterfaceIdiom == .vision {
|
||||||
return Tab.visionOSTab()
|
return AppTab.visionOSTab()
|
||||||
}
|
}
|
||||||
return sidebarTabs.tabs.map { $0.tab }
|
return sidebarTabs.tabs.map { $0.tab }
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ struct AppView: View {
|
||||||
.withSheetDestinations(sheetDestinations: $appRouterPath.presentedSheet)
|
.withSheetDestinations(sheetDestinations: $appRouterPath.presentedSheet)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func badgeFor(tab: Tab) -> Int {
|
private func badgeFor(tab: AppTab) -> Int {
|
||||||
if tab == .notifications, selectedTab != tab,
|
if tab == .notifications, selectedTab != tab,
|
||||||
let token = appAccountsManager.currentAccount.oauthToken
|
let token = appAccountsManager.currentAccount.oauthToken
|
||||||
{
|
{
|
||||||
|
@ -114,14 +114,15 @@ struct AppView: View {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
TabView(selection: $selectedTab) {
|
TabView(selection: $selectedTab) {
|
||||||
ForEach(availableTabs) { tab in
|
ForEach(availableTabs) { tab in
|
||||||
tab
|
Tab(value: tab) {
|
||||||
.makeContentView(selectedTab: $selectedTab, popToRootTab: $popToRootTab)
|
tab.makeContentView(selectedTab: $selectedTab, popToRootTab: $popToRootTab)
|
||||||
.tabItem {
|
} label: {
|
||||||
tab.label
|
tab.label
|
||||||
}
|
}
|
||||||
.tag(tab)
|
.defaultVisibility(.hidden, for: .automatic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.tabViewStyle(.tabBarOnly)
|
||||||
.introspect(.tabView, on: .iOS(.v17, .v18)) { (tabview: UITabBarController) in
|
.introspect(.tabView, on: .iOS(.v17, .v18)) { (tabview: UITabBarController) in
|
||||||
tabview.tabBar.isHidden = horizontalSizeClass == .regular
|
tabview.tabBar.isHidden = horizontalSizeClass == .regular
|
||||||
tabview.customizableViewControllers = []
|
tabview.customizableViewControllers = []
|
||||||
|
|
|
@ -28,7 +28,7 @@ struct IceCubesApp: App {
|
||||||
@State var quickLook = QuickLook.shared
|
@State var quickLook = QuickLook.shared
|
||||||
@State var theme = Theme.shared
|
@State var theme = Theme.shared
|
||||||
|
|
||||||
@State var selectedTab: Tab = .timeline
|
@State var selectedTab: AppTab = .timeline
|
||||||
@State var appRouterPath = RouterPath()
|
@State var appRouterPath = RouterPath()
|
||||||
|
|
||||||
@State var isSupporter: Bool = false
|
@State var isSupporter: Bool = false
|
||||||
|
|
|
@ -18,14 +18,14 @@ struct SideBarView<Content: View>: View {
|
||||||
@Environment(UserPreferences.self) private var userPreferences
|
@Environment(UserPreferences.self) private var userPreferences
|
||||||
@Environment(RouterPath.self) private var routerPath
|
@Environment(RouterPath.self) private var routerPath
|
||||||
|
|
||||||
@Binding var selectedTab: Tab
|
@Binding var selectedTab: AppTab
|
||||||
@Binding var popToRootTab: Tab
|
@Binding var popToRootTab: AppTab
|
||||||
var tabs: [Tab]
|
var tabs: [AppTab]
|
||||||
@ViewBuilder var content: () -> Content
|
@ViewBuilder var content: () -> Content
|
||||||
|
|
||||||
@State private var sidebarTabs = SidebarTabs.shared
|
@State private var sidebarTabs = SidebarTabs.shared
|
||||||
|
|
||||||
private func badgeFor(tab: Tab) -> Int {
|
private func badgeFor(tab: AppTab) -> Int {
|
||||||
if tab == .notifications, selectedTab != tab,
|
if tab == .notifications, selectedTab != tab,
|
||||||
let token = appAccounts.currentAccount.oauthToken
|
let token = appAccounts.currentAccount.oauthToken
|
||||||
{
|
{
|
||||||
|
@ -34,7 +34,7 @@ struct SideBarView<Content: View>: View {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makeIconForTab(tab: Tab) -> some View {
|
private func makeIconForTab(tab: AppTab) -> some View {
|
||||||
HStack {
|
HStack {
|
||||||
ZStack(alignment: .topTrailing) {
|
ZStack(alignment: .topTrailing) {
|
||||||
SideBarIcon(systemIconName: tab.iconName,
|
SideBarIcon(systemIconName: tab.iconName,
|
||||||
|
@ -83,7 +83,7 @@ struct SideBarView<Content: View>: View {
|
||||||
.offset(x: 2, y: -2)
|
.offset(x: 2, y: -2)
|
||||||
}
|
}
|
||||||
.buttonStyle(.borderedProminent)
|
.buttonStyle(.borderedProminent)
|
||||||
.help(Tab.post.title)
|
.help(AppTab.post.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makeAccountButton(account: AppAccount, showBadge: Bool) -> some View {
|
private func makeAccountButton(account: AppAccount, showBadge: Bool) -> some View {
|
||||||
|
@ -133,7 +133,7 @@ struct SideBarView<Content: View>: View {
|
||||||
if let accountName {
|
if let accountName {
|
||||||
"tab.profile-account-\(accountName)"
|
"tab.profile-account-\(accountName)"
|
||||||
} else {
|
} else {
|
||||||
Tab.profile.title
|
AppTab.profile.title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ struct ExploreTab: View {
|
||||||
@Environment(Client.self) private var client
|
@Environment(Client.self) private var client
|
||||||
@State private var routerPath = RouterPath()
|
@State private var routerPath = RouterPath()
|
||||||
@State private var scrollToTopSignal: Int = 0
|
@State private var scrollToTopSignal: Int = 0
|
||||||
@Binding var popToRootTab: Tab
|
@Binding var popToRootTab: AppTab
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack(path: $routerPath.path) {
|
NavigationStack(path: $routerPath.path) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ struct MessagesTab: View {
|
||||||
@Environment(AppAccountsManager.self) private var appAccount
|
@Environment(AppAccountsManager.self) private var appAccount
|
||||||
@State private var routerPath = RouterPath()
|
@State private var routerPath = RouterPath()
|
||||||
@State private var scrollToTopSignal: Int = 0
|
@State private var scrollToTopSignal: Int = 0
|
||||||
@Binding var popToRootTab: Tab
|
@Binding var popToRootTab: AppTab
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack(path: $routerPath.path) {
|
NavigationStack(path: $routerPath.path) {
|
||||||
|
|
|
@ -22,8 +22,8 @@ struct NotificationsTab: View {
|
||||||
@State private var routerPath = RouterPath()
|
@State private var routerPath = RouterPath()
|
||||||
@State private var scrollToTopSignal: Int = 0
|
@State private var scrollToTopSignal: Int = 0
|
||||||
|
|
||||||
@Binding var selectedTab: Tab
|
@Binding var selectedTab: AppTab
|
||||||
@Binding var popToRootTab: Tab
|
@Binding var popToRootTab: AppTab
|
||||||
|
|
||||||
let lockedType: Models.Notification.NotificationType?
|
let lockedType: Models.Notification.NotificationType?
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ struct ProfileTab: View {
|
||||||
@Environment(CurrentAccount.self) private var currentAccount
|
@Environment(CurrentAccount.self) private var currentAccount
|
||||||
@State private var routerPath = RouterPath()
|
@State private var routerPath = RouterPath()
|
||||||
@State private var scrollToTopSignal: Int = 0
|
@State private var scrollToTopSignal: Int = 0
|
||||||
@Binding var popToRootTab: Tab
|
@Binding var popToRootTab: AppTab
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack(path: $routerPath.path) {
|
NavigationStack(path: $routerPath.path) {
|
||||||
|
|
|
@ -28,7 +28,7 @@ struct SettingsTabs: View {
|
||||||
@State private var cachedRemoved = false
|
@State private var cachedRemoved = false
|
||||||
@State private var timelineCache = TimelineCache()
|
@State private var timelineCache = TimelineCache()
|
||||||
|
|
||||||
@Binding var popToRootTab: Tab
|
@Binding var popToRootTab: AppTab
|
||||||
|
|
||||||
let isModal: Bool
|
let isModal: Bool
|
||||||
|
|
||||||
|
|
|
@ -14,27 +14,27 @@ struct TabbarEntriesSettingsView: View {
|
||||||
Form {
|
Form {
|
||||||
Section {
|
Section {
|
||||||
Picker("settings.tabs.first-tab", selection: $tabs.firstTab) {
|
Picker("settings.tabs.first-tab", selection: $tabs.firstTab) {
|
||||||
ForEach(Tab.allCases) { tab in
|
ForEach(AppTab.allCases) { tab in
|
||||||
tab.label.tag(tab)
|
tab.label.tag(tab)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Picker("settings.tabs.second-tab", selection: $tabs.secondTab) {
|
Picker("settings.tabs.second-tab", selection: $tabs.secondTab) {
|
||||||
ForEach(Tab.allCases) { tab in
|
ForEach(AppTab.allCases) { tab in
|
||||||
tab.label.tag(tab)
|
tab.label.tag(tab)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Picker("settings.tabs.third-tab", selection: $tabs.thirdTab) {
|
Picker("settings.tabs.third-tab", selection: $tabs.thirdTab) {
|
||||||
ForEach(Tab.allCases) { tab in
|
ForEach(AppTab.allCases) { tab in
|
||||||
tab.label.tag(tab)
|
tab.label.tag(tab)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Picker("settings.tabs.fourth-tab", selection: $tabs.fourthTab) {
|
Picker("settings.tabs.fourth-tab", selection: $tabs.fourthTab) {
|
||||||
ForEach(Tab.allCases) { tab in
|
ForEach(AppTab.allCases) { tab in
|
||||||
tab.label.tag(tab)
|
tab.label.tag(tab)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Picker("settings.tabs.fifth-tab", selection: $tabs.fifthTab) {
|
Picker("settings.tabs.fifth-tab", selection: $tabs.fifthTab) {
|
||||||
ForEach(Tab.allCases) { tab in
|
ForEach(AppTab.allCases) { tab in
|
||||||
tab.label.tag(tab)
|
tab.label.tag(tab)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import StatusKit
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
enum Tab: Int, Identifiable, Hashable, CaseIterable, Codable {
|
enum AppTab: Int, Identifiable, Hashable, CaseIterable, Codable {
|
||||||
case timeline, notifications, mentions, explore, messages, settings, other
|
case timeline, notifications, mentions, explore, messages, settings, other
|
||||||
case trending, federated, local
|
case trending, federated, local
|
||||||
case profile
|
case profile
|
||||||
|
@ -22,16 +22,16 @@ enum Tab: Int, Identifiable, Hashable, CaseIterable, Codable {
|
||||||
rawValue
|
rawValue
|
||||||
}
|
}
|
||||||
|
|
||||||
static func loggedOutTab() -> [Tab] {
|
static func loggedOutTab() -> [AppTab] {
|
||||||
[.timeline, .settings]
|
[.timeline, .settings]
|
||||||
}
|
}
|
||||||
|
|
||||||
static func visionOSTab() -> [Tab] {
|
static func visionOSTab() -> [AppTab] {
|
||||||
[.profile, .timeline, .notifications, .mentions, .explore, .post, .settings]
|
[.profile, .timeline, .notifications, .mentions, .explore, .post, .settings]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
func makeContentView(selectedTab: Binding<Tab>, popToRootTab: Binding<Tab>) -> some View {
|
func makeContentView(selectedTab: Binding<AppTab>, popToRootTab: Binding<AppTab>) -> some View {
|
||||||
switch self {
|
switch self {
|
||||||
case .timeline:
|
case .timeline:
|
||||||
TimelineTab(popToRootTab: popToRootTab)
|
TimelineTab(popToRootTab: popToRootTab)
|
||||||
|
@ -168,7 +168,7 @@ enum Tab: Int, Identifiable, Hashable, CaseIterable, Codable {
|
||||||
@Observable
|
@Observable
|
||||||
class SidebarTabs {
|
class SidebarTabs {
|
||||||
struct SidedebarTab: Hashable, Codable {
|
struct SidedebarTab: Hashable, Codable {
|
||||||
let tab: Tab
|
let tab: AppTab
|
||||||
var enabled: Bool
|
var enabled: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ class SidebarTabs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isEnabled(_ tab: Tab) -> Bool {
|
func isEnabled(_ tab: AppTab) -> Bool {
|
||||||
tabs.first(where: { $0.tab.id == tab.id })?.enabled == true
|
tabs.first(where: { $0.tab.id == tab.id })?.enabled == true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,45 +219,45 @@ class iOSTabs {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Storage {
|
class Storage {
|
||||||
@AppStorage(TabEntries.first.rawValue) var firstTab = Tab.timeline
|
@AppStorage(TabEntries.first.rawValue) var firstTab = AppTab.timeline
|
||||||
@AppStorage(TabEntries.second.rawValue) var secondTab = Tab.notifications
|
@AppStorage(TabEntries.second.rawValue) var secondTab = AppTab.notifications
|
||||||
@AppStorage(TabEntries.third.rawValue) var thirdTab = Tab.explore
|
@AppStorage(TabEntries.third.rawValue) var thirdTab = AppTab.explore
|
||||||
@AppStorage(TabEntries.fourth.rawValue) var fourthTab = Tab.links
|
@AppStorage(TabEntries.fourth.rawValue) var fourthTab = AppTab.links
|
||||||
@AppStorage(TabEntries.fifth.rawValue) var fifthTab = Tab.profile
|
@AppStorage(TabEntries.fifth.rawValue) var fifthTab = AppTab.profile
|
||||||
}
|
}
|
||||||
|
|
||||||
private let storage = Storage()
|
private let storage = Storage()
|
||||||
public static let shared = iOSTabs()
|
public static let shared = iOSTabs()
|
||||||
|
|
||||||
var tabs: [Tab] {
|
var tabs: [AppTab] {
|
||||||
[firstTab, secondTab, thirdTab, fourthTab, fifthTab]
|
[firstTab, secondTab, thirdTab, fourthTab, fifthTab]
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstTab: Tab {
|
var firstTab: AppTab {
|
||||||
didSet {
|
didSet {
|
||||||
storage.firstTab = firstTab
|
storage.firstTab = firstTab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var secondTab: Tab {
|
var secondTab: AppTab {
|
||||||
didSet {
|
didSet {
|
||||||
storage.secondTab = secondTab
|
storage.secondTab = secondTab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var thirdTab: Tab {
|
var thirdTab: AppTab {
|
||||||
didSet {
|
didSet {
|
||||||
storage.thirdTab = thirdTab
|
storage.thirdTab = thirdTab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var fourthTab: Tab {
|
var fourthTab: AppTab {
|
||||||
didSet {
|
didSet {
|
||||||
storage.fourthTab = fourthTab
|
storage.fourthTab = fourthTab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var fifthTab: Tab {
|
var fifthTab: AppTab {
|
||||||
didSet {
|
didSet {
|
||||||
storage.fifthTab = fifthTab
|
storage.fifthTab = fifthTab
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ struct TimelineTab: View {
|
||||||
@Environment(UserPreferences.self) private var preferences
|
@Environment(UserPreferences.self) private var preferences
|
||||||
@Environment(Client.self) private var client
|
@Environment(Client.self) private var client
|
||||||
@State private var routerPath = RouterPath()
|
@State private var routerPath = RouterPath()
|
||||||
@Binding var popToRootTab: Tab
|
@Binding var popToRootTab: AppTab
|
||||||
|
|
||||||
@State private var didAppear: Bool = false
|
@State private var didAppear: Bool = false
|
||||||
@State private var timeline: TimelineFilter = .home
|
@State private var timeline: TimelineFilter = .home
|
||||||
|
@ -33,7 +33,7 @@ struct TimelineTab: View {
|
||||||
|
|
||||||
private let canFilterTimeline: Bool
|
private let canFilterTimeline: Bool
|
||||||
|
|
||||||
init(popToRootTab: Binding<Tab>, timeline: TimelineFilter? = nil) {
|
init(popToRootTab: Binding<AppTab>, timeline: TimelineFilter? = nil) {
|
||||||
canFilterTimeline = timeline == nil
|
canFilterTimeline = timeline == nil
|
||||||
_popToRootTab = popToRootTab
|
_popToRootTab = popToRootTab
|
||||||
_timeline = .init(initialValue: timeline ?? .home)
|
_timeline = .init(initialValue: timeline ?? .home)
|
||||||
|
|
|
@ -35,7 +35,7 @@ enum TabEnum: String, AppEnum, Sendable {
|
||||||
.post: .init(title: "New post")]
|
.post: .init(title: "New post")]
|
||||||
}
|
}
|
||||||
|
|
||||||
var toAppTab: Tab {
|
var toAppTab: AppTab {
|
||||||
switch self {
|
switch self {
|
||||||
case .timeline:
|
case .timeline:
|
||||||
.timeline
|
.timeline
|
||||||
|
|
|
@ -23,8 +23,11 @@ struct AccountWidgetProvider: AppIntentTimelineProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fetchAccount(configuration: AccountWidgetConfiguration) async -> Account {
|
private func fetchAccount(configuration: AccountWidgetConfiguration) async -> Account {
|
||||||
let client = Client(server: configuration.account.account.server,
|
guard let account = configuration.account else {
|
||||||
oauthToken: configuration.account.account.oauthToken)
|
return .placeholder()
|
||||||
|
}
|
||||||
|
let client = Client(server: account.account.server,
|
||||||
|
oauthToken: account.account.oauthToken)
|
||||||
do {
|
do {
|
||||||
let account: Account = try await client.get(endpoint: Accounts.verifyCredentials)
|
let account: Account = try await client.get(endpoint: Accounts.verifyCredentials)
|
||||||
return account
|
return account
|
||||||
|
|
|
@ -6,7 +6,7 @@ struct AccountWidgetConfiguration: WidgetConfigurationIntent {
|
||||||
static let description = IntentDescription("Choose the account for this widget")
|
static let description = IntentDescription("Choose the account for this widget")
|
||||||
|
|
||||||
@Parameter(title: "Account")
|
@Parameter(title: "Account")
|
||||||
var account: AppAccountEntity
|
var account: AppAccountEntity?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AccountWidgetConfiguration {
|
extension AccountWidgetConfiguration {
|
||||||
|
|
|
@ -29,9 +29,16 @@ struct HashtagPostsWidgetProvider: AppIntentTimelineProvider {
|
||||||
|
|
||||||
private func timeline(for configuration: HashtagPostsWidgetConfiguration, context: Context) async -> Timeline<PostsWidgetEntry> {
|
private func timeline(for configuration: HashtagPostsWidgetConfiguration, context: Context) async -> Timeline<PostsWidgetEntry> {
|
||||||
do {
|
do {
|
||||||
let timeline: TimelineFilter = .hashtag(tag: configuration.hashgtag, accountId: nil)
|
guard let account = configuration.account, let hashgtag = configuration.hashgtag else {
|
||||||
|
return Timeline(entries: [.init(date: Date(),
|
||||||
|
title: "#Mastodon",
|
||||||
|
statuses: [],
|
||||||
|
images: [:])],
|
||||||
|
policy: .atEnd)
|
||||||
|
}
|
||||||
|
let timeline: TimelineFilter = .hashtag(tag: hashgtag, accountId: nil)
|
||||||
let statuses = await loadStatuses(for: timeline,
|
let statuses = await loadStatuses(for: timeline,
|
||||||
account: configuration.account,
|
account: account,
|
||||||
widgetFamily: context.family)
|
widgetFamily: context.family)
|
||||||
let images = try await loadImages(urls: statuses.map { $0.account.avatar })
|
let images = try await loadImages(urls: statuses.map { $0.account.avatar })
|
||||||
return Timeline(entries: [.init(date: Date(),
|
return Timeline(entries: [.init(date: Date(),
|
||||||
|
|
|
@ -6,10 +6,10 @@ struct HashtagPostsWidgetConfiguration: WidgetConfigurationIntent {
|
||||||
static let description = IntentDescription("Choose the account and hashtag for this widget")
|
static let description = IntentDescription("Choose the account and hashtag for this widget")
|
||||||
|
|
||||||
@Parameter(title: "Account")
|
@Parameter(title: "Account")
|
||||||
var account: AppAccountEntity
|
var account: AppAccountEntity?
|
||||||
|
|
||||||
@Parameter(title: "Hashtag")
|
@Parameter(title: "Hashtag")
|
||||||
var hashgtag: String
|
var hashgtag: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension HashtagPostsWidgetConfiguration {
|
extension HashtagPostsWidgetConfiguration {
|
||||||
|
|
|
@ -18,7 +18,7 @@ struct LatestPostsWidgetProvider: AppIntentTimelineProvider {
|
||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
return .init(date: Date(),
|
return .init(date: Date(),
|
||||||
title: configuration.timeline.timeline.title,
|
title: configuration.timeline?.timeline.title ?? "",
|
||||||
statuses: [],
|
statuses: [],
|
||||||
images: [:])
|
images: [:])
|
||||||
}
|
}
|
||||||
|
@ -29,17 +29,24 @@ struct LatestPostsWidgetProvider: AppIntentTimelineProvider {
|
||||||
|
|
||||||
private func timeline(for configuration: LatestPostsWidgetConfiguration, context: Context) async -> Timeline<PostsWidgetEntry> {
|
private func timeline(for configuration: LatestPostsWidgetConfiguration, context: Context) async -> Timeline<PostsWidgetEntry> {
|
||||||
do {
|
do {
|
||||||
let statuses = await loadStatuses(for: configuration.timeline.timeline,
|
guard let timeline = configuration.timeline, let account = configuration.account else {
|
||||||
account: configuration.account,
|
return Timeline(entries: [.init(date: Date(),
|
||||||
|
title: "",
|
||||||
|
statuses: [],
|
||||||
|
images: [:])],
|
||||||
|
policy: .atEnd)
|
||||||
|
}
|
||||||
|
let statuses = await loadStatuses(for: timeline.timeline,
|
||||||
|
account: account,
|
||||||
widgetFamily: context.family)
|
widgetFamily: context.family)
|
||||||
let images = try await loadImages(urls: statuses.map { $0.account.avatar })
|
let images = try await loadImages(urls: statuses.map { $0.account.avatar })
|
||||||
return Timeline(entries: [.init(date: Date(),
|
return Timeline(entries: [.init(date: Date(),
|
||||||
title: configuration.timeline.timeline.title,
|
title: timeline.timeline.title,
|
||||||
statuses: statuses,
|
statuses: statuses,
|
||||||
images: images)], policy: .atEnd)
|
images: images)], policy: .atEnd)
|
||||||
} catch {
|
} catch {
|
||||||
return Timeline(entries: [.init(date: Date(),
|
return Timeline(entries: [.init(date: Date(),
|
||||||
title: configuration.timeline.timeline.title,
|
title: configuration.timeline?.timeline.title ?? "",
|
||||||
statuses: [],
|
statuses: [],
|
||||||
images: [:])],
|
images: [:])],
|
||||||
policy: .atEnd)
|
policy: .atEnd)
|
||||||
|
|
|
@ -6,10 +6,10 @@ struct LatestPostsWidgetConfiguration: WidgetConfigurationIntent {
|
||||||
static let description = IntentDescription("Choose the account and timeline for this widget")
|
static let description = IntentDescription("Choose the account and timeline for this widget")
|
||||||
|
|
||||||
@Parameter(title: "Account")
|
@Parameter(title: "Account")
|
||||||
var account: AppAccountEntity
|
var account: AppAccountEntity?
|
||||||
|
|
||||||
@Parameter(title: "Timeline")
|
@Parameter(title: "Timeline")
|
||||||
var timeline: TimelineFilterEntity
|
var timeline: TimelineFilterEntity?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LatestPostsWidgetConfiguration {
|
extension LatestPostsWidgetConfiguration {
|
||||||
|
|
|
@ -29,13 +29,20 @@ struct ListsWidgetProvider: AppIntentTimelineProvider {
|
||||||
|
|
||||||
private func timeline(for configuration: ListsWidgetConfiguration, context: Context) async -> Timeline<PostsWidgetEntry> {
|
private func timeline(for configuration: ListsWidgetConfiguration, context: Context) async -> Timeline<PostsWidgetEntry> {
|
||||||
do {
|
do {
|
||||||
let timeline: TimelineFilter = .list(list: configuration.timeline.list)
|
guard let account = configuration.account, let timeline = configuration.timeline else {
|
||||||
let statuses = await loadStatuses(for: timeline,
|
return Timeline(entries: [.init(date: Date(),
|
||||||
account: configuration.account,
|
title: "List name",
|
||||||
|
statuses: [],
|
||||||
|
images: [:])],
|
||||||
|
policy: .atEnd)
|
||||||
|
}
|
||||||
|
let filter: TimelineFilter = .list(list: timeline.list)
|
||||||
|
let statuses = await loadStatuses(for: filter,
|
||||||
|
account: account,
|
||||||
widgetFamily: context.family)
|
widgetFamily: context.family)
|
||||||
let images = try await loadImages(urls: statuses.map { $0.account.avatar })
|
let images = try await loadImages(urls: statuses.map { $0.account.avatar })
|
||||||
return Timeline(entries: [.init(date: Date(),
|
return Timeline(entries: [.init(date: Date(),
|
||||||
title: timeline.title,
|
title: filter.title,
|
||||||
statuses: statuses,
|
statuses: statuses,
|
||||||
images: images)], policy: .atEnd)
|
images: images)], policy: .atEnd)
|
||||||
} catch {
|
} catch {
|
||||||
|
|
|
@ -6,10 +6,10 @@ struct ListsWidgetConfiguration: WidgetConfigurationIntent {
|
||||||
static let description = IntentDescription("Choose the account and list for this widget")
|
static let description = IntentDescription("Choose the account and list for this widget")
|
||||||
|
|
||||||
@Parameter(title: "Account")
|
@Parameter(title: "Account")
|
||||||
var account: AppAccountEntity
|
var account: AppAccountEntity?
|
||||||
|
|
||||||
@Parameter(title: "List")
|
@Parameter(title: "List")
|
||||||
var timeline: ListEntity
|
var timeline: ListEntity?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ListsWidgetConfiguration {
|
extension ListsWidgetConfiguration {
|
||||||
|
|
|
@ -29,8 +29,15 @@ struct MentionsWidgetProvider: AppIntentTimelineProvider {
|
||||||
|
|
||||||
private func timeline(for configuration: MentionsWidgetConfiguration, context _: Context) async -> Timeline<PostsWidgetEntry> {
|
private func timeline(for configuration: MentionsWidgetConfiguration, context _: Context) async -> Timeline<PostsWidgetEntry> {
|
||||||
do {
|
do {
|
||||||
let client = Client(server: configuration.account.account.server,
|
guard let account = configuration.account else {
|
||||||
oauthToken: configuration.account.account.oauthToken)
|
return Timeline(entries: [.init(date: Date(),
|
||||||
|
title: "Mentions",
|
||||||
|
statuses: [],
|
||||||
|
images: [:])],
|
||||||
|
policy: .atEnd)
|
||||||
|
}
|
||||||
|
let client = Client(server: account.account.server,
|
||||||
|
oauthToken: account.account.oauthToken)
|
||||||
var excludedTypes = Models.Notification.NotificationType.allCases
|
var excludedTypes = Models.Notification.NotificationType.allCases
|
||||||
excludedTypes.removeAll(where: { $0 == .mention })
|
excludedTypes.removeAll(where: { $0 == .mention })
|
||||||
let notifications: [Models.Notification] =
|
let notifications: [Models.Notification] =
|
||||||
|
|
|
@ -6,7 +6,7 @@ struct MentionsWidgetConfiguration: WidgetConfigurationIntent {
|
||||||
static let description = IntentDescription("Choose the account for this widget")
|
static let description = IntentDescription("Choose the account for this widget")
|
||||||
|
|
||||||
@Parameter(title: "Account")
|
@Parameter(title: "Account")
|
||||||
var account: AppAccountEntity
|
var account: AppAccountEntity?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MentionsWidgetConfiguration {
|
extension MentionsWidgetConfiguration {
|
||||||
|
|
Loading…
Reference in a new issue