mirror of
https://github.com/metabolist/metatext.git
synced 2025-02-16 14:05:14 +00:00
Account statuses wip
This commit is contained in:
parent
92bc777a7e
commit
55ba5f856a
16 changed files with 236 additions and 39 deletions
|
@ -114,6 +114,16 @@ public extension ContentDatabase {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func insert(accounts: [Account]) -> AnyPublisher<Never, Error> {
|
||||||
|
databaseQueue.writePublisher {
|
||||||
|
for account in accounts {
|
||||||
|
try account.save($0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ignoreOutput()
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
func setLists(_ lists: [MastodonList]) -> AnyPublisher<Never, Error> {
|
func setLists(_ lists: [MastodonList]) -> AnyPublisher<Never, Error> {
|
||||||
databaseQueue.writePublisher {
|
databaseQueue.writePublisher {
|
||||||
for list in lists {
|
for list in lists {
|
||||||
|
@ -246,6 +256,20 @@ public extension ContentDatabase {
|
||||||
.publisher(in: databaseQueue)
|
.publisher(in: databaseQueue)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func accountObservation(id: String) -> AnyPublisher<Account?, Error> {
|
||||||
|
ValueObservation.tracking(AccountRecord.filter(Column("id") == id).accountResultRequest.fetchOne)
|
||||||
|
.removeDuplicates()
|
||||||
|
.publisher(in: databaseQueue)
|
||||||
|
.map {
|
||||||
|
if let result = $0 {
|
||||||
|
return Account(result: result)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension ContentDatabase {
|
private extension ContentDatabase {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum AccountStatusCollection: String, Codable {
|
public enum AccountStatusCollection: String, Codable, CaseIterable {
|
||||||
case statuses
|
case statuses
|
||||||
case statusesAndReplies
|
case statusesAndReplies
|
||||||
case media
|
case media
|
||||||
|
|
17
Extensions/AccountStatusCollection+Extensions.swift
Normal file
17
Extensions/AccountStatusCollection+Extensions.swift
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import ViewModels
|
||||||
|
|
||||||
|
extension AccountStatusCollection {
|
||||||
|
var title: String {
|
||||||
|
switch self {
|
||||||
|
case .statuses:
|
||||||
|
return NSLocalizedString("account.statuses", comment: "")
|
||||||
|
case .statusesAndReplies:
|
||||||
|
return NSLocalizedString("account.statuses-and-replies", comment: "")
|
||||||
|
case .media:
|
||||||
|
return NSLocalizedString("account.media", comment: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
"account.statuses" = "Posts";
|
||||||
|
"account.statuses-and-replies" = "Posts & Replies";
|
||||||
|
"account.media" = "Media";
|
||||||
"add" = "Add";
|
"add" = "Add";
|
||||||
"apns-default-message" = "New notification";
|
"apns-default-message" = "New notification";
|
||||||
"add-identity.instance-url" = "Instance URL";
|
"add-identity.instance-url" = "Instance URL";
|
||||||
|
|
|
@ -6,6 +6,7 @@ import Mastodon
|
||||||
|
|
||||||
public enum AccountEndpoint {
|
public enum AccountEndpoint {
|
||||||
case verifyCredentials
|
case verifyCredentials
|
||||||
|
case accounts(id: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AccountEndpoint: Endpoint {
|
extension AccountEndpoint: Endpoint {
|
||||||
|
@ -18,12 +19,13 @@ extension AccountEndpoint: Endpoint {
|
||||||
public var pathComponentsInContext: [String] {
|
public var pathComponentsInContext: [String] {
|
||||||
switch self {
|
switch self {
|
||||||
case .verifyCredentials: return ["verify_credentials"]
|
case .verifyCredentials: return ["verify_credentials"]
|
||||||
|
case let .accounts(id): return [id]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var method: HTTPMethod {
|
public var method: HTTPMethod {
|
||||||
switch self {
|
switch self {
|
||||||
case .verifyCredentials: return .get
|
case .verifyCredentials, .accounts: return .get
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
D0030982250C6C8500EACB32 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0030981250C6C8500EACB32 /* URL+Extensions.swift */; };
|
D0030982250C6C8500EACB32 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0030981250C6C8500EACB32 /* URL+Extensions.swift */; };
|
||||||
|
D01EF22425182B1F00650C6B /* AccountHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01EF22325182B1F00650C6B /* AccountHeaderView.swift */; };
|
||||||
D01F41D924F880C400D55A2D /* TouchFallthroughTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */; };
|
D01F41D924F880C400D55A2D /* TouchFallthroughTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */; };
|
||||||
D01F41E424F8889700D55A2D /* AttachmentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41E224F8889700D55A2D /* AttachmentsView.swift */; };
|
D01F41E424F8889700D55A2D /* AttachmentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41E224F8889700D55A2D /* AttachmentsView.swift */; };
|
||||||
D02E1F95250B13210071AD56 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02E1F94250B13210071AD56 /* SafariView.swift */; };
|
D02E1F95250B13210071AD56 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02E1F94250B13210071AD56 /* SafariView.swift */; };
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
D0625E5F250F0CFF00502611 /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0625E5E250F0CFF00502611 /* StatusView.swift */; };
|
D0625E5F250F0CFF00502611 /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0625E5E250F0CFF00502611 /* StatusView.swift */; };
|
||||||
D06B492324D4611300642749 /* KingfisherSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = D06B492224D4611300642749 /* KingfisherSwiftUI */; };
|
D06B492324D4611300642749 /* KingfisherSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = D06B492224D4611300642749 /* KingfisherSwiftUI */; };
|
||||||
D0B32F50250B373600311912 /* RegistrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B32F4F250B373600311912 /* RegistrationView.swift */; };
|
D0B32F50250B373600311912 /* RegistrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B32F4F250B373600311912 /* RegistrationView.swift */; };
|
||||||
|
D0B5FE9B251583DB00478838 /* AccountStatusCollection+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B5FE9A251583DB00478838 /* AccountStatusCollection+Extensions.swift */; };
|
||||||
D0B7434925100DBB00C13DB6 /* StatusView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D0B7434825100DBB00C13DB6 /* StatusView.xib */; };
|
D0B7434925100DBB00C13DB6 /* StatusView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D0B7434825100DBB00C13DB6 /* StatusView.xib */; };
|
||||||
D0BEB1F324F8EE8C001B0F04 /* AttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEB1F224F8EE8C001B0F04 /* AttachmentView.swift */; };
|
D0BEB1F324F8EE8C001B0F04 /* AttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEB1F224F8EE8C001B0F04 /* AttachmentView.swift */; };
|
||||||
D0BEB1F724F9A84B001B0F04 /* LoadingTableFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEB1F624F9A84B001B0F04 /* LoadingTableFooterView.swift */; };
|
D0BEB1F724F9A84B001B0F04 /* LoadingTableFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEB1F624F9A84B001B0F04 /* LoadingTableFooterView.swift */; };
|
||||||
|
@ -84,6 +86,7 @@
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
D0030981250C6C8500EACB32 /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = "<group>"; };
|
D0030981250C6C8500EACB32 /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
|
D01EF22325182B1F00650C6B /* AccountHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountHeaderView.swift; sourceTree = "<group>"; };
|
||||||
D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchFallthroughTextView.swift; sourceTree = "<group>"; };
|
D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchFallthroughTextView.swift; sourceTree = "<group>"; };
|
||||||
D01F41E224F8889700D55A2D /* AttachmentsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentsView.swift; sourceTree = "<group>"; };
|
D01F41E224F8889700D55A2D /* AttachmentsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentsView.swift; sourceTree = "<group>"; };
|
||||||
D02E1F94250B13210071AD56 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
|
D02E1F94250B13210071AD56 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -96,6 +99,7 @@
|
||||||
D085C3BB25008DEC008A6C5E /* DB */ = {isa = PBXFileReference; lastKnownFileType = folder; path = DB; sourceTree = "<group>"; };
|
D085C3BB25008DEC008A6C5E /* DB */ = {isa = PBXFileReference; lastKnownFileType = folder; path = DB; sourceTree = "<group>"; };
|
||||||
D0AD03552505814D0085A466 /* Base16 */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Base16; sourceTree = "<group>"; };
|
D0AD03552505814D0085A466 /* Base16 */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Base16; sourceTree = "<group>"; };
|
||||||
D0B32F4F250B373600311912 /* RegistrationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationView.swift; sourceTree = "<group>"; };
|
D0B32F4F250B373600311912 /* RegistrationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationView.swift; sourceTree = "<group>"; };
|
||||||
|
D0B5FE9A251583DB00478838 /* AccountStatusCollection+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountStatusCollection+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
D0B7434825100DBB00C13DB6 /* StatusView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = StatusView.xib; sourceTree = "<group>"; };
|
D0B7434825100DBB00C13DB6 /* StatusView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = StatusView.xib; sourceTree = "<group>"; };
|
||||||
D0BDF66524FD7A6400C7FA1C /* ServiceLayer */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ServiceLayer; sourceTree = "<group>"; };
|
D0BDF66524FD7A6400C7FA1C /* ServiceLayer */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ServiceLayer; sourceTree = "<group>"; };
|
||||||
D0BEB1F224F8EE8C001B0F04 /* AttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentView.swift; sourceTree = "<group>"; };
|
D0BEB1F224F8EE8C001B0F04 /* AttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -222,7 +226,6 @@
|
||||||
D0625E58250F092900502611 /* StatusListCell.swift */,
|
D0625E58250F092900502611 /* StatusListCell.swift */,
|
||||||
D0625E5E250F0CFF00502611 /* StatusView.swift */,
|
D0625E5E250F0CFF00502611 /* StatusView.swift */,
|
||||||
D0B7434825100DBB00C13DB6 /* StatusView.xib */,
|
D0B7434825100DBB00C13DB6 /* StatusView.xib */,
|
||||||
D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */,
|
|
||||||
);
|
);
|
||||||
path = Status;
|
path = Status;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -254,7 +257,7 @@
|
||||||
D0C7D42024F76169001EBDBB /* Views */ = {
|
D0C7D42024F76169001EBDBB /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D0625E55250F086B00502611 /* Status */,
|
D01EF22325182B1F00650C6B /* AccountHeaderView.swift */,
|
||||||
D0C7D42424F76169001EBDBB /* AddIdentityView.swift */,
|
D0C7D42424F76169001EBDBB /* AddIdentityView.swift */,
|
||||||
D01F41E024F8885900D55A2D /* Attachments */,
|
D01F41E024F8885900D55A2D /* Attachments */,
|
||||||
D0C7D42324F76169001EBDBB /* CustomEmojiText.swift */,
|
D0C7D42324F76169001EBDBB /* CustomEmojiText.swift */,
|
||||||
|
@ -270,8 +273,10 @@
|
||||||
D0C7D42724F76169001EBDBB /* RootView.swift */,
|
D0C7D42724F76169001EBDBB /* RootView.swift */,
|
||||||
D02E1F94250B13210071AD56 /* SafariView.swift */,
|
D02E1F94250B13210071AD56 /* SafariView.swift */,
|
||||||
D0C7D42924F76169001EBDBB /* SecondaryNavigationView.swift */,
|
D0C7D42924F76169001EBDBB /* SecondaryNavigationView.swift */,
|
||||||
|
D0625E55250F086B00502611 /* Status */,
|
||||||
D0C7D42524F76169001EBDBB /* StatusListView.swift */,
|
D0C7D42524F76169001EBDBB /* StatusListView.swift */,
|
||||||
D0C7D42E24F76169001EBDBB /* TabNavigationView.swift */,
|
D0C7D42E24F76169001EBDBB /* TabNavigationView.swift */,
|
||||||
|
D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -305,12 +310,13 @@
|
||||||
D0C7D46824F76169001EBDBB /* Extensions */ = {
|
D0C7D46824F76169001EBDBB /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
D0B5FE9A251583DB00478838 /* AccountStatusCollection+Extensions.swift */,
|
||||||
D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */,
|
D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */,
|
||||||
D0C7D46B24F76169001EBDBB /* NSMutableAttributedString+Extensions.swift */,
|
D0C7D46B24F76169001EBDBB /* NSMutableAttributedString+Extensions.swift */,
|
||||||
D0C7D46A24F76169001EBDBB /* String+Extensions.swift */,
|
D0C7D46A24F76169001EBDBB /* String+Extensions.swift */,
|
||||||
D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */,
|
D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */,
|
||||||
D0C7D46F24F76169001EBDBB /* View+Extensions.swift */,
|
|
||||||
D0030981250C6C8500EACB32 /* URL+Extensions.swift */,
|
D0030981250C6C8500EACB32 /* URL+Extensions.swift */,
|
||||||
|
D0C7D46F24F76169001EBDBB /* View+Extensions.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -506,6 +512,7 @@
|
||||||
D0625E5F250F0CFF00502611 /* StatusView.swift in Sources */,
|
D0625E5F250F0CFF00502611 /* StatusView.swift in Sources */,
|
||||||
D0625E59250F092900502611 /* StatusListCell.swift in Sources */,
|
D0625E59250F092900502611 /* StatusListCell.swift in Sources */,
|
||||||
D0C7D49D24F7616A001EBDBB /* PostingReadingPreferencesView.swift in Sources */,
|
D0C7D49D24F7616A001EBDBB /* PostingReadingPreferencesView.swift in Sources */,
|
||||||
|
D0B5FE9B251583DB00478838 /* AccountStatusCollection+Extensions.swift in Sources */,
|
||||||
D0C7D49E24F7616A001EBDBB /* SecondaryNavigationView.swift in Sources */,
|
D0C7D49E24F7616A001EBDBB /* SecondaryNavigationView.swift in Sources */,
|
||||||
D0C7D4DA24F7616A001EBDBB /* View+Extensions.swift in Sources */,
|
D0C7D4DA24F7616A001EBDBB /* View+Extensions.swift in Sources */,
|
||||||
D0C7D4D524F7616A001EBDBB /* String+Extensions.swift in Sources */,
|
D0C7D4D524F7616A001EBDBB /* String+Extensions.swift in Sources */,
|
||||||
|
@ -514,6 +521,7 @@
|
||||||
D0C7D4D924F7616A001EBDBB /* KingfisherOptionsInfo+Extensions.swift in Sources */,
|
D0C7D4D924F7616A001EBDBB /* KingfisherOptionsInfo+Extensions.swift in Sources */,
|
||||||
D0BEB1FF24F9E5BB001B0F04 /* ListsView.swift in Sources */,
|
D0BEB1FF24F9E5BB001B0F04 /* ListsView.swift in Sources */,
|
||||||
D0C7D49724F7616A001EBDBB /* IdentitiesView.swift in Sources */,
|
D0C7D49724F7616A001EBDBB /* IdentitiesView.swift in Sources */,
|
||||||
|
D01EF22425182B1F00650C6B /* AccountHeaderView.swift in Sources */,
|
||||||
D0C7D49824F7616A001EBDBB /* CustomEmojiText.swift in Sources */,
|
D0C7D49824F7616A001EBDBB /* CustomEmojiText.swift in Sources */,
|
||||||
D01F41E424F8889700D55A2D /* AttachmentsView.swift in Sources */,
|
D01F41E424F8889700D55A2D /* AttachmentsView.swift in Sources */,
|
||||||
D0BEB21124FA2A91001B0F04 /* EditFilterView.swift in Sources */,
|
D0BEB21124FA2A91001B0F04 /* EditFilterView.swift in Sources */,
|
||||||
|
|
|
@ -19,7 +19,12 @@ public struct AccountStatusesService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension AccountStatusesService {
|
public extension AccountStatusesService {
|
||||||
func statusListService(collectionPublisher: AnyPublisher<AccountStatusCollection, Never>) -> StatusListService {
|
func accountObservation() -> AnyPublisher<Account?, Error> {
|
||||||
|
contentDatabase.accountObservation(id: accountID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func statusListService(
|
||||||
|
collectionPublisher: CurrentValueSubject<AccountStatusCollection, Never>) -> StatusListService {
|
||||||
StatusListService(
|
StatusListService(
|
||||||
accountID: accountID,
|
accountID: accountID,
|
||||||
collection: collectionPublisher,
|
collection: collectionPublisher,
|
||||||
|
@ -37,4 +42,10 @@ public extension AccountStatusesService {
|
||||||
.flatMap { contentDatabase.insert(pinnedStatuses: $0, accountID: accountID) }
|
.flatMap { contentDatabase.insert(pinnedStatuses: $0, accountID: accountID) }
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fetchAccount() -> AnyPublisher<Never, Error> {
|
||||||
|
mastodonAPIClient.request(AccountEndpoint.accounts(id: accountID))
|
||||||
|
.flatMap { contentDatabase.insert(accounts: [$0]) }
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ extension StatusListService {
|
||||||
|
|
||||||
init(
|
init(
|
||||||
accountID: String,
|
accountID: String,
|
||||||
collection: AnyPublisher<AccountStatusCollection, Never>,
|
collection: CurrentValueSubject<AccountStatusCollection, Never>,
|
||||||
mastodonAPIClient: MastodonAPIClient,
|
mastodonAPIClient: MastodonAPIClient,
|
||||||
contentDatabase: ContentDatabase) {
|
contentDatabase: ContentDatabase) {
|
||||||
self.init(
|
self.init(
|
||||||
|
@ -63,33 +63,29 @@ extension StatusListService {
|
||||||
filterContext: .account,
|
filterContext: .account,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase) { maxID, minID in
|
contentDatabase: contentDatabase) { maxID, minID in
|
||||||
Just((maxID, minID)).combineLatest(collection).flatMap { params -> AnyPublisher<Never, Error> in
|
let excludeReplies: Bool
|
||||||
let ((maxID, minID), collection) = params
|
let onlyMedia: Bool
|
||||||
let excludeReplies: Bool
|
|
||||||
let onlyMedia: Bool
|
|
||||||
|
|
||||||
switch collection {
|
switch collection.value {
|
||||||
case .statuses:
|
case .statuses:
|
||||||
excludeReplies = true
|
excludeReplies = true
|
||||||
onlyMedia = false
|
onlyMedia = false
|
||||||
case .statusesAndReplies:
|
case .statusesAndReplies:
|
||||||
excludeReplies = false
|
excludeReplies = false
|
||||||
onlyMedia = false
|
onlyMedia = false
|
||||||
case .media:
|
case .media:
|
||||||
excludeReplies = true
|
excludeReplies = true
|
||||||
onlyMedia = true
|
onlyMedia = true
|
||||||
}
|
|
||||||
|
|
||||||
let endpoint = StatusesEndpoint.accountsStatuses(
|
|
||||||
id: accountID,
|
|
||||||
excludeReplies: excludeReplies,
|
|
||||||
onlyMedia: onlyMedia,
|
|
||||||
pinned: false)
|
|
||||||
return mastodonAPIClient.request(Paged(endpoint, maxID: maxID, minID: minID))
|
|
||||||
.flatMap { contentDatabase.insert(statuses: $0, accountID: accountID, collection: collection) }
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
|
||||||
|
let endpoint = StatusesEndpoint.accountsStatuses(
|
||||||
|
id: accountID,
|
||||||
|
excludeReplies: excludeReplies,
|
||||||
|
onlyMedia: onlyMedia,
|
||||||
|
pinned: false)
|
||||||
|
return mastodonAPIClient.request(Paged(endpoint, maxID: maxID, minID: minID))
|
||||||
|
.flatMap { contentDatabase.insert(statuses: $0, accountID: accountID, collection: collection.value) }
|
||||||
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import SafariServices
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import ViewModels
|
import ViewModels
|
||||||
|
|
||||||
final class StatusListViewController: UITableViewController {
|
class StatusListViewController: UITableViewController {
|
||||||
private let viewModel: StatusListViewModel
|
private let viewModel: StatusListViewModel
|
||||||
private let loadingTableFooterView = LoadingTableFooterView()
|
private let loadingTableFooterView = LoadingTableFooterView()
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
@ -77,6 +77,21 @@ final class StatusListViewController: UITableViewController {
|
||||||
self.sizeTableHeaderFooterViews()
|
self.sizeTableHeaderFooterViews()
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
if let accountsStatusesViewModel = viewModel as? AccountStatusesViewModel {
|
||||||
|
// Initial size is to avoid unsatisfiable constraint warning
|
||||||
|
let accountHeaderView = AccountHeaderView(
|
||||||
|
frame: .init(
|
||||||
|
origin: .zero,
|
||||||
|
size: .init(width: 100, height: 100)))
|
||||||
|
accountHeaderView.viewModel = accountsStatusesViewModel
|
||||||
|
accountsStatusesViewModel.$account.dropFirst().receive(on: DispatchQueue.main).sink { [weak self] _ in
|
||||||
|
accountHeaderView.viewModel = accountsStatusesViewModel
|
||||||
|
self?.sizeTableHeaderFooterViews()
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
tableView.tableHeaderView = accountHeaderView
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
|
|
@ -6,20 +6,25 @@ import Mastodon
|
||||||
import ServiceLayer
|
import ServiceLayer
|
||||||
|
|
||||||
public class AccountStatusesViewModel: StatusListViewModel {
|
public class AccountStatusesViewModel: StatusListViewModel {
|
||||||
@Published var collection: AccountStatusCollection
|
@Published public private(set) var account: Account?
|
||||||
|
@Published public var collection = AccountStatusCollection.statuses
|
||||||
private let accountStatusesService: AccountStatusesService
|
private let accountStatusesService: AccountStatusesService
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
init(accountStatusesService: AccountStatusesService) {
|
init(accountStatusesService: AccountStatusesService) {
|
||||||
self.accountStatusesService = accountStatusesService
|
self.accountStatusesService = accountStatusesService
|
||||||
|
|
||||||
var collection = Published(initialValue: AccountStatusCollection.statuses)
|
let collectionSubject = CurrentValueSubject<AccountStatusCollection, Never>(.statuses)
|
||||||
|
|
||||||
_collection = collection
|
|
||||||
|
|
||||||
super.init(
|
super.init(
|
||||||
statusListService: accountStatusesService.statusListService(
|
statusListService: accountStatusesService.statusListService(
|
||||||
collectionPublisher: collection.projectedValue.eraseToAnyPublisher()))
|
collectionPublisher: collectionSubject))
|
||||||
|
|
||||||
|
$collection.sink(receiveValue: collectionSubject.send).store(in: &cancellables)
|
||||||
|
|
||||||
|
accountStatusesService.accountObservation()
|
||||||
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
|
.assign(to: &$account)
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func request(maxID: String? = nil, minID: String? = nil) {
|
public override func request(maxID: String? = nil, minID: String? = nil) {
|
||||||
|
@ -37,3 +42,12 @@ public class AccountStatusesViewModel: StatusListViewModel {
|
||||||
collection == .statuses && statusIDs.first?.contains(status.id) ?? false
|
collection == .statuses && statusIDs.first?.contains(status.id) ?? false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension AccountStatusesViewModel {
|
||||||
|
func fetchAccount() {
|
||||||
|
accountStatusesService.fetchAccount()
|
||||||
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
|
.sink { _ in }
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import ServiceLayer
|
||||||
|
|
||||||
|
public typealias AccountStatusCollection = ServiceLayer.AccountStatusCollection
|
|
@ -28,7 +28,7 @@ public class StatusListViewModel: ObservableObject {
|
||||||
.handleEvents(receiveOutput: { [weak self] in
|
.handleEvents(receiveOutput: { [weak self] in
|
||||||
self?.determineIfScrollPositionShouldBeMaintained(newStatusSections: $0)
|
self?.determineIfScrollPositionShouldBeMaintained(newStatusSections: $0)
|
||||||
self?.cleanViewModelCache(newStatusSections: $0)
|
self?.cleanViewModelCache(newStatusSections: $0)
|
||||||
self?.statuses = Dictionary(uniqueKeysWithValues: $0.reduce([], +).map { ($0.id, $0) })
|
self?.statuses = Dictionary(uniqueKeysWithValues: Set($0.reduce([], +)).map { ($0.id, $0) })
|
||||||
})
|
})
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
|
|
|
@ -123,6 +123,13 @@ public extension StatusViewModel {
|
||||||
.eraseToAnyPublisher())
|
.eraseToAnyPublisher())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func accountSelected() {
|
||||||
|
eventsSubject.send(
|
||||||
|
Just(Event.navigation(.accountID(statusService.status.displayStatus.account.id)))
|
||||||
|
.setFailureType(to: Error.self)
|
||||||
|
.eraseToAnyPublisher())
|
||||||
|
}
|
||||||
|
|
||||||
func toggleFavorited() {
|
func toggleFavorited() {
|
||||||
eventsSubject.send(statusService.toggleFavorited().map { _ in Event.ignorableOutput }.eraseToAnyPublisher())
|
eventsSubject.send(statusService.toggleFavorited().map { _ in Event.ignorableOutput }.eraseToAnyPublisher())
|
||||||
}
|
}
|
||||||
|
|
91
Views/AccountHeaderView.swift
Normal file
91
Views/AccountHeaderView.swift
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Kingfisher
|
||||||
|
import UIKit
|
||||||
|
import ViewModels
|
||||||
|
|
||||||
|
class AccountHeaderView: UIView {
|
||||||
|
let headerImageView = UIImageView()
|
||||||
|
let noteTextView = TouchFallthroughTextView()
|
||||||
|
let segmentedControl = UISegmentedControl()
|
||||||
|
|
||||||
|
var viewModel: AccountStatusesViewModel? {
|
||||||
|
didSet {
|
||||||
|
if let account = viewModel?.account {
|
||||||
|
headerImageView.kf.setImage(with: account.header)
|
||||||
|
|
||||||
|
let noteFont = UIFont.preferredFont(forTextStyle: .callout)
|
||||||
|
let mutableNote = NSMutableAttributedString(attributedString: account.note.attributed)
|
||||||
|
let noteRange = NSRange(location: 0, length: mutableNote.length)
|
||||||
|
mutableNote.removeAttribute(.font, range: noteRange)
|
||||||
|
mutableNote.addAttributes(
|
||||||
|
[.font: noteFont as Any,
|
||||||
|
.foregroundColor: UIColor.label],
|
||||||
|
range: noteRange)
|
||||||
|
mutableNote.insert(emoji: account.emojis, view: noteTextView)
|
||||||
|
mutableNote.resizeAttachments(toLineHeight: noteFont.lineHeight)
|
||||||
|
noteTextView.attributedText = mutableNote
|
||||||
|
noteTextView.isHidden = false
|
||||||
|
} else {
|
||||||
|
noteTextView.isHidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
initializationActions()
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, unavailable)
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension AccountHeaderView {
|
||||||
|
func initializationActions() {
|
||||||
|
let baseStackView = UIStackView()
|
||||||
|
|
||||||
|
addSubview(headerImageView)
|
||||||
|
addSubview(baseStackView)
|
||||||
|
headerImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
baseStackView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
baseStackView.axis = .vertical
|
||||||
|
|
||||||
|
noteTextView.isScrollEnabled = false
|
||||||
|
baseStackView.addArrangedSubview(noteTextView)
|
||||||
|
|
||||||
|
for (index, collection) in AccountStatusCollection.allCases.enumerated() {
|
||||||
|
segmentedControl.insertSegment(
|
||||||
|
action: UIAction(title: collection.title) { [weak self] _ in
|
||||||
|
self?.viewModel?.collection = collection
|
||||||
|
self?.viewModel?.request()
|
||||||
|
},
|
||||||
|
at: index,
|
||||||
|
animated: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
segmentedControl.selectedSegmentIndex = 0
|
||||||
|
|
||||||
|
baseStackView.addArrangedSubview(segmentedControl)
|
||||||
|
|
||||||
|
let headerImageAspectRatioConstraint = headerImageView.heightAnchor.constraint(
|
||||||
|
equalTo: headerImageView.widthAnchor,
|
||||||
|
multiplier: 9 / 16)
|
||||||
|
|
||||||
|
headerImageAspectRatioConstraint.priority = .init(999)
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
headerImageAspectRatioConstraint,
|
||||||
|
headerImageView.topAnchor.constraint(equalTo: topAnchor),
|
||||||
|
headerImageView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||||
|
headerImageView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||||
|
baseStackView.topAnchor.constraint(equalTo: headerImageView.bottomAnchor),
|
||||||
|
baseStackView.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor),
|
||||||
|
baseStackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor),
|
||||||
|
baseStackView.bottomAnchor.constraint(equalTo: bottomAnchor)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
|
@ -145,6 +145,10 @@ private extension StatusView {
|
||||||
avatarButton.setBackgroundImage(highlightedButtonBackgroundImage, for: .highlighted)
|
avatarButton.setBackgroundImage(highlightedButtonBackgroundImage, for: .highlighted)
|
||||||
contextParentAvatarButton.setBackgroundImage(highlightedButtonBackgroundImage, for: .highlighted)
|
contextParentAvatarButton.setBackgroundImage(highlightedButtonBackgroundImage, for: .highlighted)
|
||||||
|
|
||||||
|
let accountAction = UIAction { [weak self] _ in self?.statusConfiguration.viewModel.accountSelected() }
|
||||||
|
|
||||||
|
avatarButton.addAction(accountAction, for: .touchUpInside)
|
||||||
|
|
||||||
let favoriteAction = UIAction { [weak self] _ in self?.statusConfiguration.viewModel.toggleFavorited() }
|
let favoriteAction = UIAction { [weak self] _ in self?.statusConfiguration.viewModel.toggleFavorited() }
|
||||||
|
|
||||||
favoriteButton.addAction(favoriteAction, for: .touchUpInside)
|
favoriteButton.addAction(favoriteAction, for: .touchUpInside)
|
||||||
|
|
Loading…
Reference in a new issue