Timeline: Add filter for followed tags
This commit is contained in:
parent
62b96cac69
commit
dcdd8402e9
|
@ -43,6 +43,8 @@ extension View {
|
|||
StatusEditorView(mode: .edit(status: status))
|
||||
case let .quoteStatusEditor(status):
|
||||
StatusEditorView(mode: .quote(status: status))
|
||||
case let .mentionStatusEditor(account, visibility):
|
||||
StatusEditorView(mode: .mention(account: account, visibility: visibility))
|
||||
case let .listEdit(list):
|
||||
ListEditView(list: list)
|
||||
case let .listAddAccount(account):
|
||||
|
|
|
@ -87,6 +87,18 @@ struct TimelineTab: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !currentAccount.tags.isEmpty {
|
||||
Menu("Followed Tags") {
|
||||
ForEach(currentAccount.tags) { tag in
|
||||
Button {
|
||||
timeline = .hashtag(tag: tag.name, accountId: nil)
|
||||
} label: {
|
||||
Label("#\(tag.name)", systemImage: "number")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var accountButton: some View {
|
||||
|
|
|
@ -61,8 +61,8 @@ public struct AccountDetailView: View {
|
|||
pinnedPostsView
|
||||
}
|
||||
StatusesListView(fetcher: viewModel)
|
||||
case let .followedTags(tags):
|
||||
makeTagsListView(tags: tags)
|
||||
case .followedTags:
|
||||
tagsListView
|
||||
case .lists:
|
||||
listsListView
|
||||
}
|
||||
|
@ -70,21 +70,6 @@ public struct AccountDetailView: View {
|
|||
}
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(theme.primaryBackgroundColor)
|
||||
.toolbar {
|
||||
if viewModel.relationship?.following == true, let account = viewModel.account {
|
||||
ToolbarItem {
|
||||
Menu {
|
||||
Button {
|
||||
routeurPath.presentedSheet = .listAddAccount(account: account)
|
||||
} label: {
|
||||
Label("Add/Remove from lists", systemImage: "list.bullet")
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "ellipsis")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
Task {
|
||||
|
@ -113,16 +98,7 @@ public struct AccountDetailView: View {
|
|||
.edgesIgnoringSafeArea(.top)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .principal) {
|
||||
if scrollOffset < -200 {
|
||||
switch viewModel.accountState {
|
||||
case let .data(account):
|
||||
account.displayNameWithEmojis.font(.headline)
|
||||
default:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
toolbarContent
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,9 +215,9 @@ public struct AccountDetailView: View {
|
|||
}
|
||||
}
|
||||
|
||||
private func makeTagsListView(tags: [Tag]) -> some View {
|
||||
private var tagsListView: some View {
|
||||
Group {
|
||||
ForEach(tags) { tag in
|
||||
ForEach(currentAccount.tags) { tag in
|
||||
HStack {
|
||||
TagRowView(tag: tag)
|
||||
Spacer()
|
||||
|
@ -250,6 +226,8 @@ public struct AccountDetailView: View {
|
|||
.padding(.horizontal, .layoutPadding)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}.task {
|
||||
await currentAccount.fetchFollowedTags()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,6 +258,9 @@ public struct AccountDetailView: View {
|
|||
}
|
||||
.padding(.horizontal, .layoutPadding)
|
||||
}
|
||||
.task {
|
||||
await currentAccount.fetchLists()
|
||||
}
|
||||
.alert("Create a new list", isPresented: $isCreateListAlertPresented) {
|
||||
TextField("List name", text: $createListTitle)
|
||||
Button("Cancel") {
|
||||
|
@ -316,6 +297,55 @@ public struct AccountDetailView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ToolbarContentBuilder
|
||||
private var toolbarContent: some ToolbarContent {
|
||||
ToolbarItem(placement: .principal) {
|
||||
if scrollOffset < -200 {
|
||||
switch viewModel.accountState {
|
||||
case let .data(account):
|
||||
account.displayNameWithEmojis.font(.headline)
|
||||
default:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Menu {
|
||||
if let account = viewModel.account {
|
||||
Section(account.acct) {
|
||||
if !viewModel.isCurrentUser {
|
||||
Button {
|
||||
routeurPath.presentedSheet = .mentionStatusEditor(account: account, visibility: .pub)
|
||||
} label: {
|
||||
Label("Mention", systemImage: "at")
|
||||
}
|
||||
Button {
|
||||
routeurPath.presentedSheet = .mentionStatusEditor(account: account, visibility: .direct)
|
||||
} label: {
|
||||
Label("Message", systemImage: "tray.full")
|
||||
}
|
||||
Divider()
|
||||
}
|
||||
|
||||
if viewModel.relationship?.following == true {
|
||||
Button {
|
||||
routeurPath.presentedSheet = .listAddAccount(account: account)
|
||||
} label: {
|
||||
Label("Add/Remove from lists", systemImage: "list.bullet")
|
||||
}
|
||||
}
|
||||
if let url = account.url {
|
||||
ShareLink(item: url)
|
||||
}
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "ellipsis")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AccountDetailView_Previews: PreviewProvider {
|
||||
|
|
|
@ -38,7 +38,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
}
|
||||
|
||||
enum TabState {
|
||||
case followedTags(tags: [Tag])
|
||||
case followedTags
|
||||
case statuses(statusesState: StatusesState)
|
||||
case lists
|
||||
}
|
||||
|
@ -62,7 +62,6 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
@Published var pinned: [Status] = []
|
||||
@Published var favourites: [Status] = []
|
||||
private var favouritesNextPage: LinkHandler?
|
||||
@Published var followedTags: [Tag] = []
|
||||
@Published var featuredTags: [FeaturedTag] = []
|
||||
@Published var fields: [Account.Field] = []
|
||||
@Published var familliarFollowers: [Account] = []
|
||||
|
@ -108,16 +107,11 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
self.featuredTags = try await featuredTags
|
||||
self.featuredTags.sort { $0.statusesCountInt > $1.statusesCountInt }
|
||||
self.fields = loadedAccount.fields
|
||||
if isCurrentUser {
|
||||
async let followedTags: [Tag] = client.get(endpoint: Accounts.followedTags)
|
||||
self.followedTags = try await followedTags
|
||||
} else {
|
||||
if client.isAuth {
|
||||
async let relationships: [Relationshionship] = client.get(endpoint: Accounts.relationships(ids: [accountId]))
|
||||
async let familliarFollowers: [FamilliarAccounts] = client.get(endpoint: Accounts.familiarFollowers(withAccount: accountId))
|
||||
self.relationship = try await relationships.first
|
||||
self.familliarFollowers = try await familliarFollowers.first?.accounts ?? []
|
||||
}
|
||||
if client.isAuth {
|
||||
async let relationships: [Relationshionship] = client.get(endpoint: Accounts.relationships(ids: [accountId]))
|
||||
async let familliarFollowers: [FamilliarAccounts] = client.get(endpoint: Accounts.familiarFollowers(withAccount: accountId))
|
||||
self.relationship = try await relationships.first
|
||||
self.familliarFollowers = try await familliarFollowers.first?.accounts ?? []
|
||||
}
|
||||
accountState = .data(account: loadedAccount)
|
||||
} catch {
|
||||
|
@ -215,7 +209,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
tabState = .statuses(statusesState: .display(statuses: favourites,
|
||||
nextPageState: favouritesNextPage != nil ? .hasNextPage : .none))
|
||||
case .followedTags:
|
||||
tabState = .followedTags(tags: followedTags)
|
||||
tabState = .followedTags
|
||||
case .lists:
|
||||
tabState = .lists
|
||||
}
|
||||
|
|
|
@ -6,18 +6,25 @@ import Network
|
|||
public class CurrentAccount: ObservableObject {
|
||||
@Published public private(set) var account: Account?
|
||||
@Published public private(set) var lists: [List] = []
|
||||
@Published public private(set) var tags: [Tag] = []
|
||||
|
||||
private var client: Client?
|
||||
|
||||
public init() {
|
||||
|
||||
}
|
||||
public init() { }
|
||||
|
||||
public func setClient(client: Client) {
|
||||
self.client = client
|
||||
Task {
|
||||
await fetchCurrentAccount()
|
||||
await fetchLists()
|
||||
|
||||
Task(priority: .userInitiated) {
|
||||
await fetchUserData()
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchUserData() async {
|
||||
await withTaskGroup(of: Void.self) { group in
|
||||
group.addTask { await self.fetchCurrentAccount() }
|
||||
group.addTask { await self.fetchLists() }
|
||||
group.addTask { await self.fetchFollowedTags() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,9 +33,7 @@ public class CurrentAccount: ObservableObject {
|
|||
account = nil
|
||||
return
|
||||
}
|
||||
Task {
|
||||
account = try? await client.get(endpoint: Accounts.verifyCredentials)
|
||||
}
|
||||
account = try? await client.get(endpoint: Accounts.verifyCredentials)
|
||||
}
|
||||
|
||||
public func fetchLists() async {
|
||||
|
@ -40,6 +45,15 @@ public class CurrentAccount: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
public func fetchFollowedTags() async {
|
||||
guard let client, client.isAuth else { return }
|
||||
do {
|
||||
tags = try await client.get(endpoint: Accounts.followedTags)
|
||||
} catch {
|
||||
tags = []
|
||||
}
|
||||
}
|
||||
|
||||
public func createList(title: String) async {
|
||||
guard let client else { return }
|
||||
do {
|
||||
|
@ -57,4 +71,26 @@ public class CurrentAccount: ObservableObject {
|
|||
lists.append(list)
|
||||
}
|
||||
}
|
||||
|
||||
public func followTag(id: String) async -> Tag? {
|
||||
guard let client else { return nil }
|
||||
do {
|
||||
let tag: Tag = try await client.post(endpoint: Tags.follow(id: id))
|
||||
tags.append(tag)
|
||||
return tag
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public func unfollowTag(id: String) async -> Tag? {
|
||||
guard let client else { return nil }
|
||||
do {
|
||||
let tag: Tag = try await client.post(endpoint: Tags.unfollow(id: id))
|
||||
tags.removeAll{ $0.id == tag.id }
|
||||
return tag
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,12 +20,13 @@ public enum SheetDestinations: Identifiable {
|
|||
case editStatusEditor(status: Status)
|
||||
case replyToStatusEditor(status: Status)
|
||||
case quoteStatusEditor(status: Status)
|
||||
case mentionStatusEditor(account: Account, visibility: Models.Visibility)
|
||||
case listEdit(list: Models.List)
|
||||
case listAddAccount(account: Account)
|
||||
|
||||
public var id: String {
|
||||
switch self {
|
||||
case .editStatusEditor, .newStatusEditor, .replyToStatusEditor, .quoteStatusEditor:
|
||||
case .editStatusEditor, .newStatusEditor, .replyToStatusEditor, .quoteStatusEditor, .mentionStatusEditor:
|
||||
return "statusEditor"
|
||||
case .listEdit:
|
||||
return "listEdit"
|
||||
|
|
|
@ -83,19 +83,23 @@ public class StreamWatcher: ObservableObject {
|
|||
} catch {
|
||||
print("Error decoding streaming event: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
self.receiveMessage()
|
||||
|
||||
case .failure:
|
||||
self.stopWatching()
|
||||
self.connect()
|
||||
if let watchedStream = self.watchedStream {
|
||||
self.watch(stream: watchedStream)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(10)) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.stopWatching()
|
||||
self.connect()
|
||||
if let watchedStream = self.watchedStream {
|
||||
self.watch(stream: watchedStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.receiveMessage()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ public struct Account: Codable, Identifiable, Equatable, Hashable {
|
|||
public let fields: [Field]
|
||||
public let locked: Bool
|
||||
public let emojis: [Emoji]
|
||||
public let url: URL?
|
||||
|
||||
public static func placeholder() -> Account {
|
||||
.init(id: UUID().uuidString,
|
||||
|
@ -46,7 +47,8 @@ public struct Account: Codable, Identifiable, Equatable, Hashable {
|
|||
lastStatusAt: nil,
|
||||
fields: [],
|
||||
locked: false,
|
||||
emojis: [])
|
||||
emojis: [],
|
||||
url: nil)
|
||||
}
|
||||
|
||||
public static func placeholders() -> [Account] {
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import Foundation
|
||||
|
||||
public struct AppInfo {
|
||||
public static let clientName = "IceCubesApp"
|
||||
public static let scheme = "icecubesapp://"
|
||||
public static let scopes = "read write follow push"
|
||||
public static let weblink = "https://github.com/Dimillian/IceCubesApp"
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import Foundation
|
||||
import Models
|
||||
|
||||
public enum Apps: Endpoint {
|
||||
case registerApp
|
||||
|
@ -14,10 +15,10 @@ public enum Apps: Endpoint {
|
|||
switch self {
|
||||
case .registerApp:
|
||||
return [
|
||||
.init(name: "client_name", value: "IceCubesApp"),
|
||||
.init(name: "redirect_uris", value: "icecubesapp://"),
|
||||
.init(name: "scopes", value: "read write follow push"),
|
||||
.init(name: "website", value: "https://github.com/Dimillian/IceCubesApp")
|
||||
.init(name: "client_name", value: AppInfo.clientName),
|
||||
.init(name: "redirect_uris", value: AppInfo.scheme),
|
||||
.init(name: "scopes", value: AppInfo.scopes),
|
||||
.init(name: "website", value: AppInfo.weblink)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import Foundation
|
||||
import Models
|
||||
|
||||
public enum Oauth: Endpoint {
|
||||
case authorize(clientId: String)
|
||||
|
@ -19,17 +20,17 @@ public enum Oauth: Endpoint {
|
|||
return [
|
||||
.init(name: "response_type", value: "code"),
|
||||
.init(name: "client_id", value: clientId),
|
||||
.init(name: "redirect_uri", value: "icecubesapp://"),
|
||||
.init(name: "scope", value: "read write follow push")
|
||||
.init(name: "redirect_uri", value: AppInfo.scheme),
|
||||
.init(name: "scope", value: AppInfo.scopes)
|
||||
]
|
||||
case let .token(code, clientId, clientSecret):
|
||||
return [
|
||||
.init(name: "grant_type", value: "authorization_code"),
|
||||
.init(name: "client_id", value: clientId),
|
||||
.init(name: "client_secret", value: clientSecret),
|
||||
.init(name: "redirect_uri", value: "icecubesapp://"),
|
||||
.init(name: "redirect_uri", value: AppInfo.scheme),
|
||||
.init(name: "code", value: code),
|
||||
.init(name: "scope", value: "read write follow push")
|
||||
.init(name: "scope", value: AppInfo.scopes)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,11 +62,13 @@ struct StatusEditorAccessoryView: View {
|
|||
|
||||
private var visibilityMenu: some View {
|
||||
Menu {
|
||||
ForEach(Models.Visibility.allCases, id: \.self) { visibility in
|
||||
Button {
|
||||
viewModel.visibility = visibility
|
||||
} label: {
|
||||
Label(visibility.title, systemImage: visibility.iconName)
|
||||
Section("Post visibility") {
|
||||
ForEach(Models.Visibility.allCases, id: \.self) { visibility in
|
||||
Button {
|
||||
viewModel.visibility = visibility
|
||||
} label: {
|
||||
Label(visibility.title, systemImage: visibility.iconName)
|
||||
}
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
|
|
|
@ -82,7 +82,7 @@ public class StatusEditorViewModel: ObservableObject {
|
|||
isPosting = true
|
||||
let postStatus: Status?
|
||||
switch mode {
|
||||
case .new, .replyTo, .quote:
|
||||
case .new, .replyTo, .quote, .mention:
|
||||
postStatus = try await client.post(endpoint: Statuses.postStatus(status: statusText.string,
|
||||
inReplyTo: mode.replyToStatus?.id,
|
||||
mediaIds: mediasImages.compactMap{ $0.mediaAttachement?.id },
|
||||
|
@ -117,6 +117,10 @@ public class StatusEditorViewModel: ObservableObject {
|
|||
visibility = status.visibility
|
||||
statusText = .init(string: mentionString)
|
||||
selectedRange = .init(location: mentionString.utf16.count, length: 0)
|
||||
case let .mention(account, visibility):
|
||||
statusText = .init(string: "@\(account.acct) ")
|
||||
self.visibility = visibility
|
||||
selectedRange = .init(location: statusText.string.utf16.count, length: 0)
|
||||
case let .edit(status):
|
||||
statusText = .init(status.content.asSafeAttributedString)
|
||||
selectedRange = .init(location: statusText.string.utf16.count, length: 0)
|
||||
|
|
|
@ -6,6 +6,7 @@ extension StatusEditorViewModel {
|
|||
case new
|
||||
case edit(status: Status)
|
||||
case quote(status: Status)
|
||||
case mention(account: Account, visibility: Visibility)
|
||||
|
||||
var isEditing: Bool {
|
||||
switch self {
|
||||
|
@ -27,7 +28,7 @@ extension StatusEditorViewModel {
|
|||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .new:
|
||||
case .new, .mention:
|
||||
return "New Post"
|
||||
case .edit:
|
||||
return "Edit your post"
|
||||
|
|
|
@ -10,7 +10,7 @@ extension Visibility {
|
|||
case .priv:
|
||||
return "lock"
|
||||
case .direct:
|
||||
return "at.circle"
|
||||
return "tray.full"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import Foundation
|
||||
import SwiftUI
|
||||
import Env
|
||||
|
||||
struct StatusRowContextMenu: View {
|
||||
@EnvironmentObject private var account: CurrentAccount
|
||||
@EnvironmentObject private var routeurPath: RouterPath
|
||||
@ObservedObject var viewModel: StatusRowViewModel
|
||||
|
||||
var body: some View {
|
||||
Button { Task {
|
||||
if viewModel.isFavourited {
|
||||
await viewModel.unFavourite()
|
||||
} else {
|
||||
await viewModel.favourite()
|
||||
}
|
||||
} } label: {
|
||||
Label(viewModel.isFavourited ? "Unfavorite" : "Favorite", systemImage: "star")
|
||||
}
|
||||
Button { Task {
|
||||
if viewModel.isReblogged {
|
||||
await viewModel.unReblog()
|
||||
} else {
|
||||
await viewModel.reblog()
|
||||
}
|
||||
} } label: {
|
||||
Label(viewModel.isReblogged ? "Unboost" : "Boost", systemImage: "arrow.left.arrow.right.circle")
|
||||
}
|
||||
|
||||
if viewModel.status.visibility == .pub {
|
||||
Button {
|
||||
routeurPath.presentedSheet = .quoteStatusEditor(status: viewModel.status)
|
||||
} label: {
|
||||
Label("Quote this post", systemImage: "quote.bubble")
|
||||
}
|
||||
}
|
||||
|
||||
if let url = viewModel.status.reblog?.url ?? viewModel.status.url {
|
||||
Button { UIApplication.shared.open(url) } label: {
|
||||
Label("View in Browser", systemImage: "safari")
|
||||
}
|
||||
}
|
||||
|
||||
if account.account?.id == viewModel.status.account.id {
|
||||
Section("Your post") {
|
||||
Button {
|
||||
Task {
|
||||
if viewModel.isPinned {
|
||||
await viewModel.unPin()
|
||||
} else {
|
||||
await viewModel.pin()
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Label(viewModel.isPinned ? "Unpin": "Pin", systemImage: viewModel.isPinned ? "pin.fill" : "pin")
|
||||
}
|
||||
Button {
|
||||
routeurPath.presentedSheet = .editStatusEditor(status: viewModel.status)
|
||||
} label: {
|
||||
Label("Edit", systemImage: "pencil")
|
||||
}
|
||||
Button(role: .destructive) { Task { await viewModel.delete() } } label: {
|
||||
Label("Delete", systemImage: "trash")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Section(viewModel.status.account.acct) {
|
||||
Button {
|
||||
routeurPath.presentedSheet = .mentionStatusEditor(account: viewModel.status.account, visibility: .pub)
|
||||
} label: {
|
||||
Label("Mention", systemImage: "at")
|
||||
}
|
||||
Button {
|
||||
routeurPath.presentedSheet = .mentionStatusEditor(account: viewModel.status.account, visibility: .direct)
|
||||
} label: {
|
||||
Label("Message", systemImage: "tray.full")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -62,7 +62,7 @@ public struct StatusRowView: View {
|
|||
}
|
||||
}
|
||||
.contextMenu {
|
||||
contextMenu
|
||||
StatusRowContextMenu(viewModel: viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ public struct StatusRowView: View {
|
|||
|
||||
private var menuButton: some View {
|
||||
Menu {
|
||||
contextMenu
|
||||
StatusRowContextMenu(viewModel: viewModel)
|
||||
} label: {
|
||||
Image(systemName: "ellipsis")
|
||||
.frame(width: 30, height: 30)
|
||||
|
@ -217,64 +217,4 @@ public struct StatusRowView: View {
|
|||
.foregroundColor(.gray)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var contextMenu: some View {
|
||||
Button { Task {
|
||||
if viewModel.isFavourited {
|
||||
await viewModel.unFavourite()
|
||||
} else {
|
||||
await viewModel.favourite()
|
||||
}
|
||||
} } label: {
|
||||
Label(viewModel.isFavourited ? "Unfavorite" : "Favorite", systemImage: "star")
|
||||
}
|
||||
Button { Task {
|
||||
if viewModel.isReblogged {
|
||||
await viewModel.unReblog()
|
||||
} else {
|
||||
await viewModel.reblog()
|
||||
}
|
||||
} } label: {
|
||||
Label(viewModel.isReblogged ? "Unboost" : "Boost", systemImage: "arrow.left.arrow.right.circle")
|
||||
}
|
||||
|
||||
if viewModel.status.visibility == .pub {
|
||||
Button {
|
||||
routeurPath.presentedSheet = .quoteStatusEditor(status: viewModel.status)
|
||||
} label: {
|
||||
Label("Quote this post", systemImage: "quote.bubble")
|
||||
}
|
||||
}
|
||||
|
||||
if let url = viewModel.status.reblog?.url ?? viewModel.status.url {
|
||||
Button { UIApplication.shared.open(url) } label: {
|
||||
Label("View in Browser", systemImage: "safari")
|
||||
}
|
||||
}
|
||||
|
||||
if account.account?.id == viewModel.status.account.id {
|
||||
Section("Your post") {
|
||||
Button {
|
||||
Task {
|
||||
if viewModel.isPinned {
|
||||
await viewModel.unPin()
|
||||
} else {
|
||||
await viewModel.pin()
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Label(viewModel.isPinned ? "Unpin": "Pin", systemImage: viewModel.isPinned ? "pin.fill" : "pin")
|
||||
}
|
||||
Button {
|
||||
routeurPath.presentedSheet = .editStatusEditor(status: viewModel.status)
|
||||
} label: {
|
||||
Label("Edit", systemImage: "pencil")
|
||||
}
|
||||
Button(role: .destructive) { Task { await viewModel.delete() } } label: {
|
||||
Label("Delete", systemImage: "trash")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,9 +139,9 @@ public struct TimelineView: View {
|
|||
Button {
|
||||
Task {
|
||||
if tag.following {
|
||||
await viewModel.unfollowTag(id: tag.name)
|
||||
viewModel.tag = await account.unfollowTag(id: tag.name)
|
||||
} else {
|
||||
await viewModel.followTag(id: tag.name)
|
||||
viewModel.tag = await account.followTag(id: tag.name)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
|
|
|
@ -24,6 +24,7 @@ class TimelineViewModel: ObservableObject, StatusesFetcher {
|
|||
if oldValue != timeline {
|
||||
statuses = []
|
||||
pendingStatuses = []
|
||||
tag = nil
|
||||
}
|
||||
await fetchStatuses(userIntent: false)
|
||||
switch timeline {
|
||||
|
@ -143,20 +144,6 @@ class TimelineViewModel: ObservableObject, StatusesFetcher {
|
|||
} catch {}
|
||||
}
|
||||
|
||||
func followTag(id: String) async {
|
||||
guard let client else { return }
|
||||
do {
|
||||
tag = try await client.post(endpoint: Tags.follow(id: id))
|
||||
} catch {}
|
||||
}
|
||||
|
||||
func unfollowTag(id: String) async {
|
||||
guard let client else { return }
|
||||
do {
|
||||
tag = try await client.post(endpoint: Tags.unfollow(id: id))
|
||||
} catch {}
|
||||
}
|
||||
|
||||
func handleEvent(event: any StreamEvent, currentAccount: CurrentAccount) {
|
||||
if let event = event as? StreamEventUpdate {
|
||||
if event.status.account.id == currentAccount.account?.id,
|
||||
|
|
Loading…
Reference in New Issue