From f2ba08e1cc78f41fc4b0958efc176324013c8fac Mon Sep 17 00:00:00 2001 From: Christopher Schindler Date: Tue, 25 Jun 2024 10:59:19 +0200 Subject: [PATCH 1/5] Add an option to delete the avatar or header (#2109) * Endpoint: add Profile endpoint * EditAccount: add an option to delete the avatar or header * EditAccount: always display avatar view --- .../Localization/Localizable.xcstrings | 34 ++++++++++++ .../Account/Edit/EditAccountView.swift | 53 ++++++++++++------- .../Account/Edit/EditAccountViewModel.swift | 26 ++++++++- .../Sources/Network/Endpoint/Profile.swift | 23 ++++++++ 4 files changed, 116 insertions(+), 20 deletions(-) create mode 100644 Packages/Network/Sources/Network/Endpoint/Profile.swift diff --git a/IceCubesApp/Resources/Localization/Localizable.xcstrings b/IceCubesApp/Resources/Localization/Localizable.xcstrings index 5331b93e..cee96b14 100644 --- a/IceCubesApp/Resources/Localization/Localizable.xcstrings +++ b/IceCubesApp/Resources/Localization/Localizable.xcstrings @@ -13465,6 +13465,23 @@ } } }, + "account.edit.avatar.delete" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete avatar" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Supprimer l'avatar" + } + } + } + }, "account.edit.display-name" : { "extractionState" : "manual", "localizations" : { @@ -13941,6 +13958,23 @@ } } }, + "account.edit.header.delete" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete header" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Supprimer la bannière" + } + } + } + }, "account.edit.metadata-name-placeholder" : { "extractionState" : "manual", "localizations" : { diff --git a/Packages/Account/Sources/Account/Edit/EditAccountView.swift b/Packages/Account/Sources/Account/Edit/EditAccountView.swift index 69aa5d06..fea5dc28 100644 --- a/Packages/Account/Sources/Account/Edit/EditAccountView.swift +++ b/Packages/Account/Sources/Account/Edit/EditAccountView.swift @@ -87,28 +87,45 @@ public struct EditAccountView: View { .frame(height: 150) } } - if let avatar = viewModel.avatar { - ZStack(alignment: .bottomLeading) { - AvatarView(avatar, config: .account) - Menu { - Button("account.edit.avatar") { - viewModel.isChangingAvatar = true - viewModel.isPhotoPickerPresented = true - } - Button("account.edit.header") { - viewModel.isChangingHeader = true - viewModel.isPhotoPickerPresented = true - } - } label: { - Image(systemName: "photo.badge.plus") - .foregroundStyle(.white) + ZStack(alignment: .bottomLeading) { + AvatarView(viewModel.avatar, config: .account) + Menu { + Button("account.edit.avatar") { + viewModel.isChangingAvatar = true + viewModel.isPhotoPickerPresented = true } - .buttonStyle(.borderedProminent) - .clipShape(Circle()) - .offset(x: -8, y: 8) + Button("account.edit.header") { + viewModel.isChangingHeader = true + viewModel.isPhotoPickerPresented = true + } + if viewModel.avatar != nil || viewModel.header != nil { + Divider() + } + if viewModel.avatar != nil { + Button("account.edit.avatar.delete", role: .destructive) { + Task { + await viewModel.deleteAvatar() + } + } + } + if viewModel.header != nil { + Button("account.edit.header.delete", role: .destructive) { + Task { + await viewModel.deleteHeader() + } + } + } + } label: { + Image(systemName: "photo.badge.plus") + .foregroundStyle(.white) } + .buttonStyle(.borderedProminent) + .clipShape(Circle()) + .offset(x: -8, y: 8) + .padding(EdgeInsets(top: 0, leading: 0, bottom: 8, trailing: 0)) } } + .frame(minWidth: 0, maxWidth: .infinity) .overlay { if viewModel.isChangingAvatar || viewModel.isChangingHeader { ZStack(alignment: .center) { diff --git a/Packages/Account/Sources/Account/Edit/EditAccountViewModel.swift b/Packages/Account/Sources/Account/Edit/EditAccountViewModel.swift index 50f10491..ddf2fd97 100644 --- a/Packages/Account/Sources/Account/Edit/EditAccountViewModel.swift +++ b/Packages/Account/Sources/Account/Edit/EditAccountViewModel.swift @@ -82,8 +82,8 @@ import SwiftUI isBot = account.bot isLocked = account.locked isDiscoverable = account.discoverable ?? false - avatar = account.avatar - header = account.header + avatar = account.haveAvatar ? account.avatar : nil + header = account.haveHeader ? account.header : nil fields = account.source?.fields.map { .init(name: $0.name, value: $0.value.asRawText) } ?? [] withAnimation { isLoading = false @@ -112,6 +112,28 @@ import SwiftUI } } + func deleteAvatar() async -> Bool { + guard let client else { return false } + do { + let response = try await client.delete(endpoint: Profile.deleteAvatar) + avatar = nil + return response?.statusCode == 200 + } catch { + return false + } + } + + func deleteHeader() async -> Bool { + guard let client else { return false } + do { + let response = try await client.delete(endpoint: Profile.deleteHeader) + header = nil + return response?.statusCode == 200 + } catch { + return false + } + } + private func uploadHeader(data: Data) async -> Bool { guard let client else { return false } do { diff --git a/Packages/Network/Sources/Network/Endpoint/Profile.swift b/Packages/Network/Sources/Network/Endpoint/Profile.swift new file mode 100644 index 00000000..c7aa3936 --- /dev/null +++ b/Packages/Network/Sources/Network/Endpoint/Profile.swift @@ -0,0 +1,23 @@ +import Foundation + +public enum Profile: Endpoint { + case deleteAvatar + case deleteHeader + + + public func path() -> String { + switch self { + case .deleteAvatar: + "profile/avatar" + case .deleteHeader: + "profile/header" + } + } + + public func queryItems() -> [URLQueryItem]? { + switch self { + case .deleteAvatar, .deleteHeader: + nil + } + } +} From 9887a81ef0f07f77550789ab7825b2509c5616e4 Mon Sep 17 00:00:00 2001 From: Nam <35841468+sunsetsonwheels@users.noreply.github.com> Date: Tue, 25 Jun 2024 18:59:43 +1000 Subject: [PATCH 2/5] Make toolbar account selector button avatar match shape option. (#2098) --- IceCubesApp/App/Tabs/ToolbarTab.swift | 3 ++- .../DesignSystem/Sources/DesignSystem/Views/AvatarView.swift | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/IceCubesApp/App/Tabs/ToolbarTab.swift b/IceCubesApp/App/Tabs/ToolbarTab.swift index 561e15fb..e28fdd17 100644 --- a/IceCubesApp/App/Tabs/ToolbarTab.swift +++ b/IceCubesApp/App/Tabs/ToolbarTab.swift @@ -9,6 +9,7 @@ struct ToolbarTab: ToolbarContent { @Environment(\.horizontalSizeClass) private var horizontalSizeClass @Environment(UserPreferences.self) private var userPreferences + @Environment(Theme.self) private var theme @Binding var routerPath: RouterPath @@ -35,7 +36,7 @@ struct ToolbarTab: ToolbarContent { (UIDevice.current.userInterfaceIdiom == .pad && horizontalSizeClass == .compact) { ToolbarItem(placement: .navigationBarLeading) { - AppAccountsSelectorView(routerPath: routerPath) + AppAccountsSelectorView(routerPath: routerPath, avatarConfig: theme.avatarShape == .circle ? .badge : .badgeRounded) } } } diff --git a/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift b/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift index ae256793..8625ec80 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift @@ -53,6 +53,7 @@ public struct AvatarView: View { #endif public static let embed = FrameConfig(width: 34, height: 34) public static let badge = FrameConfig(width: 28, height: 28, cornerRadius: 14) + public static let badgeRounded = FrameConfig(width: 28, height: 28) public static let list = FrameConfig(width: 20, height: 20, cornerRadius: 10) public static let boost = FrameConfig(width: 12, height: 12, cornerRadius: 6) } From ff1d5733a0b87abaf6cf6e9f284ecb76089dd469 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Wed, 26 Jun 2024 09:03:00 +0200 Subject: [PATCH 3/5] Tryout: Fixed size optimizations --- .../Sources/StatusKit/Row/Subviews/StatusRowHeaderView.swift | 4 ++++ .../Sources/StatusKit/Row/Subviews/StatusRowTextView.swift | 1 + 2 files changed, 5 insertions(+) diff --git a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowHeaderView.swift b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowHeaderView.swift index 98cea12f..19438d3a 100644 --- a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowHeaderView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowHeaderView.swift @@ -47,6 +47,7 @@ struct StatusRowHeaderView: View { Group { EmojiTextApp(viewModel.finalStatus.account.cachedDisplayName, emojis: viewModel.finalStatus.account.emojis) + .fixedSize(horizontal: false, vertical: true) .font(.scaledSubheadline) .foregroundColor(theme.labelColor) .emojiText.size(Font.scaledSubheadlineFont.emojiSize) @@ -59,6 +60,7 @@ struct StatusRowHeaderView: View { if !redactionReasons.contains(.placeholder) { accountBadgeView + .fixedSize(horizontal: false, vertical: true) .font(.footnote) } } @@ -69,6 +71,7 @@ struct StatusRowHeaderView: View { theme.avatarPosition == .top { Text("@\(theme.displayFullUsername ? viewModel.finalStatus.account.acct : viewModel.finalStatus.account.username)") + .fixedSize(horizontal: false, vertical: true) .font(.scaledFootnote) .foregroundStyle(.secondary) .lineLimit(1) @@ -96,6 +99,7 @@ struct StatusRowHeaderView: View { Text(" ⸱ ") + Text(viewModel.finalStatus.createdAt.relativeFormatted) } + .fixedSize(horizontal: false, vertical: true) .font(.scaledFootnote) .foregroundStyle(.secondary) .lineLimit(1) diff --git a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowTextView.swift b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowTextView.swift index 624bcd40..5ffbeeba 100644 --- a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowTextView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowTextView.swift @@ -19,6 +19,7 @@ struct StatusRowTextView: View { emojis: viewModel.finalStatus.emojis, language: viewModel.finalStatus.language, lineLimit: viewModel.lineLimit) + .fixedSize(horizontal: false, vertical: true) .font(isFocused ? .scaledBodyFocused : .scaledBody) .lineSpacing(CGFloat(theme.lineSpacing)) .foregroundColor(viewModel.textDisabled ? .gray : theme.labelColor) From cd60e0ce1a8b66188398a788a43dac0d8ef612d0 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Wed, 26 Jun 2024 09:26:34 +0200 Subject: [PATCH 4/5] Fixed action view height --- .../Sources/StatusKit/Row/Subviews/StatusRowActionsView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowActionsView.swift b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowActionsView.swift index f686a5b3..14685d14 100644 --- a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowActionsView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowActionsView.swift @@ -212,6 +212,7 @@ struct StatusRowActionsView: View { } } } + .fixedSize(horizontal: false, vertical: true) .sheet(isPresented: $showTextForSelection) { let content = viewModel.status.reblog?.content.asSafeMarkdownAttributedString ?? viewModel.status.content.asSafeMarkdownAttributedString SelectTextView(content: content) From 2ab52d3d3e11242bec4b9a152a98ea4293ca91f1 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Wed, 26 Jun 2024 09:43:27 +0200 Subject: [PATCH 5/5] Optimize some text --- .../Row/Subviews/StatusRowHeaderView.swift | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowHeaderView.swift b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowHeaderView.swift index 19438d3a..2a706560 100644 --- a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowHeaderView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowHeaderView.swift @@ -27,7 +27,7 @@ struct StatusRowHeaderView: View { } } .accessibilityElement(children: .combine) - .accessibilityLabel(Text("\(viewModel.finalStatus.account.safeDisplayName)") + Text(", ") + Text(viewModel.finalStatus.createdAt.relativeFormatted)) + .accessibilityLabel(Text("\(viewModel.finalStatus.account.safeDisplayName), \(viewModel.finalStatus.createdAt.relativeFormatted)")) .accessibilityAction { viewModel.navigateToAccountDetail(account: viewModel.finalStatus.account) } @@ -84,21 +84,17 @@ struct StatusRowHeaderView: View { } } - private var accountBadgeView: Text { + private var accountBadgeView: Text? { if (viewModel.status.reblogAsAsStatus ?? viewModel.status).account.bot { - return Text(Image(systemName: "poweroutlet.type.b.fill")) + Text(" ") + return Text("\(Image(systemName: "poweroutlet.type.b.fill")) ") } else if (viewModel.status.reblogAsAsStatus ?? viewModel.status).account.locked { - return Text(Image(systemName: "lock.fill")) + Text(" ") + return Text("\(Image(systemName: "lock.fill")) ") } - return Text("") + return nil } private var dateView: some View { - Group { - Text(Image(systemName: viewModel.finalStatus.visibility.iconName)) + - Text(" ⸱ ") + - Text(viewModel.finalStatus.createdAt.relativeFormatted) - } + Text("\(Image(systemName: viewModel.finalStatus.visibility.iconName)) ⸱ \(viewModel.finalStatus.createdAt.relativeFormatted)") .fixedSize(horizontal: false, vertical: true) .font(.scaledFootnote) .foregroundStyle(.secondary)