From 569aedeaeb7992e82dbee2c113783c20cb25297c Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Sat, 24 Dec 2022 12:20:42 +0100 Subject: [PATCH] Prepend new timeline statuses and new notifications --- .../Sources/Network/Endpoint/Endpoint.swift | 11 ++++++++++ .../Network/Endpoint/Notifications.swift | 17 ++++++++------- .../Sources/Network/Endpoint/Timelines.swift | 21 ++++++++----------- .../Notifications/NotificationsListView.swift | 4 +--- .../NotificationsViewModel.swift | 20 ++++++++++++++---- .../Sources/Timeline/TimelineFilter.swift | 8 +++---- .../Sources/Timeline/TimelineView.swift | 4 +--- .../Sources/Timeline/TimelineViewModel.swift | 11 +++++++--- 8 files changed, 59 insertions(+), 37 deletions(-) diff --git a/Packages/Network/Sources/Network/Endpoint/Endpoint.swift b/Packages/Network/Sources/Network/Endpoint/Endpoint.swift index 3a875fc8..1aacdcd3 100644 --- a/Packages/Network/Sources/Network/Endpoint/Endpoint.swift +++ b/Packages/Network/Sources/Network/Endpoint/Endpoint.swift @@ -4,3 +4,14 @@ public protocol Endpoint { func path() -> String func queryItems() -> [URLQueryItem]? } + +extension Endpoint { + func makePaginationParam(sinceId: String?, maxId: String?) -> [URLQueryItem]? { + if let sinceId { + return [.init(name: "since_id", value: sinceId)] + } else if let maxId { + return [.init(name: "max_id", value: maxId)] + } + return nil + } +} diff --git a/Packages/Network/Sources/Network/Endpoint/Notifications.swift b/Packages/Network/Sources/Network/Endpoint/Notifications.swift index 1fd5504b..857f7e85 100644 --- a/Packages/Network/Sources/Network/Endpoint/Notifications.swift +++ b/Packages/Network/Sources/Network/Endpoint/Notifications.swift @@ -1,7 +1,9 @@ import Foundation public enum Notifications: Endpoint { - case notifications(maxId: String?, onlyMentions: Bool) + case notifications(sinceId: String?, + maxId: String?, + types: [String]?) public func path() -> String { switch self { @@ -12,13 +14,12 @@ public enum Notifications: Endpoint { public func queryItems() -> [URLQueryItem]? { switch self { - case .notifications(let maxId, let onlyMentions): - var params: [URLQueryItem] = [] - if onlyMentions { - params.append(.init(name: "types[]", value: "mention")) - } - if let maxId { - params.append(.init(name: "max_id", value: maxId)) + case .notifications(let sinceId, let maxId, let types): + var params = makePaginationParam(sinceId: sinceId, maxId: maxId) ?? [] + if let types { + for type in types { + params.append(.init(name: "types[]", value: type)) + } } return params } diff --git a/Packages/Network/Sources/Network/Endpoint/Timelines.swift b/Packages/Network/Sources/Network/Endpoint/Timelines.swift index bd48c289..b72814d2 100644 --- a/Packages/Network/Sources/Network/Endpoint/Timelines.swift +++ b/Packages/Network/Sources/Network/Endpoint/Timelines.swift @@ -1,9 +1,9 @@ import Foundation public enum Timelines: Endpoint { - case pub(sinceId: String?) - case home(sinceId: String?) - case hashtag(tag: String, sinceId: String?) + case pub(sinceId: String?, maxId: String?) + case home(sinceId: String?, maxId: String?) + case hashtag(tag: String, maxId: String?) public func path() -> String { switch self { @@ -18,15 +18,12 @@ public enum Timelines: Endpoint { public func queryItems() -> [URLQueryItem]? { switch self { - case .pub(let sinceId): - guard let sinceId else { return nil } - return [.init(name: "max_id", value: sinceId)] - 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)] + case .pub(let sinceId, let maxId): + return makePaginationParam(sinceId: sinceId, maxId: maxId) + case .home(let sinceId, let maxId): + return makePaginationParam(sinceId: sinceId, maxId: maxId) + case let .hashtag(_, maxId): + return makePaginationParam(sinceId: nil, maxId: maxId) } } } diff --git a/Packages/Notifications/Sources/Notifications/NotificationsListView.swift b/Packages/Notifications/Sources/Notifications/NotificationsListView.swift index 0b27cc70..5446a4a1 100644 --- a/Packages/Notifications/Sources/Notifications/NotificationsListView.swift +++ b/Packages/Notifications/Sources/Notifications/NotificationsListView.swift @@ -37,9 +37,7 @@ public struct NotificationsListView: View { await viewModel.fetchNotifications() } .refreshable { - Task { - await viewModel.fetchNotifications() - } + await viewModel.fetchNotifications() } .navigationTitle(Text("Notifications")) .navigationBarTitleDisplayMode(.inline) diff --git a/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift b/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift index 0912a74e..1b069db3 100644 --- a/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift +++ b/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift @@ -31,15 +31,25 @@ class NotificationsViewModel: ObservableObject { } private var notifications: [Models.Notification] = [] + private var queryTypes: [String]? { + tab == .mentions ? ["mention"] : nil + } func fetchNotifications() async { guard let client else { return } do { if notifications.isEmpty { state = .loading + notifications = try await client.get(endpoint: Notifications.notifications(sinceId: nil, + maxId: nil, + types: queryTypes)) + } else if let first = notifications.first { + let newNotifications: [Models.Notification] = + try await client.get(endpoint: Notifications.notifications(sinceId: first.id, + maxId: nil, + types: queryTypes)) + notifications.insert(contentsOf: newNotifications, at: 0) } - notifications = try await client.get(endpoint: Notifications.notifications(maxId: nil, - onlyMentions: tab == .mentions)) state = .display(notifications: notifications, nextPageState: .hasNextPage) } catch { state = .error(error: error) @@ -51,8 +61,10 @@ class NotificationsViewModel: ObservableObject { do { guard let lastId = notifications.last?.id else { return } state = .display(notifications: notifications, nextPageState: .loadingNextPage) - let newNotifications: [Models.Notification] = try await client.get(endpoint: Notifications.notifications(maxId: lastId, - onlyMentions: tab == .mentions)) + let newNotifications: [Models.Notification] = + try await client.get(endpoint: Notifications.notifications(sinceId: nil, + maxId: lastId, + types: queryTypes)) notifications.append(contentsOf: newNotifications) state = .display(notifications: notifications, nextPageState: .hasNextPage) } catch { diff --git a/Packages/Timeline/Sources/Timeline/TimelineFilter.swift b/Packages/Timeline/Sources/Timeline/TimelineFilter.swift index aa012a8e..c422aa3e 100644 --- a/Packages/Timeline/Sources/Timeline/TimelineFilter.swift +++ b/Packages/Timeline/Sources/Timeline/TimelineFilter.swift @@ -25,15 +25,15 @@ public enum TimelineFilter: Hashable, Equatable { } } - func endpoint(sinceId: String?) -> Endpoint { + func endpoint(sinceId: String?, maxId: String?) -> Endpoint { switch self { - case .pub: return Timelines.pub(sinceId: sinceId) - case .home: return Timelines.home(sinceId: sinceId) + case .pub: return Timelines.pub(sinceId: sinceId, maxId: maxId) + case .home: return Timelines.home(sinceId: sinceId, maxId: maxId) case let .hashtag(tag, accountId): if let accountId { return Accounts.statuses(id: accountId, sinceId: nil, tag: tag) } else { - return Timelines.hashtag(tag: tag, sinceId: sinceId) + return Timelines.hashtag(tag: tag, maxId: maxId) } } } diff --git a/Packages/Timeline/Sources/Timeline/TimelineView.swift b/Packages/Timeline/Sources/Timeline/TimelineView.swift index 17cfe2a2..e99e35a5 100644 --- a/Packages/Timeline/Sources/Timeline/TimelineView.swift +++ b/Packages/Timeline/Sources/Timeline/TimelineView.swift @@ -42,9 +42,7 @@ public struct TimelineView: View { } } .refreshable { - Task { - await viewModel.fetchStatuses() - } + await viewModel.fetchStatuses() } } diff --git a/Packages/Timeline/Sources/Timeline/TimelineViewModel.swift b/Packages/Timeline/Sources/Timeline/TimelineViewModel.swift index 4237a60f..61e63459 100644 --- a/Packages/Timeline/Sources/Timeline/TimelineViewModel.swift +++ b/Packages/Timeline/Sources/Timeline/TimelineViewModel.swift @@ -34,8 +34,13 @@ class TimelineViewModel: ObservableObject, StatusesFetcher { func fetchStatuses() async { guard let client else { return } do { - statusesState = .loading - statuses = try await client.get(endpoint: timeline.endpoint(sinceId: nil)) + if statuses.isEmpty { + statusesState = .loading + statuses = try await client.get(endpoint: timeline.endpoint(sinceId: nil, maxId: nil)) + } else if let first = statuses.first { + let newStatuses: [Status] = try await client.get(endpoint: timeline.endpoint(sinceId: first.id, maxId: nil)) + statuses.insert(contentsOf: newStatuses, at: 0) + } statusesState = .display(statuses: statuses, nextPageState: .hasNextPage) } catch { statusesState = .error(error: error) @@ -48,7 +53,7 @@ class TimelineViewModel: ObservableObject, StatusesFetcher { do { guard let lastId = statuses.last?.id else { return } statusesState = .display(statuses: statuses, nextPageState: .loadingNextPage) - let newStatuses: [Status] = try await client.get(endpoint: timeline.endpoint(sinceId: lastId)) + let newStatuses: [Status] = try await client.get(endpoint: timeline.endpoint(sinceId: nil, maxId: lastId)) statuses.append(contentsOf: newStatuses) statusesState = .display(statuses: statuses, nextPageState: .hasNextPage) } catch {