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 MinId = EntityId
|
||||||
public typealias Limit = Int
|
public typealias Limit = Int
|
||||||
public typealias Page = Int
|
public typealias Page = Int
|
||||||
|
public typealias Offset = Int
|
||||||
|
|
||||||
public typealias Scope = String
|
public typealias Scope = String
|
||||||
public typealias Scopes = [Scope]
|
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 */; };
|
F802884F297AEED5000BDD51 /* DatabaseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F802884E297AEED5000BDD51 /* DatabaseError.swift */; };
|
||||||
F80864112975537F009F035C /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80864102975537F009F035C /* NotificationService.swift */; };
|
F80864112975537F009F035C /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80864102975537F009F035C /* NotificationService.swift */; };
|
||||||
F808641429756666009F035C /* NotificationRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F808641329756666009F035C /* NotificationRow.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 */; };
|
F8210DCF2966B600001D9973 /* ImageRowAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8210DCE2966B600001D9973 /* ImageRowAsync.swift */; };
|
||||||
F8210DD52966BB7E001D9973 /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = F8210DD42966BB7E001D9973 /* Nuke */; };
|
F8210DD52966BB7E001D9973 /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = F8210DD42966BB7E001D9973 /* Nuke */; };
|
||||||
F8210DD72966BB7E001D9973 /* NukeExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = F8210DD62966BB7E001D9973 /* NukeExtensions */; };
|
F8210DD72966BB7E001D9973 /* NukeExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = F8210DD62966BB7E001D9973 /* NukeExtensions */; };
|
||||||
|
@ -70,6 +70,9 @@
|
||||||
F88C2482295C3A4F0006098B /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2481295C3A4F0006098B /* StatusView.swift */; };
|
F88C2482295C3A4F0006098B /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2481295C3A4F0006098B /* StatusView.swift */; };
|
||||||
F88C2486295C48030006098B /* HTMLFotmattedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2485295C48030006098B /* HTMLFotmattedText.swift */; };
|
F88C2486295C48030006098B /* HTMLFotmattedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2485295C48030006098B /* HTMLFotmattedText.swift */; };
|
||||||
F88E4D42297E69FD0057491A /* StatusesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88E4D41297E69FD0057491A /* StatusesView.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 */; };
|
F88FAD21295F3944009B20C9 /* HomeFeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD20295F3944009B20C9 /* HomeFeedView.swift */; };
|
||||||
F88FAD27295F400E009B20C9 /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD26295F400E009B20C9 /* NotificationsView.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 */; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
F88FAD28295F43B8009B20C9 /* AccountData+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountData+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -257,6 +263,7 @@
|
||||||
F88ABD9329687CA4004EF61E /* ComposeView.swift */,
|
F88ABD9329687CA4004EF61E /* ComposeView.swift */,
|
||||||
F89A46DB296EAACE0062125F /* SettingsView.swift */,
|
F89A46DB296EAACE0062125F /* SettingsView.swift */,
|
||||||
F88E4D41297E69FD0057491A /* StatusesView.swift */,
|
F88E4D41297E69FD0057491A /* StatusesView.swift */,
|
||||||
|
F88E4D47297E90CD0057491A /* TrendStatusesView.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -273,6 +280,7 @@
|
||||||
F8C14393296AF21B001FE31D /* Double+Round.swift */,
|
F8C14393296AF21B001FE31D /* Double+Round.swift */,
|
||||||
F8984E4C296B648000A2610F /* UIImage+Blurhash.swift */,
|
F8984E4C296B648000A2610F /* UIImage+Blurhash.swift */,
|
||||||
F8996DEA2971D29D0043EEC6 /* View+Transition.swift */,
|
F8996DEA2971D29D0043EEC6 /* View+Transition.swift */,
|
||||||
|
F88E4D43297E82EB0057491A /* Status+MediaAttachmentType.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -427,7 +435,8 @@
|
||||||
F85E131F297409CD006A051D /* ErrorsService.swift */,
|
F85E131F297409CD006A051D /* ErrorsService.swift */,
|
||||||
F80864102975537F009F035C /* NotificationService.swift */,
|
F80864102975537F009F035C /* NotificationService.swift */,
|
||||||
F886F256297859E300879356 /* CacheImageService.swift */,
|
F886F256297859E300879356 /* CacheImageService.swift */,
|
||||||
F8163775297C3E3D00E6E04A /* LocalFeedService.swift */,
|
F8163775297C3E3D00E6E04A /* PublicTimelineService.swift */,
|
||||||
|
F88E4D45297E89DF0057491A /* TrendsService.swift */,
|
||||||
);
|
);
|
||||||
path = Services;
|
path = Services;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -571,11 +580,12 @@
|
||||||
files = (
|
files = (
|
||||||
F85D497729640A5200751DF7 /* ImageRow.swift in Sources */,
|
F85D497729640A5200751DF7 /* ImageRow.swift in Sources */,
|
||||||
F8210DDF2966CFC7001D9973 /* AttachmentData+Attachment.swift in Sources */,
|
F8210DDF2966CFC7001D9973 /* AttachmentData+Attachment.swift in Sources */,
|
||||||
|
F88E4D44297E82EB0057491A /* Status+MediaAttachmentType.swift in Sources */,
|
||||||
F89D6C4229717FDC001DA3D4 /* AccountsSection.swift in Sources */,
|
F89D6C4229717FDC001DA3D4 /* AccountsSection.swift in Sources */,
|
||||||
F80048082961E6DE00E6868A /* StatusDataHandler.swift in Sources */,
|
F80048082961E6DE00E6868A /* StatusDataHandler.swift in Sources */,
|
||||||
F866F6A0296040A8002E8F88 /* ApplicationSettings+CoreDataClass.swift in Sources */,
|
F866F6A0296040A8002E8F88 /* ApplicationSettings+CoreDataClass.swift in Sources */,
|
||||||
F8210DEA2966E4F9001D9973 /* AnimatePlaceholderModifier.swift in Sources */,
|
F8210DEA2966E4F9001D9973 /* AnimatePlaceholderModifier.swift in Sources */,
|
||||||
F8163776297C3E3D00E6E04A /* LocalFeedService.swift in Sources */,
|
F8163776297C3E3D00E6E04A /* PublicTimelineService.swift in Sources */,
|
||||||
F886F257297859E300879356 /* CacheImageService.swift in Sources */,
|
F886F257297859E300879356 /* CacheImageService.swift in Sources */,
|
||||||
F8984E4D296B648000A2610F /* UIImage+Blurhash.swift in Sources */,
|
F8984E4D296B648000A2610F /* UIImage+Blurhash.swift in Sources */,
|
||||||
F897978A2968314A00B22335 /* LoadingIndicator.swift in Sources */,
|
F897978A2968314A00B22335 /* LoadingIndicator.swift in Sources */,
|
||||||
|
@ -608,6 +618,7 @@
|
||||||
F86B7223296C4BF500EE59EC /* ContentWarning.swift in Sources */,
|
F86B7223296C4BF500EE59EC /* ContentWarning.swift in Sources */,
|
||||||
F83901A6295D8EC000456AE2 /* LabelIcon.swift in Sources */,
|
F83901A6295D8EC000456AE2 /* LabelIcon.swift in Sources */,
|
||||||
F8B1E6512973FB7E00EE0D10 /* ToastrService.swift in Sources */,
|
F8B1E6512973FB7E00EE0D10 /* ToastrService.swift in Sources */,
|
||||||
|
F88E4D48297E90CD0057491A /* TrendStatusesView.swift in Sources */,
|
||||||
F89992CE296D92E7005994BF /* AttachmentViewModel.swift in Sources */,
|
F89992CE296D92E7005994BF /* AttachmentViewModel.swift in Sources */,
|
||||||
F800480A2961EA1900E6868A /* AttachmentDataHandler.swift in Sources */,
|
F800480A2961EA1900E6868A /* AttachmentDataHandler.swift in Sources */,
|
||||||
F80048032961850500E6868A /* AttachmentData+CoreDataClass.swift in Sources */,
|
F80048032961850500E6868A /* AttachmentData+CoreDataClass.swift in Sources */,
|
||||||
|
@ -634,6 +645,7 @@
|
||||||
F8210DE72966E1D1001D9973 /* Color+Assets.swift in Sources */,
|
F8210DE72966E1D1001D9973 /* Color+Assets.swift in Sources */,
|
||||||
F88ABD9429687CA4004EF61E /* ComposeView.swift in Sources */,
|
F88ABD9429687CA4004EF61E /* ComposeView.swift in Sources */,
|
||||||
F86B7214296BFDCE00EE59EC /* UserProfileHeader.swift in Sources */,
|
F86B7214296BFDCE00EE59EC /* UserProfileHeader.swift in Sources */,
|
||||||
|
F88E4D46297E89DF0057491A /* TrendsService.swift in Sources */,
|
||||||
F85D497D29640D5900751DF7 /* InteractionRow.swift in Sources */,
|
F85D497D29640D5900751DF7 /* InteractionRow.swift in Sources */,
|
||||||
F866F6A729604629002E8F88 /* SignInView.swift in Sources */,
|
F866F6A729604629002E8F88 /* SignInView.swift in Sources */,
|
||||||
F8C14392296AF0B3001FE31D /* String+Exif.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> {
|
public func fetchAllImages(statuses: [Status]) async -> Dictionary<String, Data> {
|
||||||
var attachmentUrls: Dictionary<String, URL> = [:]
|
var attachmentUrls: Dictionary<String, URL> = [:]
|
||||||
|
|
||||||
statuses.forEach { status in
|
let statusesWithImages = statuses.getStatusesWithImagesOnly()
|
||||||
|
statusesWithImages.forEach { status in
|
||||||
status.mediaAttachments.forEach { attachment in
|
status.mediaAttachments.forEach { attachment in
|
||||||
attachmentUrls[attachment.id] = attachment.url
|
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>
|
@FetchRequest(sortDescriptors: [SortDescriptor(\.acct, order: .forward)]) var dbAccounts: FetchedResults<AccountData>
|
||||||
|
|
||||||
private enum ViewMode {
|
private enum ViewMode {
|
||||||
case home, local, federated, profile, notifications
|
case home, local, federated, profile, notifications, trending
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
@ -66,6 +66,9 @@ struct MainView: View {
|
||||||
case .home:
|
case .home:
|
||||||
HomeFeedView(accountId: applicationState.accountData?.id ?? String.empty())
|
HomeFeedView(accountId: applicationState.accountData?.id ?? String.empty())
|
||||||
.id(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:
|
case .local:
|
||||||
StatusesView(accountId: applicationState.accountData?.id ?? String.empty(), listType: .local)
|
StatusesView(accountId: applicationState.accountData?.id ?? String.empty(), listType: .local)
|
||||||
.id(applicationState.accountData?.id ?? String.empty())
|
.id(applicationState.accountData?.id ?? String.empty())
|
||||||
|
@ -99,6 +102,15 @@ struct MainView: View {
|
||||||
Image(systemName: "house")
|
Image(systemName: "house")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
viewMode = .trending
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text(self.getViewTitle(viewMode: .trending))
|
||||||
|
Image(systemName: "chart.line.uptrend.xyaxis")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
viewMode = .local
|
viewMode = .local
|
||||||
|
@ -208,6 +220,8 @@ struct MainView: View {
|
||||||
switch viewMode {
|
switch viewMode {
|
||||||
case .home:
|
case .home:
|
||||||
return "Home"
|
return "Home"
|
||||||
|
case .trending:
|
||||||
|
return "Trending"
|
||||||
case .local:
|
case .local:
|
||||||
return "Local"
|
return "Local"
|
||||||
case .federated:
|
case .federated:
|
||||||
|
|
|
@ -69,10 +69,10 @@ struct NotificationsView: View {
|
||||||
} else {
|
} else {
|
||||||
if self.notifications.isEmpty {
|
if self.notifications.isEmpty {
|
||||||
VStack {
|
VStack {
|
||||||
Image(systemName: "person.3.sequence")
|
Image(systemName: "bell")
|
||||||
.font(.largeTitle)
|
.font(.largeTitle)
|
||||||
.padding(.bottom, 4)
|
.padding(.bottom, 4)
|
||||||
Text("Unfortunately, there is no one here.")
|
Text("Unfortunately, there is nothing here.")
|
||||||
.font(.title3)
|
.font(.title3)
|
||||||
}.foregroundColor(.lightGrayColor)
|
}.foregroundColor(.lightGrayColor)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ struct SettingsView: View {
|
||||||
|
|
||||||
@State private var theme: ColorScheme?
|
@State private var theme: ColorScheme?
|
||||||
@State private var appVersion: String?
|
@State private var appVersion: String?
|
||||||
|
@State private var appBundleVersion: String?
|
||||||
|
|
||||||
var onTintChange: ((TintColor) -> Void)?
|
var onTintChange: ((TintColor) -> Void)?
|
||||||
var onThemeChange: ((Theme) -> Void)?
|
var onThemeChange: ((Theme) -> Void)?
|
||||||
|
@ -45,7 +46,7 @@ struct SettingsView: View {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Version")
|
Text("Version")
|
||||||
Spacer()
|
Spacer()
|
||||||
Text(appVersion ?? String.empty())
|
Text("\(appVersion ?? String.empty()) (\(appBundleVersion ?? String.empty()))")
|
||||||
.foregroundColor(.accentColor)
|
.foregroundColor(.accentColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,6 +61,7 @@ struct SettingsView: View {
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
self.appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
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
|
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification), perform: { _ in
|
||||||
self.theme = applicationState.theme.colorScheme() ?? self.getSystemColorScheme()
|
self.theme = applicationState.theme.colorScheme() ?? self.getSystemColorScheme()
|
||||||
|
|
|
@ -100,7 +100,7 @@ struct StatusesView: View {
|
||||||
let statuses = try await self.loadFromApi()
|
let statuses = try await self.loadFromApi()
|
||||||
var inPlaceStatuses: [StatusViewModel] = []
|
var inPlaceStatuses: [StatusViewModel] = []
|
||||||
|
|
||||||
for item in statuses {
|
for item in statuses.getStatusesWithImagesOnly() {
|
||||||
inPlaceStatuses.append(StatusViewModel(status: item))
|
inPlaceStatuses.append(StatusViewModel(status: item))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ struct StatusesView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
var inPlaceStatuses: [StatusViewModel] = []
|
var inPlaceStatuses: [StatusViewModel] = []
|
||||||
for item in previousStatuses {
|
for item in previousStatuses.getStatusesWithImagesOnly() {
|
||||||
inPlaceStatuses.append(StatusViewModel(status: item))
|
inPlaceStatuses.append(StatusViewModel(status: item))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ struct StatusesView: View {
|
||||||
let newestStatuses = try await self.loadFromApi(sinceId: firstStatusId)
|
let newestStatuses = try await self.loadFromApi(sinceId: firstStatusId)
|
||||||
|
|
||||||
var inPlaceStatuses: [StatusViewModel] = []
|
var inPlaceStatuses: [StatusViewModel] = []
|
||||||
for item in newestStatuses {
|
for item in newestStatuses.getStatusesWithImagesOnly() {
|
||||||
inPlaceStatuses.append(StatusViewModel(status: item))
|
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")
|
Label("Favourited by", systemImage: "hand.thumbsup")
|
||||||
}
|
}
|
||||||
|
|
||||||
if let url = statusViewModel.url {
|
if let url = statusViewModel.url {
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
Link(destination: url) {
|
Link(destination: url) {
|
||||||
Label("Open in browser", systemImage: "safari")
|
Label("Open in browser", systemImage: "safari")
|
||||||
}
|
}
|
||||||
|
|
||||||
ShareLink(item: url) {
|
ShareLink(item: url) {
|
||||||
Label("Share post", systemImage: "square.and.arrow.up")
|
Label("Share post", systemImage: "square.and.arrow.up")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue