From 08bb8ed8e6148daa3a48a435fcc878130088feca Mon Sep 17 00:00:00 2001
From: Sjmarf <78750526+Sjmarf@users.noreply.github.com>
Date: Tue, 3 Dec 2024 11:11:21 +0000
Subject: [PATCH 1/7] Update

---
 Mlem.xcodeproj/project.pbxproj                |  4 ++
 .../Tabs/Settings/AccountSettingsView.swift   |  6 ++
 .../Tabs/Settings/ProfileSettingsView.swift   | 66 +++++++++++++++++++
 .../App/Views/Shared/MarkdownTextEditor.swift |  2 +-
 .../Shared/Navigation/SettingsPage.swift      |  8 ++-
 Mlem/Localizable.xcstrings                    | 15 +++++
 6 files changed, 99 insertions(+), 2 deletions(-)
 create mode 100644 Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift

diff --git a/Mlem.xcodeproj/project.pbxproj b/Mlem.xcodeproj/project.pbxproj
index ea6067147..3cbb7d3db 100644
--- a/Mlem.xcodeproj/project.pbxproj
+++ b/Mlem.xcodeproj/project.pbxproj
@@ -153,6 +153,7 @@
 		0389DDD32C39E4D40005B808 /* PasteLinkButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0389DDD22C39E4D40005B808 /* PasteLinkButtonView.swift */; };
 		0389DDD52C39F1290005B808 /* CommunityListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0389DDD42C39F1290005B808 /* CommunityListRow.swift */; };
 		0389DDDB2C3AB6340005B808 /* ActionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0389DDDA2C3AB6340005B808 /* ActionBuilder.swift */; };
+		0391E0F92CFF17AE0040CCA8 /* ProfileSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0391E0F82CFF17AE0040CCA8 /* ProfileSettingsView.swift */; };
 		0397D4602C66113F002C6CDC /* CommentBodyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0397D45F2C66113F002C6CDC /* CommentBodyView.swift */; };
 		0397D4622C676B46002C6CDC /* ApiSortType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0397D4612C676B46002C6CDC /* ApiSortType+Extensions.swift */; };
 		0397D4642C676CA8002C6CDC /* FeedSortPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0397D4632C676CA8002C6CDC /* FeedSortPicker.swift */; };
@@ -569,6 +570,7 @@
 		0389DDD22C39E4D40005B808 /* PasteLinkButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteLinkButtonView.swift; sourceTree = "<group>"; };
 		0389DDD42C39F1290005B808 /* CommunityListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommunityListRow.swift; sourceTree = "<group>"; };
 		0389DDDA2C3AB6340005B808 /* ActionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionBuilder.swift; sourceTree = "<group>"; };
+		0391E0F82CFF17AE0040CCA8 /* ProfileSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSettingsView.swift; sourceTree = "<group>"; };
 		0397D45F2C66113F002C6CDC /* CommentBodyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentBodyView.swift; sourceTree = "<group>"; };
 		0397D4612C676B46002C6CDC /* ApiSortType+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ApiSortType+Extensions.swift"; sourceTree = "<group>"; };
 		0397D4632C676CA8002C6CDC /* FeedSortPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedSortPicker.swift; sourceTree = "<group>"; };
@@ -898,6 +900,7 @@
 				03267D832BED49CE009D6268 /* AccountSettingsView.swift */,
 				037DE0742CE023E3007F7B92 /* BlockListView.swift */,
 				03AB48512CBC042E00567FF9 /* AccountGeneralSettingsView.swift */,
+				0391E0F82CFF17AE0040CCA8 /* ProfileSettingsView.swift */,
 				03AB48542CBC0B8000567FF9 /* AccountAdvancedSettingsView.swift */,
 				03AB48562CBC0DFC00567FF9 /* AccountSignInSettingsView.swift */,
 				03AB48582CBC14CE00567FF9 /* AccountEmailSettingsView.swift */,
@@ -2466,6 +2469,7 @@
 				039F58972C7B68F100C61658 /* AboutMlemView.swift in Sources */,
 				035EDEFB2C2DF98700F51144 /* CommunityListRowBody.swift in Sources */,
 				CD77437F2C1BA5CE0085BB43 /* MultiplatformView.swift in Sources */,
+				0391E0F92CFF17AE0040CCA8 /* ProfileSettingsView.swift in Sources */,
 				03E614E72C0BCDC200F692A4 /* FullyQualifiedLinkView.swift in Sources */,
 				B1B78D642A51D53900F72485 /* AppDelegate.swift in Sources */,
 			);
