Add muted/blocked lists.

This commit is contained in:
Marcin Czachursk 2023-03-27 14:52:53 +02:00
parent 8ec3817c52
commit c164c2d9e6
15 changed files with 434 additions and 29 deletions

View File

@ -96,6 +96,12 @@
"userProfile.title.followBack" = "Follow back";
"userProfile.title.follow" = "Follow";
"userProfile.title.instance" = "Instance information";
"userProfile.title.blocks" = "Blocked accounts";
"userProfile.title.mutes" = "Muted accounts";
"userProfile.title.muted" = "Account muted";
"userProfile.title.unmuted" = "Account unmuted";
"userProfile.title.blocked" = "Account blocked";
"userProfile.title.unblocked" = "Account unblocked";
"userProfile.error.notExists" = "Account does not exists.";
"userProfile.error.loadingAccountFailed" = "Error during download account from server.";
"userProfile.error.muting" = "Muting/unmuting action failed.";
@ -243,6 +249,8 @@
"accounts.navigationBar.following" = "Following";
"accounts.navigationBar.favouritedBy" = "Favourited by";
"accounts.navigationBar.reboostedBy" = "Boosted by";
"accounts.navigationBar.blocked" = "Blocked accounts";
"accounts.navigationBar.mutes" = "Muted accounts";
"accounts.title.noAccounts" = "Unfortunately, there is no one here.";
"accounts.error.loadingAccountsFailed" = "Loading accounts failed.";

View File

@ -97,6 +97,12 @@
"userProfile.title.follow" = "Obserwuj";
"userProfile.title.instance" = "Informacje o instancji";
"userProfile.error.notExists" = "Konto nie istnieje.";
"userProfile.title.blocks" = "Zablokowane konta";
"userProfile.title.mutes" = "Wyciszone konta";
"userProfile.title.muted" = "Konto wyciszone";
"userProfile.title.unmuted" = "Wyciszenie wyłączone";
"userProfile.title.blocked" = "Konto zablokowane";
"userProfile.title.unblocked" = "Konto odblokowane";
"userProfile.error.notExists" = "Błąd podczas pobierania danych użytkownika.";
"userProfile.error.mute" = "Błąd podczas wyciszania użytkownika.";
"userProfile.error.block" = "Błąd podczas blokowania/odblokowywania użytkownika.";
@ -243,6 +249,8 @@
"accounts.navigationBar.following" = "Obserwowani";
"accounts.navigationBar.favouritedBy" = "Polubione przez";
"accounts.navigationBar.reboostedBy" = "Podbite przez";
"accounts.navigationBar.blocked" = "Zablokowani";
"accounts.navigationBar.mutes" = "Wyciszeni";
"accounts.title.noAccounts" = "Niestety nie ma tutaj nikogo.";
"accounts.error.loadingAccountsFailed" = "Błąd podczas wczytywania użytkownikow.";

View File

@ -0,0 +1,23 @@
//
// https://mczachurski.dev
// Copyright © 2022 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
public extension PixelfedClientAuthenticated {
func mutes(maxId: EntityId? = nil,
sinceId: EntityId? = nil,
minId: EntityId? = nil,
limit: Int? = nil,
page: Page? = nil) async throws -> [Account] {
let request = try Self.request(
for: baseURL,
target: Pixelfed.Mutes.mutes(maxId, sinceId, minId, limit, page),
withBearerToken: token
)
return try await downloadJson([Account].self, request: request)
}
}

View File

@ -0,0 +1,23 @@
//
// https://mczachurski.dev
// Copyright © 2022 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
public extension PixelfedClientAuthenticated {
func blocks(maxId: EntityId? = nil,
sinceId: EntityId? = nil,
minId: EntityId? = nil,
limit: Int? = nil,
page: Page? = nil) async throws -> [Account] {
let request = try Self.request(
for: baseURL,
target: Pixelfed.Blocks.blocks(maxId, sinceId, minId, limit, page),
withBearerToken: token
)
return try await downloadJson([Account].self, request: request)
}
}

View File

@ -8,7 +8,7 @@ import Foundation
extension Pixelfed {
public enum Blocks {
case blocks
case blocks(MaxId?, SinceId?, MinId?, Limit?, Page?)
}
}
@ -33,10 +33,40 @@ extension Pixelfed.Blocks: TargetType {
/// The parameters to be incoded in the request.
public var queryItems: [(String, String)]? {
var params: [(String, String)] = []
var maxId: MaxId? = nil
var sinceId: SinceId? = nil
var minId: MinId? = nil
var limit: Limit? = nil
var page: Page? = nil
switch self {
case .blocks:
return nil
case .blocks(let _maxId, let _sinceId, let _minId, let _limit, let _page):
maxId = _maxId
sinceId = _sinceId
minId = _minId
limit = _limit
page = _page
}
if let maxId {
params.append(("max_id", maxId))
}
if let sinceId {
params.append(("since_id", sinceId))
}
if let minId {
params.append(("min_id", minId))
}
if let limit {
params.append(("limit", "\(limit)"))
}
if let page {
params.append(("page", "\(page)"))
}
return params
}
public var headers: [String: String]? {

View File

@ -8,7 +8,7 @@ import Foundation
extension Pixelfed {
public enum Mutes {
case mutes
case mutes(MaxId?, SinceId?, MinId?, Limit?, Page?)
}
}
@ -33,10 +33,40 @@ extension Pixelfed.Mutes: TargetType {
/// The parameters to be incoded in the request.
public var queryItems: [(String, String)]? {
var params: [(String, String)] = []
var maxId: MaxId? = nil
var sinceId: SinceId? = nil
var minId: MinId? = nil
var limit: Limit? = nil
var page: Page? = nil
switch self {
case .mutes:
return nil
case .mutes(let _maxId, let _sinceId, let _minId, let _limit, let _page):
maxId = _maxId
sinceId = _sinceId
minId = _minId
limit = _limit
page = _page
}
if let maxId {
params.append(("max_id", maxId))
}
if let sinceId {
params.append(("since_id", sinceId))
}
if let minId {
params.append(("min_id", minId))
}
if let limit {
params.append(("limit", "\(limit)"))
}
if let page {
params.append(("page", "\(page)"))
}
return params
}
public var headers: [String: String]? {

View File

@ -170,6 +170,10 @@
F89D6C4429718092001DA3D4 /* AccentsSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89D6C4329718092001DA3D4 /* AccentsSectionView.swift */; };
F89D6C4629718193001DA3D4 /* GeneralSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89D6C4529718193001DA3D4 /* GeneralSectionView.swift */; };
F89D6C4A297196FF001DA3D4 /* ImageViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89D6C49297196FF001DA3D4 /* ImageViewer.swift */; };
F89F57AA29D1AE5D00001EE3 /* Client+Blocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89F57A929D1AE5D00001EE3 /* Client+Blocks.swift */; };
F89F57AC29D1AEBC00001EE3 /* Client+Mutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89F57AB29D1AEBC00001EE3 /* Client+Mutes.swift */; };
F89F57AE29D1B82700001EE3 /* TagWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89F57AD29D1B82600001EE3 /* TagWidget.swift */; };
F89F57B029D1C11200001EE3 /* RelationshipModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89F57AF29D1C11200001EE3 /* RelationshipModel.swift */; };
F8A93D7E2965FD89001D8331 /* UserProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A93D7D2965FD89001D8331 /* UserProfileView.swift */; };
F8AD061329A565620042F111 /* String+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8AD061229A565620042F111 /* String+Random.swift */; };
F8AFF7C129B259150087D083 /* HashtagsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8AFF7C029B259150087D083 /* HashtagsView.swift */; };
@ -383,6 +387,10 @@
F89D6C4529718193001DA3D4 /* GeneralSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSectionView.swift; sourceTree = "<group>"; };
F89D6C49297196FF001DA3D4 /* ImageViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewer.swift; sourceTree = "<group>"; };
F89F0605299139F6003DC875 /* Vernissage-002.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Vernissage-002.xcdatamodel"; sourceTree = "<group>"; };
F89F57A929D1AE5D00001EE3 /* Client+Blocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+Blocks.swift"; sourceTree = "<group>"; };
F89F57AB29D1AEBC00001EE3 /* Client+Mutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+Mutes.swift"; sourceTree = "<group>"; };
F89F57AD29D1B82600001EE3 /* TagWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagWidget.swift; sourceTree = "<group>"; };
F89F57AF29D1C11200001EE3 /* RelationshipModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelationshipModel.swift; sourceTree = "<group>"; };
F8A93D7D2965FD89001D8331 /* UserProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileView.swift; sourceTree = "<group>"; };
F8AD061229A565620042F111 /* String+Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Random.swift"; sourceTree = "<group>"; };
F8AFF7C029B259150087D083 /* HashtagsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagsView.swift; sourceTree = "<group>"; };
@ -570,6 +578,7 @@
F8CEEDF929ABAFD200DBED66 /* ImageFileTranseferable.swift */,
F88AB05229B3613900345EDE /* PhotoUrl.swift */,
F85D4DFD29B78C8400345267 /* HashtagModel.swift */,
F89F57AF29D1C11200001EE3 /* RelationshipModel.swift */,
);
path = Models;
sourceTree = "<group>";
@ -621,6 +630,7 @@
F891E7CF29C368750022C449 /* ImageRowItemAsync.swift */,
F85D497829640B9D00751DF7 /* ImagesCarousel.swift */,
F83CBEFA298298A1002972C8 /* ImageCarouselPicture.swift */,
F89F57AD29D1B82600001EE3 /* TagWidget.swift */,
F85D497A29640C8200751DF7 /* UsernameRow.swift */,
F85D497C29640D5900751DF7 /* InteractionRow.swift */,
F897978729681B9C00B22335 /* UserAvatar.swift */,
@ -882,11 +892,13 @@
F88FAD2C295F4AD7009B20C9 /* ApplicationState.swift */,
F8B9B344298D1FCB009CC69C /* Client.swift */,
F8B9B346298D4A7C009CC69C /* Client+Trends.swift */,
F89F57A929D1AE5D00001EE3 /* Client+Blocks.swift */,
F8B9B348298D4AA2009CC69C /* Client+Timeline.swift */,
F8B9B34A298D4ACE009CC69C /* Client+Tags.swift */,
F8B9B34C298D4AE4009CC69C /* Client+Notifications.swift */,
F8B9B34E298D4B14009CC69C /* Client+Statuses.swift */,
F8FA9916299F7DBD007AB130 /* Client+Media.swift */,
F89F57AB29D1AEBC00001EE3 /* Client+Mutes.swift */,
F8B9B350298D4B34009CC69C /* Client+Account.swift */,
F8B9B352298D4B5D009CC69C /* Client+Search.swift */,
F89AC00829A20C5C00F4159F /* Client+Places.swift */,
@ -1099,6 +1111,7 @@
F88AB05529B3626300345EDE /* ImageGrid.swift in Sources */,
F87AEB922986C44E00434FB6 /* AuthorizationSession.swift in Sources */,
F88E4D44297E82EB0057491A /* Status+MediaAttachmentType.swift in Sources */,
F89F57AC29D1AEBC00001EE3 /* Client+Mutes.swift in Sources */,
F86A4301299A97F500DF7645 /* ProductIdentifiers.swift in Sources */,
F89D6C4229717FDC001DA3D4 /* AccountsSectionView.swift in Sources */,
F80048082961E6DE00E6868A /* StatusDataHandler.swift in Sources */,
@ -1220,8 +1233,10 @@
F802884F297AEED5000BDD51 /* DatabaseError.swift in Sources */,
F86A4307299AA5E900DF7645 /* ThanksView.swift in Sources */,
F89B5CC229D01BF700549F2F /* InstanceView.swift in Sources */,
F89F57B029D1C11200001EE3 /* RelationshipModel.swift in Sources */,
F88AB05829B36B8200345EDE /* AccountsPhotoView.swift in Sources */,
F85D4971296402DC00751DF7 /* AuthorizationService.swift in Sources */,
F89F57AE29D1B82700001EE3 /* TagWidget.swift in Sources */,
F8B9B356298D4C1E009CC69C /* Client+Instance.swift in Sources */,
F88AB05329B3613900345EDE /* PhotoUrl.swift in Sources */,
F88E4D56297EAD6E0057491A /* AppRouteur.swift in Sources */,
@ -1232,6 +1247,7 @@
F8B9B34F298D4B14009CC69C /* Client+Statuses.swift in Sources */,
F89992C9296D6DC7005994BF /* CommentBodyView.swift in Sources */,
F8B9B347298D4A7C009CC69C /* Client+Trends.swift in Sources */,
F89F57AA29D1AE5D00001EE3 /* Client+Blocks.swift in Sources */,
F88FAD2D295F4AD7009B20C9 /* ApplicationState.swift in Sources */,
F88E4D54297EA7EE0057491A /* MarkdownFormattedText.swift in Sources */,
F866F6A1296040A8002E8F88 /* ApplicationSettings+CoreDataProperties.swift in Sources */,
@ -1286,7 +1302,7 @@
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 85;
CURRENT_PROJECT_VERSION = 86;
DEVELOPMENT_TEAM = B2U9FEKYP8;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = VernissageWidget/Info.plist;
@ -1314,7 +1330,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 85;
CURRENT_PROJECT_VERSION = 86;
DEVELOPMENT_TEAM = B2U9FEKYP8;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = VernissageWidget/Info.plist;
@ -1462,7 +1478,7 @@
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 85;
CURRENT_PROJECT_VERSION = 86;
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
DEVELOPMENT_TEAM = B2U9FEKYP8;
ENABLE_PREVIEWS = YES;
@ -1502,7 +1518,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 85;
CURRENT_PROJECT_VERSION = 86;
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
DEVELOPMENT_TEAM = B2U9FEKYP8;
ENABLE_PREVIEWS = YES;

