Merge branch 'main' into iOS-18

This commit is contained in:
Thomas Ricouard 2024-06-26 10:36:41 +02:00
commit 1f753e49b9
9 changed files with 131 additions and 31 deletions

View file

@ -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)
}
}
}

View file

@ -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" : {

View file

@ -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) {

View file

@ -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 {

View file

@ -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)
}

View file

@ -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
}
}
}

View file

@ -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)

View file

@ -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)
}
@ -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)
@ -81,21 +84,18 @@ 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)
.lineLimit(1)

View file

@ -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)