diff --git a/Mlem/App/Views/Root/Tabs/Settings/AccountSettingsView.swift b/Mlem/App/Views/Root/Tabs/Settings/AccountSettingsView.swift
index a91a29b0a..1bee94464 100644
--- a/Mlem/App/Views/Root/Tabs/Settings/AccountSettingsView.swift
+++ b/Mlem/App/Views/Root/Tabs/Settings/AccountSettingsView.swift
@@ -33,6 +33,12 @@ struct AccountSettingsView: View {
             
             if appState.firstSession is UserSession {
                 Section {
+                    NavigationLink(
+                        "My Profile",
+                        systemImage: Icons.personFill,
+                        destination: .settings(.profile)
+                    )
+                    .tint(palette.colorfulAccent(5))
                     NavigationLink(
                         "Sign-In & Security",
                         systemImage: "key.fill",
diff --git a/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift b/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift
new file mode 100644
index 000000000..c9a363158
--- /dev/null
+++ b/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift
@@ -0,0 +1,66 @@
+//
+//  ProfileSettingsView.swift
+//  Mlem
+//
+//  Created by Sjmarf on 2024-12-03.
+//
+
+import SwiftUI
+
+struct ProfileSettingsView: View {
+    let session: UserSession
+    @State var displayName: String = ""
+    
+    @State var bioTextView: UITextView = .init()
+    @State var uploadHistory: ImageUploadHistoryManager = .init()
+    
+    var minTextEditorHeight: CGFloat {
+        UIFont.preferredFont(forTextStyle: .body).lineHeight * 6 + 20
+    }
+    
+    var body: some View {
+        Form {
+            Section("Display Name") {
+                TextField("Display Name", text: $displayName, prompt: Text(session.account.name))
+            } footer: {
+                Text("The name that is displayed on your profile. This is not the same as your username, which cannot be changed.")
+            }
+            Section("Biography") {
+                MarkdownTextEditor(
+                    onChange: { _ in
+                    },
+                    prompt: "Write a bit about yourself...",
+                    textView: bioTextView,
+                    insets: .init(),
+//                    insets: .init(
+//                        top: Constants.main.standardSpacing,
+//                        left: Constants.main.standardSpacing,
+//                        bottom: Constants.main.standardSpacing,
+//                        right: Constants.main.standardSpacing
+//                    ),
+                    firstResponder: false,
+                    content: {
+                        MarkdownEditorToolbarView(
+                            textView: bioTextView,
+                            uploadHistory: uploadHistory,
+                            imageUploadApi: session.api
+                        )
+                    }
+                )
+                .frame(
+                    maxWidth: .infinity,
+                    minHeight: minTextEditorHeight,
+                    maxHeight: .infinity,
+                    alignment: .topLeading
+                )
+                .listRowInsets(.init(
+                    top: Constants.main.standardSpacing,
+                    leading: Constants.main.standardSpacing,
+                    bottom: Constants.main.standardSpacing,
+                    trailing: Constants.main.standardSpacing
+                ))
+            }
+        }
+        .navigationTitle("My Profile")
+    }
+}
diff --git a/Mlem/App/Views/Shared/MarkdownTextEditor.swift b/Mlem/App/Views/Shared/MarkdownTextEditor.swift
index 8532d2d31..25dc795d3 100644
--- a/Mlem/App/Views/Shared/MarkdownTextEditor.swift
+++ b/Mlem/App/Views/Shared/MarkdownTextEditor.swift
@@ -84,7 +84,7 @@ struct MarkdownTextEditor<Content: View>: UIViewRepresentable {
         placeholderLabel.sizeToFit()
         textView.addSubview(placeholderLabel)
         placeholderLabel.frame.origin = CGPoint(
-            x: 15,
+            x: insets.left + 5,
             y: insets.top + 1
         )
         placeholderLabel.textColor = UIColor(Palette.main.tertiary)
diff --git a/Mlem/App/Views/Shared/Navigation/SettingsPage.swift b/Mlem/App/Views/Shared/Navigation/SettingsPage.swift
index f1c92dc62..1d7667512 100644
--- a/Mlem/App/Views/Shared/Navigation/SettingsPage.swift
+++ b/Mlem/App/Views/Shared/Navigation/SettingsPage.swift
@@ -11,7 +11,7 @@ import SwiftUI
 enum SettingsPage: Hashable {
     case root
     case accounts, account
-    case accountGeneral, accountAdvanced, accountSignIn, accountChangeEmail
+    case profile, accountGeneral, accountAdvanced, accountSignIn, accountChangeEmail
     case general, links, sorting
     case importExportSettings
     case theme, icon
@@ -29,6 +29,12 @@ enum SettingsPage: Hashable {
             SettingsView()
         case .account:
             AccountSettingsView()
+        case .profile:
+            if let session = AppState.main.firstSession as? UserSession {
+                ProfileSettingsView(session: session)
+            } else {
+                Text(verbatim: "Error: No active user account")
+            }
         case .accountGeneral:
             AccountGeneralSettingsView()
         case .accountSignIn:
diff --git a/Mlem/Localizable.xcstrings b/Mlem/Localizable.xcstrings
index 40ba22316..4daafd11d 100644
--- a/Mlem/Localizable.xcstrings
+++ b/Mlem/Localizable.xcstrings
@@ -246,6 +246,9 @@
     },
     "Behavior" : {
 
+    },
+    "Biography" : {
+
     },
     "Block" : {
 
@@ -479,6 +482,9 @@
     },
     "Dismiss" : {
 
+    },
+    "Display Name" : {
+
     },
     "Divider" : {
 
@@ -898,6 +904,9 @@
     },
     "Most Comments" : {
 
+    },
+    "My Profile" : {
+
     },
     "Never" : {
 
@@ -1445,6 +1454,9 @@
           }
         }
       }
+    },
+    "The name that is displayed on your profile. This is not the same as your username, which cannot be changed." : {
+
     },
     "The number of child comments that are shown in a chain before the \"More Replies\" button is shown." : {
 
@@ -1697,6 +1709,9 @@
     },
     "Wrap Code Block Lines" : {
 
+    },
+    "Write a bit about yourself..." : {
+
     },
     "Year" : {
 

From 9816015e91e77db2d60b2ab0cb0de80e5542d958 Mon Sep 17 00:00:00 2001
From: Sjmarf <78750526+Sjmarf@users.noreply.github.com>
Date: Tue, 3 Dec 2024 19:16:46 +0000
Subject: [PATCH 2/7] Update

---
 .../Tabs/Settings/ProfileSettingsView.swift   | 23 ++++++++-----------
 .../App/Views/Shared/MarkdownTextEditor.swift |  6 ++++-
 2 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift b/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift
index c9a363158..2b8a84ddd 100644
--- a/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift
+++ b/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift
@@ -14,6 +14,8 @@ struct ProfileSettingsView: View {
     @State var bioTextView: UITextView = .init()
     @State var uploadHistory: ImageUploadHistoryManager = .init()
     
+    @State var avatarManager: ImageUploadManager?
+    
     var minTextEditorHeight: CGFloat {
         UIFont.preferredFont(forTextStyle: .body).lineHeight * 6 + 20
     }
@@ -31,14 +33,14 @@ struct ProfileSettingsView: View {
                     },
                     prompt: "Write a bit about yourself...",
                     textView: bioTextView,
-                    insets: .init(),
-//                    insets: .init(
-//                        top: Constants.main.standardSpacing,
-//                        left: Constants.main.standardSpacing,
-//                        bottom: Constants.main.standardSpacing,
-//                        right: Constants.main.standardSpacing
-//                    ),
+                    insets: .init(
+                        top: Constants.main.standardSpacing,
+                        left: Constants.main.standardSpacing,
+                        bottom: Constants.main.standardSpacing,
+                        right: Constants.main.standardSpacing
+                    ),
                     firstResponder: false,
+                    sizingOffset: 10,
                     content: {
                         MarkdownEditorToolbarView(
                             textView: bioTextView,
@@ -53,12 +55,7 @@ struct ProfileSettingsView: View {
                     maxHeight: .infinity,
                     alignment: .topLeading
                 )
-                .listRowInsets(.init(
-                    top: Constants.main.standardSpacing,
-                    leading: Constants.main.standardSpacing,
-                    bottom: Constants.main.standardSpacing,
-                    trailing: Constants.main.standardSpacing
-                ))
+                .listRowInsets(.init())
             }
         }
         .navigationTitle("My Profile")
diff --git a/Mlem/App/Views/Shared/MarkdownTextEditor.swift b/Mlem/App/Views/Shared/MarkdownTextEditor.swift
index 25dc795d3..fa8ffa5fc 100644
--- a/Mlem/App/Views/Shared/MarkdownTextEditor.swift
+++ b/Mlem/App/Views/Shared/MarkdownTextEditor.swift
@@ -15,6 +15,7 @@ struct MarkdownTextEditor<Content: View>: UIViewRepresentable {
     let textView: UITextView
     let placeholderLabel: UILabel = .init()
     let font: UIFont
+    let sizingOffset: CGFloat
     
     let onChange: (String) -> Void
     
@@ -35,6 +36,8 @@ struct MarkdownTextEditor<Content: View>: UIViewRepresentable {
             right: Constants.main.standardSpacing
         ),
         firstResponder: Bool = true,
+        // In forms this needs to be set to ~10 (I don't know why this is the case)
+        sizingOffset: CGFloat = 1,
         @ViewBuilder content: () -> Content
     ) {
         self.prompt = String(localized: prompt)
@@ -43,6 +46,7 @@ struct MarkdownTextEditor<Content: View>: UIViewRepresentable {
         self.font = font
         self.insets = insets
         self.firstResponder = firstResponder
+        self.sizingOffset = sizingOffset
         self.onChange = onChange
     }
  
@@ -126,7 +130,7 @@ struct MarkdownTextEditor<Content: View>: UIViewRepresentable {
         // of the rounding logic above; it still happens when simply using `contentSize`.
         return .init(
             width: dimensions.width,
-            height: calculatedHeight + 1
+            height: calculatedHeight + sizingOffset
         )
     }
  

From 34a8a6016f3bcfde31ac26312b58d7ef05d735fc Mon Sep 17 00:00:00 2001
From: Sjmarf <78750526+Sjmarf@users.noreply.github.com>
Date: Sat, 7 Dec 2024 13:36:41 +0000
Subject: [PATCH 3/7] Update

---
 Mlem.xcodeproj/project.pbxproj                |   4 +
 .../Tabs/Settings/ProfileSettingsView.swift   | 105 +++++++++++++++++-
 Mlem/App/Views/Shared/ImageUploadMenu.swift   |  38 +++++++
 .../Images/Wrappers/LargeImageView.swift      |  15 ++-
 .../Shared/MarkdownEditorToolbarView.swift    |  13 +--
 .../Shared/Navigation/SettingsPage.swift      |   4 +-
 Mlem/Localizable.xcstrings                    |   6 +
 7 files changed, 164 insertions(+), 21 deletions(-)
 create mode 100644 Mlem/App/Views/Shared/ImageUploadMenu.swift

diff --git a/Mlem.xcodeproj/project.pbxproj b/Mlem.xcodeproj/project.pbxproj
index 3cbb7d3db..094634557 100644
--- a/Mlem.xcodeproj/project.pbxproj
+++ b/Mlem.xcodeproj/project.pbxproj
@@ -154,6 +154,7 @@
 		0389DDD52C39F1290005B808 /* CommunityListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0389DDD42C39F1290005B808 /* CommunityListRow.swift */; };
 		0389DDDB2C3AB6340005B808 /* ActionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0389DDDA2C3AB6340005B808 /* ActionBuilder.swift */; };
 		0391E0F92CFF17AE0040CCA8 /* ProfileSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0391E0F82CFF17AE0040CCA8 /* ProfileSettingsView.swift */; };
+		0391E0FB2D0066240040CCA8 /* ImageUploadMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0391E0FA2D0066240040CCA8 /* ImageUploadMenu.swift */; };
 		0397D4602C66113F002C6CDC /* CommentBodyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0397D45F2C66113F002C6CDC /* CommentBodyView.swift */; };
 		0397D4622C676B46002C6CDC /* ApiSortType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0397D4612C676B46002C6CDC /* ApiSortType+Extensions.swift */; };
 		0397D4642C676CA8002C6CDC /* FeedSortPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0397D4632C676CA8002C6CDC /* FeedSortPicker.swift */; };