View File

@ -0,0 +1,21 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import PixelfedKit
/// Pixelfed 'Trends'.
extension Client {
public class Blocks: BaseClient {
public func blocks(maxId: String? = nil,
sinceId: String? = nil,
minId: String? = nil,
limit: Int = 10,
page: Int? = nil) async throws -> [Account] {
return try await pixelfedClient.blocks(maxId: maxId, sinceId: sinceId, minId: minId, limit: limit, page: page)
}
}
}

View File

@ -0,0 +1,21 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import PixelfedKit
/// Pixelfed 'Trends'.
extension Client {
public class Mutes: BaseClient {
public func mutes(maxId: String? = nil,
sinceId: String? = nil,
minId: String? = nil,
limit: Int = 10,
page: Int? = nil) async throws -> [Account] {
return try await pixelfedClient.mutes(maxId: maxId, sinceId: sinceId, minId: minId, limit: limit, page: page)
}
}
}

View File

@ -36,6 +36,8 @@ extension Client {
public var accounts: Accounts? { return Accounts(pixelfedClient: self.pixelfedClient) }
public var search: Search? { return Search(pixelfedClient: self.pixelfedClient) }
public var places: Places? { return Places(pixelfedClient: self.pixelfedClient) }
public var blocks: Blocks? { return Blocks(pixelfedClient: self.pixelfedClient) }
public var mutes: Mutes? { return Mutes(pixelfedClient: self.pixelfedClient) }
public var instances: Instances { return Instances() }
}

