mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-03-14 09:45:42 +00:00
ios: new user picker (#4821)
* ios: new user picker (#4770) * current user picker progress * one hand picker * thin bullet icon * more user picker buttons * button clickable areas * divider padding * extra space after sun * send current user option to address view * add unread count badge * with anim for apperance close * edit current profile from picker * remove you section from settings * remove help and support * simplify * move settings and sun to same row * remove redundant vstack * long press on sun/moon switches to system setting * remove back button from migrate device * smooth profile transitions * close user picker on list profiles * fix dismiss on migrate from device * fix dismiss when deleting last visible user while having hidden users * picker visibility toggle tweaks * remove strange square from profile switcher click * dirty way to save auto accept settings on dismiss * Revert "dirty way to save auto accept settings on dismiss" This reverts commite7b19ee8aa
. * consistent animation on user picker toggle * change space between profiles * remove result * ignore result * unread badge * move to sheet * half sheet on one hand ui * fix dismiss on device migration * fix desktop connect * sun to meet other action icons * fill bullet list button * fix tap in settings to take full width * icon sizings and paddings * open settings in same sheet * apply same trick as other buttons for ligth toggle * layout * open profiles sheet large when +3 users * layout * layout * paddings * paddings * remove show progress * always small user picker * fixed height * open all actions as sheets * type, color * simpler and more effective way of avoid moving around on user select * dismiss user profiles sheet on user change * connect desktop back button remove * remove back buttons from user address view * remove porgress * header inside list * alert on auto accept unsaved changes * Cancel -> Discard * revert * fix connect to desktop * remove extra space * fix share inside multi sheet * user picker and options as separate sheet * revert showShareSheet * fix current profile and all profiles selection * change alert * update * cleanup user address * remove func * alert on unsaved changes in chat prefs * fix layout * cleanup --------- Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com> * ios: fix switching profiles (#4822) * ios: different user picker layout (#4826) * ios: different user picker layout * remove section * layout, color * color * remove activeUser * fix gradient * recursive sheets * gradient padding * share sheet * layout * dismiss sheets --------- Co-authored-by: Levitating Pineapple <noreply@levitatingpineapple.com> * ios: use the same way to share from all sheets (#4829) * ios: close user picker before opening other sheets * Revert "share sheet" This reverts commit0064155825
. * dismiss/show via callback * Revert "ios: close user picker before opening other sheets" This reverts commit19110398f8
. * ios: show alerts from sheets (#4839) * padding * remove gradient * cleanup * simplify settings * padding --------- Co-authored-by: Diogo <diogofncunha@gmail.com> Co-authored-by: Levitating Pineapple <noreply@levitatingpineapple.com> Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
This commit is contained in:
parent
388609563d
commit
fb4475027d
11 changed files with 335 additions and 286 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -61,6 +61,7 @@ website/package/generated*
|
||||||
# Ignore build tool output, e.g. code coverage
|
# Ignore build tool output, e.g. code coverage
|
||||||
website/.nyc_output/
|
website/.nyc_output/
|
||||||
website/coverage/
|
website/coverage/
|
||||||
|
result
|
||||||
|
|
||||||
# Ignore API documentation
|
# Ignore API documentation
|
||||||
website/api-docs/
|
website/api-docs/
|
||||||
|
|
|
@ -38,6 +38,7 @@ struct IncomingCallView: View {
|
||||||
}
|
}
|
||||||
HStack {
|
HStack {
|
||||||
ProfilePreview(profileOf: invitation.contact, color: .white)
|
ProfilePreview(profileOf: invitation.contact, color: .white)
|
||||||
|
.padding(.vertical, 6)
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
callButton("Reject", "phone.down.fill", .red) {
|
callButton("Reject", "phone.down.fill", .red) {
|
||||||
|
|
|
@ -9,6 +9,17 @@
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import SimpleXChat
|
import SimpleXChat
|
||||||
|
|
||||||
|
enum UserPickerSheet: Identifiable {
|
||||||
|
case address
|
||||||
|
case chatPreferences
|
||||||
|
case chatProfiles
|
||||||
|
case currentProfile
|
||||||
|
case useFromDesktop
|
||||||
|
case settings
|
||||||
|
|
||||||
|
var id: Self { self }
|
||||||
|
}
|
||||||
|
|
||||||
struct ChatListView: View {
|
struct ChatListView: View {
|
||||||
@EnvironmentObject var chatModel: ChatModel
|
@EnvironmentObject var chatModel: ChatModel
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
|
@ -18,9 +29,9 @@ struct ChatListView: View {
|
||||||
@State private var searchText = ""
|
@State private var searchText = ""
|
||||||
@State private var searchShowingSimplexLink = false
|
@State private var searchShowingSimplexLink = false
|
||||||
@State private var searchChatFilteredBySimplexLink: String? = nil
|
@State private var searchChatFilteredBySimplexLink: String? = nil
|
||||||
@State private var userPickerVisible = false
|
|
||||||
@State private var showConnectDesktop = false
|
|
||||||
@State private var scrollToSearchBar = false
|
@State private var scrollToSearchBar = false
|
||||||
|
@State private var activeUserPickerSheet: UserPickerSheet? = nil
|
||||||
|
@State private var userPickerShown: Bool = false
|
||||||
|
|
||||||
@AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false
|
@AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false
|
||||||
@AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true
|
@AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true
|
||||||
|
@ -46,21 +57,44 @@ struct ChatListView: View {
|
||||||
),
|
),
|
||||||
destination: chatView
|
destination: chatView
|
||||||
) { chatListView }
|
) { chatListView }
|
||||||
if userPickerVisible {
|
}
|
||||||
Rectangle().fill(.white.opacity(0.001)).onTapGesture {
|
.sheet(isPresented: $userPickerShown) {
|
||||||
withAnimation {
|
UserPicker(activeSheet: $activeUserPickerSheet)
|
||||||
userPickerVisible.toggle()
|
.sheet(item: $activeUserPickerSheet) { sheet in
|
||||||
|
if let currentUser = chatModel.currentUser {
|
||||||
|
switch sheet {
|
||||||
|
case .address:
|
||||||
|
NavigationView {
|
||||||
|
UserAddressView(shareViaProfile: currentUser.addressShared)
|
||||||
|
.navigationTitle("Public address")
|
||||||
|
.navigationBarTitleDisplayMode(.large)
|
||||||
|
.modifier(ThemedBackground(grouped: true))
|
||||||
|
}
|
||||||
|
case .chatProfiles:
|
||||||
|
NavigationView {
|
||||||
|
UserProfilesView()
|
||||||
|
}
|
||||||
|
case .currentProfile:
|
||||||
|
NavigationView {
|
||||||
|
UserProfile()
|
||||||
|
.navigationTitle("Your current profile")
|
||||||
|
.modifier(ThemedBackground())
|
||||||
|
}
|
||||||
|
case .chatPreferences:
|
||||||
|
NavigationView {
|
||||||
|
PreferencesView(profile: currentUser.profile, preferences: currentUser.fullPreferences, currentPreferences: currentUser.fullPreferences)
|
||||||
|
.navigationTitle("Your preferences")
|
||||||
|
.navigationBarTitleDisplayMode(.large)
|
||||||
|
.modifier(ThemedBackground(grouped: true))
|
||||||
|
}
|
||||||
|
case .useFromDesktop:
|
||||||
|
ConnectDesktopView(viaSettings: false)
|
||||||
|
case .settings:
|
||||||
|
SettingsView(showSettings: $showSettings)
|
||||||
|
.navigationBarTitleDisplayMode(.large)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
UserPicker(
|
|
||||||
showSettings: $showSettings,
|
|
||||||
showConnectDesktop: $showConnectDesktop,
|
|
||||||
userPickerVisible: $userPickerVisible
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.sheet(isPresented: $showConnectDesktop) {
|
|
||||||
ConnectDesktopView()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +107,7 @@ struct ChatListView: View {
|
||||||
.navigationBarHidden(searchMode || oneHandUI)
|
.navigationBarHidden(searchMode || oneHandUI)
|
||||||
}
|
}
|
||||||
.scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center)
|
.scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center)
|
||||||
.onDisappear() { withAnimation { userPickerVisible = false } }
|
.onDisappear() { activeUserPickerSheet = nil }
|
||||||
.refreshable {
|
.refreshable {
|
||||||
AlertManager.shared.showAlert(Alert(
|
AlertManager.shared.showAlert(Alert(
|
||||||
title: Text("Reconnect servers?"),
|
title: Text("Reconnect servers?"),
|
||||||
|
@ -164,7 +198,7 @@ struct ChatListView: View {
|
||||||
let user = chatModel.currentUser ?? User.sampleData
|
let user = chatModel.currentUser ?? User.sampleData
|
||||||
ZStack(alignment: .topTrailing) {
|
ZStack(alignment: .topTrailing) {
|
||||||
ProfileImage(imageStr: user.image, size: 32, color: Color(uiColor: .quaternaryLabel))
|
ProfileImage(imageStr: user.image, size: 32, color: Color(uiColor: .quaternaryLabel))
|
||||||
.padding(.trailing, 4)
|
.padding([.top, .trailing], 3)
|
||||||
let allRead = chatModel.users
|
let allRead = chatModel.users
|
||||||
.filter { u in !u.user.activeUser && !u.user.hidden }
|
.filter { u in !u.user.activeUser && !u.user.hidden }
|
||||||
.allSatisfy { u in u.unreadCount == 0 }
|
.allSatisfy { u in u.unreadCount == 0 }
|
||||||
|
@ -173,13 +207,7 @@ struct ChatListView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if chatModel.users.filter({ u in u.user.activeUser || !u.user.hidden }).count > 1 {
|
userPickerShown = true
|
||||||
withAnimation {
|
|
||||||
userPickerVisible.toggle()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showSettings = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +297,7 @@ struct ChatListView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func unreadBadge(_ text: Text? = Text(" "), size: CGFloat = 18) -> some View {
|
private func unreadBadge(size: CGFloat = 18) -> some View {
|
||||||
Circle()
|
Circle()
|
||||||
.frame(width: size, height: size)
|
.frame(width: size, height: size)
|
||||||
.foregroundColor(theme.colors.primary)
|
.foregroundColor(theme.colors.primary)
|
||||||
|
|
|
@ -8,179 +8,228 @@ import SimpleXChat
|
||||||
|
|
||||||
struct UserPicker: View {
|
struct UserPicker: View {
|
||||||
@EnvironmentObject var m: ChatModel
|
@EnvironmentObject var m: ChatModel
|
||||||
@Environment(\.scenePhase) var scenePhase
|
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@Binding var showSettings: Bool
|
@Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize
|
||||||
@Binding var showConnectDesktop: Bool
|
@Environment(\.scenePhase) private var scenePhase: ScenePhase
|
||||||
@Binding var userPickerVisible: Bool
|
@Environment(\.colorScheme) private var colorScheme: ColorScheme
|
||||||
@State var scrollViewContentSize: CGSize = .zero
|
@Environment(\.dismiss) private var dismiss: DismissAction
|
||||||
@State var disableScrolling: Bool = true
|
@Binding var activeSheet: UserPickerSheet?
|
||||||
private let menuButtonHeight: CGFloat = 68
|
@State private var switchingProfile = false
|
||||||
@State var chatViewNameWidth: CGFloat = 0
|
|
||||||
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
if #available(iOS 16.0, *) {
|
||||||
Spacer().frame(height: 1)
|
let v = viewBody.presentationDetents([.height(420)])
|
||||||
VStack(spacing: 0) {
|
if #available(iOS 16.4, *) {
|
||||||
ScrollView {
|
v.scrollBounceBehavior(.basedOnSize)
|
||||||
ScrollViewReader { sp in
|
} else {
|
||||||
let users = m.users
|
v
|
||||||
.filter({ u in u.user.activeUser || !u.user.hidden })
|
}
|
||||||
.sorted { u, _ in u.user.activeUser }
|
} else {
|
||||||
VStack(spacing: 0) {
|
viewBody
|
||||||
ForEach(users) { u in
|
}
|
||||||
userView(u)
|
}
|
||||||
Divider()
|
|
||||||
if u.user.activeUser { Divider() }
|
private var viewBody: some View {
|
||||||
}
|
let otherUsers = m.users.filter { u in !u.user.hidden && u.user.userId != m.currentUser?.userId }
|
||||||
}
|
return List {
|
||||||
.overlay {
|
Section(header: Text("You").foregroundColor(theme.colors.secondary)) {
|
||||||
GeometryReader { geo -> Color in
|
if let user = m.currentUser {
|
||||||
DispatchQueue.main.async {
|
openSheetOnTap(label: {
|
||||||
scrollViewContentSize = geo.size
|
ZStack {
|
||||||
let scenes = UIApplication.shared.connectedScenes
|
let v = ProfilePreview(profileOf: user)
|
||||||
if let windowScene = scenes.first as? UIWindowScene {
|
.foregroundColor(.primary)
|
||||||
let layoutFrame = windowScene.windows[0].safeAreaLayoutGuide.layoutFrame
|
.padding(.leading, -8)
|
||||||
disableScrolling = scrollViewContentSize.height + menuButtonHeight + 10 < layoutFrame.height
|
if #available(iOS 16.0, *) {
|
||||||
}
|
v
|
||||||
}
|
} else {
|
||||||
return Color.clear
|
v.padding(.vertical, 4)
|
||||||
}
|
|
||||||
}
|
|
||||||
.onChange(of: userPickerVisible) { visible in
|
|
||||||
if visible, let u = users.first {
|
|
||||||
sp.scrollTo(u.id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}) {
|
||||||
|
activeSheet = .currentProfile
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.simultaneousGesture(DragGesture(minimumDistance: disableScrolling ? 0 : 10000000))
|
|
||||||
.frame(maxHeight: scrollViewContentSize.height)
|
|
||||||
|
|
||||||
menuButton("Use from desktop", icon: "desktopcomputer") {
|
openSheetOnTap(title: m.userAddress == nil ? "Create public address" : "Your public address", icon: "qrcode") {
|
||||||
showConnectDesktop = true
|
activeSheet = .address
|
||||||
withAnimation {
|
}
|
||||||
userPickerVisible.toggle()
|
|
||||||
|
openSheetOnTap(title: "Chat preferences", icon: "switch.2") {
|
||||||
|
activeSheet = .chatPreferences
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Divider()
|
}
|
||||||
menuButton("Settings", icon: "gearshape") {
|
|
||||||
showSettings = true
|
Section {
|
||||||
withAnimation {
|
if otherUsers.isEmpty {
|
||||||
userPickerVisible.toggle()
|
openSheetOnTap(title: "Your chat profiles", icon: "person.crop.rectangle.stack") {
|
||||||
|
activeSheet = .chatProfiles
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let v = userPickerRow(otherUsers, size: 44)
|
||||||
|
.padding(.leading, -11)
|
||||||
|
if #available(iOS 16.0, *) {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
|
v.padding(.vertical, 4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openSheetOnTap(title: "Use from desktop", icon: "desktopcomputer") {
|
||||||
|
activeSheet = .useFromDesktop
|
||||||
|
}
|
||||||
|
|
||||||
|
ZStack(alignment: .trailing) {
|
||||||
|
openSheetOnTap(title: "Settings", icon: "gearshape") {
|
||||||
|
activeSheet = .settings
|
||||||
|
}
|
||||||
|
Label {} icon: {
|
||||||
|
Image(systemName: colorScheme == .light ? "sun.max" : "moon.fill")
|
||||||
|
.resizable()
|
||||||
|
.symbolRenderingMode(.monochrome)
|
||||||
|
.foregroundColor(theme.colors.secondary)
|
||||||
|
.frame(maxWidth: 20, maxHeight: 20)
|
||||||
|
}
|
||||||
|
.onTapGesture {
|
||||||
|
if (colorScheme == .light) {
|
||||||
|
ThemeManager.applyTheme(systemDarkThemeDefault.get())
|
||||||
|
} else {
|
||||||
|
ThemeManager.applyTheme(DefaultTheme.LIGHT.themeName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onLongPressGesture {
|
||||||
|
ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||||
.background(
|
|
||||||
Rectangle()
|
|
||||||
.fill(theme.colors.surface)
|
|
||||||
.cornerRadius(16)
|
|
||||||
.shadow(color: .black.opacity(0.12), radius: 24, x: 0, y: 0)
|
|
||||||
)
|
|
||||||
.onPreferenceChange(DetermineWidth.Key.self) { chatViewNameWidth = $0 }
|
|
||||||
.frame(maxWidth: chatViewNameWidth > 0 ? min(300, chatViewNameWidth + 130) : 300)
|
|
||||||
.padding(8)
|
|
||||||
.opacity(userPickerVisible ? 1.0 : 0.0)
|
|
||||||
.onAppear {
|
.onAppear {
|
||||||
// This check prevents the call of listUsers after the app is suspended, and the database is closed.
|
// This check prevents the call of listUsers after the app is suspended, and the database is closed.
|
||||||
if case .active = scenePhase {
|
if case .active = scenePhase {
|
||||||
Task {
|
|
||||||
do {
|
|
||||||
let users = try await listUsersAsync()
|
|
||||||
await MainActor.run { m.users = users }
|
|
||||||
} catch {
|
|
||||||
logger.error("Error loading users \(responseError(error))")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func userView(_ u: UserInfo) -> some View {
|
|
||||||
let user = u.user
|
|
||||||
return Button(action: {
|
|
||||||
if user.activeUser {
|
|
||||||
showSettings = true
|
|
||||||
withAnimation {
|
|
||||||
userPickerVisible.toggle()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
try await changeActiveUserAsync_(user.userId, viewPwd: nil)
|
let users = try await listUsersAsync()
|
||||||
await MainActor.run { userPickerVisible = false }
|
await MainActor.run { m.users = users }
|
||||||
} catch {
|
} catch {
|
||||||
await MainActor.run {
|
logger.error("Error loading users \(responseError(error))")
|
||||||
AlertManager.shared.showAlertMsg(
|
|
||||||
title: "Error switching profile!",
|
|
||||||
message: "Error: \(responseError(error))"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, label: {
|
}
|
||||||
HStack(spacing: 0) {
|
.modifier(ThemedBackground(grouped: true))
|
||||||
ProfileImage(imageStr: user.image, size: 44, color: Color(uiColor: .tertiarySystemFill))
|
.disabled(switchingProfile)
|
||||||
.padding(.trailing, 12)
|
}
|
||||||
Text(user.chatViewName)
|
|
||||||
.fontWeight(user.activeUser ? .medium : .regular)
|
private func userPickerRow(_ users: [UserInfo], size: CGFloat) -> some View {
|
||||||
.foregroundColor(theme.colors.onBackground)
|
HStack(spacing: 6) {
|
||||||
.overlay(DetermineWidth())
|
let s = ScrollView(.horizontal) {
|
||||||
Spacer()
|
HStack(spacing: 27) {
|
||||||
if user.activeUser {
|
ForEach(users) { u in
|
||||||
Image(systemName: "checkmark")
|
if !u.user.hidden && u.user.userId != m.currentUser?.userId {
|
||||||
} else if u.unreadCount > 0 {
|
userView(u, size: size)
|
||||||
unreadCounter(u.unreadCount, color: user.showNtfs ? theme.colors.primary : theme.colors.secondary)
|
}
|
||||||
} else if !user.showNtfs {
|
}
|
||||||
Image(systemName: "speaker.slash")
|
}
|
||||||
|
.padding(.leading, 4)
|
||||||
|
.padding(.trailing, 22)
|
||||||
|
}
|
||||||
|
ZStack(alignment: .trailing) {
|
||||||
|
if #available(iOS 16.0, *) {
|
||||||
|
s.scrollIndicators(.hidden)
|
||||||
|
} else {
|
||||||
|
s
|
||||||
|
}
|
||||||
|
LinearGradient(
|
||||||
|
colors: [.clear, .black],
|
||||||
|
startPoint: .leading,
|
||||||
|
endPoint: .trailing
|
||||||
|
)
|
||||||
|
.frame(width: size, height: size + 3)
|
||||||
|
.blendMode(.destinationOut)
|
||||||
|
.allowsHitTesting(false)
|
||||||
|
}
|
||||||
|
.compositingGroup()
|
||||||
|
.padding(.top, -3) // to fit unread badge
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "chevron.right")
|
||||||
|
.foregroundColor(theme.colors.secondary)
|
||||||
|
.padding(.trailing, 4)
|
||||||
|
.onTapGesture {
|
||||||
|
activeSheet = .chatProfiles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func userView(_ u: UserInfo, size: CGFloat) -> some View {
|
||||||
|
ZStack(alignment: .topTrailing) {
|
||||||
|
ProfileImage(imageStr: u.user.image, size: size, color: Color(uiColor: .tertiarySystemGroupedBackground))
|
||||||
|
.padding([.top, .trailing], 3)
|
||||||
|
if (u.unreadCount > 0) {
|
||||||
|
unreadBadge(u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(width: size)
|
||||||
|
.onTapGesture {
|
||||||
|
switchingProfile = true
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
try await changeActiveUserAsync_(u.user.userId, viewPwd: nil)
|
||||||
|
await MainActor.run {
|
||||||
|
switchingProfile = false
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
await MainActor.run {
|
||||||
|
switchingProfile = false
|
||||||
|
AlertManager.shared.showAlertMsg(
|
||||||
|
title: "Error switching profile!",
|
||||||
|
message: "Error: \(responseError(error))"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.trailing)
|
}
|
||||||
.padding([.leading, .vertical], 12)
|
|
||||||
})
|
|
||||||
.buttonStyle(PressedButtonStyle(defaultColor: theme.colors.surface, pressedColor: Color(uiColor: .secondarySystemFill)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func menuButton(_ title: LocalizedStringKey, icon: String, action: @escaping () -> Void) -> some View {
|
private func openSheetOnTap(title: LocalizedStringKey, icon: String, action: @escaping () -> Void) -> some View {
|
||||||
Button(action: action) {
|
openSheetOnTap(label: {
|
||||||
HStack(spacing: 0) {
|
ZStack(alignment: .leading) {
|
||||||
Text(title)
|
Image(systemName: icon).frame(maxWidth: 24, maxHeight: 24, alignment: .center)
|
||||||
.overlay(DetermineWidth())
|
|
||||||
Spacer()
|
|
||||||
Image(systemName: icon)
|
|
||||||
.symbolRenderingMode(.monochrome)
|
.symbolRenderingMode(.monochrome)
|
||||||
.foregroundColor(theme.colors.secondary)
|
.foregroundColor(theme.colors.secondary)
|
||||||
|
Text(title)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
.padding(.leading, 36)
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
}, action: action)
|
||||||
.padding(.vertical, 22)
|
}
|
||||||
.frame(height: menuButtonHeight)
|
|
||||||
}
|
private func openSheetOnTap<V: View>(label: () -> V, action: @escaping () -> Void) -> some View {
|
||||||
.buttonStyle(PressedButtonStyle(defaultColor: theme.colors.surface, pressedColor: Color(uiColor: .secondarySystemFill)))
|
Button(action: action, label: label)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
}
|
||||||
|
|
||||||
|
private func unreadBadge(_ u: UserInfo) -> some View {
|
||||||
|
let size = dynamicSize(userFont).chatInfoSize
|
||||||
|
return unreadCountText(u.unreadCount)
|
||||||
|
.font(userFont <= .xxxLarge ? .caption : .caption2)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.padding(.horizontal, dynamicSize(userFont).unreadPadding)
|
||||||
|
.frame(minWidth: size, minHeight: size)
|
||||||
|
.background(u.user.showNtfs ? theme.colors.primary : theme.colors.secondary)
|
||||||
|
.cornerRadius(dynamicSize(userFont).unreadCorner)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private func unreadCounter(_ unread: Int, color: Color) -> some View {
|
|
||||||
unreadCountText(unread)
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.padding(.horizontal, 4)
|
|
||||||
.frame(minWidth: 18, minHeight: 18)
|
|
||||||
.background(color)
|
|
||||||
.cornerRadius(10)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UserPicker_Previews: PreviewProvider {
|
struct UserPicker_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
|
@State var activeSheet: UserPickerSheet?
|
||||||
|
|
||||||
let m = ChatModel()
|
let m = ChatModel()
|
||||||
m.users = [UserInfo.sampleData, UserInfo.sampleData]
|
m.users = [UserInfo.sampleData, UserInfo.sampleData]
|
||||||
return UserPicker(
|
return UserPicker(
|
||||||
showSettings: Binding.constant(false),
|
activeSheet: $activeSheet
|
||||||
showConnectDesktop: Binding.constant(false),
|
|
||||||
userPickerVisible: Binding.constant(true)
|
|
||||||
)
|
)
|
||||||
.environmentObject(m)
|
.environmentObject(m)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,15 +8,22 @@
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
func showShareSheet(items: [Any], completed: (() -> Void)? = nil) {
|
func getTopViewController() -> UIViewController? {
|
||||||
let keyWindowScene = UIApplication.shared.connectedScenes.first { $0.activationState == .foregroundActive } as? UIWindowScene
|
let keyWindowScene = UIApplication.shared.connectedScenes.first { $0.activationState == .foregroundActive } as? UIWindowScene
|
||||||
if let keyWindow = keyWindowScene?.windows.filter(\.isKeyWindow).first,
|
if let keyWindow = keyWindowScene?.windows.filter(\.isKeyWindow).first,
|
||||||
let rootViewController = keyWindow.rootViewController {
|
let rootViewController = keyWindow.rootViewController {
|
||||||
// Find the top-most presented view controller
|
// Find the top-most presented view controller
|
||||||
var topController = rootViewController
|
var topController = rootViewController
|
||||||
while let presentedViewController = topController.presentedViewController {
|
while let presentedViewController = topController.presentedViewController {
|
||||||
topController = presentedViewController
|
topController = presentedViewController
|
||||||
}
|
}
|
||||||
|
return topController
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func showShareSheet(items: [Any], completed: (() -> Void)? = nil) {
|
||||||
|
if let topController = getTopViewController() {
|
||||||
let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
|
let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
|
||||||
if let completed = completed {
|
if let completed = completed {
|
||||||
activityViewController.completionWithItemsHandler = { _, _, _, _ in
|
activityViewController.completionWithItemsHandler = { _, _, _, _ in
|
||||||
|
@ -26,3 +33,22 @@ func showShareSheet(items: [Any], completed: (() -> Void)? = nil) {
|
||||||
topController.present(activityViewController, animated: true)
|
topController.present(activityViewController, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func showAlert(
|
||||||
|
title: String,
|
||||||
|
message: String? = nil,
|
||||||
|
buttonTitle: String,
|
||||||
|
buttonAction: @escaping () -> Void,
|
||||||
|
cancelButton: Bool
|
||||||
|
) -> Void {
|
||||||
|
if let topController = getTopViewController() {
|
||||||
|
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||||
|
alert.addAction(UIAlertAction(title: buttonTitle, style: .default) { _ in
|
||||||
|
buttonAction()
|
||||||
|
})
|
||||||
|
if cancelButton {
|
||||||
|
alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "alert button"), style: .cancel))
|
||||||
|
}
|
||||||
|
topController.present(alert, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -56,8 +56,6 @@ private enum MigrateFromDeviceViewAlert: Identifiable {
|
||||||
struct MigrateFromDevice: View {
|
struct MigrateFromDevice: View {
|
||||||
@EnvironmentObject var m: ChatModel
|
@EnvironmentObject var m: ChatModel
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@Environment(\.dismiss) var dismiss: DismissAction
|
|
||||||
@Binding var showSettings: Bool
|
|
||||||
@Binding var showProgressOnSettings: Bool
|
@Binding var showProgressOnSettings: Bool
|
||||||
@State private var migrationState: MigrationFromState = .chatStopInProgress
|
@State private var migrationState: MigrationFromState = .chatStopInProgress
|
||||||
@State private var useKeychain = storeDBPassphraseGroupDefault.get()
|
@State private var useKeychain = storeDBPassphraseGroupDefault.get()
|
||||||
|
@ -106,9 +104,6 @@ struct MigrateFromDevice: View {
|
||||||
finishedView(chatDeletion)
|
finishedView(chatDeletion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.modifier(BackButton(label: "Back", disabled: $backDisabled) {
|
|
||||||
dismiss()
|
|
||||||
})
|
|
||||||
.onChange(of: migrationState) { state in
|
.onChange(of: migrationState) { state in
|
||||||
backDisabled = switch migrationState {
|
backDisabled = switch migrationState {
|
||||||
case .chatStopInProgress, .archiving, .linkShown, .finished: true
|
case .chatStopInProgress, .archiving, .linkShown, .finished: true
|
||||||
|
@ -590,7 +585,7 @@ struct MigrateFromDevice: View {
|
||||||
} catch let error {
|
} catch let error {
|
||||||
fatalError("Error starting chat \(responseError(error))")
|
fatalError("Error starting chat \(responseError(error))")
|
||||||
}
|
}
|
||||||
showSettings = false
|
dismissAllSheets(animated: true)
|
||||||
}
|
}
|
||||||
} catch let error {
|
} catch let error {
|
||||||
alert = .error(title: "Error deleting database", error: responseError(error))
|
alert = .error(title: "Error deleting database", error: responseError(error))
|
||||||
|
@ -613,9 +608,7 @@ struct MigrateFromDevice: View {
|
||||||
}
|
}
|
||||||
// Hide settings anyway if chatDbStatus is not ok, probably passphrase needs to be entered
|
// Hide settings anyway if chatDbStatus is not ok, probably passphrase needs to be entered
|
||||||
if dismiss || m.chatDbStatus != .ok {
|
if dismiss || m.chatDbStatus != .ok {
|
||||||
await MainActor.run {
|
dismissAllSheets(animated: true)
|
||||||
showSettings = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -767,6 +760,6 @@ private class MigrationChatReceiver {
|
||||||
|
|
||||||
struct MigrateFromDevice_Previews: PreviewProvider {
|
struct MigrateFromDevice_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
MigrateFromDevice(showSettings: Binding.constant(true), showProgressOnSettings: Binding.constant(false))
|
MigrateFromDevice(showProgressOnSettings: Binding.constant(false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,13 +59,6 @@ struct ConnectDesktopView: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if viaSettings {
|
if viaSettings {
|
||||||
viewBody
|
viewBody
|
||||||
.modifier(BackButton(label: "Back", disabled: Binding.constant(false)) {
|
|
||||||
if m.activeRemoteCtrl {
|
|
||||||
alert = .disconnectDesktop(action: .back)
|
|
||||||
} else {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
viewBody
|
viewBody
|
||||||
|
|
|
@ -32,6 +32,17 @@ struct PreferencesView: View {
|
||||||
.disabled(currentPreferences == preferences)
|
.disabled(currentPreferences == preferences)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onDisappear {
|
||||||
|
if currentPreferences != preferences {
|
||||||
|
showAlert(
|
||||||
|
title: NSLocalizedString("Your chat preferences", comment: "alert title"),
|
||||||
|
message: NSLocalizedString("Chat preferences were changed.", comment: "alert message"),
|
||||||
|
buttonTitle: NSLocalizedString("Save", comment: "alert button"),
|
||||||
|
buttonAction: savePreferences,
|
||||||
|
cancelButton: true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func featureSection(_ feature: ChatFeature, _ allowFeature: Binding<FeatureAllowed>) -> some View {
|
private func featureSection(_ feature: ChatFeature, _ allowFeature: Binding<FeatureAllowed>) -> some View {
|
||||||
|
|
|
@ -262,7 +262,9 @@ struct SettingsView: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
settingsView()
|
NavigationView {
|
||||||
|
settingsView()
|
||||||
|
}
|
||||||
if showProgress {
|
if showProgress {
|
||||||
progressView()
|
progressView()
|
||||||
}
|
}
|
||||||
|
@ -274,63 +276,7 @@ struct SettingsView: View {
|
||||||
|
|
||||||
@ViewBuilder func settingsView() -> some View {
|
@ViewBuilder func settingsView() -> some View {
|
||||||
let user = chatModel.currentUser
|
let user = chatModel.currentUser
|
||||||
NavigationView {
|
|
||||||
List {
|
List {
|
||||||
Section(header: Text("You").foregroundColor(theme.colors.secondary)) {
|
|
||||||
if let user = user {
|
|
||||||
NavigationLink {
|
|
||||||
UserProfile()
|
|
||||||
.navigationTitle("Your current profile")
|
|
||||||
.modifier(ThemedBackground())
|
|
||||||
} label: {
|
|
||||||
ProfilePreview(profileOf: user)
|
|
||||||
.padding(.leading, -8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationLink {
|
|
||||||
UserProfilesView(showSettings: $showSettings)
|
|
||||||
} label: {
|
|
||||||
settingsRow("person.crop.rectangle.stack", color: theme.colors.secondary) { Text("Your chat profiles") }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if let user = user {
|
|
||||||
NavigationLink {
|
|
||||||
UserAddressView(shareViaProfile: user.addressShared)
|
|
||||||
.navigationTitle("SimpleX address")
|
|
||||||
.modifier(ThemedBackground(grouped: true))
|
|
||||||
.navigationBarTitleDisplayMode(.large)
|
|
||||||
} label: {
|
|
||||||
settingsRow("qrcode", color: theme.colors.secondary) { Text("Your SimpleX address") }
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationLink {
|
|
||||||
PreferencesView(profile: user.profile, preferences: user.fullPreferences, currentPreferences: user.fullPreferences)
|
|
||||||
.navigationTitle("Your preferences")
|
|
||||||
.modifier(ThemedBackground(grouped: true))
|
|
||||||
} label: {
|
|
||||||
settingsRow("switch.2", color: theme.colors.secondary) { Text("Chat preferences") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationLink {
|
|
||||||
ConnectDesktopView(viaSettings: true)
|
|
||||||
} label: {
|
|
||||||
settingsRow("desktopcomputer", color: theme.colors.secondary) { Text("Use from desktop") }
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationLink {
|
|
||||||
MigrateFromDevice(showSettings: $showSettings, showProgressOnSettings: $showProgress)
|
|
||||||
.navigationTitle("Migrate device")
|
|
||||||
.modifier(ThemedBackground(grouped: true))
|
|
||||||
.navigationBarTitleDisplayMode(.large)
|
|
||||||
} label: {
|
|
||||||
settingsRow("tray.and.arrow.up", color: theme.colors.secondary) { Text("Migrate to another device") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.disabled(chatModel.chatRunning != true)
|
|
||||||
|
|
||||||
Section(header: Text("Settings").foregroundColor(theme.colors.secondary)) {
|
Section(header: Text("Settings").foregroundColor(theme.colors.secondary)) {
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
NotificationsView()
|
NotificationsView()
|
||||||
|
@ -381,10 +327,20 @@ struct SettingsView: View {
|
||||||
}
|
}
|
||||||
.disabled(chatModel.chatRunning != true)
|
.disabled(chatModel.chatRunning != true)
|
||||||
}
|
}
|
||||||
|
|
||||||
chatDatabaseRow()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section(header: Text("Chat database").foregroundColor(theme.colors.secondary)) {
|
||||||
|
chatDatabaseRow()
|
||||||
|
NavigationLink {
|
||||||
|
MigrateFromDevice(showProgressOnSettings: $showProgress)
|
||||||
|
.navigationTitle("Migrate device")
|
||||||
|
.modifier(ThemedBackground(grouped: true))
|
||||||
|
.navigationBarTitleDisplayMode(.large)
|
||||||
|
} label: {
|
||||||
|
settingsRow("tray.and.arrow.up", color: theme.colors.secondary) { Text("Migrate to another device") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Section(header: Text("Help").foregroundColor(theme.colors.secondary)) {
|
Section(header: Text("Help").foregroundColor(theme.colors.secondary)) {
|
||||||
if let user = user {
|
if let user = user {
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
|
@ -462,11 +418,10 @@ struct SettingsView: View {
|
||||||
}
|
}
|
||||||
.navigationTitle("Your settings")
|
.navigationTitle("Your settings")
|
||||||
.modifier(ThemedBackground(grouped: true))
|
.modifier(ThemedBackground(grouped: true))
|
||||||
}
|
.onDisappear {
|
||||||
.onDisappear {
|
chatModel.showingTerminal = false
|
||||||
chatModel.showingTerminal = false
|
chatModel.terminalItems = []
|
||||||
chatModel.terminalItems = []
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func chatDatabaseRow() -> some View {
|
private func chatDatabaseRow() -> some View {
|
||||||
|
@ -549,17 +504,18 @@ struct ProfilePreview: View {
|
||||||
HStack {
|
HStack {
|
||||||
ProfileImage(imageStr: profileOf.image, size: 44, color: color)
|
ProfileImage(imageStr: profileOf.image, size: 44, color: color)
|
||||||
.padding(.trailing, 6)
|
.padding(.trailing, 6)
|
||||||
.padding(.vertical, 6)
|
profileName().lineLimit(1)
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text(profileOf.displayName)
|
|
||||||
.fontWeight(.bold)
|
|
||||||
.font(.title2)
|
|
||||||
if profileOf.fullName != "" && profileOf.fullName != profileOf.displayName {
|
|
||||||
Text(profileOf.fullName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func profileName() -> Text {
|
||||||
|
var t = Text(profileOf.displayName).fontWeight(.semibold).font(.title2)
|
||||||
|
if profileOf.fullName != "" && profileOf.fullName != profileOf.displayName {
|
||||||
|
t = t + Text(" (" + profileOf.fullName + ")")
|
||||||
|
// .font(.callout)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SettingsView_Previews: PreviewProvider {
|
struct SettingsView_Previews: PreviewProvider {
|
||||||
|
|
|
@ -14,7 +14,6 @@ struct UserAddressView: View {
|
||||||
@Environment(\.dismiss) var dismiss: DismissAction
|
@Environment(\.dismiss) var dismiss: DismissAction
|
||||||
@EnvironmentObject private var chatModel: ChatModel
|
@EnvironmentObject private var chatModel: ChatModel
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@State var viaCreateLinkView = false
|
|
||||||
@State var shareViaProfile = false
|
@State var shareViaProfile = false
|
||||||
@State private var aas = AutoAcceptState()
|
@State private var aas = AutoAcceptState()
|
||||||
@State private var savedAAS = AutoAcceptState()
|
@State private var savedAAS = AutoAcceptState()
|
||||||
|
@ -22,7 +21,6 @@ struct UserAddressView: View {
|
||||||
@State private var showMailView = false
|
@State private var showMailView = false
|
||||||
@State private var mailViewResult: Result<MFMailComposeResult, Error>? = nil
|
@State private var mailViewResult: Result<MFMailComposeResult, Error>? = nil
|
||||||
@State private var alert: UserAddressAlert?
|
@State private var alert: UserAddressAlert?
|
||||||
@State private var showSaveDialogue = false
|
|
||||||
@State private var progressIndicator = false
|
@State private var progressIndicator = false
|
||||||
@FocusState private var keyboardVisible: Bool
|
@FocusState private var keyboardVisible: Bool
|
||||||
|
|
||||||
|
@ -44,26 +42,19 @@ struct UserAddressView: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
if viaCreateLinkView {
|
userAddressScrollView()
|
||||||
userAddressScrollView()
|
.onDisappear {
|
||||||
} else {
|
if savedAAS != aas {
|
||||||
userAddressScrollView()
|
showAlert(
|
||||||
.modifier(BackButton(disabled: Binding.constant(false)) {
|
title: NSLocalizedString("Auto-accept settings", comment: "alert title"),
|
||||||
if savedAAS == aas {
|
message: NSLocalizedString("Settings were changed.", comment: "alert message"),
|
||||||
dismiss()
|
buttonTitle: NSLocalizedString("Save", comment: "alert button"),
|
||||||
} else {
|
buttonAction: saveAAS,
|
||||||
keyboardVisible = false
|
cancelButton: true
|
||||||
showSaveDialogue = true
|
)
|
||||||
}
|
|
||||||
})
|
|
||||||
.confirmationDialog("Save settings?", isPresented: $showSaveDialogue) {
|
|
||||||
Button("Save auto-accept settings") {
|
|
||||||
saveAAS()
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
Button("Exit without saving") { dismiss() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if progressIndicator {
|
if progressIndicator {
|
||||||
ZStack {
|
ZStack {
|
||||||
if chatModel.userAddress != nil {
|
if chatModel.userAddress != nil {
|
||||||
|
@ -238,7 +229,7 @@ struct UserAddressView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Label("Create SimpleX address", systemImage: "qrcode")
|
Label("Create public address", systemImage: "qrcode")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,7 +333,7 @@ struct UserAddressView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct AutoAcceptState: Equatable {
|
private struct AutoAcceptState: Equatable {
|
||||||
var enable = false
|
var enable = false
|
||||||
var incognito = false
|
var incognito = false
|
||||||
|
@ -447,6 +438,8 @@ struct UserAddressView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let chatModel = ChatModel()
|
let chatModel = ChatModel()
|
||||||
chatModel.userAddress = UserContactLink(connReqContact: "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D")
|
chatModel.userAddress = UserContactLink(connReqContact: "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D")
|
||||||
|
|
||||||
|
|
||||||
return Group {
|
return Group {
|
||||||
UserAddressView()
|
UserAddressView()
|
||||||
.environmentObject(chatModel)
|
.environmentObject(chatModel)
|
||||||
|
|
|
@ -9,7 +9,6 @@ import SimpleXChat
|
||||||
struct UserProfilesView: View {
|
struct UserProfilesView: View {
|
||||||
@EnvironmentObject private var m: ChatModel
|
@EnvironmentObject private var m: ChatModel
|
||||||
@EnvironmentObject private var theme: AppTheme
|
@EnvironmentObject private var theme: AppTheme
|
||||||
@Binding var showSettings: Bool
|
|
||||||
@Environment(\.editMode) private var editMode
|
@Environment(\.editMode) private var editMode
|
||||||
@AppStorage(DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE) private var showHiddenProfilesNotice = true
|
@AppStorage(DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE) private var showHiddenProfilesNotice = true
|
||||||
@AppStorage(DEFAULT_SHOW_MUTE_PROFILE_ALERT) private var showMuteProfileAlert = true
|
@AppStorage(DEFAULT_SHOW_MUTE_PROFILE_ALERT) private var showMuteProfileAlert = true
|
||||||
|
@ -96,8 +95,7 @@ struct UserProfilesView: View {
|
||||||
} label: {
|
} label: {
|
||||||
Label("Add profile", systemImage: "plus")
|
Label("Add profile", systemImage: "plus")
|
||||||
}
|
}
|
||||||
.frame(height: 44)
|
.frame(height: 38)
|
||||||
.padding(.vertical, 4)
|
|
||||||
}
|
}
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("Tap to activate profile.")
|
Text("Tap to activate profile.")
|
||||||
|
@ -285,7 +283,7 @@ struct UserProfilesView: View {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
onboardingStageDefault.set(.step1_SimpleXInfo)
|
onboardingStageDefault.set(.step1_SimpleXInfo)
|
||||||
m.onboardingStage = .step1_SimpleXInfo
|
m.onboardingStage = .step1_SimpleXInfo
|
||||||
showSettings = false
|
dismissAllSheets()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -308,14 +306,14 @@ struct UserProfilesView: View {
|
||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
try await changeActiveUserAsync_(user.userId, viewPwd: userViewPassword(user))
|
try await changeActiveUserAsync_(user.userId, viewPwd: userViewPassword(user))
|
||||||
|
dismissAllSheets()
|
||||||
} catch {
|
} catch {
|
||||||
await MainActor.run { alert = .activateUserError(error: responseError(error)) }
|
await MainActor.run { alert = .activateUserError(error: responseError(error)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
ProfileImage(imageStr: user.image, size: 44)
|
ProfileImage(imageStr: user.image, size: 38)
|
||||||
.padding(.vertical, 4)
|
|
||||||
.padding(.trailing, 12)
|
.padding(.trailing, 12)
|
||||||
Text(user.chatViewName)
|
Text(user.chatViewName)
|
||||||
Spacer()
|
Spacer()
|
||||||
|
@ -415,6 +413,6 @@ public func correctPassword(_ user: User, _ pwd: String) -> Bool {
|
||||||
|
|
||||||
struct UserProfilesView_Previews: PreviewProvider {
|
struct UserProfilesView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
UserProfilesView(showSettings: Binding.constant(true))
|
UserProfilesView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue