mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-07 21:35:50 +00:00
* ios: wallpapers (#4304) * ios: wallpapers * theme selection * applied theme colors and preset wallpaper * more places with background * one more * accent color * defaults * rename * background * no change to cell color * unneeded * changes * no global tint * defaults * removed unneeded class * for merging * ios: wallpapers types (#4325) * types and api * divided types per target * creating directory for wallpapers * creating wallpaper dir at launch * ios: wallpapers appearance (#4335) * appearance * changes * refactor * scale * lambda to function --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> * ios: wallpapers user/chat overrides (#4345) * ios: wallpapers user/chat overrides * chat overrides * color picker updates colors correctly * fix state update * labels * background for light theme * small optimization * removed commented code * ios: enhancements to wallpapers (#4361) * ios: enhancements to wallpapers * colors for background * ios: wallpapers import/export (#4362) * ios: wallpapers import/export * comment * ios: wallpapers theme updates (#4365) * ios: wallpapers theme updates * group member background * colors * profile picture colors * unneeded * optimizations, images, state fixes * fixes * no editing of title color * rename Menus and alerts, refactor * tint applying fix * fixes * migration of accent and themes * fix updating system theme * migration changes * limiting color range * ios: wallpapers rename enum (#4384) * ios: wallpapers rename enum2 (#4385) * ios: wallpapers rename enum2 * change * colors were commented * fix build and look --------- Co-authored-by: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com>
185 lines
7.2 KiB
Swift
185 lines
7.2 KiB
Swift
//
|
|
// Created by Avently on 16.01.2023.
|
|
// Copyright (c) 2023 SimpleX Chat. All rights reserved.
|
|
//
|
|
|
|
import SwiftUI
|
|
import SimpleXChat
|
|
|
|
struct UserPicker: View {
|
|
@EnvironmentObject var m: ChatModel
|
|
@Environment(\.scenePhase) var scenePhase
|
|
@EnvironmentObject var theme: AppTheme
|
|
@Binding var showSettings: Bool
|
|
@Binding var showConnectDesktop: Bool
|
|
@Binding var userPickerVisible: Bool
|
|
@State var scrollViewContentSize: CGSize = .zero
|
|
@State var disableScrolling: Bool = true
|
|
private let menuButtonHeight: CGFloat = 68
|
|
@State var chatViewNameWidth: CGFloat = 0
|
|
|
|
|
|
var body: some View {
|
|
VStack {
|
|
Spacer().frame(height: 1)
|
|
VStack(spacing: 0) {
|
|
ScrollView {
|
|
ScrollViewReader { sp in
|
|
let users = m.users
|
|
.filter({ u in u.user.activeUser || !u.user.hidden })
|
|
.sorted { u, _ in u.user.activeUser }
|
|
VStack(spacing: 0) {
|
|
ForEach(users) { u in
|
|
userView(u)
|
|
Divider()
|
|
if u.user.activeUser { Divider() }
|
|
}
|
|
}
|
|
.overlay {
|
|
GeometryReader { geo -> Color in
|
|
DispatchQueue.main.async {
|
|
scrollViewContentSize = geo.size
|
|
let scenes = UIApplication.shared.connectedScenes
|
|
if let windowScene = scenes.first as? UIWindowScene {
|
|
let layoutFrame = windowScene.windows[0].safeAreaLayoutGuide.layoutFrame
|
|
disableScrolling = scrollViewContentSize.height + menuButtonHeight + 10 < layoutFrame.height
|
|
}
|
|
}
|
|
return Color.clear
|
|
}
|
|
}
|
|
.onChange(of: userPickerVisible) { visible in
|
|
if visible, let u = users.first {
|
|
sp.scrollTo(u.id)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.simultaneousGesture(DragGesture(minimumDistance: disableScrolling ? 0 : 10000000))
|
|
.frame(maxHeight: scrollViewContentSize.height)
|
|
|
|
menuButton("Use from desktop", icon: "desktopcomputer") {
|
|
showConnectDesktop = true
|
|
withAnimation {
|
|
userPickerVisible.toggle()
|
|
}
|
|
}
|
|
Divider()
|
|
menuButton("Settings", icon: "gearshape") {
|
|
showSettings = true
|
|
withAnimation {
|
|
userPickerVisible.toggle()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.clipShape(RoundedRectangle(cornerRadius: 16))
|
|
.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 {
|
|
do {
|
|
// This check prevents the call of listUsers after the app is suspended, and the database is closed.
|
|
if case .active = scenePhase {
|
|
m.users = try listUsers()
|
|
}
|
|
} catch let error {
|
|
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 {
|
|
do {
|
|
try await changeActiveUserAsync_(user.userId, viewPwd: nil)
|
|
await MainActor.run { userPickerVisible = false }
|
|
} catch {
|
|
await MainActor.run {
|
|
AlertManager.shared.showAlertMsg(
|
|
title: "Error switching profile!",
|
|
message: "Error: \(responseError(error))"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, label: {
|
|
HStack(spacing: 0) {
|
|
ProfileImage(imageStr: user.image, size: 44, color: Color(uiColor: .tertiarySystemFill))
|
|
.padding(.trailing, 12)
|
|
Text(user.chatViewName)
|
|
.fontWeight(user.activeUser ? .medium : .regular)
|
|
.foregroundColor(theme.colors.onBackground)
|
|
.overlay(DetermineWidth())
|
|
Spacer()
|
|
if user.activeUser {
|
|
Image(systemName: "checkmark")
|
|
} else if u.unreadCount > 0 {
|
|
unreadCounter(u.unreadCount, color: user.showNtfs ? theme.colors.primary : theme.colors.secondary)
|
|
} else if !user.showNtfs {
|
|
Image(systemName: "speaker.slash")
|
|
}
|
|
}
|
|
.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 {
|
|
Button(action: action) {
|
|
HStack(spacing: 0) {
|
|
Text(title)
|
|
.overlay(DetermineWidth())
|
|
Spacer()
|
|
Image(systemName: icon)
|
|
.symbolRenderingMode(.monochrome)
|
|
.foregroundColor(theme.colors.secondary)
|
|
}
|
|
.padding(.horizontal)
|
|
.padding(.vertical, 22)
|
|
.frame(height: menuButtonHeight)
|
|
}
|
|
.buttonStyle(PressedButtonStyle(defaultColor: theme.colors.surface, pressedColor: Color(uiColor: .secondarySystemFill)))
|
|
}
|
|
}
|
|
|
|
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 {
|
|
static var previews: some View {
|
|
let m = ChatModel()
|
|
m.users = [UserInfo.sampleData, UserInfo.sampleData]
|
|
return UserPicker(
|
|
showSettings: Binding.constant(false),
|
|
showConnectDesktop: Binding.constant(false),
|
|
userPickerVisible: Binding.constant(true)
|
|
)
|
|
.environmentObject(m)
|
|
}
|
|
}
|