@@ -571,6 +572,7 @@
 		0389DDD42C39F1290005B808 /* CommunityListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommunityListRow.swift; sourceTree = "<group>"; };
 		0389DDDA2C3AB6340005B808 /* ActionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionBuilder.swift; sourceTree = "<group>"; };
 		0391E0F82CFF17AE0040CCA8 /* ProfileSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSettingsView.swift; sourceTree = "<group>"; };
+		0391E0FA2D0066240040CCA8 /* ImageUploadMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageUploadMenu.swift; sourceTree = "<group>"; };
 		0397D45F2C66113F002C6CDC /* CommentBodyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentBodyView.swift; sourceTree = "<group>"; };
 		0397D4612C676B46002C6CDC /* ApiSortType+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ApiSortType+Extensions.swift"; sourceTree = "<group>"; };
 		0397D4632C676CA8002C6CDC /* FeedSortPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedSortPicker.swift; sourceTree = "<group>"; };
@@ -1506,6 +1508,7 @@
 				0355F9482C16406E00605248 /* Line.swift */,
 				03D2A6402C011F3E00ED4FF2 /* ListRow */,
 				03B431C12C45BA00001A1EB5 /* MarkdownEditorToolbarView.swift */,
+				0391E0FA2D0066240040CCA8 /* ImageUploadMenu.swift */,
 				034B94842C09348400039AF4 /* MarkdownImageView.swift */,
 				03B431B32C4481C3001A1EB5 /* MarkdownTextEditor.swift */,
 				03D3A1EE2BB9CA1D009DE55E /* MenuButton.swift */,
@@ -2326,6 +2329,7 @@
 				CD4D59162B87B38C00B82964 /* UIApplication+Extensions.swift in Sources */,
 				CDB41E8A2C83C24400BD2DE9 /* Section.swift in Sources */,
 				CDCA44B22C17675600C092B3 /* Haptic.swift in Sources */,
+				0391E0FB2D0066240040CCA8 /* ImageUploadMenu.swift in Sources */,
 				035BE08B2BDD903100F77D73 /* NavigationModel.swift in Sources */,
 				033F84BB2C2ACB96002E3EDF /* CommentView.swift in Sources */,
 				03AFD0E52C3C14D50054B8AD /* InstanceStubProviding+Extensions.swift in Sources */,
diff --git a/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift b/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift
index 2b8a84ddd..a64e20151 100644
--- a/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift
+++ b/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift
@@ -5,16 +5,31 @@
 //  Created by Sjmarf on 2024-12-03.
 //
 
