commit e1578c0337c22e38cb70859af8f337c42005286e
parent 9fa11118d3838e3a279442cf05e0e38693d3e3ab
Author: William Casarin <jb55@jb55.com>
Date: Mon, 30 Jan 2023 13:26:04 -0800
Add support for account deletion
As per apple guidelines
Changelog-Added: Added support for account deletion
Diffstat:
12 files changed, 102 insertions(+), 12 deletions(-)
diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj
@@ -116,6 +116,7 @@
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C987B56283FD07F0042CE38 /* FollowersModel.swift */; };
4C99737B28C92A9200E53835 /* ChatroomMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C99737A28C92A9200E53835 /* ChatroomMetadata.swift */; };
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; };
+ 4CAAD8AD298851D000060CEA /* AccountDeletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8AC298851D000060CEA /* AccountDeletion.swift */; };
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; };
4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */; };
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */; };
@@ -369,6 +370,7 @@
4C987B56283FD07F0042CE38 /* FollowersModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersModel.swift; sourceTree = "<group>"; };
4C99737A28C92A9200E53835 /* ChatroomMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatroomMetadata.swift; sourceTree = "<group>"; };
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; };
+ 4CAAD8AC298851D000060CEA /* AccountDeletion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeletion.swift; sourceTree = "<group>"; };
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = "<group>"; };
4CACA9DB280C38C000D9BBE8 /* Profiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profiles.swift; sourceTree = "<group>"; };
4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendedRelayView.swift; sourceTree = "<group>"; };
@@ -691,6 +693,7 @@
4CF0ABD72981980C00D66079 /* Lists.swift */,
4CF0ABEF29857E9200D66079 /* Bech32Object.swift */,
7C60CAEE298471A1009C80D6 /* CoreSVG.swift */,
+ 4CAAD8AC298851D000060CEA /* AccountDeletion.swift */,
);
path = Util;
sourceTree = "<group>";
@@ -1109,6 +1112,7 @@
9609F058296E220800069BF3 /* BannerImageView.swift in Sources */,
4C363A94282704FA006E126D /* Post.swift in Sources */,
4C216F32286E388800040376 /* DMChatView.swift in Sources */,
+ 4CAAD8AD298851D000060CEA /* AccountDeletion.swift in Sources */,
4C3EA67928FF7ABF00C48A62 /* list.c in Sources */,
4C64987E286D082C00EAE2B3 /* DirectMessagesModel.swift in Sources */,
4C363A8828236948006E126D /* BlocksView.swift in Sources */,
diff --git a/damus/ContentView.swift b/damus/ContentView.swift
@@ -73,6 +73,7 @@ struct ContentView: View {
@State var damus_state: DamusState? = nil
@State var selected_timeline: Timeline? = .home
@State var is_thread_open: Bool = false
+ @State var is_deleted_account: Bool = false
@State var is_profile_open: Bool = false
@State var event: NostrEvent? = nil
@State var active_profile: String? = nil
@@ -348,6 +349,9 @@ struct ContentView: View {
}
.onReceive(handle_notify(.like)) { like in
}
+ .onReceive(handle_notify(.deleted_account)) { notif in
+ self.is_deleted_account = true
+ }
.onReceive(handle_notify(.report)) { notif in
let target = notif.object as! ReportTarget
self.active_sheet = .report(target)
@@ -434,7 +438,13 @@ struct ContentView: View {
.onReceive(handle_notify(.new_mutes)) { notif in
home.filter_muted()
}
- .alert(NSLocalizedString("User blocked", comment: "Alert message to indicate "), isPresented: $user_blocked_confirm, actions: {
+ .alert(NSLocalizedString("Deleted Account", comment: "Alert message to indicate this is a deleted account"), isPresented: $is_deleted_account) {
+ Button(NSLocalizedString("Logout", comment: "Button to close the alert that informs that the current account has been deleted.")) {
+ is_deleted_account = false
+ notify(.logout, ())
+ }
+ }
+ .alert(NSLocalizedString("User blocked", comment: "Alert message to indicate the user has been blocked"), isPresented: $user_blocked_confirm, actions: {
Button(NSLocalizedString("Thanks!", comment: "Button to close out of alert that informs that the action to block a user was successful.")) {
user_blocked_confirm = false
}
diff --git a/damus/Models/FollowersModel.swift b/damus/Models/FollowersModel.swift
@@ -86,7 +86,7 @@ class FollowersModel: ObservableObject {
if ev.known_kind == .contacts {
handle_contact_event(ev)
} else if ev.known_kind == .metadata {
- process_metadata_event(profiles: damus_state.profiles, ev: ev)
+ process_metadata_event(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
}
case .notice(let msg):
diff --git a/damus/Models/FollowingModel.swift b/damus/Models/FollowingModel.swift
@@ -60,7 +60,7 @@ class FollowingModel {
switch nev {
case .event(_, let ev):
if ev.kind == 0 {
- process_metadata_event(profiles: damus_state.profiles, ev: ev)
+ process_metadata_event(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
}
case .notice(let msg):
print("followingmodel notice: \(msg)")
diff --git a/damus/Models/HomeModel.swift b/damus/Models/HomeModel.swift
@@ -372,7 +372,7 @@ class HomeModel: ObservableObject {
}
func handle_metadata_event(_ ev: NostrEvent) {
- process_metadata_event(profiles: damus_state.profiles, ev: ev)
+ process_metadata_event(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
}
func get_last_event_of_kind(relay_id: String, kind: Int) -> NostrEvent? {
@@ -530,10 +530,17 @@ func print_filters(relay_id: String?, filters groups: [[NostrFilter]]) {
print("-----")
}
-func process_metadata_event(profiles: Profiles, ev: NostrEvent) {
+func process_metadata_event(our_pubkey: String, profiles: Profiles, ev: NostrEvent) {
guard let profile: Profile = decode_data(Data(ev.content.utf8)) else {
return
}
+
+ if our_pubkey == ev.pubkey && (profile.deleted ?? false) {
+ DispatchQueue.main.async {
+ notify(.deleted_account, ())
+ }
+ return
+ }
var old_nip05: String? = nil
if let mprof = profiles.lookup_with_timestamp(id: ev.pubkey) {
diff --git a/damus/Models/ProfileModel.swift b/damus/Models/ProfileModel.swift
@@ -97,7 +97,7 @@ class ProfileModel: ObservableObject, Equatable {
} else if ev.known_kind == .contacts {
handle_profile_contact_event(ev)
} else if ev.known_kind == .metadata {
- process_metadata_event(profiles: damus.profiles, ev: ev)
+ process_metadata_event(our_pubkey: damus.pubkey, profiles: damus.profiles, ev: ev)
}
seen_event.insert(ev.id)
}
diff --git a/damus/Models/SearchHomeModel.swift b/damus/Models/SearchHomeModel.swift
@@ -129,7 +129,7 @@ func load_profiles(profiles_subid: String, relay_id: String, events: [NostrEvent
}
if ev.known_kind == .metadata {
- process_metadata_event(profiles: damus_state.profiles, ev: ev)
+ process_metadata_event(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
}
}
diff --git a/damus/Models/ThreadModel.swift b/damus/Models/ThreadModel.swift
@@ -190,7 +190,7 @@ class ThreadModel: ObservableObject {
}
if ev.known_kind == .metadata {
- process_metadata_event(profiles: damus_state.profiles, ev: ev)
+ process_metadata_event(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
} else if ev.is_textlike {
self.add_event(ev, privkey: self.damus_state.keypair.privkey)
} else if ev.known_kind == .channel_meta || ev.known_kind == .channel_create {
diff --git a/damus/Nostr/Nostr.swift b/damus/Nostr/Nostr.swift
@@ -24,18 +24,22 @@ struct Profile: Codable {
}
private func str(_ str: String) -> String? {
- guard let val = self.value[str] else{
+ return get_val(str)
+ }
+
+ private func get_val<T>(_ v: String) -> T? {
+ guard let val = self.value[v] else{
return nil
}
- guard let s = val.value as? String else {
+ guard let s = val.value as? T else {
return nil
}
return s
}
- private mutating func set_str(_ key: String, _ val: String?) {
+ private mutating func set_val<T>(_ key: String, _ val: T?) {
if val == nil {
self.value.removeValue(forKey: key)
return
@@ -44,6 +48,15 @@ struct Profile: Codable {
self.value[key] = AnyCodable.init(val)
}
+ private mutating func set_str(_ key: String, _ val: String?) {
+ set_val(key, val)
+ }
+
+ var deleted: Bool? {
+ get { return get_val("deleted"); }
+ set(s) { set_val("deleted", s) }
+ }
+
var display_name: String? {
get { return str("display_name"); }
set(s) { set_str("display_name", s) }
@@ -109,6 +122,10 @@ struct Profile: Codable {
return make_ln_url(self.lnurl)
}
+ init() {
+ self.value = [:]
+ }
+
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self.value = try container.decode([String: AnyCodable].self)
diff --git a/damus/Util/AccountDeletion.swift b/damus/Util/AccountDeletion.swift
@@ -0,0 +1,22 @@
+//
+// AccountDeletion.swift
+// damus
+//
+// Created by William Casarin on 2023-01-30.
+//
+
+import Foundation
+
+
+func created_deleted_account_profile(keypair: FullKeypair) -> NostrEvent {
+ var profile = Profile()
+ profile.deleted = true
+ profile.about = "account deleted"
+ profile.name = "nobody"
+
+ let content = encode_json(profile)!
+ let ev = NostrEvent(content: content, pubkey: keypair.pubkey, kind: 0)
+ ev.id = calculate_event_id(ev: ev)
+ ev.sig = sign_event(privkey: keypair.privkey, ev: ev)
+ return ev
+}
diff --git a/damus/Util/Notifications.swift b/damus/Util/Notifications.swift
@@ -95,6 +95,9 @@ extension Notification.Name {
static var new_unmutes: Notification.Name {
return Notification.Name("new_unmutes")
}
+ static var deleted_account: Notification.Name {
+ return Notification.Name("deleted_account")
+ }
}
func handle_notify(_ name: Notification.Name) -> NotificationCenter.Publisher {
diff --git a/damus/Views/ConfigView.swift b/damus/Views/ConfigView.swift
@@ -13,12 +13,14 @@ struct ConfigView: View {
@Environment(\.dismiss) var dismiss
@State var show_add_relay: Bool = false
@State var confirm_logout: Bool = false
+ @State var confirm_delete_account: Bool = false
@State var new_relay: String = ""
@State var show_privkey: Bool = false
@State var privkey: String
@State var privkey_copied: Bool = false
@State var pubkey_copied: Bool = false
@State var relays: [RelayDescriptor]
+ @State var delete_text: String = ""
@EnvironmentObject var user_settings: UserSettingsStore
let generator = UIImpactFeedbackGenerator(style: .light)
@@ -128,16 +130,41 @@ struct ConfigView: View {
KingfisherManager.shared.cache.cleanExpiredDiskCache()
}
}
-
+
Section(NSLocalizedString("Reset", comment: "Section title for resetting the user")) {
Button(NSLocalizedString("Logout", comment: "Button to logout the user.")) {
confirm_logout = true
}
+
+ if state.is_privkey_user {
+ Button(NSLocalizedString("Delete Account", comment: "Button to delete the user's account."), role: .destructive) {
+ confirm_delete_account = true
+ }
+ }
}
}
}
.navigationTitle(NSLocalizedString("Settings", comment: "Navigation title for Settings view."))
.navigationBarTitleDisplayMode(.large)
+ .alert(NSLocalizedString("Delete Account", comment: "Alert for deleting the users account."), isPresented: $confirm_delete_account) {
+ TextField("Type DELETE to delete", text: $delete_text)
+ Button(NSLocalizedString("Cancel", comment: "Cancel deleting the user."), role: .cancel) {
+ confirm_delete_account = false
+ }
+ Button(NSLocalizedString("Delete", comment: "Button for deleting the users account."), role: .destructive) {
+ guard let full_kp = state.keypair.to_full() else {
+ return
+ }
+
+ guard delete_text == "DELETE" else {
+ return
+ }
+
+ let ev = created_deleted_account_profile(keypair: full_kp)
+ state.pool.send(.event(ev))
+ notify(.logout, ())
+ }
+ }
.alert(NSLocalizedString("Logout", comment: "Alert for logging out the user."), isPresented: $confirm_logout) {
Button(NSLocalizedString("Cancel", comment: "Cancel out of logging out the user."), role: .cancel) {
confirm_logout = false