View File

@ -0,0 +1,124 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import SwiftUI
import Foundation
import PixelfedKit
public class RelationshipModel: ObservableObject {
/// The account ID.
@Published public var id: EntityId
/// Are you followed by this user?
@Published public var followedBy: Bool
/// Is this user blocking you?
@Published public var blockedBy: Bool
/// Are you muting notifications from this user?
@Published public var mutingNotifications: Bool
/// Do you have a pending follow request for this user?
@Published public var requested: Bool
/// Are you receiving this users boosts in your home timeline?
@Published public var showingReblogs: Bool
/// Have you enabled notifications for this user?
@Published public var notifying: Bool
/// Are you blocking this users domain?
@Published public var domainBlocking: Bool
/// Are you featuring this user on your profile?
@Published public var endorsed: Bool
/// Which languages are you following from this user? Array of String (ISO 639-1 language two-letter code).
@Published public var languages: [String]?
/// This users profile bio.
@Published public var note: String?
/// Are you following this user?
@Published public var following: Bool
/// Are you blocking this user?
@Published public var blocking: Bool
/// Are you muting this user?
@Published public var muting: Bool
public init() {
self.id = ""
self.following = false
self.followedBy = false
self.blocking = false
self.blockedBy = false
self.muting = false
self.mutingNotifications = false
self.requested = false
self.showingReblogs = false
self.notifying = false
self.domainBlocking = false
self.endorsed = false
self.note = nil
self.languages = []
}
public init(relationship: Relationship) {
self.id = relationship.id
self.following = relationship.following
self.followedBy = relationship.followedBy
self.blocking = relationship.blocking
self.blockedBy = relationship.blockedBy
self.muting = relationship.muting
self.mutingNotifications = relationship.mutingNotifications
self.requested = relationship.requested
self.showingReblogs = relationship.showingReblogs
self.notifying = relationship.notifying
self.domainBlocking = relationship.domainBlocking
self.endorsed = relationship.endorsed
self.languages = relationship.languages
self.note = relationship.note
}
}
extension RelationshipModel {
public func update(relationship: Relationship) {
self.id = relationship.id
self.following = relationship.following
self.followedBy = relationship.followedBy
self.blocking = relationship.blocking
self.blockedBy = relationship.blockedBy
self.muting = relationship.muting
self.mutingNotifications = relationship.mutingNotifications
self.requested = relationship.requested
self.showingReblogs = relationship.showingReblogs
self.notifying = relationship.notifying
self.domainBlocking = relationship.domainBlocking
self.endorsed = relationship.endorsed
self.languages = relationship.languages
self.note = relationship.note
}
public func update(relationship: RelationshipModel) {
self.id = relationship.id
self.following = relationship.following
self.followedBy = relationship.followedBy
self.blocking = relationship.blocking
self.blockedBy = relationship.blockedBy
self.muting = relationship.muting
self.mutingNotifications = relationship.mutingNotifications
self.requested = relationship.requested
self.showingReblogs = relationship.showingReblogs
self.notifying = relationship.notifying
self.domainBlocking = relationship.domainBlocking
self.endorsed = relationship.endorsed
self.languages = relationship.languages
self.note = relationship.note
}
}