+import MlemMiddleware
 import SwiftUI
 
 struct ProfileSettingsView: View {
-    let session: UserSession
+    @Environment(Palette.self) var palette
+    
+    let person: Person4
     @State var displayName: String = ""
     
     @State var bioTextView: UITextView = .init()
     @State var uploadHistory: ImageUploadHistoryManager = .init()
     
-    @State var avatarManager: ImageUploadManager?
+    @State var avatarUrl: URL?
+    @State var avatarManager: ImageUploadManager = .init()
+    
+    @State var bannerUrl: URL?
+    @State var bannerManager: ImageUploadManager = .init()
+    
+    init(person: Person4) {
+        self.person = person
+        self.displayName = person.displayName == person.name ? "" : person.displayName
+        bioTextView.text = person.description ?? ""
+        self._avatarUrl = .init(wrappedValue: person.avatar)
+        self._bannerUrl = .init(wrappedValue: person.banner)
+    }
     
     var minTextEditorHeight: CGFloat {
         UIFont.preferredFont(forTextStyle: .body).lineHeight * 6 + 20
@@ -23,7 +38,7 @@ struct ProfileSettingsView: View {
     var body: some View {
         Form {
             Section("Display Name") {
-                TextField("Display Name", text: $displayName, prompt: Text(session.account.name))
+                TextField("Display Name", text: $displayName, prompt: Text(person.name))
             } footer: {
                 Text("The name that is displayed on your profile. This is not the same as your username, which cannot be changed.")
             }
@@ -45,7 +60,7 @@ struct ProfileSettingsView: View {
                         MarkdownEditorToolbarView(
                             textView: bioTextView,
                             uploadHistory: uploadHistory,
-                            imageUploadApi: session.api
+                            imageUploadApi: person.api
                         )
                     }
                 )
@@ -57,7 +72,89 @@ struct ProfileSettingsView: View {
                 )
                 .listRowInsets(.init())
             }
+            avatarSection
+            bannerSection
         }
         .navigationTitle("My Profile")
+        .scrollDismissesKeyboard(.interactively)
+    }
+    
+    @ViewBuilder
+    var avatarSection: some View {
+        Section {
+            HStack(spacing: 15) {
+                CircleCroppedImageView(url: avatarUrl, frame: 48, fallback: .person)
+                    .id(avatarUrl)
+                Text("Avatar")
+                Spacer()
+                CircleImageUploadButton(imageManager: avatarManager, url: $avatarUrl, api: person.api)
+            }
+            .onChange(of: avatarManager.image?.url) {
+                avatarUrl = avatarManager.image?.url
+            }
+        }
+    }
+    
+    @ViewBuilder
+    var bannerSection: some View {
+        Section {
+            VStack(spacing: 0) {
+                if let bannerUrl {
+                    LargeImageView(url: bannerUrl, shouldBlur: false, cornerRadius: 0)
+                        .id(bannerUrl)
+                        .aspectRatio(contentMode: .fill)
+                        .frame(height: 150)
+                        .clipped()
+                } else {
+                    palette.tertiaryGroupedBackground
+                        .frame(height: 150)
+                }
+                HStack(spacing: 15) {
+                    Text("Banner")
+                    Spacer()
+                    CircleImageUploadButton(imageManager: bannerManager, url: $bannerUrl, api: person.api)
+                }
+                .padding(.horizontal, 15)
+                .padding(.vertical, 10)
+            }
+            .onChange(of: bannerManager.image?.url) {
+                bannerUrl = bannerManager.image?.url
+            }
+        }
+        .listRowInsets(.init())
+    }
+}
+
+private struct CircleImageUploadButton: View {
+    let imageManager: ImageUploadManager
+    @Binding var url: URL?
+    let api: ApiClient
+    
+    var body: some View {
+        Group {
+            if url != nil {
+                Button {
+                    url = nil
+                } label: {
+                    Image(systemName: "xmark.circle.fill")
+                        .resizable()
+                }
+            } else {
+                switch imageManager.state {
+                case .uploading:
+                    ProgressView()
+                        .controlSize(.extraLarge)
+                default:
+                    ImageUploadMenu(imageManager: imageManager, imageUploadApi: api) {
+                        Image(systemName: "plus.circle.fill")
+                            .resizable()
+                    }
+                }
+            }
+        }
+        .aspectRatio(contentMode: .fit)
+        .frame(height: 48)
+        .symbolRenderingMode(.hierarchical)
+        .fontWeight(.thin)
     }
 }
diff --git a/Mlem/App/Views/Shared/ImageUploadMenu.swift b/Mlem/App/Views/Shared/ImageUploadMenu.swift
new file mode 100644
index 000000000..579a2bd61
--- /dev/null
+++ b/Mlem/App/Views/Shared/ImageUploadMenu.swift
@@ -0,0 +1,38 @@
+//
+//  ImageUploadMenu.swift
+//  Mlem
+//
+//  Created by Sjmarf on 2024-12-04.
+//
+
+import MlemMiddleware
+import SwiftUI
+
+struct ImageUploadMenu<Label: View>: View {
+    @Environment(NavigationLayer.self) var navigation
+
+    let imageManager: ImageUploadManager
+    let imageUploadApi: ApiClient
+    @ViewBuilder let label: () -> Label
+    
+    init(imageManager: ImageUploadManager, imageUploadApi: ApiClient, @ViewBuilder label: @escaping () -> Label) {
+        self.imageManager = imageManager
+        self.imageUploadApi = imageUploadApi
+        self.label = label
+    }
+    
+    var body: some View {
+        Menu(content: {
+            Button("Photo Library", systemImage: Icons.photo) {
+                navigation.showPhotosPicker(for: imageManager, api: imageUploadApi)
+            }
+            Button("Choose File", systemImage: "folder") {
+                navigation.showFilePicker(for: imageManager, api: imageUploadApi)
+            }
+            Button("Paste", systemImage: Icons.paste) {
+                navigation.uploadImageFromClipboard(for: imageManager, api: imageUploadApi)
+            }
+        }, label: label)
+            .disabled(imageManager.state != .idle)
+    }
+}
diff --git a/Mlem/App/Views/Shared/Images/Wrappers/LargeImageView.swift b/Mlem/App/Views/Shared/Images/Wrappers/LargeImageView.swift
index 9081e3b78..b267b9f51 100644
--- a/Mlem/App/Views/Shared/Images/Wrappers/LargeImageView.swift
+++ b/Mlem/App/Views/Shared/Images/Wrappers/LargeImageView.swift
@@ -17,22 +17,29 @@ struct LargeImageView: View {
 
     let url: URL?
     let shouldBlur: Bool
-    var onTapActions: (() -> Void)?
+    let onTapActions: (() -> Void)?
+    let cornerRadius: CGFloat
     @State var blurred: Bool = false
     
-    init(url: URL?, shouldBlur: Bool, onTapActions: (() -> Void)? = nil) {
+    init(
+        url: URL?,
+        shouldBlur: Bool,
+        cornerRadius: CGFloat = Constants.main.mediumItemCornerRadius,
+        onTapActions: (() -> Void)? = nil
+    ) {
         self.url = url
         self.onTapActions = onTapActions
         self.shouldBlur = shouldBlur
+        self.cornerRadius = cornerRadius
         self._blurred = .init(wrappedValue: shouldBlur)
     }
     
     @State private var loading: MediaLoadingState?
 
     var body: some View {
-        DynamicMediaView(url: url)
+        DynamicMediaView(url: url, cornerRadius: cornerRadius)
             .dynamicBlur(blurred: blurred)
-            .clipShape(.rect(cornerRadius: Constants.main.mediumItemCornerRadius))
+            .clipShape(.rect(cornerRadius: cornerRadius))
             .overlay {
                 NsfwOverlay(blurred: $blurred, shouldBlur: shouldBlur)
             }
diff --git a/Mlem/App/Views/Shared/MarkdownEditorToolbarView.swift b/Mlem/App/Views/Shared/MarkdownEditorToolbarView.swift
index 173012294..1c8417516 100644
--- a/Mlem/App/Views/Shared/MarkdownEditorToolbarView.swift
+++ b/Mlem/App/Views/Shared/MarkdownEditorToolbarView.swift
@@ -120,18 +120,9 @@ struct MarkdownEditorToolbarView: View {
                         textView.toggleQuoteAtCursor()
                     }
                     if let imageUploadApi {
-                        Menu("Image", systemImage: Icons.uploadImage) {
-                            Button("Photo Library", systemImage: Icons.photo) {
-                                navigation.showPhotosPicker(for: imageManager, api: imageUploadApi)
-                            }
-                            Button("Choose File", systemImage: "folder") {
-                                navigation.showFilePicker(for: imageManager, api: imageUploadApi)
-                            }
-                            Button("Paste", systemImage: Icons.paste) {
-                                navigation.uploadImageFromClipboard(for: imageManager, api: imageUploadApi)
-                            }
+                        ImageUploadMenu(imageManager: imageManager, imageUploadApi: imageUploadApi) {
+                            Label("Image", systemImage: Icons.uploadImage)
                         }
-                        .disabled(imageManager.state != .idle)
                     }
                     Button("Spoiler", systemImage: Icons.spoiler) {
                         textView.wrapSelectionWithSpoiler()
diff --git a/Mlem/App/Views/Shared/Navigation/SettingsPage.swift b/Mlem/App/Views/Shared/Navigation/SettingsPage.swift
index 1d7667512..a2af22d4f 100644
--- a/Mlem/App/Views/Shared/Navigation/SettingsPage.swift
+++ b/Mlem/App/Views/Shared/Navigation/SettingsPage.swift
@@ -30,8 +30,8 @@ enum SettingsPage: Hashable {
         case .account:
             AccountSettingsView()
         case .profile:
-            if let session = AppState.main.firstSession as? UserSession {
-                ProfileSettingsView(session: session)
+            if let person = AppState.main.firstPerson {
+                ProfileSettingsView(person: person)
             } else {
                 Text(verbatim: "Error: No active user account")
             }
diff --git a/Mlem/Localizable.xcstrings b/Mlem/Localizable.xcstrings
index 4daafd11d..c86e240da 100644
--- a/Mlem/Localizable.xcstrings
+++ b/Mlem/Localizable.xcstrings
@@ -222,6 +222,9 @@
     },
     "Autoplay Media" : {
 
+    },
+    "Avatar" : {
+
     },
     "Average: %@" : {
 
@@ -243,6 +246,9 @@
     },
     "Banned from Instance" : {
 
+    },
+    "Banner" : {
+
     },
     "Behavior" : {
 

From b84a573c909ed9225a9026e777df433e0a2af6c1 Mon Sep 17 00:00:00 2001
From: Sjmarf <78750526+Sjmarf@users.noreply.github.com>
Date: Sat, 7 Dec 2024 18:01:45 +0000
Subject: [PATCH 4/7] Update

---
 Mlem.xcodeproj/project.pbxproj                |  4 +-
 .../xcshareddata/swiftpm/Package.resolved     |  4 +-
 .../Root/Tabs/Profile/Profile View.swift      |  7 ++
 .../Tabs/Settings/ProfileSettingsView.swift   | 72 +++++++++++++++++--
 4 files changed, 79 insertions(+), 8 deletions(-)

diff --git a/Mlem.xcodeproj/project.pbxproj b/Mlem.xcodeproj/project.pbxproj
index 094634557..223ede56b 100644
--- a/Mlem.xcodeproj/project.pbxproj
+++ b/Mlem.xcodeproj/project.pbxproj
@@ -2934,8 +2934,8 @@
 			isa = XCRemoteSwiftPackageReference;
 			repositoryURL = "https://github.com/mlemgroup/MlemMiddleware";
 			requirement = {
-				kind = upToNextMajorVersion;
-				minimumVersion = 0.49.0;
+				branch = "sjmarf/profile-settings";
+				kind = branch;
 			};
 		};
 /* End XCRemoteSwiftPackageReference section */
diff --git a/Mlem.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mlem.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 3644c91b7..7d4a6fed0 100644
--- a/Mlem.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/Mlem.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -51,8 +51,8 @@
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/mlemgroup/MlemMiddleware",
       "state" : {
-        "revision" : "0d307c21af9b588ea5583a512b312443b8fca059",
-        "version" : "0.49.0"
+        "branch" : "sjmarf/profile-settings",
+        "revision" : "ee8d65661e41ebd85516b92f9ef20c685111b1f7"
       }
     },
     {
diff --git a/Mlem/App/Views/Root/Tabs/Profile/Profile View.swift b/Mlem/App/Views/Root/Tabs/Profile/Profile View.swift
index 2dda148af..b8ff71c0d 100644
--- a/Mlem/App/Views/Root/Tabs/Profile/Profile View.swift	
+++ b/Mlem/App/Views/Root/Tabs/Profile/Profile View.swift	
@@ -17,6 +17,13 @@ struct ProfileView: View {
     var body: some View {
         if let person = appState.firstPerson {
             PersonView(person: .init(person), isProfileTab: true)
+                .toolbar {
+                    ToolbarItem(placement: .secondaryAction) {
+                        Button("Edit", systemImage: Icons.edit) {
+                            navigation.openSheet(.settings(.profile))
+                        }
+                    }
+                }
                 .id(person.actorId)
         } else if let instance = appState.firstSession.instance {
             InstanceView(instance: instance)
diff --git a/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift b/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift
index a64e20151..0f6dacf0a 100644
--- a/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift
+++ b/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift
@@ -9,12 +9,17 @@ import MlemMiddleware
 import SwiftUI
 
 struct ProfileSettingsView: View {
+    @Environment(NavigationLayer.self) var navigation
     @Environment(Palette.self) var palette
     
+    @Environment(\.colorScheme) var colorScheme
+    @Environment(\.dismiss) var dismiss
+    
     let person: Person4
-    @State var displayName: String = ""
+    @State var displayName: String
     
     @State var bioTextView: UITextView = .init()
+    @State var bioHasChanged: Bool = false
     @State var uploadHistory: ImageUploadHistoryManager = .init()
     
     @State var avatarUrl: URL?
@@ -23,9 +28,11 @@ struct ProfileSettingsView: View {
     @State var bannerUrl: URL?
     @State var bannerManager: ImageUploadManager = .init()
     
+    @State var isSubmitting: Bool = false
+    
     init(person: Person4) {
         self.person = person
-        self.displayName = person.displayName == person.name ? "" : person.displayName
+        self._displayName = .init(wrappedValue: person.displayName == person.name ? "" : person.displayName)
         bioTextView.text = person.description ?? ""
         self._avatarUrl = .init(wrappedValue: person.avatar)
         self._bannerUrl = .init(wrappedValue: person.banner)
@@ -39,12 +46,15 @@ struct ProfileSettingsView: View {
         Form {
             Section("Display Name") {
                 TextField("Display Name", text: $displayName, prompt: Text(person.name))
+                    .autocorrectionDisabled()
+                    .textInputAutocapitalization(.never)
             } footer: {
                 Text("The name that is displayed on your profile. This is not the same as your username, which cannot be changed.")
             }
             Section("Biography") {
                 MarkdownTextEditor(
-                    onChange: { _ in
+                    onChange: { newValue in
+                        bioHasChanged = (person.description ?? "") != newValue
                     },
                     prompt: "Write a bit about yourself...",
                     textView: bioTextView,
@@ -76,7 +86,44 @@ struct ProfileSettingsView: View {
             bannerSection
         }
         .navigationTitle("My Profile")
+        .navigationBarTitleDisplayMode(.inline)
         .scrollDismissesKeyboard(.interactively)
+        .navigationBarBackButtonHidden(showToolbarOptions)
+        .interactiveDismissDisabled(showToolbarOptions)
+        .toolbar {
+            if showToolbarOptions {
+                ToolbarItem(placement: .topBarLeading) {
+                    Button("Cancel") {
+                        displayName = person.displayName == person.name ? "" : person.displayName
+                        bioTextView.text = person.description ?? ""
+                        bioHasChanged = false
+                        avatarUrl = person.avatar
+                        bannerUrl = person.banner
+                    }
+                    .disabled(isSubmitting)
+                }
+                ToolbarItem(placement: .topBarTrailing) {
+                    if isSubmitting {
+                        ProgressView()
+                    } else {
+                        Button("Save") {
+                            Task { @MainActor in
+                                await submit()
+                            }
+                        }
+                    }
+                }
+            } else if navigation.isInsideSheet {
+                ToolbarItem(placement: .topBarTrailing) {
+                    CloseButtonView()
+                }
+            }
+        }
+    }
+    
+    var showToolbarOptions: Bool {
+        let originalDisplayName = (person.displayName == person.name) ? "" : person.displayName
+        return bioHasChanged || displayName != originalDisplayName || avatarUrl != person.avatar || bannerUrl != person.banner
     }
     
     @ViewBuilder
@@ -106,7 +153,7 @@ struct ProfileSettingsView: View {
                         .frame(height: 150)
                         .clipped()
                 } else {
-                    palette.tertiaryGroupedBackground
+                    palette.secondary.opacity(0.5)
                         .frame(height: 150)
                 }
                 HStack(spacing: 15) {
@@ -123,6 +170,23 @@ struct ProfileSettingsView: View {
         }
         .listRowInsets(.init())
     }
+    
+    @MainActor
+    func submit() async {
+        isSubmitting = true
+        do {
+            try await person.updateProfile(
+                displayName: displayName.isEmpty ? nil : displayName,
+                description: bioTextView.text.isEmpty ? nil : bioTextView.text,
+                avatar: avatarUrl,
+                banner: bannerUrl
+            )
+            dismiss()
+        } catch {
+            handleError(error)
+        }
+        isSubmitting = false
+    }
 }
 
 private struct CircleImageUploadButton: View {

From 5949babb3879f795afbbfd1c7017254444752fe7 Mon Sep 17 00:00:00 2001
From: Sjmarf <78750526+Sjmarf@users.noreply.github.com>
Date: Sun, 8 Dec 2024 18:18:38 +0000
Subject: [PATCH 5/7] Create Package.resolved

---
 .../xcshareddata/swiftpm/Package.resolved     | 168 ++++++++++++++++++
 1 file changed, 168 insertions(+)
 create mode 100644 Mlem.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

diff --git a/Mlem.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mlem.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 000000000..7d4a6fed0
--- /dev/null
+++ b/Mlem.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,168 @@
+{
+  "originHash" : "e91c2cec61691543665fad5843f30eafc5288bb40fc3a60d50c7d7e9194f3135",
+  "pins" : [
+    {
+      "identity" : "combine-schedulers",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/pointfreeco/combine-schedulers",
+      "state" : {
+        "revision" : "ec62f32d21584214a4b27c8cee2b2ad70ab2c38a",
+        "version" : "0.11.0"
+      }
+    },
+    {
+      "identity" : "gifu",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/kaishin/Gifu",
+      "state" : {
+        "branch" : "master",
+        "revision" : "639e40d5dbd86dfa94fc61296c270b49af6f8a2a"
+      }
+    },
+    {
+      "identity" : "keychainaccess",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/kishikawakatsumi/KeychainAccess.git",
+      "state" : {
+        "branch" : "master",
+        "revision" : "e0c7eebc5a4465a3c4680764f26b7a61f567cdaf"
+      }
+    },
+    {
+      "identity" : "lemmymarkdownui",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/mlemgroup/LemmyMarkdownUI",
+      "state" : {
+        "revision" : "b42b1e301194fd56a144cf199ab2b807a0413a97",
+        "version" : "0.5.2"
+      }
+    },
+    {
+      "identity" : "libwebp-xcode",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/SDWebImage/libwebp-Xcode.git",
+      "state" : {
+        "revision" : "b2b1d20a90b14d11f6ef4241da6b81c1d3f171e4",
+        "version" : "1.3.2"
+      }
+    },
+    {
+      "identity" : "mlemmiddleware",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/mlemgroup/MlemMiddleware",
+      "state" : {
+        "branch" : "sjmarf/profile-settings",
+        "revision" : "ee8d65661e41ebd85516b92f9ef20c685111b1f7"
+      }
+    },
+    {
+      "identity" : "nuke",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/kean/Nuke",
+      "state" : {
+        "revision" : "0ead44350d2737db384908569c012fe67c421e4d",
+        "version" : "12.8.0"
+      }
+    },
+    {
+      "identity" : "sdwebimage",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/SDWebImage/SDWebImage.git",
+      "state" : {
+        "revision" : "8a1be70a625683bc04d6903e2935bf23f3c6d609",
+        "version" : "5.19.7"
+      }
+    },
+    {
+      "identity" : "sdwebimageswiftui",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/SDWebImage/SDWebImageSwiftUI",
+      "state" : {
+        "revision" : "5aa947356f4ea49a0c3b9968564267f6ea5abea7",
+        "version" : "3.1.2"
+      }
+    },
+    {
+      "identity" : "sdwebimagewebpcoder",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/SDWebImage/SDWebImageWebPCoder",
+      "state" : {
+        "revision" : "f534cfe830a7807ecc3d0332127a502426cfa067",
+        "version" : "0.14.6"
+      }
+    },
+    {
+      "identity" : "semaphore",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/groue/Semaphore",
+      "state" : {
+        "revision" : "2543679282aa6f6c8ecf2138acd613ed20790bc2",
+        "version" : "0.1.0"
+      }
+    },
+    {
+      "identity" : "swift-clocks",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/pointfreeco/swift-clocks",
+      "state" : {
+        "revision" : "0fbaebfc013715dab44d715a4d350ba37f297e4d",
+        "version" : "0.4.0"
+      }
+    },
+    {
+      "identity" : "swift-concurrency-extras",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/pointfreeco/swift-concurrency-extras",
+      "state" : {
+        "revision" : "a46265bb4f75808b0e15d971eebc408f557870a3",
+        "version" : "0.1.2"
+      }
+    },
+    {
+      "identity" : "swift-dependencies",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/pointfreeco/swift-dependencies",
+      "state" : {
+        "revision" : "16fd42ae04c6e7f74a6a86395d04722c641cccee",
+        "version" : "0.6.0"
+      }
+    },
+    {
+      "identity" : "swiftui-flow",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/tevelee/SwiftUI-Flow",
+      "state" : {
+        "revision" : "b528bd06ae70fbd2936d9561a456fb6ea65bc7ff",
+        "version" : "2.5.0"
+      }
+    },
+    {
+      "identity" : "swiftui-introspect",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/siteline/SwiftUI-Introspect",
+      "state" : {
+        "revision" : "807f73ce09a9b9723f12385e592b4e0aaebd3336",
+        "version" : "1.3.0"
+      }
+    },
+    {
+      "identity" : "swiftyjson",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/SwiftyJSON/SwiftyJSON.git",
+      "state" : {
+        "revision" : "2b6054efa051565954e1d2b9da831680026cd768",
+        "version" : "4.3.0"
+      }
+    },
+    {
+      "identity" : "xctest-dynamic-overlay",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
+      "state" : {
+        "revision" : "50843cbb8551db836adec2290bb4bc6bac5c1865",
+        "version" : "0.9.0"
+      }
+    }
+  ],
+  "version" : 3
+}

From eae6b63b7a3d482e58e44b5710fb5350dda296e8 Mon Sep 17 00:00:00 2001
From: Sjmarf <78750526+Sjmarf@users.noreply.github.com>
Date: Mon, 9 Dec 2024 21:52:09 +0000
Subject: [PATCH 6/7] Update

---
 Mlem.xcodeproj/project.pbxproj                |  14 +-
 .../xcshareddata/swiftpm/Package.resolved     | 168 ++++++++++++++++++
 2 files changed, 171 insertions(+), 11 deletions(-)
 create mode 100644 Mlem.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

diff --git a/Mlem.xcodeproj/project.pbxproj b/Mlem.xcodeproj/project.pbxproj
index 11d38ca68..48484e0d1 100644
--- a/Mlem.xcodeproj/project.pbxproj
+++ b/Mlem.xcodeproj/project.pbxproj
@@ -195,9 +195,9 @@
 		03AB48552CBC0B8000567FF9 /* AccountAdvancedSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AB48542CBC0B8000567FF9 /* AccountAdvancedSettingsView.swift */; };
 		03AB48572CBC0DFC00567FF9 /* AccountSignInSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AB48562CBC0DFC00567FF9 /* AccountSignInSettingsView.swift */; };
 		03AB48592CBC14CE00567FF9 /* AccountEmailSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AB48582CBC14CE00567FF9 /* AccountEmailSettingsView.swift */; };
+		03AD09E82CF88007001EF9F7 /* MoreRepliesButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AD09E72CF88007001EF9F7 /* MoreRepliesButton.swift */; };
 		03AD0A822CFDBFA0001EF9F7 /* AccountLocalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AD0A812CFDBFA0001EF9F7 /* AccountLocalSettingsView.swift */; };
 		03AD0A842CFDC557001EF9F7 /* AccountNicknameFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AD0A832CFDC557001EF9F7 /* AccountNicknameFieldView.swift */; };
-		03AD09E82CF88007001EF9F7 /* MoreRepliesButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AD09E72CF88007001EF9F7 /* MoreRepliesButton.swift */; };
 		03AF91DD2C1B23E500E56644 /* ImageViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AF91DC2C1B23E500E56644 /* ImageViewer.swift */; };
 		03AF91DF2C1B243D00E56644 /* ZoomableContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AF91DE2C1B243D00E56644 /* ZoomableContainer.swift */; };
 		03AF91E12C1B25DE00E56644 /* UIDevice+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AF91E02C1B25DE00E56644 /* UIDevice+Extensions.swift */; };
@@ -614,9 +614,9 @@
 		03AB48542CBC0B8000567FF9 /* AccountAdvancedSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountAdvancedSettingsView.swift; sourceTree = "<group>"; };
 		03AB48562CBC0DFC00567FF9 /* AccountSignInSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSignInSettingsView.swift; sourceTree = "<group>"; };
 		03AB48582CBC14CE00567FF9 /* AccountEmailSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountEmailSettingsView.swift; sourceTree = "<group>"; };
+		03AD09E72CF88007001EF9F7 /* MoreRepliesButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreRepliesButton.swift; sourceTree = "<group>"; };
 		03AD0A812CFDBFA0001EF9F7 /* AccountLocalSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountLocalSettingsView.swift; sourceTree = "<group>"; };
 		03AD0A832CFDC557001EF9F7 /* AccountNicknameFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountNicknameFieldView.swift; sourceTree = "<group>"; };
-		03AD09E72CF88007001EF9F7 /* MoreRepliesButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreRepliesButton.swift; sourceTree = "<group>"; };
 		03AF91DC2C1B23E500E56644 /* ImageViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewer.swift; sourceTree = "<group>"; };
 		03AF91DE2C1B243D00E56644 /* ZoomableContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZoomableContainer.swift; sourceTree = "<group>"; };
 		03AF91E02C1B25DE00E56644 /* UIDevice+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Extensions.swift"; sourceTree = "<group>"; };
@@ -2927,7 +2927,7 @@
 			repositoryURL = "https://github.com/mlemgroup/MlemMiddleware";
 			requirement = {
 				kind = upToNextMajorVersion;
-				minimumVersion = 0.51.0;
+				minimumVersion = 0.52.0;
 			};
 		};
 		CDE4AC402CA3706400981010 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */ = {
@@ -2946,14 +2946,6 @@
 				minimumVersion = 0.14.6;
 			};
 		};
-		CDFA5CAE2CEAB8AF00FDF998 /* XCRemoteSwiftPackageReference "MlemMiddleware" */ = {
-			isa = XCRemoteSwiftPackageReference;
-			repositoryURL = "https://github.com/mlemgroup/MlemMiddleware";
-			requirement = {
-				branch = "sjmarf/profile-settings";
-				kind = branch;
-			};
-		};
 /* End XCRemoteSwiftPackageReference section */
 
 /* Begin XCSwiftPackageProductDependency section */
diff --git a/Mlem.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mlem.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 000000000..53c8be7e0
--- /dev/null
+++ b/Mlem.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,168 @@
+{
+  "originHash" : "e91c2cec61691543665fad5843f30eafc5288bb40fc3a60d50c7d7e9194f3135",
+  "pins" : [
+    {
+      "identity" : "combine-schedulers",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/pointfreeco/combine-schedulers",
+      "state" : {
+        "revision" : "ec62f32d21584214a4b27c8cee2b2ad70ab2c38a",
+        "version" : "0.11.0"
+      }
+    },
+    {
+      "identity" : "gifu",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/kaishin/Gifu",
+      "state" : {
+        "branch" : "master",
+        "revision" : "639e40d5dbd86dfa94fc61296c270b49af6f8a2a"
+      }
+    },
+    {
+      "identity" : "keychainaccess",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/kishikawakatsumi/KeychainAccess.git",
+      "state" : {
+        "branch" : "master",
+        "revision" : "e0c7eebc5a4465a3c4680764f26b7a61f567cdaf"
+      }
+    },
+    {
+      "identity" : "lemmymarkdownui",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/mlemgroup/LemmyMarkdownUI",
+      "state" : {
+        "revision" : "b42b1e301194fd56a144cf199ab2b807a0413a97",
+        "version" : "0.5.2"
+      }
+    },
+    {
+      "identity" : "libwebp-xcode",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/SDWebImage/libwebp-Xcode.git",
+      "state" : {
+        "revision" : "b2b1d20a90b14d11f6ef4241da6b81c1d3f171e4",
+        "version" : "1.3.2"
+      }
+    },
+    {
+      "identity" : "mlemmiddleware",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/mlemgroup/MlemMiddleware",
+      "state" : {
+        "revision" : "51db2875046e216fb01c8233cc43eadd147dd240",
+        "version" : "0.52.0"
+      }
+    },
+    {
+      "identity" : "nuke",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/kean/Nuke",
+      "state" : {
+        "revision" : "0ead44350d2737db384908569c012fe67c421e4d",
+        "version" : "12.8.0"
+      }
+    },
+    {
+      "identity" : "sdwebimage",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/SDWebImage/SDWebImage.git",
+      "state" : {
+        "revision" : "8a1be70a625683bc04d6903e2935bf23f3c6d609",
+        "version" : "5.19.7"
+      }
+    },
+    {
+      "identity" : "sdwebimageswiftui",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/SDWebImage/SDWebImageSwiftUI",
+      "state" : {
+        "revision" : "5aa947356f4ea49a0c3b9968564267f6ea5abea7",
+        "version" : "3.1.2"
+      }
+    },
+    {
+      "identity" : "sdwebimagewebpcoder",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/SDWebImage/SDWebImageWebPCoder",
+      "state" : {
+        "revision" : "f534cfe830a7807ecc3d0332127a502426cfa067",
+        "version" : "0.14.6"
+      }
+    },
+    {
+      "identity" : "semaphore",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/groue/Semaphore",
+      "state" : {
+        "revision" : "2543679282aa6f6c8ecf2138acd613ed20790bc2",
+        "version" : "0.1.0"
+      }
+    },
+    {
+      "identity" : "swift-clocks",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/pointfreeco/swift-clocks",
+      "state" : {
+        "revision" : "0fbaebfc013715dab44d715a4d350ba37f297e4d",
+        "version" : "0.4.0"
+      }
+    },
+    {
+      "identity" : "swift-concurrency-extras",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/pointfreeco/swift-concurrency-extras",
+      "state" : {
+        "revision" : "a46265bb4f75808b0e15d971eebc408f557870a3",
+        "version" : "0.1.2"
+      }
+    },
+    {
+      "identity" : "swift-dependencies",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/pointfreeco/swift-dependencies",
+      "state" : {
+        "revision" : "16fd42ae04c6e7f74a6a86395d04722c641cccee",
+        "version" : "0.6.0"
+      }
+    },
+    {
+      "identity" : "swiftui-flow",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/tevelee/SwiftUI-Flow",
+      "state" : {
+        "revision" : "b528bd06ae70fbd2936d9561a456fb6ea65bc7ff",
+        "version" : "2.5.0"
+      }
+    },
+    {
+      "identity" : "swiftui-introspect",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/siteline/SwiftUI-Introspect",
+      "state" : {
+        "revision" : "807f73ce09a9b9723f12385e592b4e0aaebd3336",
+        "version" : "1.3.0"
+      }
+    },
+    {
+      "identity" : "swiftyjson",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/SwiftyJSON/SwiftyJSON.git",
+      "state" : {
+        "revision" : "2b6054efa051565954e1d2b9da831680026cd768",
+        "version" : "4.3.0"
+      }
+    },
+    {
+      "identity" : "xctest-dynamic-overlay",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
+      "state" : {
+        "revision" : "50843cbb8551db836adec2290bb4bc6bac5c1865",
+        "version" : "0.9.0"
+      }
+    }
+  ],
+  "version" : 3
+}

From 28c1103299bcf091b351e2269b939973ba5d94cf Mon Sep 17 00:00:00 2001
From: Sjmarf <78750526+Sjmarf@users.noreply.github.com>
Date: Mon, 9 Dec 2024 21:54:13 +0000
Subject: [PATCH 7/7] Implement requested changes

---
 Mlem/App/Configuration/Icons.swift                            | 2 ++
 .../App/Views/Pages/PostEditor/PostEditorView+ImageView.swift | 2 +-
 Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift   | 4 ++--
 Mlem/App/Views/Shared/ImageUploadMenu.swift                   | 2 +-
 4 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/Mlem/App/Configuration/Icons.swift b/Mlem/App/Configuration/Icons.swift
index 472fdf4a1..d5b61bd68 100644
--- a/Mlem/App/Configuration/Icons.swift
+++ b/Mlem/App/Configuration/Icons.swift
@@ -201,6 +201,7 @@ enum Icons {
     static let close: String = "multiply"
     static let closeCircle: String = "xmark.circle"
     static let closeCircleFill: String = "xmark.circle.fill"
+    static let addCircleFill: String = "plus.circle.fill"
     static let cakeDay: String = "birthday.cake"
     static let cakeDayFill: String = "birthday.cake.fill"
     static let undoCircleFill: String = "arrow.uturn.backward.circle.fill"
@@ -246,6 +247,7 @@ enum Icons {
     static let refresh: String = "arrow.clockwise"
     static let select: String = "selection.pin.in.out"
     static let crossPost: String = "shuffle"
+    static let chooseFile: String = "folder"
     
     // settings
     static let upvoteOnSave: String = "arrow.up.heart"
diff --git a/Mlem/App/Views/Pages/PostEditor/PostEditorView+ImageView.swift b/Mlem/App/Views/Pages/PostEditor/PostEditorView+ImageView.swift
index afa542ebf..5c8480433 100644
--- a/Mlem/App/Views/Pages/PostEditor/PostEditorView+ImageView.swift
+++ b/Mlem/App/Views/Pages/PostEditor/PostEditorView+ImageView.swift
@@ -91,7 +91,7 @@ extension PostEditorView {
                     guard let imageManager else { return }
                     navigation.showPhotosPicker(for: imageManager, api: primaryApi)
                 }
-                Button("Files", systemImage: "folder") {
+                Button("Files", systemImage: Icons.chooseFile) {
                     guard let imageManager else { return }
                     navigation.showFilePicker(for: imageManager, api: primaryApi)
                 }
diff --git a/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift b/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift
index 0f6dacf0a..bda4eb8d4 100644
--- a/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift
+++ b/Mlem/App/Views/Root/Tabs/Settings/ProfileSettingsView.swift
@@ -200,7 +200,7 @@ private struct CircleImageUploadButton: View {
                 Button {
                     url = nil
                 } label: {
-                    Image(systemName: "xmark.circle.fill")
+                    Image(systemName: Icons.closeCircleFill)
                         .resizable()
                 }
             } else {
@@ -210,7 +210,7 @@ private struct CircleImageUploadButton: View {
                         .controlSize(.extraLarge)
                 default:
                     ImageUploadMenu(imageManager: imageManager, imageUploadApi: api) {
-                        Image(systemName: "plus.circle.fill")
+                        Image(systemName: Icons.addCircleFill)
                             .resizable()
                     }
                 }
diff --git a/Mlem/App/Views/Shared/ImageUploadMenu.swift b/Mlem/App/Views/Shared/ImageUploadMenu.swift
index 579a2bd61..2f3649f8b 100644
--- a/Mlem/App/Views/Shared/ImageUploadMenu.swift
+++ b/Mlem/App/Views/Shared/ImageUploadMenu.swift
@@ -26,7 +26,7 @@ struct ImageUploadMenu<Label: View>: View {
             Button("Photo Library", systemImage: Icons.photo) {
                 navigation.showPhotosPicker(for: imageManager, api: imageUploadApi)
             }
-            Button("Choose File", systemImage: "folder") {
+            Button("Choose File", systemImage: Icons.chooseFile) {
                 navigation.showFilePicker(for: imageManager, api: imageUploadApi)
             }
             Button("Paste", systemImage: Icons.paste) {