core, ios: unhiding user profiles always requires password (#2101)

This commit is contained in:
Evgeny Poberezkin
2023-03-29 19:28:06 +01:00
committed by GitHub
parent a8c8137ade
commit 935d826a21
7 changed files with 88 additions and 70 deletions

View File

@@ -162,16 +162,16 @@ func apiHideUser(_ userId: Int64, viewPwd: String) async throws -> User {
try await setUserPrivacy_(.apiHideUser(userId: userId, viewPwd: viewPwd))
}
func apiUnhideUser(_ userId: Int64, viewPwd: String?) async throws -> User {
func apiUnhideUser(_ userId: Int64, viewPwd: String) async throws -> User {
try await setUserPrivacy_(.apiUnhideUser(userId: userId, viewPwd: viewPwd))
}
func apiMuteUser(_ userId: Int64, viewPwd: String?) async throws -> User {
try await setUserPrivacy_(.apiMuteUser(userId: userId, viewPwd: viewPwd))
func apiMuteUser(_ userId: Int64) async throws -> User {
try await setUserPrivacy_(.apiMuteUser(userId: userId))
}
func apiUnmuteUser(_ userId: Int64, viewPwd: String?) async throws -> User {
try await setUserPrivacy_(.apiUnmuteUser(userId: userId, viewPwd: viewPwd))
func apiUnmuteUser(_ userId: Int64) async throws -> User {
try await setUserPrivacy_(.apiUnmuteUser(userId: userId))
}
func setUserPrivacy_(_ cmd: ChatCommand) async throws -> User {

View File

@@ -44,10 +44,12 @@ struct UserProfilesView: View {
private enum UserProfileAction: Identifiable {
case deleteUser(user: User, delSMPQueues: Bool)
case unhideUser(user: User)
var id: String {
switch self {
case let .deleteUser(user, delSMPQueues): return "deleteUser \(user.userId) \(delSMPQueues)"
case let .unhideUser(user): return "unhideUser \(user.userId)"
}
}
}
@@ -207,43 +209,58 @@ struct UserProfilesView: View {
@ViewBuilder private func profileActionView(_ action: UserProfileAction) -> some View {
let passwordValid = actionPassword == actionPassword.trimmingCharacters(in: .whitespaces)
switch action {
case let .deleteUser(user, delSMPQueues):
let actionEnabled = actionPassword != "" && passwordValid && correctPassword(user, actionPassword)
List {
Text("Delete user")
.font(.title)
.bold()
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.listRowBackground(Color.clear)
Section() {
ProfilePreview(profileOf: user).padding(.leading, -8)
}
let passwordField = PassphraseField(key: $actionPassword, placeholder: "Profile password", valid: passwordValid)
let actionEnabled: (User) -> Bool = { user in actionPassword != "" && passwordValid && correctPassword(user, actionPassword) }
List {
switch action {
case let .deleteUser(user, delSMPQueues):
actionHeader("Delete user", user)
Section {
PassphraseField(key: $actionPassword, placeholder: "Profile password", valid: passwordValid)
passwordField
settingsRow("trash") {
Button("Delete user", role: .destructive) {
profileAction = nil
Task { await removeUser(user, delSMPQueues, viewPwd: actionPassword) }
}
.disabled(!actionEnabled)
.disabled(!actionEnabled(user))
}
} footer: {
if actionEnabled {
if actionEnabled(user) {
Text("All chats and messages will be deleted - this cannot be undone!")
.font(.callout)
}
}
case let .unhideUser(user):
actionHeader("Unhide user", user)
Section {
passwordField
settingsRow("lock.open") {
Button("Unhide user") {
profileAction = nil
setUserPrivacy(user) { try await apiUnhideUser(user.userId, viewPwd: actionPassword) }
}
.disabled(!actionEnabled(user))
}
}
}
}
}
@ViewBuilder func actionHeader(_ title: LocalizedStringKey, _ user: User) -> some View {
Text(title)
.font(.title)
.bold()
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.listRowBackground(Color.clear)
Section() {
ProfilePreview(profileOf: user).padding(.leading, -8)
}
}
private func deleteModeButton(_ title: LocalizedStringKey, _ delSMPQueues: Bool) -> some View {
Button(title, role: .destructive) {
if let user = userToDelete {
if user.hidden && user.activeUser && !correctPassword(user, searchTextOrPassword) {
if passwordEntryRequired(user) {
profileAction = .deleteUser(user: user, delSMPQueues: delSMPQueues)
} else {
alert = .deleteUser(user: user, delSMPQueues: delSMPQueues)
@@ -252,6 +269,10 @@ struct UserProfilesView: View {
}
}
private func passwordEntryRequired(_ user: User) -> Bool {
user.hidden && user.activeUser && !correctPassword(user, searchTextOrPassword)
}
private func removeUser(_ user: User, _ delSMPQueues: Bool, viewPwd: String?) async {
do {
if user.activeUser {
@@ -307,7 +328,11 @@ struct UserProfilesView: View {
.swipeActions(edge: .leading, allowsFullSwipe: true) {
if user.hidden {
Button("Unhide") {
setUserPrivacy(user) { try await apiUnhideUser(user.userId, viewPwd: userViewPassword(user)) }
if passwordEntryRequired(user) {
profileAction = .unhideUser(user: user)
} else {
setUserPrivacy(user) { try await apiUnhideUser(user.userId, viewPwd: searchTextOrPassword) }
}
}
.tint(.green)
} else {
@@ -321,12 +346,12 @@ struct UserProfilesView: View {
if user.showNtfs {
Button("Mute") {
setUserPrivacy(user, successAlert: showMuteProfileAlert ? .muteProfileAlert : nil) {
try await apiMuteUser(user.userId, viewPwd: userViewPassword(user))
try await apiMuteUser(user.userId)
}
}
} else {
Button("Unmute") {
setUserPrivacy(user) { try await apiUnmuteUser(user.userId, viewPwd: userViewPassword(user)) }
setUserPrivacy(user) { try await apiUnmuteUser(user.userId) }
}
}
}

View File

@@ -18,9 +18,9 @@ public enum ChatCommand {
case listUsers
case apiSetActiveUser(userId: Int64, viewPwd: String?)
case apiHideUser(userId: Int64, viewPwd: String)
case apiUnhideUser(userId: Int64, viewPwd: String?)
case apiMuteUser(userId: Int64, viewPwd: String?)
case apiUnmuteUser(userId: Int64, viewPwd: String?)
case apiUnhideUser(userId: Int64, viewPwd: String)
case apiMuteUser(userId: Int64)
case apiUnmuteUser(userId: Int64)
case apiDeleteUser(userId: Int64, delSMPQueues: Bool, viewPwd: String?)
case startChat(subscribe: Bool, expire: Bool)
case apiStopChat
@@ -111,9 +111,9 @@ public enum ChatCommand {
case .listUsers: return "/users"
case let .apiSetActiveUser(userId, viewPwd): return "/_user \(userId)\(maybePwd(viewPwd))"
case let .apiHideUser(userId, viewPwd): return "/_hide user \(userId) \(encodeJSON(viewPwd))"
case let .apiUnhideUser(userId, viewPwd): return "/_unhide user \(userId)\(maybePwd(viewPwd))"
case let .apiMuteUser(userId, viewPwd): return "/_mute user \(userId)\(maybePwd(viewPwd))"
case let .apiUnmuteUser(userId, viewPwd): return "/_unmute user \(userId)\(maybePwd(viewPwd))"
case let .apiUnhideUser(userId, viewPwd): return "/_unhide user \(userId) \(encodeJSON(viewPwd))"
case let .apiMuteUser(userId): return "/_mute user \(userId)"
case let .apiUnmuteUser(userId): return "/_unmute user \(userId)"
case let .apiDeleteUser(userId, delSMPQueues, viewPwd): return "/_delete user \(userId) del_smp=\(onOff(delSMPQueues))\(maybePwd(viewPwd))"
case let .startChat(subscribe, expire): return "/_start subscribe=\(onOff(subscribe)) expire=\(onOff(expire))"
case .apiStopChat: return "/_stop"
@@ -332,10 +332,6 @@ public enum ChatCommand {
return .apiHideUser(userId: userId, viewPwd: obfuscate(viewPwd))
case let .apiUnhideUser(userId, viewPwd):
return .apiUnhideUser(userId: userId, viewPwd: obfuscate(viewPwd))
case let .apiMuteUser(userId, viewPwd):
return .apiMuteUser(userId: userId, viewPwd: obfuscate(viewPwd))
case let .apiUnmuteUser(userId, viewPwd):
return .apiUnmuteUser(userId: userId, viewPwd: obfuscate(viewPwd))
case let .apiDeleteUser(userId, delSMPQueues, viewPwd):
return .apiDeleteUser(userId: userId, delSMPQueues: delSMPQueues, viewPwd: obfuscate(viewPwd))
default: return self
@@ -349,9 +345,8 @@ public enum ChatCommand {
private func obfuscate(_ s: String?) -> String? {
if let s = s {
return obfuscate(s)
} else {
return nil
}
return nil
}
private func onOff(_ b: Bool) -> String {