View File

@ -14,6 +14,8 @@ struct AccountsView: View {
case following(entityId: String)
case reblogged(entityId: String)
case favourited(entityId: String)
case blocks
case mutes
case search(query: String)
}
@ -122,6 +124,10 @@ struct AccountsView: View {
return NSLocalizedString("accounts.navigationBar.favouritedBy", comment: "Favourited by")
case .reblogged:
return NSLocalizedString("accounts.navigationBar.reboostedBy", comment: "Reboosted by")
case .blocks:
return NSLocalizedString("accounts.navigationBar.blocked", comment: "Blocked")
case .mutes:
return NSLocalizedString("accounts.navigationBar.mutes", comment: "Mutes")
case .search(let query):
return query
}
@ -147,6 +153,20 @@ struct AccountsView: View {
} else {
return []
}
case .blocks:
// TODO: Workaround for not working paging for favourites/reblogged issues: https://github.com/pixelfed/pixelfed/issues/4182.
if page == 1 {
return try await self.client.blocks?.blocks(limit: 40, page: page) ?? []
} else {
return []
}
case .mutes:
// TODO: Workaround for not working paging for favourites/reblogged issues: https://github.com/pixelfed/pixelfed/issues/4182.
if page == 1 {
return try await self.client.mutes?.mutes(limit: 40, page: page) ?? []
} else {
return []
}
case .search(let query):
// TODO: Workaround for not working paging for favourites/reblogged issues: https://github.com/pixelfed/pixelfed/issues/4182.
if page == 1 {

View File

@ -13,10 +13,22 @@ struct UserProfileHeaderView: View {
@EnvironmentObject private var routerPath: RouterPath
@State var account: Account
@State var relationship: Relationship? = nil
@ObservedObject var relationship = RelationshipModel()
var body: some View {
VStack(alignment: .leading) {
HStack(alignment: .top) {
Spacer()
if self.relationship.muting == true {
TagWidget(value: "Muted", color: .accentColor, systemImage: "message.and.waveform.fill")
}
if self.relationship.blocking == true {
TagWidget(value: "Blocked", color: .dangerColor, systemImage: "hand.raised.fill")
}
}
HStack(alignment: .center) {
UserAvatar(accountAvatar: account.avatar, size: .profile)
@ -54,7 +66,7 @@ struct UserProfileHeaderView: View {
}
}.foregroundColor(.mainTextColor)
}
HStack (alignment: .center) {
VStack(alignment: .leading) {
Text(account.displayNameWithoutEmojis)
@ -72,7 +84,7 @@ struct UserProfileHeaderView: View {
self.otherAccountActionButtons()
}
}
if let note = account.note, !note.asMarkdown.isEmpty {
MarkdownFormattedText(note.asMarkdown)
.font(.subheadline)
@ -105,23 +117,23 @@ struct UserProfileHeaderView: View {
await onRelationshipButtonTap()
} label: {
HStack {
Image(systemName: relationship?.following == true ? "person.badge.minus" : "person.badge.plus")
Text(relationship?.following == true ? "userProfile.title.unfollow" : (relationship?.followedBy == true ? "userProfile.title.followBack" : "userProfile.title.follow"), comment: "Follow/unfollow actions")
Image(systemName: relationship.following == true ? "person.badge.minus" : "person.badge.plus")
Text(relationship.following == true ? "userProfile.title.unfollow" : (relationship.followedBy == true ? "userProfile.title.followBack" : "userProfile.title.follow"), comment: "Follow/unfollow actions")
}
}
.buttonStyle(.borderedProminent)
.tint(relationship?.following == true ? .dangerColor : .accentColor)
.tint(relationship.following == true ? .dangerColor : .accentColor)
}
private func onRelationshipButtonTap() async {
do {
if self.relationship?.following == true {
if self.relationship.following == true {
if let relationship = try await self.client.accounts?.unfollow(account: self.account.id) {
self.relationship = relationship
self.relationship.following = relationship.following
}
} else {
if let relationship = try await self.client.accounts?.follow(account: self.account.id) {
self.relationship = relationship
self.relationship.following = relationship.following
}
}
} catch {

View File

@ -17,8 +17,8 @@ struct UserProfileView: View {
@State public var accountDisplayName: String?
@State public var accountUserName: String
@StateObject private var relationship = RelationshipModel()
@State private var account: Account? = nil
@State private var relationship: Relationship? = nil
@State private var state: ViewState = .loading
@State private var viewId = UUID().uuidString
@ -87,7 +87,15 @@ struct UserProfileView: View {
async let accountTask = self.client.accounts?.account(withId: self.accountId)
// Wait for download account and relationships.
(self.relationship, self.account) = try await (relationshipTask, accountTask)
let (relationshipFromApi, accountFromApi) = try await (relationshipTask, accountTask)
if let relationshipFromApi {
self.relationship.update(relationship: relationshipFromApi)
} else {
self.relationship.update(relationship: RelationshipModel())
}
self.account = accountFromApi
self.state = .loaded
} catch {
@ -117,7 +125,7 @@ struct UserProfileView: View {
await onMuteAccount(account: account)
}
} label: {
if self.relationship?.muting == true {
if self.relationship.muting == true {
Label(NSLocalizedString("userProfile.title.unmute", comment: "Unute"), systemImage: "message.and.waveform.fill")
} else {
Label(NSLocalizedString("userProfile.title.mute", comment: "Mute"), systemImage: "message.and.waveform")
@ -129,7 +137,7 @@ struct UserProfileView: View {
await onBlockAccount(account: account)
}
} label: {
if self.relationship?.blocking == true {
if self.relationship.blocking == true {
Label(NSLocalizedString("userProfile.title.unblock", comment: "Unblock"), systemImage: "hand.raised.fill")
} else {
Label(NSLocalizedString("userProfile.title.block", comment: "Block"), systemImage: "hand.raised")
@ -164,6 +172,16 @@ struct UserProfileView: View {
NavigationLink(value: RouteurDestinations.instance) {
Label(NSLocalizedString("userProfile.title.instance", comment: "Instance information"), systemImage: "server.rack")
}
Divider()
NavigationLink(value: RouteurDestinations.accounts(listType: .blocks)) {
Label(NSLocalizedString("userProfile.title.blocks", comment: "Blocked accounts"), systemImage: "hand.raised.fill")
}
NavigationLink(value: RouteurDestinations.accounts(listType: .mutes)) {
Label(NSLocalizedString("userProfile.title.mutes", comment: "Muted accounts"), systemImage: "message.and.waveform.fill")
}
Divider()
@ -190,13 +208,19 @@ struct UserProfileView: View {
private func onMuteAccount(account: Account) async {
do {
if self.relationship?.muting == true {
if self.relationship.muting == true {
if let relationship = try await self.client.accounts?.unmute(account: account.id) {
self.relationship = relationship
ToastrService.shared.showSuccess("userProfile.title.unmuted", imageSystemName: "message.and.waveform")
withAnimation(.linear) {
self.relationship.muting = relationship.muting
}
}
} else {
if let relationship = try await self.client.accounts?.mute(account: account.id) {
self.relationship = relationship
ToastrService.shared.showSuccess("userProfile.title.muted", imageSystemName: "message.and.waveform.fill")
withAnimation(.linear) {
self.relationship.muting = relationship.muting
}
}
}
} catch {
@ -206,13 +230,19 @@ struct UserProfileView: View {
private func onBlockAccount(account: Account) async {
do {
if self.relationship?.blocking == true {
if self.relationship.blocking == true {
if let relationship = try await self.client.accounts?.unblock(account: account.id) {
self.relationship = relationship
ToastrService.shared.showSuccess("userProfile.title.unblocked", imageSystemName: "hand.raised")
withAnimation(.linear) {
self.relationship.blocking = relationship.blocking
}
}
} else {
if let relationship = try await self.client.accounts?.block(account: account.id) {
self.relationship = relationship
ToastrService.shared.showSuccess("userProfile.title.blocked", imageSystemName: "hand.raised.fill")
withAnimation(.linear) {
self.relationship.blocking = relationship.blocking
}
}
}
} catch {

View File

@ -0,0 +1,37 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import SwiftUI
struct TagWidget: View {
private let value: String
private let color: Color
private let systemImage: String?
init(value: String, color: Color, systemImage: String? = nil) {
self.value = value
self.color = color
self.systemImage = systemImage
}
var body: some View {
HStack {
if let systemImage {
Image(systemName: systemImage)
.foregroundColor(.white)
.font(.footnote)
}
Text(self.value)
.foregroundColor(.white)
.font(.footnote)
.fontWeight(.semibold)
}
.padding(.horizontal, 8)
.padding(.vertical, 2)
.background(Capsule().foregroundColor(self.color))
}
}