Add list with trends.
This commit is contained in:
parent
2685acc76d
commit
9dcbd5336b
|
@ -20,6 +20,7 @@ public typealias MaxId = EntityId
|
|||
public typealias MinId = EntityId
|
||||
public typealias Limit = Int
|
||||
public typealias Page = Int
|
||||
public typealias Offset = Int
|
||||
|
||||
public typealias Scope = String
|
||||
public typealias Scopes = [Scope]
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension MastodonClientAuthenticated {
|
||||
|
||||
func statusesTrends(range: Mastodon.PixelfedTrends.TrendRange) async throws -> [Status] {
|
||||
let request = try Self.request(
|
||||
for: baseURL,
|
||||
target: Mastodon.PixelfedTrends.statuses(range),
|
||||
withBearerToken: token
|
||||
)
|
||||
|
||||
return try await downloadJson([Status].self, request: request)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Mastodon {
|
||||
public enum PixelfedTrends {
|
||||
case statuses(TrendRange?)
|
||||
}
|
||||
}
|
||||
|
||||
extension Mastodon.PixelfedTrends: TargetType {
|
||||
public enum TrendRange: String {
|
||||
case daily = "daily"
|
||||
case monthly = "monthly"
|
||||
case yearly = "yearly"
|
||||
}
|
||||
|
||||
fileprivate var apiPath: String { return "/api/pixelfed/v2/discover/posts/trending" }
|
||||
|
||||
/// The path to be appended to `baseURL` to form the full `URL`.
|
||||
public var path: String {
|
||||
switch self {
|
||||
case .statuses(_):
|
||||
return "\(apiPath)"
|
||||
}
|
||||
}
|
||||
|
||||
/// The HTTP method used in the request.
|
||||
public var method: Method {
|
||||
switch self {
|
||||
case .statuses:
|
||||
return .get
|
||||
}
|
||||
}
|
||||
|
||||
/// The parameters to be incoded in the request.
|
||||
public var queryItems: [(String, String)]? {
|
||||
var params: [(String, String)] = []
|
||||
|
||||
var trendRange: TrendRange? = nil
|
||||
|
||||
switch self {
|
||||
case .statuses(let _trendRange):
|
||||
trendRange = _trendRange
|
||||
}
|
||||
|
||||
switch trendRange {
|
||||
case .daily:
|
||||
params.append(("range", "daily"))
|
||||
case .monthly:
|
||||
params.append(("range", "monthly"))
|
||||
case .yearly:
|
||||
params.append(("range", "yearly"))
|
||||
case .none:
|
||||
params.append(("range", "daily"))
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
public var headers: [String: String]? {
|
||||
[:].contentTypeApplicationJson
|
||||
}
|
||||
|
||||
public var httpBody: Data? {
|
||||
nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Mastodon {
|
||||
public enum Trends {
|
||||
case tags(Offset?, Limit?)
|
||||
case statuses(Offset?, Limit?)
|
||||
case links(Offset?, Limit?)
|
||||
}
|
||||
}
|
||||
|
||||
extension Mastodon.Trends: TargetType {
|
||||
fileprivate var apiPath: String { return "/api/v1/trends" }
|
||||
|
||||
/// The path to be appended to `baseURL` to form the full `URL`.
|
||||
public var path: String {
|
||||
switch self {
|
||||
case .tags(_, _):
|
||||
return "\(apiPath)/tags"
|
||||
case .statuses(_, _):
|
||||
return "\(apiPath)/statuses"
|
||||
case .links(_, _):
|
||||
return "\(apiPath)/links"
|
||||
}
|
||||
}
|
||||
|
||||
/// The HTTP method used in the request.
|
||||
public var method: Method {
|
||||
switch self {
|
||||
case .tags, .statuses, .links:
|
||||
return .get
|
||||
}
|
||||
}
|
||||
|
||||
/// The parameters to be incoded in the request.
|
||||
public var queryItems: [(String, String)]? {
|
||||
var params: [(String, String)] = []
|
||||
|
||||
var offset: Offset? = nil
|
||||
var limit: Limit? = nil
|
||||
|
||||
switch self {
|
||||
case .tags(let _offset, let _limit):
|
||||
offset = _offset
|
||||
limit = _limit
|
||||
case .statuses(let _offset, let _limit):
|
||||
offset = _offset
|
||||
limit = _limit
|
||||
case .links(let _offset, let _limit):
|
||||
offset = _offset
|
||||
limit = _limit
|
||||
}
|
||||
|
||||
if let offset {
|
||||
params.append(("offset", "\(offset)"))
|
||||
}
|
||||
if let limit {
|
||||
params.append(("limit", "\(limit)"))
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
public var headers: [String: String]? {
|
||||
[:].contentTypeApplicationJson
|
||||
}
|
||||
|
||||
public var httpBody: Data? {
|
||||
nil
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
F802884F297AEED5000BDD51 /* DatabaseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F802884E297AEED5000BDD51 /* DatabaseError.swift */; };
|
||||
F80864112975537F009F035C /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80864102975537F009F035C /* NotificationService.swift */; };
|
||||
F808641429756666009F035C /* NotificationRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F808641329756666009F035C /* NotificationRow.swift */; };
|
||||
F8163776297C3E3D00E6E04A /* LocalFeedService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8163775297C3E3D00E6E04A /* LocalFeedService.swift */; };
|
||||
F8163776297C3E3D00E6E04A /* PublicTimelineService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8163775297C3E3D00E6E04A /* PublicTimelineService.swift */; };
|
||||
F8210DCF2966B600001D9973 /* ImageRowAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8210DCE2966B600001D9973 /* ImageRowAsync.swift */; };
|
||||
F8210DD52966BB7E001D9973 /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = F8210DD42966BB7E001D9973 /* Nuke */; };
|
||||
F8210DD72966BB7E001D9973 /* NukeExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = F8210DD62966BB7E001D9973 /* NukeExtensions */; };
|
||||
|
@ -70,6 +70,9 @@
|
|||
F88C2482295C3A4F0006098B /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2481295C3A4F0006098B /* StatusView.swift */; };
|
||||
F88C2486295C48030006098B /* HTMLFotmattedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2485295C48030006098B /* HTMLFotmattedText.swift */; };
|
||||
F88E4D42297E69FD0057491A /* StatusesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88E4D41297E69FD0057491A /* StatusesView.swift */; };
|
||||
F88E4D44297E82EB0057491A /* Status+MediaAttachmentType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88E4D43297E82EB0057491A /* Status+MediaAttachmentType.swift */; };
|
||||
F88E4D46297E89DF0057491A /* TrendsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88E4D45297E89DF0057491A /* TrendsService.swift */; };
|
||||
F88E4D48297E90CD0057491A /* TrendStatusesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88E4D47297E90CD0057491A /* TrendStatusesView.swift */; };
|
||||
F88FAD21295F3944009B20C9 /* HomeFeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD20295F3944009B20C9 /* HomeFeedView.swift */; };
|
||||
F88FAD27295F400E009B20C9 /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD26295F400E009B20C9 /* NotificationsView.swift */; };
|
||||
F88FAD2A295F43B8009B20C9 /* AccountData+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD28295F43B8009B20C9 /* AccountData+CoreDataClass.swift */; };
|
||||
|
@ -114,7 +117,7 @@
|
|||
F802884E297AEED5000BDD51 /* DatabaseError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseError.swift; sourceTree = "<group>"; };
|
||||
F80864102975537F009F035C /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
||||
F808641329756666009F035C /* NotificationRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRow.swift; sourceTree = "<group>"; };
|
||||
F8163775297C3E3D00E6E04A /* LocalFeedService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalFeedService.swift; sourceTree = "<group>"; };
|
||||
F8163775297C3E3D00E6E04A /* PublicTimelineService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineService.swift; sourceTree = "<group>"; };
|
||||
F8210DCE2966B600001D9973 /* ImageRowAsync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRowAsync.swift; sourceTree = "<group>"; };
|
||||
F8210DDC2966CF17001D9973 /* StatusData+Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusData+Status.swift"; sourceTree = "<group>"; };
|
||||
F8210DDE2966CFC7001D9973 /* AttachmentData+Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttachmentData+Attachment.swift"; sourceTree = "<group>"; };
|
||||
|
@ -168,6 +171,9 @@
|
|||
F88C2481295C3A4F0006098B /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = "<group>"; };
|
||||
F88C2485295C48030006098B /* HTMLFotmattedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTMLFotmattedText.swift; sourceTree = "<group>"; };
|
||||
F88E4D41297E69FD0057491A /* StatusesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusesView.swift; sourceTree = "<group>"; };
|
||||
F88E4D43297E82EB0057491A /* Status+MediaAttachmentType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Status+MediaAttachmentType.swift"; sourceTree = "<group>"; };
|
||||
F88E4D45297E89DF0057491A /* TrendsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendsService.swift; sourceTree = "<group>"; };
|
||||
F88E4D47297E90CD0057491A /* TrendStatusesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendStatusesView.swift; sourceTree = "<group>"; };
|
||||
F88FAD20295F3944009B20C9 /* HomeFeedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeFeedView.swift; sourceTree = "<group>"; };
|
||||
F88FAD26295F400E009B20C9 /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = "<group>"; };
|
||||
F88FAD28295F43B8009B20C9 /* AccountData+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountData+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
|
@ -257,6 +263,7 @@
|
|||
F88ABD9329687CA4004EF61E /* ComposeView.swift */,
|
||||
F89A46DB296EAACE0062125F /* SettingsView.swift */,
|
||||
F88E4D41297E69FD0057491A /* StatusesView.swift */,
|
||||
F88E4D47297E90CD0057491A /* TrendStatusesView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
|
@ -273,6 +280,7 @@
|
|||
F8C14393296AF21B001FE31D /* Double+Round.swift */,
|
||||
F8984E4C296B648000A2610F /* UIImage+Blurhash.swift */,
|
||||
F8996DEA2971D29D0043EEC6 /* View+Transition.swift */,
|
||||
F88E4D43297E82EB0057491A /* Status+MediaAttachmentType.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
|
@ -427,7 +435,8 @@
|
|||
F85E131F297409CD006A051D /* ErrorsService.swift */,
|
||||
F80864102975537F009F035C /* NotificationService.swift */,
|
||||
F886F256297859E300879356 /* CacheImageService.swift */,
|
||||
F8163775297C3E3D00E6E04A /* LocalFeedService.swift */,
|
||||
F8163775297C3E3D00E6E04A /* PublicTimelineService.swift */,
|
||||
F88E4D45297E89DF0057491A /* TrendsService.swift */,
|
||||
);
|
||||
path = Services;
|
||||
sourceTree = "<group>";
|
||||
|
@ -571,11 +580,12 @@
|
|||
files = (
|
||||
F85D497729640A5200751DF7 /* ImageRow.swift in Sources */,
|
||||
F8210DDF2966CFC7001D9973 /* AttachmentData+Attachment.swift in Sources */,
|
||||
F88E4D44297E82EB0057491A /* Status+MediaAttachmentType.swift in Sources */,
|
||||
F89D6C4229717FDC001DA3D4 /* AccountsSection.swift in Sources */,
|
||||
F80048082961E6DE00E6868A /* StatusDataHandler.swift in Sources */,
|
||||
F866F6A0296040A8002E8F88 /* ApplicationSettings+CoreDataClass.swift in Sources */,
|
||||
F8210DEA2966E4F9001D9973 /* AnimatePlaceholderModifier.swift in Sources */,
|
||||
F8163776297C3E3D00E6E04A /* LocalFeedService.swift in Sources */,
|
||||
F8163776297C3E3D00E6E04A /* PublicTimelineService.swift in Sources */,
|
||||
F886F257297859E300879356 /* CacheImageService.swift in Sources */,
|
||||
F8984E4D296B648000A2610F /* UIImage+Blurhash.swift in Sources */,
|
||||
F897978A2968314A00B22335 /* LoadingIndicator.swift in Sources */,
|
||||
|
@ -608,6 +618,7 @@
|
|||
F86B7223296C4BF500EE59EC /* ContentWarning.swift in Sources */,
|
||||
F83901A6295D8EC000456AE2 /* LabelIcon.swift in Sources */,
|
||||
F8B1E6512973FB7E00EE0D10 /* ToastrService.swift in Sources */,
|
||||
F88E4D48297E90CD0057491A /* TrendStatusesView.swift in Sources */,
|
||||
F89992CE296D92E7005994BF /* AttachmentViewModel.swift in Sources */,
|
||||
F800480A2961EA1900E6868A /* AttachmentDataHandler.swift in Sources */,
|
||||
F80048032961850500E6868A /* AttachmentData+CoreDataClass.swift in Sources */,
|
||||
|
@ -634,6 +645,7 @@
|
|||
F8210DE72966E1D1001D9973 /* Color+Assets.swift in Sources */,
|
||||
F88ABD9429687CA4004EF61E /* ComposeView.swift in Sources */,
|
||||
F86B7214296BFDCE00EE59EC /* UserProfileHeader.swift in Sources */,
|
||||
F88E4D46297E89DF0057491A /* TrendsService.swift in Sources */,
|
||||
F85D497D29640D5900751DF7 /* InteractionRow.swift in Sources */,
|
||||
F866F6A729604629002E8F88 /* SignInView.swift in Sources */,
|
||||
F8C14392296AF0B3001FE31D /* String+Exif.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MastodonKit
|
||||
|
||||
extension [Status] {
|
||||
func getStatusesWithImagesOnly() -> [Status] {
|
||||
return self.filter { status in
|
||||
status.mediaAttachments.contains { mediaAttachment in
|
||||
mediaAttachment.type == .image
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -189,7 +189,8 @@ public class HomeTimelineService {
|
|||
public func fetchAllImages(statuses: [Status]) async -> Dictionary<String, Data> {
|
||||
var attachmentUrls: Dictionary<String, URL> = [:]
|
||||
|
||||
statuses.forEach { status in
|
||||
let statusesWithImages = statuses.getStatusesWithImagesOnly()
|
||||
statusesWithImages.forEach { status in
|
||||
status.mediaAttachments.forEach { attachment in
|
||||
attachmentUrls[attachment.id] = attachment.url
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MastodonKit
|
||||
|
||||
public class TrendsService {
|
||||
public static let shared = TrendsService()
|
||||
private init() { }
|
||||
|
||||
public func statuses(accountData: AccountData?,
|
||||
range: Mastodon.PixelfedTrends.TrendRange) async throws -> [Status] {
|
||||
guard let accessToken = accountData?.accessToken, let serverUrl = accountData?.serverUrl else {
|
||||
return []
|
||||
}
|
||||
|
||||
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
|
||||
return try await client.statusesTrends(range: range)
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ struct MainView: View {
|
|||
@FetchRequest(sortDescriptors: [SortDescriptor(\.acct, order: .forward)]) var dbAccounts: FetchedResults<AccountData>
|
||||
|
||||
private enum ViewMode {
|
||||
case home, local, federated, profile, notifications
|
||||
case home, local, federated, profile, notifications, trending
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
|
@ -66,6 +66,9 @@ struct MainView: View {
|
|||
case .home:
|
||||
HomeFeedView(accountId: applicationState.accountData?.id ?? String.empty())
|
||||
.id(applicationState.accountData?.id ?? String.empty())
|
||||
case .trending:
|
||||
TrendStatusesView(accountId: applicationState.accountData?.id ?? String.empty())
|
||||
.id(applicationState.accountData?.id ?? String.empty())
|
||||
case .local:
|
||||
StatusesView(accountId: applicationState.accountData?.id ?? String.empty(), listType: .local)
|
||||
.id(applicationState.accountData?.id ?? String.empty())
|
||||
|
@ -99,6 +102,15 @@ struct MainView: View {
|
|||
Image(systemName: "house")
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
viewMode = .trending
|
||||
} label: {
|
||||
HStack {
|
||||
Text(self.getViewTitle(viewMode: .trending))
|
||||
Image(systemName: "chart.line.uptrend.xyaxis")
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
viewMode = .local
|
||||
|
@ -208,6 +220,8 @@ struct MainView: View {
|
|||
switch viewMode {
|
||||
case .home:
|
||||
return "Home"
|
||||
case .trending:
|
||||
return "Trending"
|
||||
case .local:
|
||||
return "Local"
|
||||
case .federated:
|
||||
|
|
|
@ -69,10 +69,10 @@ struct NotificationsView: View {
|
|||
} else {
|
||||
if self.notifications.isEmpty {
|
||||
VStack {
|
||||
Image(systemName: "person.3.sequence")
|
||||
Image(systemName: "bell")
|
||||
.font(.largeTitle)
|
||||
.padding(.bottom, 4)
|
||||
Text("Unfortunately, there is no one here.")
|
||||
Text("Unfortunately, there is nothing here.")
|
||||
.font(.title3)
|
||||
}.foregroundColor(.lightGrayColor)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ struct SettingsView: View {
|
|||
|
||||
@State private var theme: ColorScheme?
|
||||
@State private var appVersion: String?
|
||||
@State private var appBundleVersion: String?
|
||||
|
||||
var onTintChange: ((TintColor) -> Void)?
|
||||
var onThemeChange: ((Theme) -> Void)?
|
||||
|
@ -45,7 +46,7 @@ struct SettingsView: View {
|
|||
HStack {
|
||||
Text("Version")
|
||||
Spacer()
|
||||
Text(appVersion ?? String.empty())
|
||||
Text("\(appVersion ?? String.empty()) (\(appBundleVersion ?? String.empty()))")
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +61,7 @@ struct SettingsView: View {
|
|||
}
|
||||
.task {
|
||||
self.appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
||||
self.appBundleVersion = Bundle.main.infoDictionary?["CFBundleVersion"] as? String
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification), perform: { _ in
|
||||
self.theme = applicationState.theme.colorScheme() ?? self.getSystemColorScheme()
|
||||
|
|
|
@ -100,7 +100,7 @@ struct StatusesView: View {
|
|||
let statuses = try await self.loadFromApi()
|
||||
var inPlaceStatuses: [StatusViewModel] = []
|
||||
|
||||
for item in statuses {
|
||||
for item in statuses.getStatusesWithImagesOnly() {
|
||||
inPlaceStatuses.append(StatusViewModel(status: item))
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ struct StatusesView: View {
|
|||
}
|
||||
|
||||
var inPlaceStatuses: [StatusViewModel] = []
|
||||
for item in previousStatuses {
|
||||
for item in previousStatuses.getStatusesWithImagesOnly() {
|
||||
inPlaceStatuses.append(StatusViewModel(status: item))
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ struct StatusesView: View {
|
|||
let newestStatuses = try await self.loadFromApi(sinceId: firstStatusId)
|
||||
|
||||
var inPlaceStatuses: [StatusViewModel] = []
|
||||
for item in newestStatuses {
|
||||
for item in newestStatuses.getStatusesWithImagesOnly() {
|
||||
inPlaceStatuses.append(StatusViewModel(status: item))
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import MastodonKit
|
||||
|
||||
struct TrendStatusesView: View {
|
||||
|
||||
@EnvironmentObject private var applicationState: ApplicationState
|
||||
@State public var accountId: String
|
||||
|
||||
@State private var firstLoadFinished = false
|
||||
@State private var tabSelectedValue: Mastodon.PixelfedTrends.TrendRange = .daily
|
||||
|
||||
@State private var statusViewModels: [StatusViewModel] = []
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
|
||||
Picker(selection: $tabSelectedValue, label: Text("")) {
|
||||
Text("Daily").tag(Mastodon.PixelfedTrends.TrendRange.daily)
|
||||
Text("Monthly").tag(Mastodon.PixelfedTrends.TrendRange.monthly)
|
||||
Text("Yearly").tag(Mastodon.PixelfedTrends.TrendRange.yearly)
|
||||
|
||||
}
|
||||
.padding()
|
||||
.pickerStyle(SegmentedPickerStyle())
|
||||
.onChange(of: tabSelectedValue) { _ in
|
||||
Task {
|
||||
do {
|
||||
self.firstLoadFinished = false;
|
||||
self.statusViewModels = []
|
||||
try await self.loadStatuses()
|
||||
} catch {
|
||||
ErrorService.shared.handle(error, message: "Loading statuses failed.", showToastr: !Task.isCancelled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VStack(alignment: .center) {
|
||||
if firstLoadFinished == true {
|
||||
ForEach(self.statusViewModels, id: \.uniqueId) { item in
|
||||
NavigationLink(destination: StatusView(statusId: item.id,
|
||||
imageBlurhash: item.mediaAttachments.first?.blurhash,
|
||||
imageWidth: item.getImageWidth(),
|
||||
imageHeight: item.getImageHeight())
|
||||
.environmentObject(applicationState)) {
|
||||
ImageRowAsync(statusViewModel: item)
|
||||
}
|
||||
.buttonStyle(EmptyButtonStyle())
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationBarTitle("Trends")
|
||||
.overlay(alignment: .center) {
|
||||
if firstLoadFinished == false {
|
||||
LoadingIndicator()
|
||||
} else {
|
||||
if self.statusViewModels.isEmpty {
|
||||
VStack {
|
||||
Image(systemName: "photo.on.rectangle.angled")
|
||||
.font(.largeTitle)
|
||||
.padding(.bottom, 4)
|
||||
Text("Unfortunately, there are no photos here.")
|
||||
.font(.title3)
|
||||
}.foregroundColor(.lightGrayColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
.task {
|
||||
do {
|
||||
try await self.loadStatuses()
|
||||
} catch {
|
||||
ErrorService.shared.handle(error, message: "Loading statuses failed.", showToastr: !Task.isCancelled)
|
||||
}
|
||||
}.refreshable {
|
||||
do {
|
||||
try await self.loadStatuses()
|
||||
} catch {
|
||||
ErrorService.shared.handle(error, message: "Loading statuses failed.", showToastr: !Task.isCancelled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadStatuses() async throws {
|
||||
guard firstLoadFinished == false else {
|
||||
return
|
||||
}
|
||||
|
||||
let statuses = try await TrendsService.shared.statuses(
|
||||
accountData: self.applicationState.accountData,
|
||||
range: tabSelectedValue)
|
||||
|
||||
var inPlaceStatuses: [StatusViewModel] = []
|
||||
|
||||
for item in statuses.getStatusesWithImagesOnly() {
|
||||
inPlaceStatuses.append(StatusViewModel(status: item))
|
||||
}
|
||||
|
||||
self.statusViewModels = inPlaceStatuses
|
||||
self.firstLoadFinished = true
|
||||
}
|
||||
}
|
|
@ -122,14 +122,14 @@ struct InteractionRow: View {
|
|||
) {
|
||||
Label("Favourited by", systemImage: "hand.thumbsup")
|
||||
}
|
||||
|
||||
|
||||
if let url = statusViewModel.url {
|
||||
Divider()
|
||||
|
||||
Link(destination: url) {
|
||||
Label("Open in browser", systemImage: "safari")
|
||||
}
|
||||
|
||||
|
||||
ShareLink(item: url) {
|
||||
Label("Share post", systemImage: "square.and.arrow.up")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue