Skip to content

Commit

Permalink
Merge pull request #1643 from planetary-social/delete-account-textfield
Browse files Browse the repository at this point in the history
Add Delete confirmation  before deleting account
  • Loading branch information
pelumy authored Oct 17, 2024
2 parents b56bc81 + d5332ed commit 508b51e
Show file tree
Hide file tree
Showing 11 changed files with 350 additions and 25 deletions.
8 changes: 8 additions & 0 deletions Nos.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@
045EDCF32CAAF47600B67964 /* FlagSuccessView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 045EDCF22CAAF47600B67964 /* FlagSuccessView.swift */; };
045EDD052CAC025700B67964 /* ScrollViewProxy+Animate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 045EDD042CAC025700B67964 /* ScrollViewProxy+Animate.swift */; };
0496D6312C975E6900D29375 /* FlagOptionPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0496D6302C975E6900D29375 /* FlagOptionPicker.swift */; };
04C9D7272CBF09C200EAAD4D /* TextField+PlaceHolderStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C9D7262CBF09C200EAAD4D /* TextField+PlaceHolderStyle.swift */; };
04F16AA72CBDBD91003AD693 /* DeleteConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F16AA62CBDBD91003AD693 /* DeleteConfirmationView.swift */; };
2D06BB9D2AE249D70085F509 /* ThreadRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D06BB9C2AE249D70085F509 /* ThreadRootView.swift */; };
2D4010A22AD87DF300F93AD4 /* KnownFollowersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D4010A12AD87DF300F93AD4 /* KnownFollowersView.swift */; };
3A1C296F2B2A537C0020B753 /* Moderation.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 3A1C296E2B2A537C0020B753 /* Moderation.xcstrings */; };
Expand Down Expand Up @@ -671,6 +673,8 @@
045EDCF22CAAF47600B67964 /* FlagSuccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlagSuccessView.swift; sourceTree = "<group>"; };
045EDD042CAC025700B67964 /* ScrollViewProxy+Animate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ScrollViewProxy+Animate.swift"; sourceTree = "<group>"; };
0496D6302C975E6900D29375 /* FlagOptionPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlagOptionPicker.swift; sourceTree = "<group>"; };
04C9D7262CBF09C200EAAD4D /* TextField+PlaceHolderStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TextField+PlaceHolderStyle.swift"; sourceTree = "<group>"; };
04F16AA62CBDBD91003AD693 /* DeleteConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteConfirmationView.swift; sourceTree = "<group>"; };
2D06BB9C2AE249D70085F509 /* ThreadRootView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadRootView.swift; sourceTree = "<group>"; };
2D4010A12AD87DF300F93AD4 /* KnownFollowersView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KnownFollowersView.swift; sourceTree = "<group>"; };
3A1C296E2B2A537C0020B753 /* Moderation.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Moderation.xcstrings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1229,6 +1233,7 @@
children = (
C9F84C22298DC7B900C6714D /* SettingsView.swift */,
5BFF66B32A58853D00AA79DD /* PublishedEventsView.swift */,
04F16AA62CBDBD91003AD693 /* DeleteConfirmationView.swift */,
);
path = Settings;
sourceTree = "<group>";
Expand Down Expand Up @@ -1708,6 +1713,7 @@
50089A162C98678600834588 /* View+ListRowGradientBackground.swift */,
C93EC2FC29C3785C0012EE2A /* View+RoundedCorner.swift */,
50DE6B1A2C6B88FE0065665D /* View+StyledBorder.swift */,
04C9D7262CBF09C200EAAD4D /* TextField+PlaceHolderStyle.swift */,
);
path = Modifiers;
sourceTree = "<group>";
Expand Down Expand Up @@ -2340,6 +2346,7 @@
03C7E7A22CB9CD150054624C /* PointDownEmojiTipViewStyle.swift in Sources */,
C9A0DAE029C697A100466635 /* AboutView.swift in Sources */,
0496D6312C975E6900D29375 /* FlagOptionPicker.swift in Sources */,
04F16AA72CBDBD91003AD693 /* DeleteConfirmationView.swift in Sources */,
A351E1A229BA92240009B7F6 /* ProfileEditView.swift in Sources */,
C9DFA969299BEC33006929C1 /* CardStyle.swift in Sources */,
C95D68AD299E721700429F86 /* ProfileView.swift in Sources */,
Expand Down Expand Up @@ -2505,6 +2512,7 @@
C9DC6CBA2C1739AD00E1CFB3 /* View+HandleURLsInRouter.swift in Sources */,
508B2B612C9EF65300C14034 /* NSPersistentContainer+Nos.swift in Sources */,
5B79F6552BA123D4002DA9BE /* WizardSheetBadgeText.swift in Sources */,
04C9D7272CBF09C200EAAD4D /* TextField+PlaceHolderStyle.swift in Sources */,
C9DEC04D29894BED0078B43A /* Author+CoreDataClass.swift in Sources */,
C905B0772A619E99009B8A78 /* LPLinkViewRepresentable.swift in Sources */,
C95D68A7299E6FF000429F86 /* KeyFixture.swift in Sources */,
Expand Down
20 changes: 20 additions & 0 deletions Nos/Assets/Colors.xcassets/action-sheet-bg.colorset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x2C",
"green" : "0x26",
"red" : "0x28"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x6D",
"green" : "0x63",
"red" : "0x66"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x19",
"green" : "0x09",
"red" : "0x0E"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x1A",
"green" : "0x16",
"red" : "0x17"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x21",
"green" : "0x1D",
"red" : "0x1E"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x6D",
"green" : "0x63",
"red" : "0x66"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
26 changes: 24 additions & 2 deletions Nos/Assets/Localization/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -4390,6 +4390,17 @@
}
}
},
"deleteAccountMessage" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Deleting your account will delete all your data from Nos servers and ask all your relays to do the same."
}
}
}
},
"deleteMyAccount" : {
"extractionState" : "manual",
"localizations" : {
Expand Down Expand Up @@ -13796,7 +13807,7 @@
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Your **@username** is your identity in the Nos community.\u2028\nChoose a name that reflects you or your organization. Make it memorable and distinct!"
"value" : "Your **@username** is your identity in the Nos community.\nChoose a name that reflects you or your organization. Make it memorable and distinct!"
}
},
"es" : {
Expand Down Expand Up @@ -18430,6 +18441,17 @@
}
}
},
"typeDelete" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Type Delete"
}
}
}
},
"unfollow" : {
"extractionState" : "manual",
"localizations" : {
Expand Down Expand Up @@ -18962,7 +18984,7 @@
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Well done, you've successfully claimed your **@username**!\u2028\nYou can share this name with other people in the Nostr and Fediverse communities to make it easy to find you."
"value" : "Well done, you've successfully claimed your **@username**!\nYou can share this name with other people in the Nostr and Fediverse communities to make it easy to find you."
}
},
"es" : {
Expand Down
28 changes: 28 additions & 0 deletions Nos/Views/Modifiers/TextField+PlaceHolderStyle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import SwiftUI

/// Displays a placeholder text when required.
/// - Parameters:
/// - show: A Boolean indicating whether to show the placeholder.
/// - placeholder: The text to display as the placeholder.
struct PlaceholderStyle: ViewModifier {
var show: Bool
var placeholder: String

/// Displays the placeholder text if `show` is true, overlaying it
/// on the content view.
func body(content: Content) -> some View {
ZStack(alignment: .leading) {
if show {
Text(placeholder)
.foregroundColor(Color.actionSheetTextfieldPlaceholder)
.padding(.leading, 9)
.font(.headline)
.fontWeight(.bold)
}
content
.foregroundColor(Color.white)
.font(.headline)
.fontWeight(.bold)
}
}
}
130 changes: 130 additions & 0 deletions Nos/Views/Settings/DeleteConfirmationView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import SwiftUI

/// This view displays a title and message, along with a text field
/// for user confirmation. It provides buttons to cancel the action
/// or proceed with the deletion (which is enabled only when the
/// correct confirmation text is entered).
/// - Parameters:
/// - requiredText: The text that must be entered to confirm the deletion.
/// - onDelete: A closure that is called when the delete button is confirmed.
/// - onCancel: A closure that is called when the cancel button is selected.
struct DeleteConfirmationView: View {
@State private var confirmationText: String = ""

var requiredText: String
var onDelete: (() -> Void)
var onCancel: (() -> Void)

var isDeleteDisabled: Bool {
confirmationText != requiredText
}

var body: some View {
VStack(spacing: 0) {
VStack(spacing: 0) {
titleMessageView()

confirmationTextField(
confirmationText: $confirmationText,
requiredText: requiredText
)
}
.padding()

Divider()
.frame(height: 1)
.background(Color.actionSheetDivider)

actionButtonsView(
isDeleteDisabled: isDeleteDisabled,
onDelete: onDelete,
onCancel: onCancel
)
}
.background(Color.actionSheetBg)
.cornerRadius(10)
.frame(width: 302, height: 228)
.shadow(color: Color.black.opacity(0.25), radius: 25, x: 0, y: 10)
}

/// Creates the title and message view for the delete confirmation.
private func titleMessageView() -> some View {
VStack(spacing: 0) {
Text(String(localized: "deleteAccount"))
.font(.headline)
.fontWeight(.bold)
.padding(.bottom, 10)
.padding(.top, 18)

Text(String(localized: "deleteAccountMessage"))
.font(.footnote)
.fixedSize(horizontal: false, vertical: true)
.multilineTextAlignment(.center)
.foregroundColor(.white)
.padding(.horizontal)
.padding(.bottom, 15)
}
}

/// Creates the text field to confirm the user's input.
/// - Parameters:
/// - confirmationText: A binding to the user's input text.
/// - requiredText: The text that must be entered for confirmation.
/// - Returns: A `TextField` configured for confirming user's input.
private func confirmationTextField(confirmationText: Binding<String>, requiredText: String) -> some View {
let isTyping = !confirmationText.wrappedValue.isEmpty

return TextField("", text: confirmationText)
.foregroundColor(.white)
.autocapitalization(.allCharacters)
.padding(10)
.cornerRadius(8)
.overlay(
RoundedRectangle(cornerRadius: 4)
.stroke(isTyping ? .white : Color.actionSheetTextfieldPlaceholder, lineWidth: 1)
)
.modifier(
PlaceholderStyle(
show: confirmationText.wrappedValue.isEmpty,
placeholder: String(localized: "typeDelete")
)
)
.background(isTyping ? Color.actionSheetTextfieldBgBright : Color.actionSheetTextfieldBgDim)
.padding(.horizontal, 22)
.padding(.bottom, 5)
}

/// Creates the cancel and delete buttons.
private func actionButtonsView(
isDeleteDisabled: Bool,
onDelete: @escaping () -> Void,
onCancel: @escaping () -> Void
) -> some View {
HStack {
Button(String(localized: "cancel")) {
onCancel()
}
.frame(maxWidth: .infinity)
.foregroundColor(.blue)

Divider()
.frame(width: 1, height: 50)
.background(Color.actionSheetDivider)

Button(String(localized: "delete")) {
onDelete()
}
.frame(maxWidth: .infinity)
.foregroundColor(isDeleteDisabled ? Color.actionSheetDivider : .red)
.disabled(isDeleteDisabled)
}
}
}

#Preview {
DeleteConfirmationView(
requiredText: String(localized: "deleteAccount").uppercased(),
onDelete: {},
onCancel: {}
)
}
Loading

0 comments on commit 508b51e

Please sign in to comment.