Open HashTag

This commit is contained in:
Thomas Ricouard 2022-12-20 15:37:51 +01:00
parent 029b625acd
commit 3d7042832e
8 changed files with 85 additions and 36 deletions

View file

@ -15,6 +15,8 @@ extension View {
AccountDetailView(account: account)
case let .statusDetail(id):
StatusDetailView(statusId: id)
case let .hashTag(tag):
TimelineView(timeline: .hashtag(tag: tag))
}
}
}

View file

@ -16,10 +16,12 @@ struct IceCubesApp: App {
.tabItem {
Label("Home", systemImage: "globe")
}
NotificationsTab()
.tabItem {
Label("Notifications", systemImage: "bell")
}
if appAccountsManager.currentClient.isAuth {
NotificationsTab()
.tabItem {
Label("Notifications", systemImage: "bell")
}
}
SettingsTabs()
.tabItem {
Label("Settings", systemImage: "gear")

View file

@ -2,7 +2,13 @@ import Foundation
import SwiftUI
import Models
public class Client: ObservableObject {
public class Client: ObservableObject, Equatable {
public static func == (lhs: Client, rhs: Client) -> Bool {
lhs.isAuth == rhs.isAuth &&
lhs.server == rhs.server &&
lhs.oauthToken?.accessToken == rhs.oauthToken?.accessToken
}
public enum Version: String {
case v1
}

View file

@ -3,6 +3,7 @@ import Foundation
public enum Timelines: Endpoint {
case pub(sinceId: String?)
case home(sinceId: String?)
case hashtag(tag: String, sinceId: String?)
public func path() -> String {
switch self {
@ -10,6 +11,8 @@ public enum Timelines: Endpoint {
return "timelines/public"
case .home:
return "timelines/home"
case let .hashtag(tag, _):
return "timelines/tag/\(tag)"
}
}
@ -21,6 +24,9 @@ public enum Timelines: Endpoint {
case .home(let sinceId):
guard let sinceId else { return nil }
return [.init(name: "max_id", value: sinceId)]
case let .hashtag(_, sinceId):
guard let sinceId else { return nil }
return [.init(name: "max_id", value: sinceId)]
}
}
}

View file

@ -6,6 +6,7 @@ public enum RouteurDestinations: Hashable {
case accountDetail(id: String)
case accountDetailWithAccount(account: Account)
case statusDetail(id: String)
case hashTag(tag: String)
}
public enum SheetDestinations: Identifiable {
@ -30,7 +31,11 @@ public class RouterPath: ObservableObject {
}
public func handleStatus(status: AnyStatus, url: URL) -> OpenURLAction.Result {
if let mention = status.mentions.first(where: { $0.url == url }) {
if url.pathComponents.contains(where: { $0 == "tags" }),
let tag = url.pathComponents.last {
navigate(to: .hashTag(tag: tag))
return .handled
} else if let mention = status.mentions.first(where: { $0.url == url }) {
navigate(to: .accountDetail(id: mention.id))
return .handled
}

View file

@ -0,0 +1,36 @@
import Foundation
import Models
import Network
public enum TimelineFilter: Hashable, Equatable {
case pub, home
case hashtag(tag: String)
public func hash(into hasher: inout Hasher) {
hasher.combine(title())
}
static func availableTimeline() -> [TimelineFilter] {
return [.pub, .home]
}
func title() -> String {
switch self {
case .pub:
return "Public"
case .home:
return "Home"
case let .hashtag(tag):
return tag
}
}
func endpoint(sinceId: String?) -> Timelines {
switch self {
case .pub: return .pub(sinceId: sinceId)
case .home: return .home(sinceId: sinceId)
case let .hashtag(tag):
return .hashtag(tag: tag, sinceId: sinceId)
}
}
}

View file

@ -8,9 +8,12 @@ import DesignSystem
public struct TimelineView: View {
@EnvironmentObject private var client: Client
@StateObject private var viewModel = TimelineViewModel()
@State private var didAppear = false
public init() {}
private let filter: TimelineFilter?
public init(timeline: TimelineFilter? = nil) {
self.filter = timeline
}
public var body: some View {
ScrollView {
@ -19,33 +22,38 @@ public struct TimelineView: View {
}
.padding(.top, DS.Constants.layoutPadding)
}
.navigationTitle(viewModel.timeline.rawValue)
.navigationTitle(filter?.title() ?? viewModel.timeline.title())
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
timelineFilterButton
if filter == nil {
ToolbarItem(placement: .navigationBarTrailing) {
timelineFilterButton
}
}
}
.task {
.onAppear {
viewModel.client = client
if !didAppear {
await viewModel.fetchStatuses()
didAppear = true
if let filter {
viewModel.timeline = filter
} else {
viewModel.timeline = client.isAuth ? .home : .pub
}
}
.refreshable {
await viewModel.fetchStatuses()
Task {
await viewModel.fetchStatuses()
}
}
}
private var timelineFilterButton: some View {
Menu {
ForEach(TimelineViewModel.TimelineFilter.allCases, id: \.self) { filter in
ForEach(TimelineFilter.availableTimeline(), id: \.self) { filter in
Button {
viewModel.timeline = filter
} label: {
Text(filter.rawValue)
Text(filter.title())
}
}
} label: {

View file

@ -5,30 +5,14 @@ import Status
@MainActor
class TimelineViewModel: ObservableObject, StatusesFetcher {
enum TimelineFilter: String, CaseIterable {
case pub = "Public"
case home = "Home"
func endpoint(sinceId: String?) -> Timelines {
switch self {
case .pub: return .pub(sinceId: sinceId)
case .home: return .home(sinceId: sinceId)
}
}
}
var client: Client? {
didSet {
timeline = client?.isAuth == true ? .home : .pub
}
}
var client: Client?
private var statuses: [Status] = []
@Published var statusesState: StatusesState = .loading
@Published var timeline: TimelineFilter = .pub {
didSet {
if oldValue != timeline {
if oldValue != timeline || statuses.isEmpty {
Task {
await fetchStatuses()
}