mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
Compare commits
204 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dd8081a9d4 | |||
| d65909cb53 | |||
| e99b543c2f | |||
| 312d315d73 | |||
| cdf5520084 | |||
| 170e8ddeaf | |||
| 68f7956512 | |||
| e424b3eb66 | |||
| fb7a414e3c | |||
| 5966d6d648 | |||
| c6ebc9a4d9 | |||
| 05ec8b0676 | |||
| 6980f643ff | |||
| b60dd3e3e8 | |||
| 17a5770442 | |||
| 439bbc15a5 | |||
| 3688141972 | |||
| d79f8f8345 | |||
| 923ad7470c | |||
| 833abb252e | |||
| c60207f211 | |||
| d5665a421f | |||
| b2aa0ffc3c | |||
| e50a5cfaa1 | |||
| 73ec44a9be | |||
| 59fe6ab8ce | |||
| ec64be7c89 | |||
| 41984e56c0 | |||
| c435510b2e | |||
| de65d0e0a2 | |||
| 9cde37c6f5 | |||
| 9514acf6aa | |||
| 40d54f3f16 | |||
| 3249c5e463 | |||
| e440fba582 | |||
| 43cedbce26 | |||
| ebad009553 | |||
| 35cf113d98 | |||
| e82d2cbea2 | |||
| a0e056994e | |||
| e4d8ea17fc | |||
| e69db17804 | |||
| 9282cca396 | |||
| ad2eaeb004 | |||
| 3f44c9af24 | |||
| 4296515473 | |||
| 88830a2e09 | |||
| c8a269e391 | |||
| a45897007c | |||
| 42ea1df342 | |||
| 47214e33ea | |||
| ff239d81e5 | |||
| 0a6e47dd61 | |||
| ee75219ed3 | |||
| 2e337a753f | |||
| 9918d91def | |||
| 82dd5751c1 | |||
| de637fab50 | |||
| cb49f6f01d | |||
| 6d0a83aa58 | |||
| 05f57e98ad | |||
| 6a66525927 | |||
| 9530a6055a | |||
| f5ed8debcc | |||
| 355d2449c5 | |||
| f7382cdd6f | |||
| dc8d10d068 | |||
| 951245d33f | |||
| 521b901cc9 | |||
| 6311ba451b | |||
| 754c76d6fd | |||
| aea7ff1c89 | |||
| 77ac972e09 | |||
| 256f85024f | |||
| 870f9e42dd | |||
| 0a4a3a24e1 | |||
| 8a66390a78 | |||
| 3d48eded3d | |||
| 6546426ec0 | |||
| 4a7ceb00fb | |||
| 53560378bb | |||
| cdb3b6aafd | |||
| 9f3d3e8ba4 | |||
| 047aad592e | |||
| 087acd9180 | |||
| 0b822e4a5c | |||
| f8a469488e | |||
| 3b5e806418 | |||
| 79e208193a | |||
| ef5c13b1c1 | |||
| 38533213d2 | |||
| 5f1aa6fa9d | |||
| b0002fe07d | |||
| ef21fd1d26 | |||
| 4a311b9578 | |||
| b8da5e225b | |||
| f27de052cf | |||
| 5cc537f14c | |||
| dc8ca4cf89 | |||
| b62dd801f1 | |||
| 0c096e2c89 | |||
| cc127e56fe | |||
| 1781495ee3 | |||
| 831231d8e6 | |||
| 45102442f4 | |||
| f323c8e112 | |||
| 3bdc6b5e28 | |||
| d8373262bc | |||
| 3597d34716 | |||
| bd4259e89e | |||
| 55ead740cc | |||
| 5ef0eda2d7 | |||
| 49a9b0e7d6 | |||
| 45ada450a2 | |||
| 307a1b3c5e | |||
| ed6b3bbead | |||
| 901610eec5 | |||
| 7d4127c51d | |||
| 13215d91d7 | |||
| e1a8099474 | |||
| daa8d9bb21 | |||
| 5fcbade1bc | |||
| 3937ffa9a6 | |||
| 80ddb50e1c | |||
| f6e66f1c53 | |||
| 0c23ff9ae3 | |||
| 1570bc2b99 | |||
| 1e2104cabf | |||
| f3014f258d | |||
| f0991cc0ba | |||
| 74b78a8d7b | |||
| 82cd70a75c | |||
| fe4eb7b5af | |||
| c459e71d02 | |||
| 2516d5a393 | |||
| 477d98d75a | |||
| 4253cd7fb9 | |||
| ca78958667 | |||
| 1f5b80d560 | |||
| 2de111e76c | |||
| 8343285d93 | |||
| 5dbe2b2745 | |||
| fb9485190d | |||
| 6881600e06 | |||
| 9ed723bafa | |||
| 9ded1c9821 | |||
| bb374c68b1 | |||
| c3e82a6a4e | |||
| 7c12e82042 | |||
| e7e66ff873 | |||
| c4d7e5307c | |||
| d6b9a45a39 | |||
| 7fd3b4d6ba | |||
| 4004aafbc5 | |||
| 95008eeeaf | |||
| c7a8992043 | |||
| ea2b5f2ccf | |||
| ed9f277421 | |||
| 5c14c3b349 | |||
| d8fb31f167 | |||
| 02db38ffd3 | |||
| 7692195bfa | |||
| c435cbdc7b | |||
| effc281271 | |||
| 41eb2e5689 | |||
| 67d74a0a27 | |||
| f66405e79b | |||
| 74d186af16 | |||
| 187fef0c5a | |||
| 4782cab507 | |||
| bcbee67709 | |||
| 2501cbe55d | |||
| 2bd049db87 | |||
| 6b8b9ab4fd | |||
| 30db24265e | |||
| 316d605899 | |||
| b4257f7767 | |||
| a3f2d5c919 | |||
| cf46469cd5 | |||
| 0312fde818 | |||
| 9defa44f0c | |||
| 915b53054c | |||
| f81557b4fd | |||
| e273bd1239 | |||
| a63caf4640 | |||
| e7f0234134 | |||
| 340552321e | |||
| 98a3fc214d | |||
| 6a578cfe3c | |||
| dacc075fe8 | |||
| 55418e2bc0 | |||
| f2b5c0f3a8 | |||
| 5ebdf5dba9 | |||
| 8e045764df | |||
| 503d3d77e6 | |||
| 81bd7d97c5 | |||
| 8f57925067 | |||
| 9bf99db82e | |||
| 5615cdbf1a | |||
| d802ae0058 | |||
| 8f2278198c | |||
| 10937a5a4e | |||
| 6aff6e9804 | |||
| 95477cae7e |
@@ -234,8 +234,6 @@ You can use SimpleX with your own servers and still communicate with people usin
|
||||
|
||||
Recent and important updates:
|
||||
|
||||
[Jun 4, 2024. SimpleX network: private message routing, v5.8 released with IP address protection and chat themes](./blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md)
|
||||
|
||||
[Apr 26, 2024. SimpleX network: legally binding transparency, v5.7 released with better calls and messages.](./blog/20240426-simplex-legally-binding-transparency-v5-7-better-user-experience.md)
|
||||
|
||||
[Mar 23, 2024. SimpleX network: real privacy and stable profits, non-profits for protocols, v5.6 released with quantum resistant e2e encryption and simple profile migration.](./blog/20240323-simplex-network-privacy-non-profit-v5-6-quantum-resistant-e2e-encryption-simple-migration.md)
|
||||
@@ -384,10 +382,10 @@ Please also join [#simplex-devs](https://simplex.chat/contact#/?v=1-2&smp=smp%3A
|
||||
- ✅ Private notes.
|
||||
- ✅ Improve sending videos (including encryption of locally stored videos).
|
||||
- ✅ Post-quantum resistant key exchange in double ratchet protocol.
|
||||
- ✅ Message delivery relay for senders (to conceal IP address from the recipients' servers and to reduce the traffic).
|
||||
- 🏗 Improve stability and reduce battery usage.
|
||||
- 🏗 Improve experience for the new users.
|
||||
- 🏗 Large groups, communities and public channels.
|
||||
- 🏗 Message delivery relay for senders (to conceal IP address from the recipients' servers and to reduce the traffic).
|
||||
- Privacy & security slider - a simple way to set all settings at once.
|
||||
- SMP queue redundancy and rotation (manual is supported).
|
||||
- Include optional message into connection request sent via contact address.
|
||||
|
||||
@@ -17,7 +17,6 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
if #available(iOS 17.0, *) { trackKeyboard() }
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(pasteboardChanged), name: UIPasteboard.changedNotification, object: nil)
|
||||
removePasscodesIfReinstalled()
|
||||
prepareForLaunch()
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -142,10 +141,6 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private func prepareForLaunch() {
|
||||
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
|
||||
}
|
||||
|
||||
static func keepScreenOn(_ on: Bool) {
|
||||
UIApplication.shared.isIdleTimerDisabled = on
|
||||
}
|
||||
@@ -153,15 +148,13 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
|
||||
class SceneDelegate: NSObject, ObservableObject, UIWindowSceneDelegate {
|
||||
var window: UIWindow?
|
||||
static var windowStatic: UIWindow?
|
||||
var windowScene: UIWindowScene?
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||
UITableView.appearance().backgroundColor = .clear
|
||||
guard let windowScene = scene as? UIWindowScene else { return }
|
||||
self.windowScene = windowScene
|
||||
window = windowScene.keyWindow
|
||||
SceneDelegate.windowStatic = windowScene.keyWindow
|
||||
ThemeManager.applyTheme(currentThemeDefault.get())
|
||||
window?.tintColor = UIColor(cgColor: getUIAccentColorDefault())
|
||||
window?.overrideUserInterfaceStyle = getUserInterfaceStyleDefault()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "wallpaper_cats@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB |
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "wallpaper_flowers@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 173 KiB |
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "wallpaper_hearts@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 79 KiB |
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "wallpaper_kids@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 168 KiB |
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "wallpaper_school@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 233 KiB |
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "wallpaper_travel@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 79 KiB |
@@ -14,8 +14,6 @@ struct ContentView: View {
|
||||
@ObservedObject var alertManager = AlertManager.shared
|
||||
@ObservedObject var callController = CallController.shared
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@EnvironmentObject var sceneDelegate: SceneDelegate
|
||||
|
||||
var contentAccessAuthenticationExtended: Bool
|
||||
|
||||
@@ -53,16 +51,6 @@ struct ContentView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if #available(iOS 16.0, *) {
|
||||
allViews()
|
||||
.scrollContentBackground(.hidden)
|
||||
} else {
|
||||
// on iOS 15 scroll view background disabled in SceneDelegate
|
||||
allViews()
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder func allViews() -> some View {
|
||||
ZStack {
|
||||
let showCallArea = chatModel.activeCall != nil && chatModel.activeCall?.callState != .waitCapabilities && chatModel.activeCall?.callState != .invitationAccepted
|
||||
// contentView() has to be in a single branch, so that enabling authentication doesn't trigger re-rendering and close settings.
|
||||
@@ -108,8 +96,6 @@ struct ContentView: View {
|
||||
initializationView()
|
||||
}
|
||||
}
|
||||
//.tint(theme.colors.primary)
|
||||
.background(theme.colors.background)
|
||||
.alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! }
|
||||
.sheet(isPresented: $showSettings) {
|
||||
SettingsView(showSettings: $showSettings)
|
||||
@@ -152,11 +138,6 @@ struct ContentView: View {
|
||||
break
|
||||
}
|
||||
}
|
||||
.onChange(of: colorScheme) { scheme in
|
||||
if sceneDelegate.window?.overrideUserInterfaceStyle == .unspecified {
|
||||
reactOnDarkThemeChanges(scheme == .dark)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func contentView() -> some View {
|
||||
@@ -243,8 +224,8 @@ struct ContentView: View {
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity )
|
||||
.background(
|
||||
Rectangle()
|
||||
.fill(theme.colors.background)
|
||||
)
|
||||
.fill(.background)
|
||||
)
|
||||
}
|
||||
|
||||
private func mainView() -> some View {
|
||||
|
||||
@@ -422,16 +422,6 @@ final class ChatModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
func updateCurrentUserUiThemes(uiThemes: ThemeModeOverrides?) {
|
||||
guard var current = currentUser else { return }
|
||||
current.uiThemes = uiThemes
|
||||
let i = users.firstIndex(where: { $0.user.userId == current.userId })
|
||||
if let i {
|
||||
users[i].user = current
|
||||
}
|
||||
currentUser = current
|
||||
}
|
||||
|
||||
func addLiveDummy(_ chatInfo: ChatInfo) -> ChatItem {
|
||||
let cItem = ChatItem.liveDummy(chatInfo.chatType)
|
||||
withAnimation {
|
||||
|
||||
@@ -205,43 +205,6 @@ func moveTempFileFromURL(_ url: URL) -> CryptoFile? {
|
||||
}
|
||||
}
|
||||
|
||||
func saveWallpaperFile(url: URL) -> String? {
|
||||
let destFile = URL(fileURLWithPath: generateNewFileName(getWallpaperDirectory().path + "/" + "wallpaper", "jpg", fullPath: true))
|
||||
do {
|
||||
try FileManager.default.copyItem(atPath: url.path, toPath: destFile.path)
|
||||
return destFile.lastPathComponent
|
||||
} catch {
|
||||
logger.error("FileUtils.saveWallpaperFile error: \(error.localizedDescription)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func saveWallpaperFile(image: UIImage) -> String? {
|
||||
let hasAlpha = imageHasAlpha(image)
|
||||
let destFile = URL(fileURLWithPath: generateNewFileName(getWallpaperDirectory().path + "/" + "wallpaper", hasAlpha ? "png" : "jpg", fullPath: true))
|
||||
let dataResized = resizeImageToDataSize(image, maxDataSize: 5_000_000, hasAlpha: hasAlpha)
|
||||
do {
|
||||
try dataResized!.write(to: destFile)
|
||||
return destFile.lastPathComponent
|
||||
} catch {
|
||||
logger.error("FileUtils.saveWallpaperFile error: \(error.localizedDescription)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func removeWallpaperFile(fileName: String? = nil) {
|
||||
do {
|
||||
try FileManager.default.contentsOfDirectory(atPath: getWallpaperDirectory().path).forEach {
|
||||
if URL(fileURLWithPath: $0).lastPathComponent == fileName { try FileManager.default.removeItem(atPath: $0) }
|
||||
}
|
||||
} catch {
|
||||
logger.error("FileUtils.removeWallpaperFile error: \(error.localizedDescription)")
|
||||
}
|
||||
if let fileName {
|
||||
WallpaperType.cachedImages.removeValue(forKey: fileName)
|
||||
}
|
||||
}
|
||||
|
||||
func generateNewFileName(_ prefix: String, _ ext: String, fullPath: Bool = false) -> String {
|
||||
uniqueCombine("\(prefix)_\(getTimestamp()).\(ext)", fullPath: fullPath)
|
||||
}
|
||||
|
||||
@@ -242,8 +242,14 @@ func apiSuspendChat(timeoutMicroseconds: Int) {
|
||||
logger.error("apiSuspendChat error: \(String(describing: r))")
|
||||
}
|
||||
|
||||
func apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String, ctrl: chat_ctrl? = nil) throws {
|
||||
let r = chatSendCmdSync(.apiSetAppFilePaths(filesFolder: filesFolder, tempFolder: tempFolder, assetsFolder: assetsFolder), ctrl)
|
||||
func apiSetTempFolder(tempFolder: String, ctrl: chat_ctrl? = nil) throws {
|
||||
let r = chatSendCmdSync(.setTempFolder(tempFolder: tempFolder), ctrl)
|
||||
if case .cmdOk = r { return }
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiSetFilesFolder(filesFolder: String, ctrl: chat_ctrl? = nil) throws {
|
||||
let r = chatSendCmdSync(.setFilesFolder(filesFolder: filesFolder), ctrl)
|
||||
if case .cmdOk = r { return }
|
||||
throw r
|
||||
}
|
||||
@@ -825,21 +831,6 @@ func apiSetConnectionAlias(connId: Int64, localAlias: String) async throws -> Pe
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiSetUserUIThemes(userId: Int64, themes: ThemeModeOverrides?) async -> Bool {
|
||||
let r = await chatSendCmd(.apiSetUserUIThemes(userId: userId, themes: themes))
|
||||
if case .cmdOk = r { return true }
|
||||
logger.error("apiSetUserUIThemes bad response: \(String(describing: r))")
|
||||
return false
|
||||
}
|
||||
|
||||
func apiSetChatUIThemes(chatId: ChatId, themes: ThemeModeOverrides?) async -> Bool {
|
||||
let r = await chatSendCmd(.apiSetChatUIThemes(chatId: chatId, themes: themes))
|
||||
if case .cmdOk = r { return true }
|
||||
logger.error("apiSetChatUIThemes bad response: \(String(describing: r))")
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
func apiCreateUserAddress() async throws -> String {
|
||||
let userId = try currentUserId("apiCreateUserAddress")
|
||||
let r = await chatSendCmd(.apiCreateMyAddress(userId: userId))
|
||||
@@ -1362,7 +1353,8 @@ func initializeChat(start: Bool, confirmStart: Bool = false, dbKey: String? = ni
|
||||
if encryptionStartedDefault.get() {
|
||||
encryptionStartedDefault.set(false)
|
||||
}
|
||||
try apiSetAppFilePaths(filesFolder: getAppFilesDirectory().path, tempFolder: getTempFilesDirectory().path, assetsFolder: getWallpaperDirectory().deletingLastPathComponent().path)
|
||||
try apiSetTempFolder(tempFolder: getTempFilesDirectory().path)
|
||||
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
|
||||
try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get())
|
||||
m.chatInitialized = true
|
||||
m.currentUser = try apiGetActiveUser()
|
||||
@@ -1447,7 +1439,8 @@ func startChatWithTemporaryDatabase(ctrl: chat_ctrl) throws -> User? {
|
||||
logger.debug("startChatWithTemporaryDatabase")
|
||||
let migrationActiveUser = try? apiGetActiveUser(ctrl: ctrl) ?? apiCreateActiveUser(Profile(displayName: "Temp", fullName: ""), ctrl: ctrl)
|
||||
try setNetworkConfig(getNetCfg(), ctrl: ctrl)
|
||||
try apiSetAppFilePaths(filesFolder: getMigrationTempFilesDirectory().path, tempFolder: getMigrationTempFilesDirectory().path, assetsFolder: getWallpaperDirectory().deletingLastPathComponent().path, ctrl: ctrl)
|
||||
try apiSetTempFolder(tempFolder: getMigrationTempFilesDirectory().path, ctrl: ctrl)
|
||||
try apiSetFilesFolder(filesFolder: getMigrationTempFilesDirectory().path, ctrl: ctrl)
|
||||
_ = try apiStartChat(ctrl: ctrl)
|
||||
return migrationActiveUser
|
||||
}
|
||||
@@ -1833,15 +1826,11 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
if let aChatItem = aChatItem {
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
}
|
||||
case let .rcvFileError(user, aChatItem, _, _):
|
||||
case let .rcvFileError(user, aChatItem, _):
|
||||
if let aChatItem = aChatItem {
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
Task { cleanupFile(aChatItem) }
|
||||
}
|
||||
case let .rcvFileWarning(user, aChatItem, _, _):
|
||||
if let aChatItem = aChatItem {
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
}
|
||||
case let .sndFileStart(user, aChatItem, _):
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
case let .sndFileComplete(user, aChatItem, _):
|
||||
@@ -1863,10 +1852,6 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
Task { cleanupFile(aChatItem) }
|
||||
}
|
||||
case let .sndFileWarning(user, aChatItem, _, _):
|
||||
if let aChatItem = aChatItem {
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
}
|
||||
case let .callInvitation(invitation):
|
||||
await MainActor.run {
|
||||
m.callInvitations[invitation.contact.id] = invitation
|
||||
@@ -1964,28 +1949,12 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
let state = UIRemoteCtrlSessionState.connected(remoteCtrl: remoteCtrl, sessionCode: m.remoteCtrlSession?.sessionCode ?? "")
|
||||
m.remoteCtrlSession = m.remoteCtrlSession?.updateState(state)
|
||||
}
|
||||
case let .remoteCtrlStopped(_, rcStopReason):
|
||||
case .remoteCtrlStopped:
|
||||
// This delay is needed to cancel the session that fails on network failure,
|
||||
// e.g. when user did not grant permission to access local network yet.
|
||||
if let sess = m.remoteCtrlSession {
|
||||
await MainActor.run {
|
||||
m.remoteCtrlSession = nil
|
||||
dismissAllSheets() {
|
||||
switch rcStopReason {
|
||||
case .connectionFailed(.errorAgent(.RCP(.identity))):
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title: "Connection with desktop stopped",
|
||||
message: "This link was used with another mobile device, please create a new link on the desktop."
|
||||
)
|
||||
default:
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("Connection with desktop stopped"),
|
||||
message: Text("Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers."),
|
||||
primaryButton: .default(Text("Ok")),
|
||||
secondaryButton: .default(Text("Copy error")) { UIPasteboard.general.string = String(describing: rcStopReason) }
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
if case .connected = sess.sessionState {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
|
||||
@@ -39,7 +39,6 @@ struct SimpleXApp: App {
|
||||
// so that it's computed by the time view renders, and not on event after rendering
|
||||
ContentView(contentAccessAuthenticationExtended: !authenticationExpired())
|
||||
.environmentObject(chatModel)
|
||||
.environmentObject(AppTheme.shared)
|
||||
.onOpenURL { url in
|
||||
logger.debug("ContentView.onOpenURL: \(url)")
|
||||
chatModel.appOpenUrl = url
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
//
|
||||
// ThemeShared.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Avently on 14.06.2024.
|
||||
// Copyright © 2024 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
var CurrentColors: ThemeManager.ActiveTheme = ThemeManager.currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) {
|
||||
didSet {
|
||||
AppTheme.shared.name = CurrentColors.name
|
||||
AppTheme.shared.base = CurrentColors.base
|
||||
AppTheme.shared.colors.updateColorsFrom(CurrentColors.colors)
|
||||
AppTheme.shared.appColors.updateColorsFrom(CurrentColors.appColors)
|
||||
AppTheme.shared.wallpaper.updateWallpaperFrom(CurrentColors.wallpaper)
|
||||
AppTheme.shared.objectWillChange.send()
|
||||
}
|
||||
}
|
||||
|
||||
var MenuTextColor: Color { if isInDarkTheme() { AppTheme.shared.colors.onBackground.opacity(0.8) } else { Color.black } }
|
||||
var NoteFolderIconColor: Color { AppTheme.shared.appColors.primaryVariant2 }
|
||||
|
||||
func isInDarkTheme() -> Bool { !CurrentColors.colors.isLight }
|
||||
|
||||
class AppTheme: ObservableObject {
|
||||
static let shared = AppTheme(name: CurrentColors.name, base: CurrentColors.base, colors: CurrentColors.colors, appColors: CurrentColors.appColors, wallpaper: CurrentColors.wallpaper)
|
||||
|
||||
var name: String
|
||||
var base: DefaultTheme
|
||||
@ObservedObject var colors: Colors
|
||||
@ObservedObject var appColors: AppColors
|
||||
@ObservedObject var wallpaper: AppWallpaper
|
||||
|
||||
init(name: String, base: DefaultTheme, colors: Colors, appColors: AppColors, wallpaper: AppWallpaper) {
|
||||
self.name = name
|
||||
self.base = base
|
||||
self.colors = colors
|
||||
self.appColors = appColors
|
||||
self.wallpaper = wallpaper
|
||||
}
|
||||
}
|
||||
|
||||
struct ThemedBackground: ViewModifier {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.background(
|
||||
theme.base == DefaultTheme.SIMPLEX
|
||||
? LinearGradient(
|
||||
colors: [
|
||||
theme.colors.background.lighter(0.4),
|
||||
theme.colors.background.darker(0.4)
|
||||
],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
: LinearGradient(
|
||||
colors: [],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
)
|
||||
.background(
|
||||
theme.base == DefaultTheme.SIMPLEX
|
||||
? Color.clear
|
||||
: theme.colors.background
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func reactOnDarkThemeChanges(_ isDark: Bool) {
|
||||
systemInDarkThemeCurrently = isDark
|
||||
//sceneDelegate.window?.overrideUserInterfaceStyle == .unspecified
|
||||
if currentThemeDefault.get() == DefaultTheme.SYSTEM_THEME_NAME && CurrentColors.colors.isLight == isDark {
|
||||
// Change active colors from light to dark and back based on system theme
|
||||
ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
extension ThemeWallpaper {
|
||||
public func importFromString() -> ThemeWallpaper {
|
||||
if preset == nil, let image {
|
||||
// Need to save image from string and to save its path
|
||||
if let data = Data(base64Encoded: dropImagePrefix(image)),
|
||||
let parsed = UIImage(data: data),
|
||||
let filename = saveWallpaperFile(image: parsed) {
|
||||
var copy = self
|
||||
copy.image = nil
|
||||
copy.imageFile = filename
|
||||
return copy
|
||||
} else {
|
||||
return ThemeWallpaper()
|
||||
}
|
||||
} else {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
func withFilledWallpaperBase64() -> ThemeWallpaper {
|
||||
let aw = toAppWallpaper()
|
||||
let type = aw.type
|
||||
let preset: String? = if case let WallpaperType.Preset(filename, _) = type { filename } else { nil }
|
||||
let scale: Float? = if case let WallpaperType.Preset(_, scale) = type { scale } else { if case let WallpaperType.Image(_, scale, _) = type { scale } else { 1.0 } }
|
||||
let scaleType: WallpaperScaleType? = if case let WallpaperType.Image(_, _, scaleType) = type { scaleType } else { nil }
|
||||
let image: String? = if case WallpaperType.Image = type, let image = type.uiImage { resizeImageToStrSize(image, maxDataSize: 5_000_000) } else { nil }
|
||||
return ThemeWallpaper (
|
||||
preset: preset,
|
||||
scale: scale,
|
||||
scaleType: scaleType,
|
||||
background: aw.background?.toReadableHex(),
|
||||
tint: aw.tint?.toReadableHex(),
|
||||
image: image,
|
||||
imageFile: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
//
|
||||
// ThemeManager.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Avently on 03.06.2024.
|
||||
// Copyright © 2024 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
class ThemeManager {
|
||||
struct ActiveTheme {
|
||||
let name: String
|
||||
let base: DefaultTheme
|
||||
let colors: Colors
|
||||
let appColors: AppColors
|
||||
var wallpaper: AppWallpaper = AppWallpaper(background: nil, tint: nil, type: .Empty)
|
||||
}
|
||||
|
||||
private static func systemDarkThemeColors() -> (Colors, DefaultTheme) {
|
||||
switch systemDarkThemeDefault.get() {
|
||||
case DefaultTheme.DARK.themeName: (DarkColorPalette, DefaultTheme.DARK)
|
||||
case DefaultTheme.SIMPLEX.themeName: (SimplexColorPalette, DefaultTheme.SIMPLEX)
|
||||
case DefaultTheme.BLACK.themeName: (BlackColorPalette, DefaultTheme.BLACK)
|
||||
default: (SimplexColorPalette, DefaultTheme.SIMPLEX)
|
||||
}
|
||||
}
|
||||
|
||||
private static func nonSystemThemeName() -> String {
|
||||
let themeName = currentThemeDefault.get()
|
||||
return if themeName != DefaultTheme.SYSTEM_THEME_NAME {
|
||||
themeName
|
||||
} else {
|
||||
systemInDarkThemeCurrently ? systemDarkThemeDefault.get() : DefaultTheme.LIGHT.themeName
|
||||
}
|
||||
}
|
||||
|
||||
static func defaultActiveTheme(_ appSettingsTheme: [ThemeOverrides]) -> ThemeOverrides? {
|
||||
let nonSystemThemeName = nonSystemThemeName()
|
||||
let defaultThemeId = currentThemeIdsDefault.get()[nonSystemThemeName]
|
||||
return appSettingsTheme.getTheme(defaultThemeId)
|
||||
}
|
||||
|
||||
static func defaultActiveTheme(_ perUserTheme: ThemeModeOverrides?, _ appSettingsTheme: [ThemeOverrides]) -> ThemeModeOverride {
|
||||
let perUserTheme = !CurrentColors.colors.isLight ? perUserTheme?.dark : perUserTheme?.light
|
||||
if let perUserTheme {
|
||||
return perUserTheme
|
||||
}
|
||||
let defaultTheme = defaultActiveTheme(appSettingsTheme)
|
||||
return ThemeModeOverride(mode: CurrentColors.base.mode, colors: defaultTheme?.colors ?? ThemeColors(), wallpaper: defaultTheme?.wallpaper)
|
||||
}
|
||||
|
||||
static func currentColors(_ themeOverridesForType: WallpaperType?, _ perChatTheme: ThemeModeOverride?, _ perUserTheme: ThemeModeOverrides?, _ appSettingsTheme: [ThemeOverrides]) -> ActiveTheme {
|
||||
let themeName = currentThemeDefault.get()
|
||||
let nonSystemThemeName = nonSystemThemeName()
|
||||
let defaultTheme = defaultActiveTheme(appSettingsTheme)
|
||||
|
||||
let baseTheme = switch nonSystemThemeName {
|
||||
case DefaultTheme.LIGHT.themeName: ActiveTheme(name: DefaultTheme.LIGHT.themeName, base: DefaultTheme.LIGHT, colors: LightColorPalette.clone(), appColors: LightColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.LIGHT)))
|
||||
case DefaultTheme.DARK.themeName: ActiveTheme(name: DefaultTheme.DARK.themeName, base: DefaultTheme.DARK, colors: DarkColorPalette.clone(), appColors: DarkColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.DARK)))
|
||||
case DefaultTheme.SIMPLEX.themeName: ActiveTheme(name: DefaultTheme.SIMPLEX.themeName, base: DefaultTheme.SIMPLEX, colors: SimplexColorPalette.clone(), appColors: SimplexColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.SIMPLEX)))
|
||||
case DefaultTheme.BLACK.themeName: ActiveTheme(name: DefaultTheme.BLACK.themeName, base: DefaultTheme.BLACK, colors: BlackColorPalette.clone(), appColors: BlackColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.BLACK)))
|
||||
default: ActiveTheme(name: DefaultTheme.LIGHT.themeName, base: DefaultTheme.LIGHT, colors: LightColorPalette.clone(), appColors: LightColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.LIGHT)))
|
||||
}
|
||||
|
||||
let perUserTheme = baseTheme.colors.isLight ? perUserTheme?.light : perUserTheme?.dark
|
||||
let theme = appSettingsTheme.sameTheme(themeOverridesForType ?? perChatTheme?.type ?? perUserTheme?.type ?? defaultTheme?.wallpaper?.toAppWallpaper().type, nonSystemThemeName) ?? defaultTheme
|
||||
|
||||
if theme == nil && perUserTheme == nil && perChatTheme == nil && themeOverridesForType == nil {
|
||||
return ActiveTheme(name: themeName, base: baseTheme.base, colors: baseTheme.colors, appColors: baseTheme.appColors, wallpaper: baseTheme.wallpaper)
|
||||
}
|
||||
let presetWallpaperTheme: ThemeColors? = if let wallpaper = perChatTheme?.wallpaper {
|
||||
if let preset = wallpaper.preset { PresetWallpaper.from(preset)?.colors[baseTheme.base] } else { nil }
|
||||
} else if let wallpaper = perUserTheme?.wallpaper {
|
||||
if let preset = wallpaper.preset { PresetWallpaper.from(preset)?.colors[baseTheme.base] } else { nil }
|
||||
} else {
|
||||
if let preset = theme?.wallpaper?.preset { PresetWallpaper.from(preset)?.colors[baseTheme.base] } else { nil }
|
||||
}
|
||||
|
||||
let themeOrEmpty = theme ?? ThemeOverrides(base: baseTheme.base)
|
||||
let colors = themeOrEmpty.toColors(themeOrEmpty.base, perChatTheme?.colors, perUserTheme?.colors, presetWallpaperTheme)
|
||||
return ActiveTheme(
|
||||
name: themeName,
|
||||
base: baseTheme.base,
|
||||
colors: colors,
|
||||
appColors: themeOrEmpty.toAppColors(themeOrEmpty.base, perChatTheme?.colors, perChatTheme?.type, perUserTheme?.colors, perUserTheme?.type, presetWallpaperTheme),
|
||||
wallpaper: themeOrEmpty.toAppWallpaper(themeOverridesForType, perChatTheme, perUserTheme, colors.background)
|
||||
)
|
||||
}
|
||||
|
||||
static func currentThemeOverridesForExport(_ perChatTheme: ThemeModeOverride?, _ perUserTheme: ThemeModeOverrides?) -> ThemeOverrides {
|
||||
let current = currentColors(nil, perChatTheme, perUserTheme, themeOverridesDefault.get())
|
||||
let wType = current.wallpaper.type
|
||||
let wBackground = current.wallpaper.background
|
||||
let wTint = current.wallpaper.tint
|
||||
let w: ThemeWallpaper? = if case WallpaperType.Empty = wType {
|
||||
nil
|
||||
} else {
|
||||
ThemeWallpaper.from(wType, wBackground?.toReadableHex(), wTint?.toReadableHex()).withFilledWallpaperBase64()
|
||||
}
|
||||
return ThemeOverrides(
|
||||
themeId: "",
|
||||
base: current.base,
|
||||
colors: ThemeColors.from(current.colors, current.appColors),
|
||||
wallpaper: w
|
||||
)
|
||||
}
|
||||
|
||||
static func applyTheme(_ theme: String) {
|
||||
currentThemeDefault.set(theme)
|
||||
CurrentColors = currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get())
|
||||
SceneDelegate.windowStatic?.tintColor = UIColor(CurrentColors.colors.primary)
|
||||
SceneDelegate.windowStatic?.backgroundColor = UIColor(CurrentColors.colors.background)
|
||||
SceneDelegate.windowStatic?.overrideUserInterfaceStyle = switch currentThemeDefault.get() {
|
||||
case DefaultTheme.LIGHT.themeName: .light
|
||||
case DefaultTheme.SYSTEM_THEME_NAME: .unspecified
|
||||
default: .dark
|
||||
}
|
||||
}
|
||||
|
||||
static func changeDarkTheme(_ theme: String) {
|
||||
systemDarkThemeDefault.set(theme)
|
||||
CurrentColors = currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get())
|
||||
}
|
||||
|
||||
static func saveAndApplyThemeColor(_ baseTheme: DefaultTheme, _ name: ThemeColor, _ color: Color? = nil, _ pref: CodableDefault<[ThemeOverrides]>? = nil) {
|
||||
let nonSystemThemeName = baseTheme.themeName
|
||||
let pref = pref ?? themeOverridesDefault
|
||||
let overrides = pref.get()
|
||||
let themeId = currentThemeIdsDefault.get()[nonSystemThemeName]
|
||||
let prevValue = overrides.getTheme(themeId) ?? ThemeOverrides(base: baseTheme)
|
||||
pref.set(overrides.replace(prevValue.withUpdatedColor(name, color?.toReadableHex())))
|
||||
var themeIds = currentThemeIdsDefault.get()
|
||||
themeIds[nonSystemThemeName] = prevValue.themeId
|
||||
currentThemeIdsDefault.set(themeIds)
|
||||
CurrentColors = currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get())
|
||||
}
|
||||
|
||||
static func applyThemeColor(name: ThemeColor, color: Color? = nil, pref: Binding<ThemeModeOverride>) {
|
||||
pref.wrappedValue = pref.wrappedValue.withUpdatedColor(name, color?.toReadableHex())
|
||||
}
|
||||
|
||||
static func saveAndApplyWallpaper(_ baseTheme: DefaultTheme, _ type: WallpaperType?, _ pref: CodableDefault<[ThemeOverrides]>?) {
|
||||
let nonSystemThemeName = baseTheme.themeName
|
||||
let pref = pref ?? themeOverridesDefault
|
||||
let overrides = pref.get()
|
||||
let theme = overrides.sameTheme(type, baseTheme.themeName)
|
||||
var prevValue = theme ?? ThemeOverrides(base: baseTheme)
|
||||
prevValue.wallpaper = if let type {
|
||||
if case WallpaperType.Empty = type {
|
||||
nil as ThemeWallpaper?
|
||||
} else {
|
||||
ThemeWallpaper.from(type, prevValue.wallpaper?.background, prevValue.wallpaper?.tint)
|
||||
}
|
||||
} else {
|
||||
nil
|
||||
}
|
||||
pref.set(overrides.replace(prevValue))
|
||||
var themeIds = currentThemeIdsDefault.get()
|
||||
themeIds[nonSystemThemeName] = prevValue.themeId
|
||||
currentThemeIdsDefault.set(themeIds)
|
||||
CurrentColors = currentColors( nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get())
|
||||
}
|
||||
|
||||
static func copyFromSameThemeOverrides(_ type: WallpaperType?, _ lowerLevelOverride: ThemeModeOverride?, _ pref: Binding<ThemeModeOverride>) -> Bool {
|
||||
let overrides = themeOverridesDefault.get()
|
||||
let sameWallpaper: ThemeWallpaper? = if let wallpaper = lowerLevelOverride?.wallpaper, lowerLevelOverride?.type?.sameType(type) == true {
|
||||
wallpaper
|
||||
} else {
|
||||
overrides.sameTheme(type, CurrentColors.base.themeName)?.wallpaper
|
||||
}
|
||||
guard let sameWallpaper else {
|
||||
if let type {
|
||||
var w: ThemeWallpaper = ThemeWallpaper.from(type, nil, nil)
|
||||
w.scale = nil
|
||||
w.scaleType = nil
|
||||
pref.wrappedValue = ThemeModeOverride(mode: CurrentColors.base.mode, wallpaper: w)
|
||||
} else {
|
||||
// Make an empty wallpaper to override any top level ones
|
||||
pref.wrappedValue = ThemeModeOverride(mode: CurrentColors.base.mode, wallpaper: ThemeWallpaper())
|
||||
}
|
||||
return true
|
||||
}
|
||||
var type = sameWallpaper.toAppWallpaper().type
|
||||
if case let WallpaperType.Image(filename, scale, scaleType) = type, sameWallpaper.imageFile == filename {
|
||||
// same image file. Needs to be copied first in order to be able to remove the file once it's not needed anymore without affecting main theme override
|
||||
if let filename = saveWallpaperFile(url: getWallpaperFilePath(filename)) {
|
||||
type = WallpaperType.Image(filename, scale, scaleType)
|
||||
} else {
|
||||
logger.error("Error while copying wallpaper from global overrides to chat overrides")
|
||||
return false
|
||||
}
|
||||
}
|
||||
var prevValue = pref.wrappedValue
|
||||
var w = ThemeWallpaper.from(type, nil, nil)
|
||||
w.scale = nil
|
||||
w.scaleType = nil
|
||||
prevValue.colors = ThemeColors()
|
||||
prevValue.wallpaper = w
|
||||
pref.wrappedValue = prevValue
|
||||
return true
|
||||
}
|
||||
|
||||
static func applyWallpaper(_ type: WallpaperType?, _ pref: Binding<ThemeModeOverride>) {
|
||||
var prevValue = pref.wrappedValue
|
||||
prevValue.wallpaper = if let type {
|
||||
ThemeWallpaper.from(type, prevValue.wallpaper?.background, prevValue.wallpaper?.tint)
|
||||
} else {
|
||||
nil
|
||||
}
|
||||
pref.wrappedValue = prevValue
|
||||
}
|
||||
|
||||
static func saveAndApplyThemeOverrides(_ theme: ThemeOverrides, _ pref: CodableDefault<[ThemeOverrides]>? = nil) {
|
||||
let wallpaper = theme.wallpaper?.importFromString()
|
||||
let nonSystemThemeName = theme.base.themeName
|
||||
let pref: CodableDefault<[ThemeOverrides]> = pref ?? themeOverridesDefault
|
||||
let overrides = pref.get()
|
||||
var prevValue = overrides.getTheme(nil, wallpaper?.toAppWallpaper().type, theme.base) ?? ThemeOverrides(base: theme.base)
|
||||
if let imageFile = prevValue.wallpaper?.imageFile {
|
||||
try? FileManager.default.removeItem(at: getWallpaperFilePath(imageFile))
|
||||
}
|
||||
prevValue.base = theme.base
|
||||
prevValue.colors = theme.colors
|
||||
prevValue.wallpaper = wallpaper
|
||||
pref.set(overrides.replace(prevValue))
|
||||
currentThemeDefault.set(nonSystemThemeName)
|
||||
var currentThemeIds = currentThemeIdsDefault.get()
|
||||
currentThemeIds[nonSystemThemeName] = prevValue.themeId
|
||||
currentThemeIdsDefault.set(currentThemeIds)
|
||||
CurrentColors = currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get())
|
||||
}
|
||||
|
||||
static func resetAllThemeColors(_ pref: CodableDefault<[ThemeOverrides]>? = nil) {
|
||||
let nonSystemThemeName = nonSystemThemeName()
|
||||
let pref: CodableDefault<[ThemeOverrides]> = pref ?? themeOverridesDefault
|
||||
let overrides = pref.get()
|
||||
guard let themeId = currentThemeIdsDefault.get()[nonSystemThemeName],
|
||||
var prevValue = overrides.getTheme(themeId)
|
||||
else { return }
|
||||
prevValue.colors = ThemeColors()
|
||||
prevValue.wallpaper?.background = nil
|
||||
prevValue.wallpaper?.tint = nil
|
||||
pref.set(overrides.replace(prevValue))
|
||||
CurrentColors = currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get())
|
||||
}
|
||||
|
||||
static func resetAllThemeColors(_ pref: Binding<ThemeModeOverride>) {
|
||||
var prevValue = pref.wrappedValue
|
||||
prevValue.colors = ThemeColors()
|
||||
prevValue.wallpaper?.background = nil
|
||||
prevValue.wallpaper?.tint = nil
|
||||
pref.wrappedValue = prevValue
|
||||
}
|
||||
|
||||
static func removeTheme(_ themeId: String?) {
|
||||
var themes = themeOverridesDefault.get().map { $0 }
|
||||
themes.removeAll(where: { $0.themeId == themeId })
|
||||
themeOverridesDefault.set(themes)
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// MPVolumeView.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Avently on 24.04.2024.
|
||||
// Created by Stanislav on 24.04.2024.
|
||||
// Copyright © 2024 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import SimpleXChat
|
||||
|
||||
struct IncomingCallView: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@ObservedObject var cc = CallController.shared
|
||||
|
||||
var body: some View {
|
||||
@@ -44,7 +43,7 @@ struct IncomingCallView: View {
|
||||
cc.endCall(invitation: invitation)
|
||||
}
|
||||
|
||||
callButton("Ignore", "multiply", theme.colors.primary) {
|
||||
callButton("Ignore", "multiply", .accentColor) {
|
||||
cc.activeCallInvitation = nil
|
||||
}
|
||||
|
||||
@@ -64,7 +63,7 @@ struct IncomingCallView: View {
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
.frame(maxWidth: .infinity)
|
||||
.modifier(ThemedBackground())
|
||||
.background(Color(uiColor: .tertiarySystemGroupedBackground))
|
||||
.onAppear { dismissAllSheets() }
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,6 @@ enum SendReceipts: Identifiable, Hashable {
|
||||
|
||||
struct ChatInfoView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@ObservedObject var chat: Chat
|
||||
@State var contact: Contact
|
||||
@@ -242,7 +241,6 @@ struct ChatInfoView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarHidden(true)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
||||
@@ -372,7 +370,6 @@ struct ChatInfoView: View {
|
||||
)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationTitle("Security code")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
Label(
|
||||
contact.verified ? "View security code" : "Verify security code",
|
||||
@@ -389,7 +386,6 @@ struct ChatInfoView: View {
|
||||
currentFeaturesAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences)
|
||||
)
|
||||
.navigationBarTitle("Contact preferences")
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
Label("Contact preferences", systemImage: "switch.2")
|
||||
@@ -438,7 +434,7 @@ struct ChatInfoView: View {
|
||||
HStack {
|
||||
Text("Network status")
|
||||
Image(systemName: "info.circle")
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
.font(.system(size: 14))
|
||||
Spacer()
|
||||
Text(chatModel.contactNetworkStatus(contact).statusString)
|
||||
|
||||
@@ -11,7 +11,6 @@ import SimpleXChat
|
||||
|
||||
struct CICallItemView: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@ObservedObject var chat: Chat
|
||||
var chatItem: ChatItem
|
||||
var status: CICallStatus
|
||||
@@ -36,7 +35,7 @@ struct CICallItemView: View {
|
||||
case .error: missedCallIcon(sent).foregroundColor(.orange)
|
||||
}
|
||||
|
||||
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary, showStatus: false, showEdited: false)
|
||||
CIMetaView(chat: chat, chatItem: chatItem, showStatus: false, showEdited: false)
|
||||
.padding(.bottom, 8)
|
||||
.padding(.horizontal, 12)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import SimpleXChat
|
||||
|
||||
struct CIFeaturePreferenceView: View {
|
||||
@ObservedObject var chat: Chat
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var chatItem: ChatItem
|
||||
var feature: ChatFeature
|
||||
var allowed: FeatureAllowed
|
||||
@@ -46,7 +45,7 @@ struct CIFeaturePreferenceView: View {
|
||||
r = r
|
||||
+ Text(acceptText)
|
||||
.fontWeight(.medium)
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
+ Text(" ")
|
||||
}
|
||||
r = r + chatItem.timestampText
|
||||
|
||||
@@ -12,7 +12,6 @@ import SimpleXChat
|
||||
struct CIFileView: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
let file: CIFile?
|
||||
let edited: Bool
|
||||
|
||||
@@ -57,16 +56,14 @@ struct CIFileView: View {
|
||||
case .sndTransfer: return false
|
||||
case .sndComplete: return true
|
||||
case .sndCancelled: return false
|
||||
case .sndError: return true
|
||||
case .sndWarning: return true
|
||||
case .sndError: return false
|
||||
case .rcvInvitation: return true
|
||||
case .rcvAccepted: return true
|
||||
case .rcvTransfer: return false
|
||||
case .rcvAborted: return true
|
||||
case .rcvComplete: return true
|
||||
case .rcvCancelled: return false
|
||||
case .rcvError: return true
|
||||
case .rcvWarning: return true
|
||||
case .rcvError: return false
|
||||
case .invalid: return false
|
||||
}
|
||||
}
|
||||
@@ -111,18 +108,6 @@ struct CIFileView: View {
|
||||
if let fileSource = getLoadedFileSource(file) {
|
||||
saveCryptoFile(fileSource)
|
||||
}
|
||||
case let .rcvError(rcvFileError):
|
||||
logger.debug("CIFileView fileAction - in .rcvError")
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("File error"),
|
||||
message: Text(rcvFileError.errorInfo)
|
||||
))
|
||||
case let .rcvWarning(rcvFileError):
|
||||
logger.debug("CIFileView fileAction - in .rcvWarning")
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("Temporary file error"),
|
||||
message: Text(rcvFileError.errorInfo)
|
||||
))
|
||||
case .sndStored:
|
||||
logger.debug("CIFileView fileAction - in .sndStored")
|
||||
if file.fileProtocol == .local, let fileSource = getLoadedFileSource(file) {
|
||||
@@ -133,18 +118,6 @@ struct CIFileView: View {
|
||||
if let fileSource = getLoadedFileSource(file) {
|
||||
saveCryptoFile(fileSource)
|
||||
}
|
||||
case let .sndError(sndFileError):
|
||||
logger.debug("CIFileView fileAction - in .sndError")
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("File error"),
|
||||
message: Text(sndFileError.errorInfo)
|
||||
))
|
||||
case let .sndWarning(sndFileError):
|
||||
logger.debug("CIFileView fileAction - in .sndWarning")
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("Temporary file error"),
|
||||
message: Text(sndFileError.errorInfo)
|
||||
))
|
||||
default: break
|
||||
}
|
||||
}
|
||||
@@ -168,10 +141,9 @@ struct CIFileView: View {
|
||||
case .sndComplete: fileIcon("doc.fill", innerIcon: "checkmark", innerIconSize: 10)
|
||||
case .sndCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
|
||||
case .sndError: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
|
||||
case .sndWarning: fileIcon("doc.fill", innerIcon: "exclamationmark.triangle.fill", innerIconSize: 10)
|
||||
case .rcvInvitation:
|
||||
if fileSizeValid(file) {
|
||||
fileIcon("arrow.down.doc.fill", color: theme.colors.primary)
|
||||
fileIcon("arrow.down.doc.fill", color: .accentColor)
|
||||
} else {
|
||||
fileIcon("doc.fill", color: .orange, innerIcon: "exclamationmark", innerIconSize: 12)
|
||||
}
|
||||
@@ -183,11 +155,10 @@ struct CIFileView: View {
|
||||
progressView()
|
||||
}
|
||||
case .rcvAborted:
|
||||
fileIcon("doc.fill", color: theme.colors.primary, innerIcon: "exclamationmark.arrow.circlepath", innerIconSize: 12)
|
||||
fileIcon("doc.fill", color: .accentColor, innerIcon: "exclamationmark.arrow.circlepath", innerIconSize: 12)
|
||||
case .rcvComplete: fileIcon("doc.fill")
|
||||
case .rcvCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
|
||||
case .rcvError: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
|
||||
case .rcvWarning: fileIcon("doc.fill", innerIcon: "exclamationmark.triangle.fill", innerIconSize: 10)
|
||||
case .invalid: fileIcon("doc.fill", innerIcon: "questionmark", innerIconSize: 10)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -11,7 +11,7 @@ import SimpleXChat
|
||||
|
||||
struct CIGroupInvitationView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@ObservedObject var chat: Chat
|
||||
var chatItem: ChatItem
|
||||
var groupInvitation: CIGroupInvitation
|
||||
@@ -42,7 +42,7 @@ struct CIGroupInvitationView: View {
|
||||
.overlay(DetermineWidth())
|
||||
(
|
||||
Text(chatIncognito ? "Tap to join incognito" : "Tap to join")
|
||||
.foregroundColor(inProgress ? .secondary : chatIncognito ? .indigo : theme.colors.primary)
|
||||
.foregroundColor(inProgress ? .secondary : chatIncognito ? .indigo : .accentColor)
|
||||
.font(.callout)
|
||||
+ Text(" ")
|
||||
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy)
|
||||
@@ -65,11 +65,11 @@ struct CIGroupInvitationView: View {
|
||||
}
|
||||
}
|
||||
|
||||
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary, showStatus: false, showEdited: false)
|
||||
CIMetaView(chat: chat, chatItem: chatItem, showStatus: false, showEdited: false)
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(chatItemFrameColor(chatItem, theme))
|
||||
.background(chatItemFrameColor(chatItem, colorScheme))
|
||||
.cornerRadius(18)
|
||||
.textSelection(.disabled)
|
||||
.onPreferenceChange(DetermineWidth.Key.self) { frameWidth = $0 }
|
||||
@@ -99,7 +99,7 @@ struct CIGroupInvitationView: View {
|
||||
private func groupInfoView(_ action: Bool) -> some View {
|
||||
var color: Color
|
||||
if action && !inProgress {
|
||||
color = chatIncognito ? .indigo : theme.colors.primary
|
||||
color = chatIncognito ? .indigo : .accentColor
|
||||
} else {
|
||||
color = Color(uiColor: .tertiaryLabel)
|
||||
}
|
||||
|
||||
@@ -60,26 +60,6 @@ struct CIImageView: View {
|
||||
case .rcvTransfer: () // ?
|
||||
case .rcvComplete: () // ?
|
||||
case .rcvCancelled: () // TODO
|
||||
case let .rcvError(rcvFileError):
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("File error"),
|
||||
message: Text(rcvFileError.errorInfo)
|
||||
))
|
||||
case let .rcvWarning(rcvFileError):
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("Temporary file error"),
|
||||
message: Text(rcvFileError.errorInfo)
|
||||
))
|
||||
case let .sndError(sndFileError):
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("File error"),
|
||||
message: Text(sndFileError.errorInfo)
|
||||
))
|
||||
case let .sndWarning(sndFileError):
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("Temporary file error"),
|
||||
message: Text(sndFileError.errorInfo)
|
||||
))
|
||||
default: ()
|
||||
}
|
||||
}
|
||||
@@ -119,16 +99,14 @@ struct CIImageView: View {
|
||||
case .sndComplete: fileIcon("checkmark", 10, 13)
|
||||
case .sndCancelled: fileIcon("xmark", 10, 13)
|
||||
case .sndError: fileIcon("xmark", 10, 13)
|
||||
case .sndWarning: fileIcon("exclamationmark.triangle.fill", 10, 13)
|
||||
case .rcvInvitation: fileIcon("arrow.down", 10, 13)
|
||||
case .rcvAccepted: fileIcon("ellipsis", 14, 11)
|
||||
case .rcvTransfer: progressView()
|
||||
case .rcvAborted: fileIcon("exclamationmark.arrow.circlepath", 14, 11)
|
||||
case .rcvComplete: EmptyView()
|
||||
case .rcvCancelled: fileIcon("xmark", 10, 13)
|
||||
case .rcvError: fileIcon("xmark", 10, 13)
|
||||
case .rcvWarning: fileIcon("exclamationmark.triangle.fill", 10, 13)
|
||||
case .invalid: fileIcon("questionmark", 10, 13)
|
||||
default: EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ func invalidJSONView(_ json: String) -> some View {
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
.padding()
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
struct CIInvalidJSONView_Previews: PreviewProvider {
|
||||
|
||||
@@ -11,7 +11,6 @@ import SimpleXChat
|
||||
|
||||
struct CIMemberCreatedContactView: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var chatItem: ChatItem
|
||||
|
||||
var body: some View {
|
||||
@@ -44,7 +43,7 @@ struct CIMemberCreatedContactView: View {
|
||||
r = r
|
||||
+ Text(openText)
|
||||
.fontWeight(.medium)
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
+ Text(" ")
|
||||
}
|
||||
r = r + chatItem.timestampText
|
||||
|
||||
@@ -11,9 +11,8 @@ import SimpleXChat
|
||||
|
||||
struct CIMetaView: View {
|
||||
@ObservedObject var chat: Chat
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var chatItem: ChatItem
|
||||
var metaColor: Color
|
||||
var metaColor = Color.secondary
|
||||
var paleMetaColor = Color(UIColor.tertiaryLabel)
|
||||
var showStatus = true
|
||||
var showEdited = true
|
||||
@@ -64,7 +63,6 @@ func ciMetaText(
|
||||
chatTTL: Int?,
|
||||
encrypted: Bool?,
|
||||
color: Color = .clear,
|
||||
primaryColor: Color = .accentColor,
|
||||
transparent: Bool = false,
|
||||
sent: SentCheckmark? = nil,
|
||||
showStatus: Bool = true,
|
||||
@@ -87,7 +85,7 @@ func ciMetaText(
|
||||
r = r + statusIconText("arrow.forward", color.opacity(0.67)).font(.caption2)
|
||||
}
|
||||
if showStatus {
|
||||
if let (icon, statusColor) = meta.statusIcon(color, primaryColor) {
|
||||
if let (icon, statusColor) = meta.statusIcon(color) {
|
||||
let t = Text(Image(systemName: icon)).font(.caption2)
|
||||
let gap = Text(" ").kerning(-1.25)
|
||||
let t1 = t.foregroundColor(transparent ? .clear : statusColor.opacity(0.67))
|
||||
@@ -114,16 +112,15 @@ private func statusIconText(_ icon: String, _ color: Color) -> Text {
|
||||
}
|
||||
|
||||
struct CIMetaView_Previews: PreviewProvider {
|
||||
static let metaColor = Color.secondary
|
||||
static var previews: some View {
|
||||
Group {
|
||||
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete)), metaColor: metaColor)
|
||||
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .partial)), metaColor: metaColor)
|
||||
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .ok, sndProgress: .complete)), metaColor: metaColor)
|
||||
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .ok, sndProgress: .partial)), metaColor: metaColor)
|
||||
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .badMsgHash, sndProgress: .complete)), metaColor: metaColor)
|
||||
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete), itemEdited: true), metaColor: metaColor)
|
||||
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getDeletedContentSample(), metaColor: metaColor)
|
||||
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete)))
|
||||
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .partial)))
|
||||
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .ok, sndProgress: .complete)))
|
||||
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .ok, sndProgress: .partial)))
|
||||
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .badMsgHash, sndProgress: .complete)))
|
||||
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete), itemEdited: true))
|
||||
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getDeletedContentSample())
|
||||
}
|
||||
.previewLayout(.fixed(width: 360, height: 100))
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ let decryptErrorReason: LocalizedStringKey = "It can happen when you or your con
|
||||
|
||||
struct CIRcvDecryptionError: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@ObservedObject var chat: Chat
|
||||
var msgDecryptError: MsgDecryptError
|
||||
var msgCount: UInt32
|
||||
@@ -115,18 +114,18 @@ struct CIRcvDecryptionError: View {
|
||||
}
|
||||
(
|
||||
Text(Image(systemName: "exclamationmark.arrow.triangle.2.circlepath"))
|
||||
.foregroundColor(syncSupported ? theme.colors.primary : theme.colors.secondary)
|
||||
.foregroundColor(syncSupported ? .accentColor : .secondary)
|
||||
.font(.callout)
|
||||
+ Text(" ")
|
||||
+ Text("Fix connection")
|
||||
.foregroundColor(syncSupported ? theme.colors.primary : theme.colors.secondary)
|
||||
.foregroundColor(syncSupported ? .accentColor : .secondary)
|
||||
.font(.callout)
|
||||
+ Text(" ")
|
||||
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showViaProxy: showSentViaProxy)
|
||||
)
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary)
|
||||
CIMetaView(chat: chat, chatItem: chatItem)
|
||||
.padding(.horizontal, 12)
|
||||
}
|
||||
.onTapGesture(perform: { onClick() })
|
||||
@@ -146,7 +145,7 @@ struct CIRcvDecryptionError: View {
|
||||
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showViaProxy: showSentViaProxy)
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary)
|
||||
CIMetaView(chat: chat, chatItem: chatItem)
|
||||
.padding(.horizontal, 12)
|
||||
}
|
||||
.onTapGesture(perform: { onClick() })
|
||||
|
||||
@@ -192,7 +192,7 @@ struct CIVideoView: View {
|
||||
.disabled(!canBePlayed)
|
||||
}
|
||||
}
|
||||
fileStatusIcon()
|
||||
loadingIndicator()
|
||||
}
|
||||
.onAppear {
|
||||
addObserver(player, url)
|
||||
@@ -258,11 +258,11 @@ struct CIVideoView: View {
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: w)
|
||||
fileStatusIcon()
|
||||
loadingIndicator()
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func fileStatusIcon() -> some View {
|
||||
@ViewBuilder private func loadingIndicator() -> some View {
|
||||
if let file = chatItem.file {
|
||||
switch file.fileStatus {
|
||||
case .sndStored:
|
||||
@@ -279,22 +279,7 @@ struct CIVideoView: View {
|
||||
}
|
||||
case .sndComplete: fileIcon("checkmark", 10, 13)
|
||||
case .sndCancelled: fileIcon("xmark", 10, 13)
|
||||
case let .sndError(sndFileError):
|
||||
fileIcon("xmark", 10, 13)
|
||||
.onTapGesture {
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("File error"),
|
||||
message: Text(sndFileError.errorInfo)
|
||||
))
|
||||
}
|
||||
case let .sndWarning(sndFileError):
|
||||
fileIcon("exclamationmark.triangle.fill", 10, 13)
|
||||
.onTapGesture {
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("Temporary file error"),
|
||||
message: Text(sndFileError.errorInfo)
|
||||
))
|
||||
}
|
||||
case .sndError: fileIcon("xmark", 10, 13)
|
||||
case .rcvInvitation: fileIcon("arrow.down", 10, 13)
|
||||
case .rcvAccepted: fileIcon("ellipsis", 14, 11)
|
||||
case let .rcvTransfer(rcvProgress, rcvTotal):
|
||||
@@ -304,25 +289,10 @@ struct CIVideoView: View {
|
||||
progressView()
|
||||
}
|
||||
case .rcvAborted: fileIcon("exclamationmark.arrow.circlepath", 14, 11)
|
||||
case .rcvComplete: EmptyView()
|
||||
case .rcvCancelled: fileIcon("xmark", 10, 13)
|
||||
case let .rcvError(rcvFileError):
|
||||
fileIcon("xmark", 10, 13)
|
||||
.onTapGesture {
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("File error"),
|
||||
message: Text(rcvFileError.errorInfo)
|
||||
))
|
||||
}
|
||||
case let .rcvWarning(rcvFileError):
|
||||
fileIcon("exclamationmark.triangle.fill", 10, 13)
|
||||
.onTapGesture {
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("Temporary file error"),
|
||||
message: Text(rcvFileError.errorInfo)
|
||||
))
|
||||
}
|
||||
case .rcvError: fileIcon("xmark", 10, 13)
|
||||
case .invalid: fileIcon("questionmark", 10, 13)
|
||||
default: EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import SimpleXChat
|
||||
|
||||
struct CIVoiceView: View {
|
||||
@ObservedObject var chat: Chat
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var chatItem: ChatItem
|
||||
let recordingFile: CIFile?
|
||||
let duration: Int
|
||||
@@ -93,7 +92,7 @@ struct CIVoiceView: View {
|
||||
}
|
||||
|
||||
private func metaView() -> some View {
|
||||
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary)
|
||||
CIMetaView(chat: chat, chatItem: chatItem)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +118,7 @@ struct VoiceMessagePlayerTime: View {
|
||||
|
||||
struct VoiceMessagePlayer: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
var chatItem: ChatItem
|
||||
var recordingFile: CIFile?
|
||||
var recordingTime: TimeInterval
|
||||
@@ -135,53 +134,18 @@ struct VoiceMessagePlayer: View {
|
||||
ZStack {
|
||||
if let recordingFile = recordingFile {
|
||||
switch recordingFile.fileStatus {
|
||||
case .sndStored:
|
||||
if recordingFile.fileProtocol == .local {
|
||||
playbackButton()
|
||||
} else {
|
||||
loadingIcon()
|
||||
}
|
||||
case .sndTransfer: loadingIcon()
|
||||
case .sndStored: playbackButton()
|
||||
case .sndTransfer: playbackButton()
|
||||
case .sndComplete: playbackButton()
|
||||
case .sndCancelled: playbackButton()
|
||||
case let .sndError(sndFileError):
|
||||
fileStatusIcon("multiply", 14)
|
||||
.onTapGesture {
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("File error"),
|
||||
message: Text(sndFileError.errorInfo)
|
||||
))
|
||||
}
|
||||
case let .sndWarning(sndFileError):
|
||||
fileStatusIcon("exclamationmark.triangle.fill", 16)
|
||||
.onTapGesture {
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("Temporary file error"),
|
||||
message: Text(sndFileError.errorInfo)
|
||||
))
|
||||
}
|
||||
case .sndError: playbackButton()
|
||||
case .rcvInvitation: downloadButton(recordingFile, "play.fill")
|
||||
case .rcvAccepted: loadingIcon()
|
||||
case .rcvTransfer: loadingIcon()
|
||||
case .rcvAborted: downloadButton(recordingFile, "exclamationmark.arrow.circlepath")
|
||||
case .rcvComplete: playbackButton()
|
||||
case .rcvCancelled: playPauseIcon("play.fill", Color(uiColor: .tertiaryLabel))
|
||||
case let .rcvError(rcvFileError):
|
||||
fileStatusIcon("multiply", 14)
|
||||
.onTapGesture {
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("File error"),
|
||||
message: Text(rcvFileError.errorInfo)
|
||||
))
|
||||
}
|
||||
case let .rcvWarning(rcvFileError):
|
||||
fileStatusIcon("exclamationmark.triangle.fill", 16)
|
||||
.onTapGesture {
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("Temporary file error"),
|
||||
message: Text(rcvFileError.errorInfo)
|
||||
))
|
||||
}
|
||||
case .rcvError: playPauseIcon("play.fill", Color(uiColor: .tertiaryLabel))
|
||||
case .invalid: playPauseIcon("play.fill", Color(uiColor: .tertiaryLabel))
|
||||
}
|
||||
} else {
|
||||
@@ -245,7 +209,7 @@ struct VoiceMessagePlayer: View {
|
||||
.foregroundColor(color)
|
||||
.padding(.leading, image == "play.fill" ? 4 : 0)
|
||||
.frame(width: 56, height: 56)
|
||||
.background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear)
|
||||
.background(showBackground ? chatItemFrameColor(chatItem, colorScheme) : .clear)
|
||||
.clipShape(Circle())
|
||||
if recordingTime > 0 {
|
||||
ProgressCircle(length: recordingTime, progress: $playbackTime)
|
||||
@@ -267,7 +231,6 @@ struct VoiceMessagePlayer: View {
|
||||
}
|
||||
|
||||
private struct ProgressCircle: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var length: TimeInterval
|
||||
@Binding var progress: TimeInterval?
|
||||
|
||||
@@ -275,7 +238,7 @@ struct VoiceMessagePlayer: View {
|
||||
Circle()
|
||||
.trim(from: 0, to: ((progress ?? TimeInterval(0)) / length))
|
||||
.stroke(
|
||||
theme.colors.primary,
|
||||
Color.accentColor,
|
||||
style: StrokeStyle(lineWidth: 3)
|
||||
)
|
||||
.rotationEffect(.degrees(-90))
|
||||
@@ -283,22 +246,11 @@ struct VoiceMessagePlayer: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func fileStatusIcon(_ image: String, _ size: CGFloat) -> some View {
|
||||
Image(systemName: image)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: size, height: size)
|
||||
.foregroundColor(Color(uiColor: .tertiaryLabel))
|
||||
.frame(width: 56, height: 56)
|
||||
.background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
|
||||
private func loadingIcon() -> some View {
|
||||
ProgressView()
|
||||
.frame(width: 30, height: 30)
|
||||
.frame(width: 56, height: 56)
|
||||
.background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear)
|
||||
.background(showBackground ? chatItemFrameColor(chatItem, colorScheme) : .clear)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct DeletedItemView: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@ObservedObject var chat: Chat
|
||||
var chatItem: ChatItem
|
||||
|
||||
@@ -19,12 +19,12 @@ struct DeletedItemView: View {
|
||||
Text(chatItem.content.text)
|
||||
.foregroundColor(.secondary)
|
||||
.italic()
|
||||
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary)
|
||||
CIMetaView(chat: chat, chatItem: chatItem)
|
||||
.padding(.horizontal, 12)
|
||||
}
|
||||
.padding(.leading, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(chatItemFrameColor(chatItem, theme))
|
||||
.background(chatItemFrameColor(chatItem, colorScheme))
|
||||
.cornerRadius(18)
|
||||
.textSelection(.disabled)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import SimpleXChat
|
||||
|
||||
struct EmojiItemView: View {
|
||||
@ObservedObject var chat: Chat
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var chatItem: ChatItem
|
||||
|
||||
var body: some View {
|
||||
@@ -19,7 +18,7 @@ struct EmojiItemView: View {
|
||||
emojiText(chatItem.content.text)
|
||||
.padding(.top, 8)
|
||||
.padding(.horizontal, 6)
|
||||
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary)
|
||||
CIMetaView(chat: chat, chatItem: chatItem)
|
||||
.padding(.bottom, 8)
|
||||
.padding(.horizontal, 12)
|
||||
}
|
||||
|
||||
@@ -9,9 +9,16 @@
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
let notesChatColorLight = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.21)
|
||||
let notesChatColorDark = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.19)
|
||||
let sentColorLight = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.12)
|
||||
let sentColorDark = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.17)
|
||||
private let sentQuoteColorLight = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.11)
|
||||
private let sentQuoteColorDark = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.09)
|
||||
|
||||
struct FramedItemView: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@ObservedObject var chat: Chat
|
||||
var chatItem: ChatItem
|
||||
@Binding var revealed: Bool
|
||||
@@ -76,10 +83,7 @@ struct FramedItemView: View {
|
||||
.accessibilityLabel("")
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
metaColor = metaColor == .secondary ? theme.colors.secondary : metaColor
|
||||
}
|
||||
.background(chatItemFrameColorMaybeImageOrVideo(chatItem, theme))
|
||||
.background(chatItemFrameColorMaybeImageOrVideo(chatItem, colorScheme))
|
||||
.cornerRadius(18)
|
||||
.onPreferenceChange(DetermineWidth.Key.self) { msgWidth = $0 }
|
||||
|
||||
@@ -171,13 +175,13 @@ struct FramedItemView: View {
|
||||
.font(.caption)
|
||||
.lineLimit(1)
|
||||
}
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
.foregroundColor(.secondary)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.top, 6)
|
||||
.padding(.bottom, pad || (chatItem.quotedItem == nil && chatItem.meta.itemForwarded == nil) ? 6 : 0)
|
||||
.overlay(DetermineWidth())
|
||||
.frame(minWidth: msgWidth, alignment: .leading)
|
||||
.background(chatItemFrameContextColor(chatItem, theme))
|
||||
.background(chatItemFrameContextColor(chatItem, colorScheme))
|
||||
if let mediaWidth = maxMediaWidth(), mediaWidth < maxWidth {
|
||||
v.frame(maxWidth: mediaWidth, alignment: .leading)
|
||||
} else {
|
||||
@@ -229,7 +233,7 @@ struct FramedItemView: View {
|
||||
// if enable this always, size of the framed voice message item will be incorrect after end of playback
|
||||
.overlay { if case .voice = chatItem.content.msgContent {} else { DetermineWidth() } }
|
||||
.frame(minWidth: msgWidth, alignment: .leading)
|
||||
.background(chatItemFrameContextColor(chatItem, theme))
|
||||
.background(chatItemFrameContextColor(chatItem, colorScheme))
|
||||
|
||||
if let mediaWidth = maxMediaWidth(), mediaWidth < maxWidth {
|
||||
v.frame(maxWidth: mediaWidth, alignment: .leading)
|
||||
@@ -244,7 +248,7 @@ struct FramedItemView: View {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(sender)
|
||||
.font(.caption)
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
.foregroundColor(.secondary)
|
||||
.lineLimit(1)
|
||||
ciQuotedMsgTextView(qi, lines: 2)
|
||||
}
|
||||
@@ -359,22 +363,22 @@ func onlyImageOrVideo(_ ci: ChatItem) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func chatItemFrameColorMaybeImageOrVideo(_ ci: ChatItem, _ theme: AppTheme) -> Color {
|
||||
func chatItemFrameColorMaybeImageOrVideo(_ ci: ChatItem, _ colorScheme: ColorScheme) -> Color {
|
||||
onlyImageOrVideo(ci)
|
||||
? Color.clear
|
||||
: chatItemFrameColor(ci, theme)
|
||||
: chatItemFrameColor(ci, colorScheme)
|
||||
}
|
||||
|
||||
func chatItemFrameColor(_ ci: ChatItem, _ theme: AppTheme) -> Color {
|
||||
func chatItemFrameColor(_ ci: ChatItem, _ colorScheme: ColorScheme) -> Color {
|
||||
ci.chatDir.sent
|
||||
? theme.appColors.sentMessage
|
||||
: theme.appColors.receivedMessage
|
||||
? (colorScheme == .light ? sentColorLight : sentColorDark)
|
||||
: Color(uiColor: .tertiarySystemGroupedBackground)
|
||||
}
|
||||
|
||||
func chatItemFrameContextColor(_ ci: ChatItem, _ theme: AppTheme) -> Color {
|
||||
func chatItemFrameContextColor(_ ci: ChatItem, _ colorScheme: ColorScheme) -> Color {
|
||||
ci.chatDir.sent
|
||||
? theme.appColors.sentQuote
|
||||
: theme.appColors.receivedQuote
|
||||
? (colorScheme == .light ? sentQuoteColorLight : sentQuoteColorDark)
|
||||
: Color(uiColor: .quaternarySystemFill)
|
||||
}
|
||||
|
||||
struct FramedItemView_Previews: PreviewProvider {
|
||||
|
||||
@@ -11,7 +11,6 @@ import SimpleXChat
|
||||
|
||||
struct IntegrityErrorItemView: View {
|
||||
@ObservedObject var chat: Chat
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var msgError: MsgErrorType
|
||||
var chatItem: ChatItem
|
||||
|
||||
@@ -55,7 +54,6 @@ struct IntegrityErrorItemView: View {
|
||||
|
||||
struct CIMsgError: View {
|
||||
@ObservedObject var chat: Chat
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var chatItem: ChatItem
|
||||
var onTap: () -> Void
|
||||
|
||||
@@ -64,7 +62,7 @@ struct CIMsgError: View {
|
||||
Text(chatItem.content.text)
|
||||
.foregroundColor(.red)
|
||||
.italic()
|
||||
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary)
|
||||
CIMetaView(chat: chat, chatItem: chatItem)
|
||||
.padding(.horizontal, 12)
|
||||
}
|
||||
.padding(.leading, 12)
|
||||
|
||||
@@ -11,7 +11,7 @@ import SimpleXChat
|
||||
|
||||
struct MarkedDeletedItemView: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@ObservedObject var chat: Chat
|
||||
var chatItem: ChatItem
|
||||
@Binding var revealed: Bool
|
||||
@@ -22,7 +22,7 @@ struct MarkedDeletedItemView: View {
|
||||
.foregroundColor(.secondary)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(chatItemFrameColor(chatItem, theme))
|
||||
.background(chatItemFrameColor(chatItem, colorScheme))
|
||||
.cornerRadius(18)
|
||||
.textSelection(.disabled)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ struct ChatItemForwardingView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
@ViewBuilder private func forwardListView() -> some View {
|
||||
@@ -60,13 +59,9 @@ struct ChatItemForwardingView: View {
|
||||
.cornerRadius(12)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
.background(Color(.systemGroupedBackground))
|
||||
} else {
|
||||
ZStack {
|
||||
emptyList()
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.modifier(ThemedBackground())
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,11 @@ import SimpleXChat
|
||||
struct ChatItemInfoView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
var ci: ChatItem
|
||||
@Binding var chatItemInfo: ChatItemInfo?
|
||||
@State private var selection: CIInfoTab = .history
|
||||
@State private var alert: CIInfoViewAlert? = nil
|
||||
@State private var messageStatusLimited: Bool = true
|
||||
@State private var fileStatusLimited: Bool = true
|
||||
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
|
||||
|
||||
enum CIInfoTab {
|
||||
@@ -101,14 +99,12 @@ struct ChatItemInfoView: View {
|
||||
Label("History", systemImage: "clock")
|
||||
}
|
||||
.tag(CIInfoTab.history)
|
||||
.modifier(ThemedBackground())
|
||||
if let qi = ci.quotedItem {
|
||||
quoteTab(qi)
|
||||
.tabItem {
|
||||
Label("In reply to", systemImage: "arrowshape.turn.up.left")
|
||||
}
|
||||
.tag(CIInfoTab.quote)
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
if let forwardedFromItem = chatItemInfo?.forwardedFromChatItem {
|
||||
forwardedFromTab(forwardedFromItem)
|
||||
@@ -116,7 +112,6 @@ struct ChatItemInfoView: View {
|
||||
Label(local ? "Saved" : "Forwarded", systemImage: "arrowshape.turn.up.forward")
|
||||
}
|
||||
.tag(CIInfoTab.forwarded)
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
@@ -126,7 +121,6 @@ struct ChatItemInfoView: View {
|
||||
}
|
||||
} else {
|
||||
historyTab()
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,35 +157,6 @@ struct ChatItemInfoView: View {
|
||||
if developerTools {
|
||||
infoRow("Database ID", "\(meta.itemId)")
|
||||
infoRow("Record updated at", localTimestamp(meta.updatedAt))
|
||||
let msv = infoRow("Message status", ci.meta.itemStatus.id)
|
||||
Group {
|
||||
if messageStatusLimited {
|
||||
msv.lineLimit(1)
|
||||
} else {
|
||||
msv
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
messageStatusLimited.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
if let file = ci.file {
|
||||
let fsv = infoRow("File status", file.fileStatus.id)
|
||||
Group {
|
||||
if fileStatusLimited {
|
||||
fsv.lineLimit(1)
|
||||
} else {
|
||||
fsv
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
fileStatusLimited.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,7 +196,7 @@ struct ChatItemInfoView: View {
|
||||
textBubble(itemVersion.msgContent.text, itemVersion.formattedText, nil)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(chatItemFrameColor(ci, theme))
|
||||
.background(chatItemFrameColor(ci, colorScheme))
|
||||
.cornerRadius(18)
|
||||
.contextMenu {
|
||||
if itemVersion.msgContent.text != "" {
|
||||
@@ -300,7 +265,7 @@ struct ChatItemInfoView: View {
|
||||
textBubble(qi.text, qi.formattedText, qi.getSender(nil))
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(quotedMsgFrameColor(qi, theme))
|
||||
.background(quotedMsgFrameColor(qi, colorScheme))
|
||||
.cornerRadius(18)
|
||||
.contextMenu {
|
||||
if qi.text != "" {
|
||||
@@ -324,10 +289,10 @@ struct ChatItemInfoView: View {
|
||||
.frame(maxWidth: maxWidth, alignment: .leading)
|
||||
}
|
||||
|
||||
func quotedMsgFrameColor(_ qi: CIQuote, _ theme: AppTheme) -> Color {
|
||||
func quotedMsgFrameColor(_ qi: CIQuote, _ colorScheme: ColorScheme) -> Color {
|
||||
(qi.chatDir?.sent ?? false)
|
||||
? theme.appColors.sentMessage
|
||||
: theme.appColors.receivedMessage
|
||||
? (colorScheme == .light ? sentColorLight : sentColorDark)
|
||||
: Color(uiColor: .tertiarySystemGroupedBackground)
|
||||
}
|
||||
|
||||
@ViewBuilder private func forwardedFromTab(_ forwardedFromItem: AChatItem) -> some View {
|
||||
@@ -449,7 +414,7 @@ struct ChatItemInfoView: View {
|
||||
.foregroundColor(.secondary).opacity(0.67)
|
||||
}
|
||||
let v = Group {
|
||||
if let (icon, statusColor) = status.statusIcon(theme.colors.secondary, theme.colors.primary) {
|
||||
if let (icon, statusColor) = status.statusIcon(Color.secondary) {
|
||||
switch status {
|
||||
case .sndRcvd:
|
||||
ZStack(alignment: .trailing) {
|
||||
@@ -508,12 +473,8 @@ struct ChatItemInfoView: View {
|
||||
if developerTools {
|
||||
shareText += [
|
||||
String.localizedStringWithFormat(NSLocalizedString("Database ID: %d", comment: "copied message info"), meta.itemId),
|
||||
String.localizedStringWithFormat(NSLocalizedString("Record updated at: %@", comment: "copied message info"), localTimestamp(meta.updatedAt)),
|
||||
String.localizedStringWithFormat(NSLocalizedString("Message status: %@", comment: "copied message info"), meta.itemStatus.id)
|
||||
String.localizedStringWithFormat(NSLocalizedString("Record updated at: %@", comment: "copied message info"), localTimestamp(meta.updatedAt))
|
||||
]
|
||||
if let file = ci.file {
|
||||
shareText += [String.localizedStringWithFormat(NSLocalizedString("File status: %@", comment: "copied message info"), file.fileStatus.id)]
|
||||
}
|
||||
}
|
||||
if let qi = ci.quotedItem {
|
||||
shareText += ["", NSLocalizedString("## In reply to", comment: "copied message info")]
|
||||
|
||||
@@ -14,7 +14,7 @@ private let memberImageSize: CGFloat = 34
|
||||
|
||||
struct ChatView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@State var theme: AppTheme = buildTheme()
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
@Environment(\.scenePhase) var scenePhase
|
||||
@@ -60,16 +60,7 @@ struct ChatView: View {
|
||||
Divider()
|
||||
}
|
||||
ZStack(alignment: .trailing) {
|
||||
let wallpaperImage = theme.wallpaper.type.image
|
||||
let wallpaperType = theme.wallpaper.type
|
||||
let backgroundColor = theme.wallpaper.background ?? wallpaperType.defaultBackgroundColor(theme.base, theme.colors.background)
|
||||
let tintColor = theme.wallpaper.tint ?? wallpaperType.defaultTintColor(theme.base)
|
||||
chatItemsList()
|
||||
.if(wallpaperImage != nil) { view in
|
||||
view.modifier(
|
||||
ChatViewBackground(image: wallpaperImage!, imageType: wallpaperType, background: backgroundColor, tint: tintColor)
|
||||
)
|
||||
}
|
||||
if let proxy = scrollProxy {
|
||||
floatingButtons(proxy)
|
||||
}
|
||||
@@ -87,9 +78,7 @@ struct ChatView: View {
|
||||
}
|
||||
.padding(.top, 1)
|
||||
.navigationTitle(cInfo.chatViewName)
|
||||
.background(theme.colors.background)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.environmentObject(theme)
|
||||
.onAppear {
|
||||
initChatView()
|
||||
}
|
||||
@@ -100,7 +89,6 @@ struct ChatView: View {
|
||||
chat = c
|
||||
}
|
||||
initChatView()
|
||||
theme = buildTheme()
|
||||
} else {
|
||||
dismiss()
|
||||
}
|
||||
@@ -396,7 +384,7 @@ struct ChatView: View {
|
||||
circleButton {
|
||||
unreadCountText(unreadAbove)
|
||||
.font(.callout)
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
.onTapGesture { scrollUp(proxy) }
|
||||
.contextMenu {
|
||||
@@ -416,13 +404,13 @@ struct ChatView: View {
|
||||
circleButton {
|
||||
unreadCountText(counts.unreadBelow)
|
||||
.font(.callout)
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
.onTapGesture { scrollToBottom(proxy) }
|
||||
} else if counts.totalBelow > 16 {
|
||||
circleButton {
|
||||
Image(systemName: "chevron.down")
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
.onTapGesture { scrollToBottom(proxy) }
|
||||
}
|
||||
@@ -525,7 +513,7 @@ struct ChatView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ViewBuilder private func chatItemView(_ ci: ChatItem, _ maxWidth: CGFloat) -> some View {
|
||||
ChatItemWithMenu(
|
||||
chat: chat,
|
||||
@@ -540,7 +528,7 @@ struct ChatView: View {
|
||||
|
||||
private struct ChatItemWithMenu: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@ObservedObject var chat: Chat
|
||||
var chatItem: ChatItem
|
||||
var maxWidth: CGFloat
|
||||
@@ -610,7 +598,7 @@ struct ChatView: View {
|
||||
.padding(.top, 7)
|
||||
}
|
||||
HStack(alignment: .top, spacing: 8) {
|
||||
ProfileImage(imageStr: member.memberProfile.image, size: memberImageSize, backgroundColor: theme.colors.background)
|
||||
ProfileImage(imageStr: member.memberProfile.image, size: memberImageSize)
|
||||
.onTapGesture {
|
||||
if chatView.membersLoaded {
|
||||
selectedMember = m.getGroupMember(member.groupMemberId)
|
||||
@@ -674,8 +662,7 @@ struct ChatView: View {
|
||||
playbackState: $playbackState,
|
||||
playbackTime: $playbackTime
|
||||
)
|
||||
.environmentObject(theme)
|
||||
.uiKitContextMenu(hasImageOrVideo: ci.content.msgContent?.isImageOrVideo == true, maxWidth: maxWidth, itemWidth: $itemWidth, menu: uiMenu, allowMenu: $allowMenu, backgroundColor: theme.colors.background)
|
||||
.uiKitContextMenu(hasImageOrVideo: ci.content.msgContent?.isImageOrVideo == true, maxWidth: maxWidth, itemWidth: $itemWidth, menu: uiMenu, allowMenu: $allowMenu)
|
||||
.accessibilityLabel("")
|
||||
if ci.content.msgContent != nil && (ci.meta.itemDeleted == nil || revealed) && ci.reactions.count > 0 {
|
||||
chatItemReactions(ci)
|
||||
@@ -742,7 +729,7 @@ struct ChatView: View {
|
||||
Text("\(r.totalReacted)")
|
||||
.font(.caption)
|
||||
.fontWeight(r.userReacted ? .bold : .light)
|
||||
.foregroundColor(r.userReacted ? theme.colors.primary : theme.colors.secondary)
|
||||
.foregroundColor(r.userReacted ? .accentColor : .secondary)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 6)
|
||||
@@ -1232,27 +1219,6 @@ struct ChatView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func buildTheme() -> AppTheme {
|
||||
if let cId = ChatModel.shared.chatId, let chat = ChatModel.shared.getChat(cId) {
|
||||
let perChatTheme = if case let .direct(contact) = chat.chatInfo {
|
||||
contact.uiThemes?.preferredMode(!AppTheme.shared.colors.isLight)
|
||||
} else if case let .group(groupInfo) = chat.chatInfo {
|
||||
groupInfo.uiThemes?.preferredMode(!AppTheme.shared.colors.isLight)
|
||||
} else {
|
||||
nil as ThemeModeOverride?
|
||||
}
|
||||
let overrides = if perChatTheme != nil {
|
||||
ThemeManager.currentColors(nil, perChatTheme, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get())
|
||||
} else {
|
||||
nil as ThemeManager.ActiveTheme?
|
||||
}
|
||||
let theme = overrides ?? CurrentColors
|
||||
return AppTheme(name: theme.name, base: theme.base, colors: theme.colors, appColors: theme.appColors, wallpaper: theme.wallpaper)
|
||||
} else {
|
||||
return AppTheme.shared
|
||||
}
|
||||
}
|
||||
|
||||
struct ToggleNtfsButton: View {
|
||||
@ObservedObject var chat: Chat
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ComposeFileView: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
let fileName: String
|
||||
let cancelFile: (() -> Void)
|
||||
let cancelEnabled: Bool
|
||||
@@ -33,7 +33,7 @@ struct ComposeFileView: View {
|
||||
.padding(.vertical, 1)
|
||||
.padding(.trailing, 12)
|
||||
.frame(height: 50)
|
||||
.background(theme.appColors.sentMessage)
|
||||
.background(colorScheme == .light ? sentColorLight : sentColorDark)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.top, 8)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct ComposeImageView: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
let images: [String]
|
||||
let cancelImage: (() -> Void)
|
||||
let cancelEnabled: Bool
|
||||
@@ -48,7 +48,7 @@ struct ComposeImageView: View {
|
||||
}
|
||||
.padding(.vertical, 1)
|
||||
.padding(.trailing, 12)
|
||||
.background(theme.appColors.sentMessage)
|
||||
.background(colorScheme == .light ? sentColorLight : sentColorDark)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.top, 8)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func getLinkPreview(url: URL, cb: @escaping (LinkPreview?) -> Void) {
|
||||
}
|
||||
|
||||
struct ComposeLinkView: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
let linkPreview: LinkPreview?
|
||||
var cancelPreview: (() -> Void)? = nil
|
||||
let cancelEnabled: Bool
|
||||
@@ -62,7 +62,7 @@ struct ComposeLinkView: View {
|
||||
}
|
||||
.padding(.vertical, 1)
|
||||
.padding(.trailing, 12)
|
||||
.background(theme.appColors.sentMessage)
|
||||
.background(colorScheme == .light ? sentColorLight : sentColorDark)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.top, 8)
|
||||
}
|
||||
|
||||
@@ -254,7 +254,6 @@ enum UploadContent: Equatable {
|
||||
|
||||
struct ComposeView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@ObservedObject var chat: Chat
|
||||
@Binding var composeState: ComposeState
|
||||
@Binding var keyboardVisible: Bool
|
||||
@@ -354,10 +353,10 @@ struct ComposeView: View {
|
||||
keyboardVisible: $keyboardVisible,
|
||||
sendButtonColor: chat.chatInfo.incognito
|
||||
? .indigo.opacity(colorScheme == .dark ? 1 : 0.7)
|
||||
: theme.colors.primary
|
||||
: .accentColor
|
||||
)
|
||||
.padding(.trailing, 12)
|
||||
.background(theme.colors.background)
|
||||
.background(.background)
|
||||
.disabled(!chat.userCanSend)
|
||||
|
||||
if chat.userIsObserver {
|
||||
|
||||
@@ -25,7 +25,7 @@ func voiceMessageTime_(_ time: TimeInterval?) -> String {
|
||||
|
||||
struct ComposeVoiceView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
var recordingFileName: String
|
||||
@Binding var recordingTime: TimeInterval?
|
||||
@Binding var recordingState: VoiceMessageRecordingState
|
||||
@@ -50,7 +50,7 @@ struct ComposeVoiceView: View {
|
||||
}
|
||||
.padding(.vertical, 1)
|
||||
.frame(height: ComposeVoiceView.previewHeight)
|
||||
.background(theme.appColors.sentMessage)
|
||||
.background(colorScheme == .light ? sentColorLight : sentColorDark)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.top, 8)
|
||||
}
|
||||
@@ -80,7 +80,7 @@ struct ComposeVoiceView: View {
|
||||
Button {
|
||||
startPlayback()
|
||||
} label: {
|
||||
playPauseIcon("play.fill", theme.colors.primary)
|
||||
playPauseIcon("play.fill")
|
||||
}
|
||||
Text(voiceMessageTime_(recordingTime))
|
||||
case .playing:
|
||||
@@ -88,7 +88,7 @@ struct ComposeVoiceView: View {
|
||||
audioPlayer?.pause()
|
||||
playbackState = .paused
|
||||
} label: {
|
||||
playPauseIcon("pause.fill", theme.colors.primary)
|
||||
playPauseIcon("pause.fill")
|
||||
}
|
||||
Text(voiceMessageTime_(playbackTime))
|
||||
case .paused:
|
||||
@@ -96,7 +96,7 @@ struct ComposeVoiceView: View {
|
||||
audioPlayer?.play()
|
||||
playbackState = .playing
|
||||
} label: {
|
||||
playPauseIcon("play.fill", theme.colors.primary)
|
||||
playPauseIcon("play.fill")
|
||||
}
|
||||
Text(voiceMessageTime_(playbackTime))
|
||||
}
|
||||
@@ -131,7 +131,7 @@ struct ComposeVoiceView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func playPauseIcon(_ image: String, _ color: Color) -> some View {
|
||||
private func playPauseIcon(_ image: String, _ color: Color = .accentColor) -> some View {
|
||||
Image(systemName: image)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
@@ -162,7 +162,6 @@ struct ComposeVoiceView: View {
|
||||
}
|
||||
|
||||
private struct ProgressBar: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var length: TimeInterval
|
||||
@Binding var progress: TimeInterval?
|
||||
|
||||
@@ -170,7 +169,7 @@ struct ComposeVoiceView: View {
|
||||
GeometryReader { geometry in
|
||||
ZStack {
|
||||
Rectangle()
|
||||
.fill(theme.colors.primary)
|
||||
.fill(Color.accentColor)
|
||||
.frame(width: min(CGFloat((progress ?? TimeInterval(0)) / length) * geometry.size.width, geometry.size.width), height: 4)
|
||||
.animation(.linear, value: progress)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ContextInvitingContactMemberView: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
@@ -20,7 +20,7 @@ struct ContextInvitingContactMemberView: View {
|
||||
.padding(12)
|
||||
.frame(minHeight: 50)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(theme.appColors.sentMessage)
|
||||
.background(colorScheme == .light ? sentColorLight : sentColorDark)
|
||||
.padding(.top, 8)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct ContextItemView: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@ObservedObject var chat: Chat
|
||||
let contextItem: ChatItem
|
||||
let contextIcon: String
|
||||
@@ -44,7 +44,7 @@ struct ContextItemView: View {
|
||||
.padding(12)
|
||||
.frame(minHeight: 50)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(chatItemFrameColor(contextItem, theme))
|
||||
.background(chatItemFrameColor(contextItem, colorScheme))
|
||||
.padding(.top, 8)
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ struct NativeTextEditor: UIViewRepresentable {
|
||||
|
||||
func makeUIView(context: Context) -> UITextView {
|
||||
let field = CustomUITextField(height: _height)
|
||||
field.backgroundColor = .clear
|
||||
field.text = text
|
||||
field.textAlignment = alignment(text)
|
||||
field.autocapitalizationType = .sentences
|
||||
|
||||
@@ -13,7 +13,6 @@ private let liveMsgInterval: UInt64 = 3000_000000
|
||||
|
||||
struct SendMessageView: View {
|
||||
@Binding var composeState: ComposeState
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var sendMessage: (Int?) -> Void
|
||||
var sendLiveMessage: (() async -> Void)? = nil
|
||||
var updateLiveMessage: (() async -> Void)? = nil
|
||||
@@ -248,7 +247,6 @@ struct SendMessageView: View {
|
||||
}
|
||||
|
||||
private struct RecordVoiceMessageButton: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var startVoiceMessageRecording: (() -> Void)?
|
||||
var finishVoiceMessageRecording: (() -> Void)?
|
||||
@Binding var holdingVMR: Bool
|
||||
@@ -258,7 +256,7 @@ struct SendMessageView: View {
|
||||
var body: some View {
|
||||
Button(action: {}) {
|
||||
Image(systemName: "mic.fill")
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
.disabled(disabled)
|
||||
.frame(width: 29, height: 29)
|
||||
@@ -325,7 +323,7 @@ struct SendMessageView: View {
|
||||
Image(systemName: "multiply")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
.frame(width: 15, height: 15)
|
||||
}
|
||||
.frame(width: 29, height: 29)
|
||||
@@ -342,7 +340,7 @@ struct SendMessageView: View {
|
||||
Image(systemName: "bolt.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
.frame(width: 20, height: 20)
|
||||
}
|
||||
.frame(width: 29, height: 29)
|
||||
@@ -385,7 +383,7 @@ struct SendMessageView: View {
|
||||
}
|
||||
Task {
|
||||
_ = try? await Task.sleep(nanoseconds: liveMsgInterval)
|
||||
while await composeState.liveMessage != nil {
|
||||
while composeState.liveMessage != nil {
|
||||
await update()
|
||||
_ = try? await Task.sleep(nanoseconds: liveMsgInterval)
|
||||
}
|
||||
@@ -396,7 +394,7 @@ struct SendMessageView: View {
|
||||
private func finishVoiceMessageRecordingButton() -> some View {
|
||||
Button(action: { finishVoiceMessageRecording?() }) {
|
||||
Image(systemName: "stop.fill")
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
.disabled(composeState.inProgress)
|
||||
.frame(width: 29, height: 29)
|
||||
|
||||
@@ -21,7 +21,6 @@ struct AddGroupMembersView: View {
|
||||
|
||||
struct AddGroupMembersViewCommon: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var chat: Chat
|
||||
@State var groupInfo: GroupInfo
|
||||
var creatingGroup: Bool = false
|
||||
@@ -126,7 +125,6 @@ struct AddGroupMembersViewCommon: View {
|
||||
.onChange(of: selectedContacts) { _ in
|
||||
searchFocussed = false
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
private func inviteMembersButton() -> some View {
|
||||
@@ -178,7 +176,7 @@ struct AddGroupMembersViewCommon: View {
|
||||
} else {
|
||||
if checked {
|
||||
icon = "checkmark.circle.fill"
|
||||
iconColor = theme.colors.primary
|
||||
iconColor = .accentColor
|
||||
} else {
|
||||
icon = "circle"
|
||||
iconColor = Color(uiColor: .tertiaryLabel)
|
||||
|
||||
@@ -135,7 +135,6 @@ struct GroupChatInfoView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarHidden(true)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
||||
@@ -211,7 +210,6 @@ struct GroupChatInfoView: View {
|
||||
private struct MemberRowView: View {
|
||||
var groupInfo: GroupInfo
|
||||
@ObservedObject var groupMember: GMember
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var user: Bool = false
|
||||
@Binding var alert: GroupChatInfoViewAlert?
|
||||
|
||||
@@ -284,7 +282,7 @@ struct GroupChatInfoView: View {
|
||||
Button {
|
||||
alert = .unblockMemberAlert(mem: member)
|
||||
} label: {
|
||||
Label("Unblock member", systemImage: "hand.raised.slash").foregroundColor(theme.colors.primary)
|
||||
Label("Unblock member", systemImage: "hand.raised.slash").foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -296,7 +294,7 @@ struct GroupChatInfoView: View {
|
||||
Button {
|
||||
alert = .unblockForAllAlert(mem: member)
|
||||
} label: {
|
||||
Label("Unblock for all", systemImage: "hand.raised.slash").foregroundColor(theme.colors.primary)
|
||||
Label("Unblock for all", systemImage: "hand.raised.slash").foregroundColor(.accentColor)
|
||||
}
|
||||
} else {
|
||||
Button {
|
||||
@@ -335,7 +333,6 @@ struct GroupChatInfoView: View {
|
||||
creatingGroup: false
|
||||
)
|
||||
.navigationBarTitle("Group link")
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
if groupLink == nil {
|
||||
@@ -353,7 +350,6 @@ struct GroupChatInfoView: View {
|
||||
groupProfile: groupInfo.groupProfile
|
||||
)
|
||||
.navigationBarTitle("Group profile")
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
Label("Edit group profile", systemImage: "pencil")
|
||||
@@ -368,7 +364,6 @@ struct GroupChatInfoView: View {
|
||||
welcomeText: groupInfo.groupProfile.description ?? ""
|
||||
)
|
||||
.navigationTitle("Welcome message")
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
groupInfo.groupProfile.description == nil
|
||||
@@ -523,7 +518,6 @@ func groupPreferencesButton(_ groupInfo: Binding<GroupInfo>, _ creatingGroup: Bo
|
||||
creatingGroup: creatingGroup
|
||||
)
|
||||
.navigationBarTitle("Group preferences")
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
if creatingGroup {
|
||||
|
||||
@@ -133,7 +133,6 @@ struct GroupLinkView: View {
|
||||
shouldCreate = false
|
||||
}
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
private func createGroupLink() {
|
||||
|
||||
@@ -247,7 +247,6 @@ struct GroupMemberInfoView: View {
|
||||
ProgressView().scaleEffect(2)
|
||||
}
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
func connectViaAddressButton(_ contactLink: String) -> some View {
|
||||
@@ -375,7 +374,6 @@ struct GroupMemberInfoView: View {
|
||||
)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationTitle("Security code")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
Label(
|
||||
member.verified ? "View security code" : "Verify security code",
|
||||
|
||||
@@ -66,7 +66,6 @@ struct VerifyCodeView: View {
|
||||
ScanCodeView(connectionVerified: $connectionVerified, verify: verify)
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
.navigationTitle("Scan code")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
Label("Scan code", systemImage: "qrcode")
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ private let rowHeights: [DynamicTypeSize: CGFloat] = [
|
||||
|
||||
struct ChatListNavLink: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.dynamicTypeSize) private var dynamicTypeSize
|
||||
@ObservedObject var chat: Chat
|
||||
@State private var showContactRequestDialog = false
|
||||
@@ -225,7 +224,7 @@ struct ChatListNavLink: View {
|
||||
} label: {
|
||||
Label("Join", systemImage: chat.chatInfo.incognito ? "theatermasks" : "ipad.and.arrow.forward")
|
||||
}
|
||||
.tint(chat.chatInfo.incognito ? .indigo : theme.colors.primary)
|
||||
.tint(chat.chatInfo.incognito ? .indigo : .accentColor)
|
||||
}
|
||||
|
||||
@ViewBuilder private func markReadButton() -> some View {
|
||||
@@ -235,14 +234,14 @@ struct ChatListNavLink: View {
|
||||
} label: {
|
||||
Label("Read", systemImage: "checkmark")
|
||||
}
|
||||
.tint(theme.colors.primary)
|
||||
.tint(Color.accentColor)
|
||||
} else {
|
||||
Button {
|
||||
Task { await markChatUnread(chat) }
|
||||
} label: {
|
||||
Label("Unread", systemImage: "circlebadge.fill")
|
||||
}
|
||||
.tint(theme.colors.primary)
|
||||
.tint(Color.accentColor)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -307,7 +306,7 @@ struct ChatListNavLink: View {
|
||||
Button {
|
||||
Task { await acceptContactRequest(incognito: false, contactRequest: contactRequest) }
|
||||
} label: { Label("Accept", systemImage: "checkmark") }
|
||||
.tint(theme.colors.primary)
|
||||
.tint(.accentColor)
|
||||
Button {
|
||||
Task { await acceptContactRequest(incognito: true, contactRequest: contactRequest) }
|
||||
} label: {
|
||||
@@ -347,7 +346,7 @@ struct ChatListNavLink: View {
|
||||
} label: {
|
||||
Label("Name", systemImage: "pencil")
|
||||
}
|
||||
.tint(theme.colors.primary)
|
||||
.tint(.accentColor)
|
||||
}
|
||||
.frame(height: rowHeights[dynamicTypeSize])
|
||||
.appSheet(isPresented: $showContactConnectionInfo) {
|
||||
@@ -355,7 +354,6 @@ struct ChatListNavLink: View {
|
||||
if case let .contactConnection(contactConnection) = chat.chatInfo {
|
||||
ContactConnectionInfo(contactConnection: contactConnection)
|
||||
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil)
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import SimpleXChat
|
||||
|
||||
struct ChatListView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Binding var showSettings: Bool
|
||||
@State private var searchMode = false
|
||||
@FocusState private var searchFocussed
|
||||
@@ -87,7 +86,6 @@ struct ChatListView: View {
|
||||
))
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.background(theme.colors.background)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarHidden(searchMode)
|
||||
.toolbar {
|
||||
@@ -138,7 +136,7 @@ struct ChatListView: View {
|
||||
showUnreadAndFavorites = !showUnreadAndFavorites
|
||||
} label: {
|
||||
Image(systemName: "line.3.horizontal.decrease.circle" + (showUnreadAndFavorites ? ".fill" : ""))
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,14 +154,12 @@ struct ChatListView: View {
|
||||
searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink
|
||||
)
|
||||
.listRowSeparator(.hidden)
|
||||
.listRowBackground(Color.clear)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
ForEach(cs, id: \.viewId) { chat in
|
||||
ChatListNavLink(chat: chat)
|
||||
.padding(.trailing, -16)
|
||||
.disabled(chatModel.chatRunning != true || chatModel.deletedChats.contains(chat.chatInfo.id))
|
||||
.listRowBackground(Color.clear)
|
||||
}
|
||||
.offset(x: -8)
|
||||
}
|
||||
@@ -183,7 +179,7 @@ struct ChatListView: View {
|
||||
private func unreadBadge(_ text: Text? = Text(" "), size: CGFloat = 18) -> some View {
|
||||
Circle()
|
||||
.frame(width: size, height: size)
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
|
||||
private func onboardingButtons() -> some View {
|
||||
@@ -194,7 +190,7 @@ struct ChatListView: View {
|
||||
p.addLine(to: CGPoint(x: 0, y: 10))
|
||||
p.addLine(to: CGPoint(x: 8, y: 0))
|
||||
}
|
||||
.fill(theme.colors.primary)
|
||||
.fill(Color.accentColor)
|
||||
.frame(width: 20, height: 10)
|
||||
.padding(.trailing, 12)
|
||||
|
||||
@@ -217,7 +213,7 @@ struct ChatListView: View {
|
||||
.padding(.vertical, 10)
|
||||
.padding(.horizontal, 20)
|
||||
}
|
||||
.background(theme.colors.primary)
|
||||
.background(Color.accentColor)
|
||||
.foregroundColor(.white)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||
}
|
||||
@@ -280,7 +276,6 @@ struct ChatListView: View {
|
||||
|
||||
struct ChatListSearchBar: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Binding var searchMode: Bool
|
||||
@FocusState.Binding var searchFocussed: Bool
|
||||
@Binding var searchText: String
|
||||
@@ -335,7 +330,7 @@ struct ChatListSearchBar: View {
|
||||
|
||||
if searchFocussed {
|
||||
Text("Cancel")
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
.onTapGesture {
|
||||
searchText = ""
|
||||
searchFocussed = false
|
||||
|
||||
@@ -11,7 +11,6 @@ import SimpleXChat
|
||||
|
||||
struct ChatPreviewView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@ObservedObject var chat: Chat
|
||||
@Binding var progressByTimeout: Bool
|
||||
@State var deleting: Bool = false
|
||||
@@ -95,7 +94,7 @@ struct ChatPreviewView: View {
|
||||
case let .group(groupInfo):
|
||||
let v = previewTitle(t)
|
||||
switch (groupInfo.membership.memberStatus) {
|
||||
case .memInvited: v.foregroundColor(deleting ? .secondary : chat.chatInfo.incognito ? .indigo : theme.colors.primary)
|
||||
case .memInvited: v.foregroundColor(deleting ? .secondary : chat.chatInfo.incognito ? .indigo : .accentColor)
|
||||
case .memAccepted: v.foregroundColor(.secondary)
|
||||
default: if deleting { v.foregroundColor(.secondary) } else { v }
|
||||
}
|
||||
@@ -134,7 +133,7 @@ struct ChatPreviewView: View {
|
||||
.foregroundColor(.white)
|
||||
.padding(.horizontal, 4)
|
||||
.frame(minWidth: 18, minHeight: 18)
|
||||
.background(chat.chatInfo.ntfsEnabled || chat.chatInfo.chatType == .local ? theme.colors.primary : theme.colors.secondary)
|
||||
.background(chat.chatInfo.ntfsEnabled || chat.chatInfo.chatType == .local ? Color.accentColor : Color.secondary)
|
||||
.cornerRadius(10)
|
||||
} else if !chat.chatInfo.ntfsEnabled && chat.chatInfo.chatType != .local {
|
||||
Image(systemName: "speaker.slash.fill")
|
||||
@@ -152,7 +151,7 @@ struct ChatPreviewView: View {
|
||||
|
||||
private func messageDraft(_ draft: ComposeState) -> Text {
|
||||
let msg = draft.message
|
||||
return image("rectangle.and.pencil.and.ellipsis", color: theme.colors.primary)
|
||||
return image("rectangle.and.pencil.and.ellipsis", color: .accentColor)
|
||||
+ attachment()
|
||||
+ messageText(msg, parseSimpleXMarkdown(msg), nil, preview: true, showSecrets: false)
|
||||
|
||||
@@ -207,7 +206,7 @@ struct ChatPreviewView: View {
|
||||
case let .direct(contact):
|
||||
if contact.activeConn == nil && contact.profile.contactLink != nil {
|
||||
chatPreviewInfoText("Tap to Connect")
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
} else if !contact.ready && contact.activeConn != nil {
|
||||
if contact.nextSendGrpInv {
|
||||
chatPreviewInfoText("send direct message")
|
||||
|
||||
@@ -11,7 +11,6 @@ import SimpleXChat
|
||||
|
||||
struct ContactConnectionInfo: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@State var contactConnection: PendingContactConnection
|
||||
@State private var alert: CCInfoAlert?
|
||||
@@ -83,7 +82,6 @@ struct ContactConnectionInfo: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
if #available(iOS 16, *) {
|
||||
v
|
||||
} else {
|
||||
@@ -151,7 +149,7 @@ struct ContactConnectionInfo: View {
|
||||
HStack(spacing: 6) {
|
||||
Text("Incognito")
|
||||
Image(systemName: "info.circle")
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
.onTapGesture {
|
||||
@@ -180,7 +178,6 @@ private func oneTimeLinkLearnMoreButton() -> some View {
|
||||
NavigationLink {
|
||||
AddContactLearnMore(showTitle: false)
|
||||
.navigationTitle("One-time invitation link")
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
settingsRow("info.circle") {
|
||||
|
||||
@@ -11,7 +11,6 @@ import SimpleXChat
|
||||
|
||||
struct ContactRequestView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var contactRequest: UserContactRequest
|
||||
@ObservedObject var chat: Chat
|
||||
|
||||
@@ -24,7 +23,7 @@ struct ContactRequestView: View {
|
||||
Text(contactRequest.chatViewName)
|
||||
.font(.title3)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
.padding(.leading, 8)
|
||||
.frame(alignment: .topLeading)
|
||||
Spacer()
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
private let fillColorDark = Color(uiColor: UIColor(red: 0.11, green: 0.11, blue: 0.11, alpha: 255))
|
||||
private let fillColorLight = Color(uiColor: UIColor(red: 0.99, green: 0.99, blue: 0.99, alpha: 255))
|
||||
|
||||
struct UserPicker: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@Environment(\.scenePhase) var scenePhase
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Binding var showSettings: Bool
|
||||
@Binding var showConnectDesktop: Bool
|
||||
@Binding var userPickerVisible: Bool
|
||||
@@ -19,6 +21,9 @@ struct UserPicker: View {
|
||||
private let menuButtonHeight: CGFloat = 68
|
||||
@State var chatViewNameWidth: CGFloat = 0
|
||||
|
||||
var fillColor: Color {
|
||||
colorScheme == .dark ? fillColorDark : fillColorLight
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
@@ -77,7 +82,7 @@ struct UserPicker: View {
|
||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||
.background(
|
||||
Rectangle()
|
||||
.fill(theme.colors.surface)
|
||||
.fill(fillColor)
|
||||
.cornerRadius(16)
|
||||
.shadow(color: .black.opacity(0.12), radius: 24, x: 0, y: 0)
|
||||
)
|
||||
@@ -132,7 +137,7 @@ struct UserPicker: View {
|
||||
if user.activeUser {
|
||||
Image(systemName: "checkmark")
|
||||
} else if u.unreadCount > 0 {
|
||||
unreadCounter(u.unreadCount, color: user.showNtfs ? theme.colors.primary : theme.colors.secondary)
|
||||
unreadCounter(u.unreadCount, color: user.showNtfs ? .accentColor : .secondary)
|
||||
} else if !user.showNtfs {
|
||||
Image(systemName: "speaker.slash")
|
||||
}
|
||||
@@ -140,7 +145,7 @@ struct UserPicker: View {
|
||||
.padding(.trailing)
|
||||
.padding([.leading, .vertical], 12)
|
||||
})
|
||||
.buttonStyle(PressedButtonStyle(defaultColor: theme.colors.surface, pressedColor: Color(uiColor: .secondarySystemFill)))
|
||||
.buttonStyle(PressedButtonStyle(defaultColor: fillColor, pressedColor: Color(uiColor: .secondarySystemFill)))
|
||||
}
|
||||
|
||||
private func menuButton(_ title: LocalizedStringKey, icon: String, action: @escaping () -> Void) -> some View {
|
||||
@@ -157,7 +162,7 @@ struct UserPicker: View {
|
||||
.padding(.vertical, 22)
|
||||
.frame(height: menuButtonHeight)
|
||||
}
|
||||
.buttonStyle(PressedButtonStyle(defaultColor: theme.colors.surface, pressedColor: Color(uiColor: .secondarySystemFill)))
|
||||
.buttonStyle(PressedButtonStyle(defaultColor: fillColor, pressedColor: Color(uiColor: .secondarySystemFill)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,6 @@ struct DatabaseView: View {
|
||||
NavigationLink {
|
||||
DatabaseEncryptionView(useKeychain: $useKeychain, migration: false)
|
||||
.navigationTitle("Database passphrase")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
Text("Database passphrase")
|
||||
}
|
||||
@@ -145,7 +144,6 @@ struct DatabaseView: View {
|
||||
NavigationLink {
|
||||
ChatArchiveView(archiveName: archiveName)
|
||||
.navigationTitle(title)
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
Text(title)
|
||||
}
|
||||
@@ -357,7 +355,6 @@ struct DatabaseView: View {
|
||||
Task {
|
||||
do {
|
||||
try await apiDeleteStorage()
|
||||
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
|
||||
do {
|
||||
let config = ArchiveConfig(archivePath: archivePath.path)
|
||||
let archiveErrors = try await apiImportArchive(config: config)
|
||||
|
||||
@@ -189,7 +189,6 @@ struct MigrateToAppGroupView: View {
|
||||
Task {
|
||||
do {
|
||||
try apiSaveAppSettings(settings: AppSettings.current.prepareForExport())
|
||||
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
|
||||
try await apiExportArchive(config: config)
|
||||
await MainActor.run { setV3DBMigration(.exported) }
|
||||
} catch let error {
|
||||
@@ -232,7 +231,6 @@ func exportChatArchive(_ storagePath: URL? = nil) async throws -> URL {
|
||||
if !ChatModel.shared.chatDbChanged {
|
||||
try apiSaveAppSettings(settings: AppSettings.current.prepareForExport())
|
||||
}
|
||||
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
|
||||
try await apiExportArchive(config: config)
|
||||
if storagePath == nil {
|
||||
deleteOldArchive()
|
||||
|
||||
@@ -10,7 +10,7 @@ import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct ChatInfoImage: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@ObservedObject var chat: Chat
|
||||
var size: CGFloat
|
||||
var color = Color(uiColor: .tertiarySystemGroupedBackground)
|
||||
@@ -24,7 +24,8 @@ struct ChatInfoImage: View {
|
||||
case .contactRequest: iconName = "person.crop.circle.fill"
|
||||
default: iconName = "circle.fill"
|
||||
}
|
||||
let iconColor = if case .local = chat.chatInfo { theme.appColors.primaryVariant2 } else { color }
|
||||
let notesColor = colorScheme == .light ? notesChatColorLight : notesChatColorDark
|
||||
let iconColor = if case .local = chat.chatInfo { notesColor } else { color }
|
||||
return ProfileImage(
|
||||
imageStr: chat.chatInfo.image,
|
||||
iconName: iconName,
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
//
|
||||
// ChatWallpaper.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Avently on 14.06.2024.
|
||||
// Copyright © 2024 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct ChatViewBackground: ViewModifier {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var image: Image
|
||||
var imageType: WallpaperType
|
||||
var background: Color
|
||||
var tint: Color
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content.background(
|
||||
Canvas { context, size in
|
||||
var image = context.resolve(image)
|
||||
image.shading = .color(tint)
|
||||
let rect = CGRectMake(0, 0, size.width, size.height)
|
||||
func repeatDraw(_ imageScale: CGFloat) {
|
||||
let scale = imageScale
|
||||
for h in 0 ... Int(size.height / image.size.height / scale) {
|
||||
for w in 0 ... Int(size.width / image.size.width / scale) {
|
||||
let rect = CGRectMake(CGFloat(w) * image.size.width * scale, CGFloat(h) * image.size.height * scale, image.size.width * scale, image.size.height * scale)
|
||||
context.draw(image, in: rect, style: FillStyle())
|
||||
}
|
||||
}
|
||||
}
|
||||
context.fill(Path(rect), with: .color(background))
|
||||
switch imageType {
|
||||
case let WallpaperType.Preset(filename, scale): repeatDraw(CGFloat((scale ?? 1) * (PresetWallpaper.from(filename)?.scale ?? 1)))
|
||||
case let WallpaperType.Image(_, scale, scaleType):
|
||||
let scaleType = scaleType ?? WallpaperScaleType.fill
|
||||
switch scaleType {
|
||||
case WallpaperScaleType.repeat: repeatDraw(CGFloat(scale ?? 1))
|
||||
case WallpaperScaleType.fill: fallthrough
|
||||
case WallpaperScaleType.fit:
|
||||
// let scale = scaleType.contentScale.computeScaleFactor(Size(image.width.toFloat(), image.height.toFloat()), Size(size.width, size.height))
|
||||
// let scaledWidth = (image.width * scale.scaleX).roundToInt()
|
||||
// let scaledHeight = (image.height * scale.scaleY).roundToInt()
|
||||
// drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
// if (scaleType == WallpaperScaleType.FIT) {
|
||||
// if (scaledWidth < size.width) {
|
||||
// // has black lines at left and right sides
|
||||
// var x = (size.width - scaledWidth) / 2
|
||||
// while (x > 0) {
|
||||
// drawImage(image, dstOffset = IntOffset(x = (x - scaledWidth).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
// x -= scaledWidth
|
||||
// }
|
||||
// x = size.width - (size.width - scaledWidth) / 2
|
||||
// while (x < size.width) {
|
||||
// drawImage(image, dstOffset = IntOffset(x = x.roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
// x += scaledWidth
|
||||
// }
|
||||
// } else {
|
||||
// // has black lines at top and bottom sides
|
||||
// var y = (size.height - scaledHeight) / 2
|
||||
// while (y > 0) {
|
||||
// drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = (y - scaledHeight).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
// y -= scaledHeight
|
||||
// }
|
||||
// y = size.height - (size.height - scaledHeight) / 2
|
||||
// while (y < size.height) {
|
||||
// drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = y.roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
// y += scaledHeight
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
context.fill(Path(rect), with: .color(tint))
|
||||
}
|
||||
case WallpaperType.Empty: ()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension PresetWallpaper {
|
||||
public func toType(_ base: DefaultTheme, _ scale: Float? = nil) -> WallpaperType {
|
||||
WallpaperType.Preset(
|
||||
filename,
|
||||
scale ?? themeOverridesDefault.get().first { $0.wallpaper != nil && $0.wallpaper!.preset == filename && $0.base == base }?.wallpaper?.scale ?? 1
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import UIKit
|
||||
import SwiftUI
|
||||
|
||||
extension View {
|
||||
func uiKitContextMenu(hasImageOrVideo: Bool, maxWidth: CGFloat, itemWidth: Binding<CGFloat>, menu: Binding<UIMenu>, allowMenu: Binding<Bool>, backgroundColor: Color) -> some View {
|
||||
func uiKitContextMenu(hasImageOrVideo: Bool, maxWidth: CGFloat, itemWidth: Binding<CGFloat>, menu: Binding<UIMenu>, allowMenu: Binding<Bool>) -> some View {
|
||||
Group {
|
||||
if allowMenu.wrappedValue {
|
||||
if hasImageOrVideo {
|
||||
@@ -19,10 +19,10 @@ extension View {
|
||||
self.environmentObject(ChatModel.shared)
|
||||
.overlay(DetermineWidthImageVideoItem())
|
||||
.onPreferenceChange(DetermineWidthImageVideoItem.Key.self) { itemWidth.wrappedValue = $0 == 0 ? maxWidth : $0 }
|
||||
, maxWidth: maxWidth, itemWidth: itemWidth, menu: menu, backgroundColor: backgroundColor)
|
||||
, maxWidth: maxWidth, itemWidth: itemWidth, menu: menu)
|
||||
.frame(maxWidth: itemWidth.wrappedValue)
|
||||
} else {
|
||||
InteractionView(content: self.environmentObject(ChatModel.shared), maxWidth: maxWidth, itemWidth: itemWidth, menu: menu, backgroundColor: backgroundColor)
|
||||
InteractionView(content: self.environmentObject(ChatModel.shared), maxWidth: maxWidth, itemWidth: itemWidth, menu: menu)
|
||||
.fixedSize(horizontal: true, vertical: false)
|
||||
}
|
||||
} else {
|
||||
@@ -42,14 +42,11 @@ struct InteractionView<Content: View>: UIViewRepresentable {
|
||||
var maxWidth: CGFloat
|
||||
var itemWidth: Binding<CGFloat>
|
||||
@Binding var menu: UIMenu
|
||||
var backgroundColor: Color
|
||||
|
||||
func makeUIView(context: Context) -> UIView {
|
||||
let view = HostingViewHolder()
|
||||
view.backgroundColor = .clear
|
||||
let hostView = UIHostingController(rootView: content)
|
||||
// hostView.view.backgroundColor = UIColor(backgroundColor)
|
||||
hostView.view.backgroundColor = .clear
|
||||
view.contentSize = hostView.view.intrinsicContentSize
|
||||
hostView.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
let constraints = [
|
||||
@@ -75,7 +72,6 @@ struct InteractionView<Content: View>: UIViewRepresentable {
|
||||
if was != (uiView as! HostingViewHolder).contentSize {
|
||||
uiView.invalidateIntrinsicContentSize()
|
||||
}
|
||||
//uiView.backgroundColor = UIColor(backgroundColor)
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
|
||||
@@ -16,7 +16,6 @@ struct ProfileImage: View {
|
||||
var iconName: String = "person.crop.circle.fill"
|
||||
var size: CGFloat
|
||||
var color = Color(uiColor: .tertiarySystemGroupedBackground)
|
||||
var backgroundColor: Color? = nil
|
||||
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var radius = defaultProfileImageCorner
|
||||
|
||||
var body: some View {
|
||||
@@ -29,7 +28,6 @@ struct ProfileImage: View {
|
||||
.resizable()
|
||||
.foregroundColor(color)
|
||||
.frame(width: size, height: size)
|
||||
.background(backgroundColor != nil ? backgroundColor! : .clear)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
//
|
||||
// ViewModifiers.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Avently on 12.06.2024.
|
||||
// Copyright © 2024 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension View {
|
||||
@ViewBuilder func `if`<Content: View>(_ condition: @autoclosure () -> Bool, transform: (Self) -> Content) -> some View {
|
||||
if condition() {
|
||||
transform(self)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,7 @@ struct PasscodeEntry: View {
|
||||
ZStack {
|
||||
Circle()
|
||||
.frame(width: h, height: h)
|
||||
.foregroundColor(AppTheme.shared.colors.background)
|
||||
.foregroundColor(Color(uiColor: .systemBackground))
|
||||
label()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ struct PasscodeView: View {
|
||||
}
|
||||
.padding(.horizontal, 40)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(AppTheme.shared.colors.background)
|
||||
.background(Color(uiColor: .systemBackground))
|
||||
}
|
||||
|
||||
private func verticalPasscodeView(_ g: GeometryProxy) -> some View {
|
||||
|
||||
@@ -53,7 +53,6 @@ private enum MigrateFromDeviceViewAlert: Identifiable {
|
||||
|
||||
struct MigrateFromDevice: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@Binding var showSettings: Bool
|
||||
@Binding var showProgressOnSettings: Bool
|
||||
@@ -216,7 +215,7 @@ struct MigrateFromDevice: View {
|
||||
Section {
|
||||
Button(action: { migrationState = .archiving }) {
|
||||
settingsRow("tray.and.arrow.up") {
|
||||
Text("Archive and upload").foregroundColor(theme.colors.primary)
|
||||
Text("Archive and upload").foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
@@ -250,7 +249,7 @@ struct MigrateFromDevice: View {
|
||||
}
|
||||
}
|
||||
let ratio = Float(uploadedBytes) / Float(totalBytes)
|
||||
MigrateFromDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: uploadedBytes, countStyle: .binary)) uploaded", theme.colors.primary)
|
||||
MigrateFromDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: uploadedBytes, countStyle: .binary)) uploaded")
|
||||
}
|
||||
.onAppear {
|
||||
startUploading(totalBytes, archivePath)
|
||||
@@ -264,7 +263,7 @@ struct MigrateFromDevice: View {
|
||||
migrationState = .uploadProgress(uploadedBytes: 0, totalBytes: totalBytes, fileId: 0, archivePath: archivePath, ctrl: nil)
|
||||
}) {
|
||||
settingsRow("tray.and.arrow.up") {
|
||||
Text("Repeat upload").foregroundColor(theme.colors.primary)
|
||||
Text("Repeat upload").foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
@@ -300,7 +299,7 @@ struct MigrateFromDevice: View {
|
||||
}
|
||||
Button(action: { finishMigration(fileId, ctrl) }) {
|
||||
settingsRow("checkmark") {
|
||||
Text("Finalize migration").foregroundColor(theme.colors.primary)
|
||||
Text("Finalize migration").foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
} footer: {
|
||||
@@ -341,7 +340,7 @@ struct MigrateFromDevice: View {
|
||||
}
|
||||
Button(action: { alert = .deleteChat() }) {
|
||||
settingsRow("trash.fill") {
|
||||
Text("Delete database from this device").foregroundColor(theme.colors.primary)
|
||||
Text("Delete database from this device").foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
@@ -380,7 +379,7 @@ struct MigrateFromDevice: View {
|
||||
.truncationMode(.middle)
|
||||
}
|
||||
|
||||
static func largeProgressView(_ value: Float, _ title: String, _ description: LocalizedStringKey, _ primaryColor: Color) -> some View {
|
||||
static func largeProgressView(_ value: Float, _ title: String, _ description: LocalizedStringKey) -> some View {
|
||||
ZStack {
|
||||
VStack {
|
||||
Text(description)
|
||||
@@ -390,7 +389,7 @@ struct MigrateFromDevice: View {
|
||||
Text(title)
|
||||
.font(.system(size: 54))
|
||||
.bold()
|
||||
.foregroundColor(primaryColor)
|
||||
.foregroundColor(.accentColor)
|
||||
|
||||
Text(description)
|
||||
.font(.title3)
|
||||
@@ -399,7 +398,7 @@ struct MigrateFromDevice: View {
|
||||
Circle()
|
||||
.trim(from: 0, to: CGFloat(value))
|
||||
.stroke(
|
||||
primaryColor,
|
||||
Color.accentColor,
|
||||
style: StrokeStyle(lineWidth: 27)
|
||||
)
|
||||
.rotationEffect(.degrees(180))
|
||||
|
||||
@@ -91,7 +91,6 @@ private enum MigrateToDeviceViewAlert: Identifiable {
|
||||
|
||||
struct MigrateToDevice: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
|
||||
@Binding var migrationState: MigrationToState?
|
||||
@@ -244,7 +243,7 @@ struct MigrateToDevice: View {
|
||||
}
|
||||
}
|
||||
let ratio = Float(downloadedBytes) / Float(max(totalBytes, 1))
|
||||
MigrateFromDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: downloadedBytes, countStyle: .binary)) downloaded", theme.colors.primary)
|
||||
MigrateFromDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: downloadedBytes, countStyle: .binary)) downloaded")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +255,7 @@ struct MigrateToDevice: View {
|
||||
migrationState = .linkDownloading(link: link)
|
||||
}) {
|
||||
settingsRow("tray.and.arrow.down") {
|
||||
Text("Repeat download").foregroundColor(theme.colors.primary)
|
||||
Text("Repeat download").foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
@@ -294,7 +293,7 @@ struct MigrateToDevice: View {
|
||||
migrationState = .archiveImport(archivePath: archivePath)
|
||||
}) {
|
||||
settingsRow("square.and.arrow.down") {
|
||||
Text("Repeat import").foregroundColor(theme.colors.primary)
|
||||
Text("Repeat import").foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
@@ -335,7 +334,7 @@ struct MigrateToDevice: View {
|
||||
migrationState = .migration(passphrase: passphrase, confirmation: confirmation, useKeychain: useKeychain)
|
||||
}) {
|
||||
settingsRow("square.and.arrow.down") {
|
||||
Text(button).foregroundColor(theme.colors.primary)
|
||||
Text(button).foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -365,7 +364,6 @@ struct MigrateToDevice: View {
|
||||
}
|
||||
|
||||
struct OnionView: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@State var appSettings: AppSettings
|
||||
@State private var onionHosts: OnionHosts = .no
|
||||
var finishMigration: (AppSettings) -> Void
|
||||
@@ -383,7 +381,7 @@ struct MigrateToDevice: View {
|
||||
finishMigration(appSettings)
|
||||
}) {
|
||||
settingsRow("checkmark") {
|
||||
Text("Apply").foregroundColor(theme.colors.primary)
|
||||
Text("Apply").foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
@@ -477,7 +475,6 @@ struct MigrateToDevice: View {
|
||||
chatInitControllerRemovingDatabases()
|
||||
}
|
||||
try await apiDeleteStorage()
|
||||
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
|
||||
do {
|
||||
let config = ArchiveConfig(archivePath: archivePath)
|
||||
let archiveErrors = try await apiImportArchive(config: config)
|
||||
|
||||
@@ -30,7 +30,6 @@ struct AddContactLearnMore: View {
|
||||
}
|
||||
.listRowBackground(Color.clear)
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import SimpleXChat
|
||||
|
||||
struct AddGroupView: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@AppStorage(GROUP_DEFAULT_INCOGNITO, store: groupDefaults) private var incognitoDefault = false
|
||||
@State private var chat: Chat?
|
||||
@@ -96,7 +95,7 @@ struct AddGroupView: View {
|
||||
Section {
|
||||
groupNameTextField()
|
||||
Button(action: createGroup) {
|
||||
settingsRow("checkmark", color: theme.colors.primary) { Text("Create group") }
|
||||
settingsRow("checkmark", color: .accentColor) { Text("Create group") }
|
||||
}
|
||||
.disabled(!canCreateProfile())
|
||||
IncognitoToggle(incognitoEnabled: $incognitoDefault)
|
||||
@@ -145,7 +144,6 @@ struct AddGroupView: View {
|
||||
profile.image = nil
|
||||
}
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
func groupNameTextField() -> some View {
|
||||
|
||||
@@ -94,7 +94,7 @@ struct NewChatView: View {
|
||||
.background(
|
||||
// Rectangle is needed for swipe gesture to work on mostly empty views (creatingLinkProgressView and retryButton)
|
||||
Rectangle()
|
||||
.fill(AppTheme.shared.colors.background)
|
||||
.fill(Color(uiColor: .systemGroupedBackground))
|
||||
)
|
||||
.animation(.easeInOut(duration: 0.3333), value: selection)
|
||||
.gesture(DragGesture(minimumDistance: 20.0, coordinateSpace: .local)
|
||||
@@ -113,7 +113,7 @@ struct NewChatView: View {
|
||||
}
|
||||
)
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
.background(Color(.systemGroupedBackground))
|
||||
.onChange(of: invitationUsed) { used in
|
||||
if used && !(m.showingInvitation?.connChatUsed ?? true) {
|
||||
m.markShowingInvitationUsed()
|
||||
@@ -367,12 +367,11 @@ struct ScannerInView: View {
|
||||
@Binding var showQRCodeScanner: Bool
|
||||
let processQRCode: (_ resp: Result<ScanResult, ScanError>) -> Void
|
||||
@State private var cameraAuthorizationStatus: AVAuthorizationStatus?
|
||||
var scanMode: ScanMode = .continuous
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if showQRCodeScanner, case .authorized = cameraAuthorizationStatus {
|
||||
CodeScannerView(codeTypes: [.qr], scanMode: scanMode, completion: processQRCode)
|
||||
CodeScannerView(codeTypes: [.qr], scanMode: .continuous, completion: processQRCode)
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.cornerRadius(12)
|
||||
.listRowBackground(Color.clear)
|
||||
@@ -437,7 +436,6 @@ struct ScannerInView: View {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func linkTextView(_ link: String) -> some View {
|
||||
Text(link)
|
||||
.lineLimit(1)
|
||||
@@ -488,7 +486,6 @@ func strHasSingleSimplexLink(_ str: String) -> FormattedText? {
|
||||
}
|
||||
|
||||
struct IncognitoToggle: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Binding var incognitoEnabled: Bool
|
||||
@State private var showIncognitoSheet = false
|
||||
|
||||
@@ -502,7 +499,7 @@ struct IncognitoToggle: View {
|
||||
HStack(spacing: 6) {
|
||||
Text("Incognito")
|
||||
Image(systemName: "info.circle")
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
.onTapGesture {
|
||||
|
||||
@@ -66,7 +66,6 @@ struct CreateProfile: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Create your profile")
|
||||
.modifier(ThemedBackground())
|
||||
.alert(item: $alert) { a in userProfileAlert(a, $displayName) }
|
||||
.onAppear() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
@@ -79,7 +78,6 @@ struct CreateProfile: View {
|
||||
|
||||
struct CreateFirstProfile: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@State private var displayName: String = ""
|
||||
@FocusState private var focusDisplayName
|
||||
@@ -91,9 +89,9 @@ struct CreateFirstProfile: View {
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
Text("Your profile, contacts and delivered messages are stored on your device.")
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
.foregroundColor(.secondary)
|
||||
Text("The profile is only shared with your contacts.")
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
.foregroundColor(.secondary)
|
||||
.padding(.bottom)
|
||||
}
|
||||
.padding(.bottom)
|
||||
|
||||
@@ -44,7 +44,6 @@ struct HowItWorks: View {
|
||||
.lineLimit(10)
|
||||
.padding()
|
||||
.frame(maxHeight: .infinity, alignment: .top)
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ struct SetNotificationsMode: View {
|
||||
}
|
||||
|
||||
struct NtfModeSelector: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var mode: NotificationsMode
|
||||
@Binding var selection: NotificationsMode
|
||||
@State private var tapped = false
|
||||
@@ -88,7 +87,7 @@ struct NtfModeSelector: View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(mode.label)
|
||||
.font(.headline)
|
||||
.foregroundColor(selection == mode ? theme.colors.primary : theme.colors.secondary)
|
||||
.foregroundColor(selection == mode ? .accentColor : .secondary)
|
||||
Text(ntfModeDescription(mode))
|
||||
.lineLimit(10)
|
||||
.font(.subheadline)
|
||||
@@ -96,11 +95,11 @@ struct NtfModeSelector: View {
|
||||
.padding(12)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(tapped ? Color(uiColor: .secondarySystemFill) : theme.colors.background)
|
||||
.background(Color(uiColor: tapped ? .secondarySystemFill : .systemBackground))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 18))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 18)
|
||||
.stroke(selection == mode ? theme.colors.primary : Color(uiColor: .secondarySystemFill), lineWidth: 2)
|
||||
.stroke(selection == mode ? Color.accentColor : Color(uiColor: .secondarySystemFill), lineWidth: 2)
|
||||
)
|
||||
._onButtonGesture { down in
|
||||
tapped = down
|
||||
|
||||
@@ -79,7 +79,7 @@ struct SimpleXInfo: View {
|
||||
MigrateToDevice(migrationState: $m.migrationState)
|
||||
}
|
||||
.navigationTitle("Migrate here")
|
||||
.modifier(ThemedBackground())
|
||||
.background(colorScheme == .light ? Color(uiColor: .tertiarySystemGroupedBackground) : .clear)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showHowItWorks) {
|
||||
|
||||
@@ -178,32 +178,26 @@ struct ConnectDesktopView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Connect to desktop")
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
private func connectingDesktopView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?) -> some View {
|
||||
ZStack {
|
||||
List {
|
||||
Section("Connecting to desktop") {
|
||||
ctrlDeviceNameText(session, rc)
|
||||
ctrlDeviceVersionText(session)
|
||||
}
|
||||
List {
|
||||
Section("Connecting to desktop") {
|
||||
ctrlDeviceNameText(session, rc)
|
||||
ctrlDeviceVersionText(session)
|
||||
}
|
||||
|
||||
if let sessCode = session.sessionCode {
|
||||
Section("Session code") {
|
||||
sessionCodeText(sessCode)
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
disconnectButton()
|
||||
if let sessCode = session.sessionCode {
|
||||
Section("Session code") {
|
||||
sessionCodeText(sessCode)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Connecting to desktop")
|
||||
|
||||
ProgressView().scaleEffect(2)
|
||||
Section {
|
||||
disconnectButton()
|
||||
}
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
.navigationTitle("Connecting to desktop")
|
||||
}
|
||||
|
||||
private func searchingDesktopView() -> some View {
|
||||
@@ -221,7 +215,6 @@ struct ConnectDesktopView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Connecting to desktop")
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
@ViewBuilder private func foundDesktopView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo, _ compatible: Bool) -> some View {
|
||||
@@ -249,7 +242,6 @@ struct ConnectDesktopView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Found desktop")
|
||||
.modifier(ThemedBackground())
|
||||
|
||||
if compatible && connectRemoteViaMulticastAuto {
|
||||
v.onAppear { confirmKnownDesktop(rc) }
|
||||
@@ -279,7 +271,6 @@ struct ConnectDesktopView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Verify connection")
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
private func ctrlDeviceNameText(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?) -> Text {
|
||||
@@ -320,7 +311,6 @@ struct ConnectDesktopView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Connected to desktop")
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
private func sessionCodeText(_ code: String) -> some View {
|
||||
@@ -342,7 +332,13 @@ struct ConnectDesktopView: View {
|
||||
|
||||
private func scanDesctopAddressView() -> some View {
|
||||
Section("Scan QR code from desktop") {
|
||||
ScannerInView(showQRCodeScanner: $showQRCodeScanner, processQRCode: processDesktopQRCode, scanMode: .oncePerCode)
|
||||
CodeScannerView(codeTypes: [.qr], scanMode: .oncePerCode, completion: processDesktopQRCode)
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.cornerRadius(12)
|
||||
.listRowBackground(Color.clear)
|
||||
.listRowSeparator(.hidden)
|
||||
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,7 +391,6 @@ struct ConnectDesktopView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Linked desktops")
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
private func remoteCtrlView(_ rc: RemoteCtrlInfo) -> some View {
|
||||
|
||||
@@ -106,7 +106,6 @@ struct TerminalView: View {
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
.navigationTitle("Chat console")
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
func scrollToBottom(_ proxy: ScrollViewProxy, animation: Animation = .default) {
|
||||
@@ -131,7 +130,6 @@ struct TerminalView: View {
|
||||
}
|
||||
}
|
||||
.onDisappear { terminalItem = nil }
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
func consoleSendMessage() {
|
||||
|
||||
@@ -44,11 +44,6 @@ extension AppSettings {
|
||||
if let val = androidCallOnLockScreen { def.setValue(val.rawValue, forKey: ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN) }
|
||||
if let val = iosCallKitEnabled { callKitEnabledGroupDefault.set(val) }
|
||||
if let val = iosCallKitCallsInRecents { def.setValue(val, forKey: DEFAULT_CALL_KIT_CALLS_IN_RECENTS) }
|
||||
if let val = uiProfileImageCornerRadius { def.setValue(val, forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) }
|
||||
if let val = uiColorScheme { def.setValue(val, forKey: DEFAULT_CURRENT_THEME) }
|
||||
if let val = uiDarkColorScheme { def.setValue(val, forKey: DEFAULT_SYSTEM_DARK_THEME) }
|
||||
if let val = uiCurrentThemeIds { def.setValue(val, forKey: DEFAULT_CURRENT_THEME_IDS) }
|
||||
if let val = uiThemes { def.setValue(val.skipDuplicates(), forKey: DEFAULT_THEME_OVERRIDES) }
|
||||
}
|
||||
|
||||
public static var current: AppSettings {
|
||||
@@ -74,11 +69,6 @@ extension AppSettings {
|
||||
c.androidCallOnLockScreen = AppSettingsLockScreenCalls(rawValue: def.string(forKey: ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN)!)
|
||||
c.iosCallKitEnabled = callKitEnabledGroupDefault.get()
|
||||
c.iosCallKitCallsInRecents = def.bool(forKey: DEFAULT_CALL_KIT_CALLS_IN_RECENTS)
|
||||
c.uiProfileImageCornerRadius = def.float(forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS)
|
||||
c.uiColorScheme = currentThemeDefault.get()
|
||||
c.uiDarkColorScheme = systemDarkThemeDefault.get()
|
||||
c.uiCurrentThemeIds = currentThemeIdsDefault.get()
|
||||
c.uiThemes = themeOverridesDefault.get()
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,12 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
let defaultAccentColor = CGColor.init(red: 0, green: 0.533, blue: 1, alpha: 1)
|
||||
|
||||
//let interfaceStyles: [UIUserInterfaceStyle] = [.unspecified, .light, .dark]
|
||||
let interfaceStyles: [UIUserInterfaceStyle] = [.unspecified, .light, .dark]
|
||||
|
||||
let colorModesLocalized: [LocalizedStringKey] = ["System", "Light", "Dark"]
|
||||
let colorModesNames: [DefaultThemeMode?] = [nil, DefaultThemeMode.light, DefaultThemeMode.dark]
|
||||
|
||||
let darkThemesLocalized: [LocalizedStringKey] = ["Dark", "SimpleX", "Black"]
|
||||
let darkThemesNames: [String] = [DefaultTheme.DARK.themeName, DefaultTheme.SIMPLEX.themeName, DefaultTheme.BLACK.themeName]
|
||||
let interfaceStyleNames: [LocalizedStringKey] = ["System", "Light", "Dark"]
|
||||
|
||||
let appSettingsURL = URL(string: UIApplication.openSettingsURLString)!
|
||||
|
||||
@@ -27,11 +22,7 @@ struct AppearanceSettings: View {
|
||||
@EnvironmentObject var sceneDelegate: SceneDelegate
|
||||
@State private var iconLightTapped = false
|
||||
@State private var iconDarkTapped = false
|
||||
//@State private var userInterfaceStyle = getUserInterfaceStyleDefault()
|
||||
@State private var colorMode: DefaultThemeMode? = {
|
||||
if currentThemeDefault.get() == DefaultTheme.SYSTEM_THEME_NAME { nil as DefaultThemeMode? } else { CurrentColors.base.mode }
|
||||
}()
|
||||
@State private var darkModeTheme: String = UserDefaults.standard.string(forKey: DEFAULT_SYSTEM_DARK_THEME) ?? DefaultTheme.SIMPLEX.themeName
|
||||
@State private var userInterfaceStyle = getUserInterfaceStyleDefault()
|
||||
@State private var uiTintColor = getUIAccentColorDefault()
|
||||
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var profileImageCornerRadius = defaultProfileImageCorner
|
||||
|
||||
@@ -74,15 +65,9 @@ struct AppearanceSettings: View {
|
||||
}
|
||||
|
||||
Section {
|
||||
Picker("Color mode", selection: $colorMode) {
|
||||
ForEach(Array(colorModesNames.enumerated()), id: \.element) { index, mode in
|
||||
Text(colorModesLocalized[index])
|
||||
}
|
||||
}
|
||||
.frame(height: 36)
|
||||
Picker("Dark mode colors", selection: $darkModeTheme) {
|
||||
ForEach(Array(darkThemesNames.enumerated()), id: \.element) { index, darkTheme in
|
||||
Text(darkThemesLocalized[index])
|
||||
Picker("Theme", selection: $userInterfaceStyle) {
|
||||
ForEach(interfaceStyles, id: \.self) { style in
|
||||
Text(interfaceStyleNames[interfaceStyles.firstIndex(of: style) ?? 0])
|
||||
}
|
||||
}
|
||||
.frame(height: 36)
|
||||
@@ -97,24 +82,9 @@ struct AppearanceSettings: View {
|
||||
Text("Reset colors").font(.callout)
|
||||
}
|
||||
}
|
||||
.onChange(of: colorMode) { mode in
|
||||
guard let mode else {
|
||||
ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME)
|
||||
return
|
||||
}
|
||||
if case DefaultThemeMode.light = mode {
|
||||
ThemeManager.applyTheme(DefaultTheme.LIGHT.themeName)
|
||||
} else if case DefaultThemeMode.dark = mode {
|
||||
ThemeManager.applyTheme(systemDarkThemeDefault.get())
|
||||
}
|
||||
}
|
||||
.onChange(of: darkModeTheme) { darkTheme in
|
||||
ThemeManager.changeDarkTheme(darkTheme)
|
||||
if currentThemeDefault.get() == DefaultTheme.SYSTEM_THEME_NAME {
|
||||
ThemeManager.applyTheme(currentThemeDefault.get())
|
||||
} else if currentThemeDefault.get() != DefaultTheme.LIGHT.themeName {
|
||||
ThemeManager.applyTheme(systemDarkThemeDefault.get())
|
||||
}
|
||||
.onChange(of: userInterfaceStyle) { _ in
|
||||
sceneDelegate.window?.overrideUserInterfaceStyle = userInterfaceStyle
|
||||
setUserInterfaceStyleDefault(userInterfaceStyle)
|
||||
}
|
||||
.onChange(of: uiTintColor) { _ in
|
||||
sceneDelegate.window?.tintColor = UIColor(cgColor: uiTintColor)
|
||||
@@ -166,24 +136,24 @@ func setUIAccentColorDefault(_ color: CGColor) {
|
||||
}
|
||||
}
|
||||
|
||||
//func getUserInterfaceStyleDefault() -> UIUserInterfaceStyle {
|
||||
// switch UserDefaults.standard.integer(forKey: DEFAULT_USER_INTERFACE_STYLE) {
|
||||
// case 1: return .light
|
||||
// case 2: return .dark
|
||||
// default: return .unspecified
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func setUserInterfaceStyleDefault(_ style: UIUserInterfaceStyle) {
|
||||
// var v: Int
|
||||
// switch style {
|
||||
// case .unspecified: v = 0
|
||||
// case .light: v = 1
|
||||
// case .dark: v = 2
|
||||
// default: v = 0
|
||||
// }
|
||||
// UserDefaults.standard.set(v, forKey: DEFAULT_USER_INTERFACE_STYLE)
|
||||
//}
|
||||
func getUserInterfaceStyleDefault() -> UIUserInterfaceStyle {
|
||||
switch UserDefaults.standard.integer(forKey: DEFAULT_USER_INTERFACE_STYLE) {
|
||||
case 1: return .light
|
||||
case 2: return .dark
|
||||
default: return .unspecified
|
||||
}
|
||||
}
|
||||
|
||||
func setUserInterfaceStyleDefault(_ style: UIUserInterfaceStyle) {
|
||||
var v: Int
|
||||
switch style {
|
||||
case .unspecified: v = 0
|
||||
case .light: v = 1
|
||||
case .dark: v = 2
|
||||
default: v = 0
|
||||
}
|
||||
UserDefaults.standard.set(v, forKey: DEFAULT_USER_INTERFACE_STYLE)
|
||||
}
|
||||
|
||||
struct AppearanceSettings_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
|
||||
@@ -22,7 +22,6 @@ struct CallSettings: View {
|
||||
NavigationLink {
|
||||
RTCServers()
|
||||
.navigationTitle("Your ICE servers")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
Text("WebRTC ICE servers")
|
||||
}
|
||||
|
||||
@@ -70,7 +70,6 @@ struct HiddenProfileView: View {
|
||||
message: Text(savePasswordError ?? "")
|
||||
)
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
var passwordValid: Bool { hidePassword == hidePassword.trimmingCharacters(in: .whitespaces) }
|
||||
|
||||
@@ -27,7 +27,6 @@ struct IncognitoHelp: View {
|
||||
}
|
||||
.listRowBackground(Color.clear)
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,6 @@ struct NetworkAndServers: View {
|
||||
NavigationLink {
|
||||
ProtocolServersView(serverProtocol: .smp)
|
||||
.navigationTitle("Your SMP servers")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
Text("SMP servers")
|
||||
}
|
||||
@@ -55,7 +54,6 @@ struct NetworkAndServers: View {
|
||||
NavigationLink {
|
||||
ProtocolServersView(serverProtocol: .xftp)
|
||||
.navigationTitle("Your XFTP servers")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
Text("XFTP servers")
|
||||
}
|
||||
@@ -75,7 +73,6 @@ struct NetworkAndServers: View {
|
||||
NavigationLink {
|
||||
AdvancedNetworkSettings()
|
||||
.navigationTitle("Network settings")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
Text("Advanced network settings")
|
||||
}
|
||||
@@ -113,7 +110,6 @@ struct NetworkAndServers: View {
|
||||
NavigationLink {
|
||||
RTCServers()
|
||||
.navigationTitle("Your ICE servers")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
Text("WebRTC ICE servers")
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ struct NotificationsView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Send notifications")
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.alert(item: $showAlert) { alert in
|
||||
if let token = m.deviceToken {
|
||||
@@ -69,7 +68,6 @@ struct NotificationsView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Show preview")
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
} label: {
|
||||
HStack {
|
||||
@@ -174,7 +172,6 @@ func ntfModeDescription(_ mode: NotificationsMode) -> LocalizedStringKey {
|
||||
}
|
||||
|
||||
struct SelectionListView<Item: SelectableItem>: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var list: [Item]
|
||||
@Binding var selection: Item
|
||||
var onSelection: ((Item) -> Void)?
|
||||
@@ -188,11 +185,11 @@ struct SelectionListView<Item: SelectableItem>: View {
|
||||
if selection == item {
|
||||
Image(systemName: "checkmark")
|
||||
.resizable().scaledToFit().frame(width: 16)
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.listRowBackground(tapped == item ? Color(uiColor: .secondarySystemFill) : theme.colors.background)
|
||||
.listRowBackground(Color(uiColor: tapped == item ? .secondarySystemFill : .systemBackground))
|
||||
.onTapGesture {
|
||||
if selection == item { return }
|
||||
if let f = onSelection {
|
||||
|
||||
@@ -48,7 +48,6 @@ struct PrivacySettings: View {
|
||||
NavigationLink {
|
||||
SimplexLockView(prefPerformLA: $prefPerformLA, currentLAMode: $currentLAMode)
|
||||
.navigationTitle("SimpleX Lock")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
if prefPerformLA {
|
||||
settingsRow("lock.fill", color: .green) {
|
||||
@@ -333,7 +332,6 @@ struct SimplexLockView: View {
|
||||
@Binding var prefPerformLA: Bool
|
||||
@Binding var currentLAMode: LAMode
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@AppStorage(DEFAULT_LA_NOTICE_SHOWN) private var prefLANoticeShown = false
|
||||
@State private var laMode: LAMode = privacyLocalAuthModeDefault.get()
|
||||
@AppStorage(DEFAULT_LA_LOCK_DELAY) private var laLockDelay = 30
|
||||
@@ -418,7 +416,7 @@ struct SimplexLockView: View {
|
||||
HStack(spacing: 6) {
|
||||
Text("Enable self-destruct")
|
||||
Image(systemName: "info.circle")
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.foregroundColor(.accentColor)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
.onTapGesture {
|
||||
|
||||
@@ -94,7 +94,6 @@ struct ProtocolServersView: View {
|
||||
}
|
||||
.sheet(isPresented: $showScanProtoServer) {
|
||||
ScanProtocolServer(servers: $servers)
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
.modifier(BackButton(disabled: Binding.constant(false)) {
|
||||
if saveDisabled {
|
||||
@@ -172,7 +171,6 @@ struct ProtocolServersView: View {
|
||||
serverToEdit: srv
|
||||
)
|
||||
.navigationBarTitle(srv.preset ? "Preset server" : "Your server")
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
let address = parseServerAddress(srv.server)
|
||||
|
||||
@@ -89,7 +89,7 @@ struct SetDeliveryReceiptsView: View {
|
||||
.padding()
|
||||
.padding(.horizontal)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(AppTheme.shared.colors.background)
|
||||
.background(Color(uiColor: .systemBackground))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ let DEFAULT_ENCRYPTION_STARTED_AT = "encryptionStartedAt"
|
||||
let DEFAULT_ACCENT_COLOR_RED = "accentColorRed"
|
||||
let DEFAULT_ACCENT_COLOR_GREEN = "accentColorGreen"
|
||||
let DEFAULT_ACCENT_COLOR_BLUE = "accentColorBlue"
|
||||
//let DEFAULT_USER_INTERFACE_STYLE = "userInterfaceStyle"
|
||||
let DEFAULT_USER_INTERFACE_STYLE = "userInterfaceStyle"
|
||||
let DEFAULT_PROFILE_IMAGE_CORNER_RADIUS = "profileImageCornerRadius"
|
||||
let DEFAULT_CONNECT_VIA_LINK_TAB = "connectViaLinkTab"
|
||||
let DEFAULT_LIVE_MESSAGE_ALERT_SHOWN = "liveMessageAlertShown"
|
||||
@@ -62,11 +62,6 @@ let DEFAULT_CONNECT_REMOTE_VIA_MULTICAST = "connectRemoteViaMulticast"
|
||||
let DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO = "connectRemoteViaMulticastAuto"
|
||||
let DEFAULT_SHOW_SENT_VIA_RPOXY = "showSentViaProxy"
|
||||
|
||||
let DEFAULT_CURRENT_THEME = "currentTheme"
|
||||
let DEFAULT_SYSTEM_DARK_THEME = "systemDarkTheme"
|
||||
let DEFAULT_CURRENT_THEME_IDS = "currentThemeIds"
|
||||
let DEFAULT_THEME_OVERRIDES = "themeOverrides"
|
||||
|
||||
let ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN = "androidCallOnLockScreen"
|
||||
|
||||
let appDefaults: [String: Any] = [
|
||||
@@ -93,7 +88,7 @@ let appDefaults: [String: Any] = [
|
||||
DEFAULT_ACCENT_COLOR_RED: 0.000,
|
||||
DEFAULT_ACCENT_COLOR_GREEN: 0.533,
|
||||
DEFAULT_ACCENT_COLOR_BLUE: 1.000,
|
||||
//DEFAULT_USER_INTERFACE_STYLE: 0,
|
||||
DEFAULT_USER_INTERFACE_STYLE: 0,
|
||||
DEFAULT_PROFILE_IMAGE_CORNER_RADIUS: defaultProfileImageCorner,
|
||||
DEFAULT_CONNECT_VIA_LINK_TAB: ConnectViaLinkTab.scan.rawValue,
|
||||
DEFAULT_LIVE_MESSAGE_ALERT_SHOWN: false,
|
||||
@@ -106,12 +101,7 @@ let appDefaults: [String: Any] = [
|
||||
DEFAULT_CONNECT_REMOTE_VIA_MULTICAST: true,
|
||||
DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO: true,
|
||||
DEFAULT_SHOW_SENT_VIA_RPOXY: false,
|
||||
ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN: AppSettingsLockScreenCalls.show.rawValue,
|
||||
|
||||
DEFAULT_THEME_OVERRIDES: "{}",
|
||||
DEFAULT_CURRENT_THEME: DefaultTheme.SYSTEM_THEME_NAME,
|
||||
DEFAULT_SYSTEM_DARK_THEME: DefaultTheme.SIMPLEX.themeName,
|
||||
DEFAULT_CURRENT_THEME_IDS: "{}"
|
||||
ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN: AppSettingsLockScreenCalls.show.rawValue
|
||||
]
|
||||
|
||||
// not used anymore
|
||||
@@ -158,66 +148,14 @@ let onboardingStageDefault = EnumDefault<OnboardingStage>(defaults: UserDefaults
|
||||
|
||||
let customDisappearingMessageTimeDefault = IntDefault(defaults: UserDefaults.standard, forKey: DEFAULT_CUSTOM_DISAPPEARING_MESSAGE_TIME)
|
||||
|
||||
let currentThemeDefault = StringDefault(defaults: UserDefaults.standard, forKey: DEFAULT_CURRENT_THEME, withDefault: DefaultTheme.SYSTEM_THEME_NAME)
|
||||
let systemDarkThemeDefault = StringDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SYSTEM_DARK_THEME, withDefault: DefaultTheme.SIMPLEX.themeName)
|
||||
let currentThemeIdsDefault = CodableDefault<[String: String]>(defaults: UserDefaults.standard, forKey: DEFAULT_CURRENT_THEME_IDS, withDefault: [:] )
|
||||
let themeOverridesDefault: CodableDefault<[ThemeOverrides]> = CodableDefault(defaults: UserDefaults.standard, forKey: DEFAULT_THEME_OVERRIDES, withDefault: [])
|
||||
|
||||
func setGroupDefaults() {
|
||||
privacyAcceptImagesGroupDefault.set(UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_ACCEPT_IMAGES))
|
||||
}
|
||||
|
||||
public class StringDefault {
|
||||
var defaults: UserDefaults
|
||||
var key: String
|
||||
var defaultValue: String
|
||||
|
||||
public init(defaults: UserDefaults = UserDefaults.standard, forKey: String, withDefault: String) {
|
||||
self.defaults = defaults
|
||||
self.key = forKey
|
||||
self.defaultValue = withDefault
|
||||
}
|
||||
|
||||
public func get() -> String {
|
||||
defaults.string(forKey: key) ?? defaultValue
|
||||
}
|
||||
|
||||
public func set(_ value: String) {
|
||||
defaults.set(value, forKey: key)
|
||||
defaults.synchronize()
|
||||
}
|
||||
}
|
||||
|
||||
public class CodableDefault<T: Codable> {
|
||||
var defaults: UserDefaults
|
||||
var key: String
|
||||
var defaultValue: T
|
||||
|
||||
public init(defaults: UserDefaults = UserDefaults.standard, forKey: String, withDefault: T) {
|
||||
self.defaults = defaults
|
||||
self.key = forKey
|
||||
self.defaultValue = withDefault
|
||||
}
|
||||
|
||||
public func get() -> T {
|
||||
if let value = defaults.string(forKey: key) {
|
||||
return decodeJSON(value) ?? defaultValue
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
public func set(_ value: T) {
|
||||
defaults.set(encodeJSON(value), forKey: key)
|
||||
defaults.synchronize()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct SettingsView: View {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var sceneDelegate: SceneDelegate
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Binding var showSettings: Bool
|
||||
@State private var showProgress: Bool = false
|
||||
|
||||
@@ -242,7 +180,6 @@ struct SettingsView: View {
|
||||
NavigationLink {
|
||||
UserProfile()
|
||||
.navigationTitle("Your current profile")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
ProfilePreview(profileOf: user)
|
||||
.padding(.leading, -8)
|
||||
@@ -260,7 +197,6 @@ struct SettingsView: View {
|
||||
NavigationLink {
|
||||
UserAddressView(shareViaProfile: user.addressShared)
|
||||
.navigationTitle("SimpleX address")
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
settingsRow("qrcode") { Text("Your SimpleX address") }
|
||||
@@ -269,7 +205,6 @@ struct SettingsView: View {
|
||||
NavigationLink {
|
||||
PreferencesView(profile: user.profile, preferences: user.fullPreferences, currentPreferences: user.fullPreferences)
|
||||
.navigationTitle("Your preferences")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
settingsRow("switch.2") { Text("Chat preferences") }
|
||||
}
|
||||
@@ -284,7 +219,6 @@ struct SettingsView: View {
|
||||
NavigationLink {
|
||||
MigrateFromDevice(showSettings: $showSettings, showProgressOnSettings: $showProgress)
|
||||
.navigationTitle("Migrate device")
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
settingsRow("tray.and.arrow.up") { Text("Migrate to another device") }
|
||||
@@ -296,7 +230,6 @@ struct SettingsView: View {
|
||||
NavigationLink {
|
||||
NotificationsView()
|
||||
.navigationTitle("Notifications")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
HStack {
|
||||
notificationsIcon()
|
||||
@@ -308,7 +241,6 @@ struct SettingsView: View {
|
||||
NavigationLink {
|
||||
NetworkAndServers()
|
||||
.navigationTitle("Network & servers")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
settingsRow("externaldrive.connected.to.line.below") { Text("Network & servers") }
|
||||
}
|
||||
@@ -317,7 +249,6 @@ struct SettingsView: View {
|
||||
NavigationLink {
|
||||
CallSettings()
|
||||
.navigationTitle("Your calls")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
settingsRow("video") { Text("Audio & video calls") }
|
||||
}
|
||||
@@ -326,7 +257,6 @@ struct SettingsView: View {
|
||||
NavigationLink {
|
||||
PrivacySettings()
|
||||
.navigationTitle("Your privacy")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
settingsRow("lock") { Text("Privacy & security") }
|
||||
}
|
||||
@@ -336,7 +266,6 @@ struct SettingsView: View {
|
||||
NavigationLink {
|
||||
AppearanceSettings()
|
||||
.navigationTitle("Appearance")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
settingsRow("sun.max") { Text("Appearance") }
|
||||
}
|
||||
@@ -351,7 +280,6 @@ struct SettingsView: View {
|
||||
NavigationLink {
|
||||
ChatHelp(showSettings: $showSettings)
|
||||
.navigationTitle("Welcome \(user.displayName)!")
|
||||
.modifier(ThemedBackground())
|
||||
.frame(maxHeight: .infinity, alignment: .top)
|
||||
} label: {
|
||||
settingsRow("questionmark") { Text("How to use it") }
|
||||
@@ -359,7 +287,6 @@ struct SettingsView: View {
|
||||
}
|
||||
NavigationLink {
|
||||
WhatsNewView(viaSettings: true)
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
} label: {
|
||||
settingsRow("plus") { Text("What's new") }
|
||||
@@ -367,7 +294,6 @@ struct SettingsView: View {
|
||||
NavigationLink {
|
||||
SimpleXInfo(onboarding: false)
|
||||
.navigationBarTitle("", displayMode: .inline)
|
||||
.modifier(ThemedBackground())
|
||||
.frame(maxHeight: .infinity, alignment: .top)
|
||||
} label: {
|
||||
settingsRow("info") { Text("About SimpleX Chat") }
|
||||
@@ -407,21 +333,18 @@ struct SettingsView: View {
|
||||
NavigationLink {
|
||||
DeveloperView()
|
||||
.navigationTitle("Developer tools")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
settingsRow("chevron.left.forwardslash.chevron.right") { Text("Developer tools") }
|
||||
}
|
||||
NavigationLink {
|
||||
VersionView()
|
||||
.navigationBarTitle("App version")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
Text("v\(appVersion ?? "?") (\(appBuild ?? "?"))")
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Your settings")
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
.onDisappear {
|
||||
chatModel.showingTerminal = false
|
||||
@@ -433,7 +356,6 @@ struct SettingsView: View {
|
||||
NavigationLink {
|
||||
DatabaseView(showSettings: $showSettings, chatItemTTL: chatModel.chatItemTTL)
|
||||
.navigationTitle("Your chat database")
|
||||
.modifier(ThemedBackground())
|
||||
} label: {
|
||||
let color: Color = chatModel.chatDbEncrypted == false ? .orange : .secondary
|
||||
settingsRow("internaldrive", color: color) {
|
||||
|
||||
@@ -300,7 +300,6 @@ struct UserAddressView: View {
|
||||
NavigationLink {
|
||||
UserAddressLearnMore()
|
||||
.navigationTitle("SimpleX address")
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
settingsRow("info.circle") {
|
||||
|
||||
@@ -8,7 +8,6 @@ import SimpleXChat
|
||||
|
||||
struct UserProfilesView: View {
|
||||
@EnvironmentObject private var m: ChatModel
|
||||
@EnvironmentObject private var theme: AppTheme
|
||||
@Binding var showSettings: Bool
|
||||
@Environment(\.editMode) private var editMode
|
||||
@AppStorage(DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE) private var showHiddenProfilesNotice = true
|
||||
@@ -112,7 +111,6 @@ struct UserProfilesView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Your chat profiles")
|
||||
.modifier(ThemedBackground())
|
||||
.searchable(text: $searchTextOrPassword, placement: .navigationBarDrawer(displayMode: .always))
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
@@ -239,7 +237,6 @@ struct UserProfilesView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.modifier(ThemedBackground())
|
||||
}
|
||||
|
||||
@ViewBuilder func actionHeader(_ title: LocalizedStringKey, _ user: User) -> some View {
|
||||
@@ -359,7 +356,7 @@ struct UserProfilesView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.tint(theme.colors.primary)
|
||||
.tint(.accentColor)
|
||||
}
|
||||
}
|
||||
if #available(iOS 16, *) {
|
||||
|
||||
@@ -439,7 +439,8 @@ func doStartChat() -> DBMigrationResult? {
|
||||
logger.debug("NotificationService active user \(String(describing: user))")
|
||||
do {
|
||||
try setNetworkConfig(networkConfig)
|
||||
try apiSetAppFilePaths(filesFolder: getAppFilesDirectory().path, tempFolder: getTempFilesDirectory().path, assetsFolder: getWallpaperDirectory().deletingLastPathComponent().path)
|
||||
try apiSetTempFolder(tempFolder: getTempFilesDirectory().path)
|
||||
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
|
||||
try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get())
|
||||
// prevent suspension while starting chat
|
||||
suspendLock.wait()
|
||||
@@ -659,8 +660,14 @@ func apiSuspendChat(timeoutMicroseconds: Int) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String) throws {
|
||||
let r = sendSimpleXCmd(.apiSetAppFilePaths(filesFolder: filesFolder, tempFolder: tempFolder, assetsFolder: assetsFolder))
|
||||
func apiSetTempFolder(tempFolder: String) throws {
|
||||
let r = sendSimpleXCmd(.setTempFolder(tempFolder: tempFolder))
|
||||
if case .cmdOk = r { return }
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiSetFilesFolder(filesFolder: String) throws {
|
||||
let r = sendSimpleXCmd(.setFilesFolder(filesFolder: filesFolder))
|
||||
if case .cmdOk = r { return }
|
||||
throw r
|
||||
}
|
||||
|
||||
@@ -163,6 +163,11 @@
|
||||
64466DC829FC2B3B00E3D48D /* CreateSimpleXAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */; };
|
||||
64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DCB29FFE3E800E3D48D /* MailView.swift */; };
|
||||
6448BBB628FA9D56000D2AB9 /* GroupLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */; };
|
||||
6449333A2AF8E51000AC506E /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933352AF8E51000AC506E /* libgmpxx.a */; };
|
||||
6449333B2AF8E51000AC506E /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933362AF8E51000AC506E /* libgmp.a */; };
|
||||
6449333C2AF8E51000AC506E /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933372AF8E51000AC506E /* libffi.a */; };
|
||||
6449333D2AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933382AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a */; };
|
||||
6449333E2AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933392AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a */; };
|
||||
644EFFDE292BCD9D00525D5B /* ComposeVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */; };
|
||||
644EFFE0292CFD7F00525D5B /* CIVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFDF292CFD7F00525D5B /* CIVoiceView.swift */; };
|
||||
644EFFE2292D089800525D5B /* FramedCIVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFE1292D089800525D5B /* FramedCIVoiceView.swift */; };
|
||||
@@ -186,15 +191,8 @@
|
||||
64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */; };
|
||||
8C05382E2B39887E006436DC /* VideoUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C05382D2B39887E006436DC /* VideoUtils.swift */; };
|
||||
8C69FE7D2B8C7D2700267E38 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */; };
|
||||
8C74C3E52C1B900600039E77 /* ThemeTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */; };
|
||||
8C74C3E72C1B901900039E77 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C852B072C1086D100BA61E8 /* Color.swift */; };
|
||||
8C74C3E82C1B905B00039E77 /* ChatWallpaperTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C804B1D2C11F966007A63C8 /* ChatWallpaperTypes.swift */; };
|
||||
8C74C3EA2C1B90AF00039E77 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */; };
|
||||
8C74C3EC2C1B92A900039E77 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C74C3EB2C1B92A900039E77 /* Theme.swift */; };
|
||||
8C74C3EE2C1B942300039E77 /* ChatWallpaper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */; };
|
||||
8C7D949A2B88952700B7B9E1 /* MigrateToDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */; };
|
||||
8C7DF3202B7CDB0A00C886D0 /* MigrateFromDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */; };
|
||||
8C7F8F0E2C19C0C100D16888 /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7F8F0D2C19C0C100D16888 /* ViewModifiers.swift */; };
|
||||
8C81482C2BD91CD4002CBEC3 /* AudioDevicePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C81482B2BD91CD4002CBEC3 /* AudioDevicePicker.swift */; };
|
||||
8CC4ED902BD7B8530078AEE8 /* CallAudioDeviceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC4ED8F2BD7B8530078AEE8 /* CallAudioDeviceManager.swift */; };
|
||||
8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */; };
|
||||
@@ -465,6 +463,11 @@
|
||||
64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSimpleXAddress.swift; sourceTree = "<group>"; };
|
||||
64466DCB29FFE3E800E3D48D /* MailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailView.swift; sourceTree = "<group>"; };
|
||||
6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupLinkView.swift; sourceTree = "<group>"; };
|
||||
644933352AF8E51000AC506E /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
644933362AF8E51000AC506E /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
644933372AF8E51000AC506E /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
644933382AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
644933392AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a"; sourceTree = "<group>"; };
|
||||
644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeVoiceView.swift; sourceTree = "<group>"; };
|
||||
644EFFDF292CFD7F00525D5B /* CIVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIVoiceView.swift; sourceTree = "<group>"; };
|
||||
644EFFE1292D089800525D5B /* FramedCIVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FramedCIVoiceView.swift; sourceTree = "<group>"; };
|
||||
@@ -490,16 +493,9 @@
|
||||
64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncognitoHelp.swift; sourceTree = "<group>"; };
|
||||
8C05382D2B39887E006436DC /* VideoUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoUtils.swift; sourceTree = "<group>"; };
|
||||
8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
|
||||
8C74C3EB2C1B92A900039E77 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
|
||||
8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWallpaper.swift; sourceTree = "<group>"; };
|
||||
8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToDevice.swift; sourceTree = "<group>"; };
|
||||
8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateFromDevice.swift; sourceTree = "<group>"; };
|
||||
8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeTypes.swift; sourceTree = "<group>"; };
|
||||
8C7F8F0D2C19C0C100D16888 /* ViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = "<group>"; };
|
||||
8C804B1D2C11F966007A63C8 /* ChatWallpaperTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWallpaperTypes.swift; sourceTree = "<group>"; };
|
||||
8C81482B2BD91CD4002CBEC3 /* AudioDevicePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioDevicePicker.swift; sourceTree = "<group>"; };
|
||||
8C852B072C1086D100BA61E8 /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = "<group>"; };
|
||||
8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = "<group>"; };
|
||||
8CC4ED8F2BD7B8530078AEE8 /* CallAudioDeviceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallAudioDeviceManager.swift; sourceTree = "<group>"; };
|
||||
8CC956ED2BC0041000412A11 /* NetworkObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkObserver.swift; sourceTree = "<group>"; };
|
||||
D72A9087294BD7A70047C86D /* NativeTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeTextEditor.swift; sourceTree = "<group>"; };
|
||||
@@ -674,8 +670,6 @@
|
||||
64C3B0202A0D359700E19930 /* CustomTimePicker.swift */,
|
||||
5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */,
|
||||
8C05382D2B39887E006436DC /* VideoUtils.swift */,
|
||||
8C7F8F0D2C19C0C100D16888 /* ViewModifiers.swift */,
|
||||
8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
@@ -700,7 +694,6 @@
|
||||
5CA059C2279559F40002BEB4 /* Shared */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8C74C3E92C1B909200039E77 /* Theme */,
|
||||
5CA059C3279559F40002BEB4 /* SimpleXApp.swift */,
|
||||
5C36027227F47AD5009F19D9 /* AppDelegate.swift */,
|
||||
5CA059C4279559F40002BEB4 /* ContentView.swift */,
|
||||
@@ -837,7 +830,6 @@
|
||||
5CE2BA692845308900EC33A6 /* SimpleXChat */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8C86EBE32C0DAE3700E12243 /* Theme */,
|
||||
5CDCAD5228186F9500503DA2 /* AppGroup.swift */,
|
||||
5CDCAD7228188CFF00503DA2 /* ChatTypes.swift */,
|
||||
5CDCAD7428188D2900503DA2 /* APITypes.swift */,
|
||||
@@ -931,15 +923,6 @@
|
||||
path = Group;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8C74C3E92C1B909200039E77 /* Theme */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */,
|
||||
8C74C3EB2C1B92A900039E77 /* Theme.swift */,
|
||||
);
|
||||
path = Theme;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8C7D94982B8894D300B7B9E1 /* Migration */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -949,16 +932,6 @@
|
||||
path = Migration;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8C86EBE32C0DAE3700E12243 /* Theme */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */,
|
||||
8C852B072C1086D100BA61E8 /* Color.swift */,
|
||||
8C804B1D2C11F966007A63C8 /* ChatWallpaperTypes.swift */,
|
||||
);
|
||||
path = Theme;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
@@ -1189,7 +1162,6 @@
|
||||
5C93293129239BED0090FFF9 /* ProtocolServerView.swift in Sources */,
|
||||
5C9CC7AD28C55D7800BEF955 /* DatabaseEncryptionView.swift in Sources */,
|
||||
5CBD285A295711D700EC2CF4 /* ImageUtils.swift in Sources */,
|
||||
8C74C3EC2C1B92A900039E77 /* Theme.swift in Sources */,
|
||||
6419EC562AB8BC8B004A607A /* ContextInvitingContactMemberView.swift in Sources */,
|
||||
5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */,
|
||||
8C7D949A2B88952700B7B9E1 /* MigrateToDevice.swift in Sources */,
|
||||
@@ -1222,7 +1194,6 @@
|
||||
5CB0BA90282713D900B3292C /* SimpleXInfo.swift in Sources */,
|
||||
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */,
|
||||
5CC868F329EB540C0017BBFD /* CIRcvDecryptionError.swift in Sources */,
|
||||
8C7F8F0E2C19C0C100D16888 /* ViewModifiers.swift in Sources */,
|
||||
5C35CFCB27B2E91D00FB6C6D /* NtfManager.swift in Sources */,
|
||||
5C9D13A3282187BB00AB8B43 /* WebRTC.swift in Sources */,
|
||||
5C9A5BDB2871E05400A5B906 /* SetNotificationsMode.swift in Sources */,
|
||||
@@ -1247,7 +1218,6 @@
|
||||
5CB346E72868D76D001FD2EF /* NotificationsView.swift in Sources */,
|
||||
647F090E288EA27B00644C40 /* GroupMemberInfoView.swift in Sources */,
|
||||
646BB38E283FDB6D001CE359 /* LocalAuthenticationUtils.swift in Sources */,
|
||||
8C74C3EA2C1B90AF00039E77 /* ThemeManager.swift in Sources */,
|
||||
5C7505A227B65FDB00BE3227 /* CIMetaView.swift in Sources */,
|
||||
5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */,
|
||||
5C35CFC827B2782E00FB6C6D /* BGManager.swift in Sources */,
|
||||
@@ -1265,7 +1235,6 @@
|
||||
6454036F2822A9750090DDFF /* ComposeFileView.swift in Sources */,
|
||||
5C5DB70E289ABDD200730FFF /* AppearanceSettings.swift in Sources */,
|
||||
5C5F2B6D27EBC3FE006A9D5F /* ImagePicker.swift in Sources */,
|
||||
8C74C3EE2C1B942300039E77 /* ChatWallpaper.swift in Sources */,
|
||||
5C3CCFCC2AE6BD3100C3F0C3 /* ConnectDesktopView.swift in Sources */,
|
||||
5C9C2DA92899DA6F00CC63B1 /* NetworkAndServers.swift in Sources */,
|
||||
5C6BA667289BD954009B8ECC /* DismissSheets.swift in Sources */,
|
||||
@@ -1350,7 +1319,6 @@
|
||||
5C00168128C4FE760094D739 /* KeyChain.swift in Sources */,
|
||||
5CE2BA97284537A800EC33A6 /* dummy.m in Sources */,
|
||||
5CE2BA922845340900EC33A6 /* FileUtils.swift in Sources */,
|
||||
8C74C3E72C1B901900039E77 /* Color.swift in Sources */,
|
||||
5CD67B902B0E858A00C510B1 /* hs_init.c in Sources */,
|
||||
5CE2BA91284533A300EC33A6 /* Notifications.swift in Sources */,
|
||||
5CE2BA79284530CC00EC33A6 /* SimpleXChat.docc in Sources */,
|
||||
@@ -1359,9 +1327,7 @@
|
||||
5CE2BA8F284533A300EC33A6 /* APITypes.swift in Sources */,
|
||||
5C9D811A2AA8727A001D49FD /* CryptoFile.swift in Sources */,
|
||||
5CE2BA8C284533A300EC33A6 /* AppGroup.swift in Sources */,
|
||||
8C74C3E52C1B900600039E77 /* ThemeTypes.swift in Sources */,
|
||||
5CE2BA8D284533A300EC33A6 /* CallTypes.swift in Sources */,
|
||||
8C74C3E82C1B905B00039E77 /* ChatWallpaperTypes.swift in Sources */,
|
||||
5CE2BA8E284533A300EC33A6 /* API.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
@@ -29,7 +29,8 @@ public enum ChatCommand {
|
||||
case apiStopChat
|
||||
case apiActivateChat(restoreChat: Bool)
|
||||
case apiSuspendChat(timeoutMicroseconds: Int)
|
||||
case apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String)
|
||||
case setTempFolder(tempFolder: String)
|
||||
case setFilesFolder(filesFolder: String)
|
||||
case apiSetEncryptLocalFiles(enable: Bool)
|
||||
case apiExportArchive(config: ArchiveConfig)
|
||||
case apiImportArchive(config: ArchiveConfig)
|
||||
@@ -105,8 +106,6 @@ public enum ChatCommand {
|
||||
case apiSetContactPrefs(contactId: Int64, preferences: Preferences)
|
||||
case apiSetContactAlias(contactId: Int64, localAlias: String)
|
||||
case apiSetConnectionAlias(connId: Int64, localAlias: String)
|
||||
case apiSetUserUIThemes(userId: Int64, themes: ThemeModeOverrides?)
|
||||
case apiSetChatUIThemes(chatId: String, themes: ThemeModeOverrides?)
|
||||
case apiCreateMyAddress(userId: Int64)
|
||||
case apiDeleteMyAddress(userId: Int64)
|
||||
case apiShowMyAddress(userId: Int64)
|
||||
@@ -170,7 +169,8 @@ public enum ChatCommand {
|
||||
case .apiStopChat: return "/_stop"
|
||||
case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))"
|
||||
case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)"
|
||||
case let .apiSetAppFilePaths(filesFolder, tempFolder, assetsFolder): return "/set file paths \(encodeJSON(AppFilePaths(appFilesFolder: filesFolder, appTempFolder: tempFolder, appAssetsFolder: assetsFolder)))"
|
||||
case let .setTempFolder(tempFolder): return "/_temp_folder \(tempFolder)"
|
||||
case let .setFilesFolder(filesFolder): return "/_files_folder \(filesFolder)"
|
||||
case let .apiSetEncryptLocalFiles(enable): return "/_files_encrypt \(onOff(enable))"
|
||||
case let .apiExportArchive(cfg): return "/_db export \(encodeJSON(cfg))"
|
||||
case let .apiImportArchive(cfg): return "/_db import \(encodeJSON(cfg))"
|
||||
@@ -268,8 +268,6 @@ public enum ChatCommand {
|
||||
case let .apiSetContactPrefs(contactId, preferences): return "/_set prefs @\(contactId) \(encodeJSON(preferences))"
|
||||
case let .apiSetContactAlias(contactId, localAlias): return "/_set alias @\(contactId) \(localAlias.trimmingCharacters(in: .whitespaces))"
|
||||
case let .apiSetConnectionAlias(connId, localAlias): return "/_set alias :\(connId) \(localAlias.trimmingCharacters(in: .whitespaces))"
|
||||
case let .apiSetUserUIThemes(userId, themes): return "/_set theme user \(userId) \(themes != nil ? encodeJSON(themes) : "")"
|
||||
case let .apiSetChatUIThemes(chatId, themes): return "/_set theme \(chatId) \(themes != nil ? encodeJSON(themes) : "")"
|
||||
case let .apiCreateMyAddress(userId): return "/_address \(userId)"
|
||||
case let .apiDeleteMyAddress(userId): return "/_delete_address \(userId)"
|
||||
case let .apiShowMyAddress(userId): return "/_show_address \(userId)"
|
||||
@@ -327,7 +325,8 @@ public enum ChatCommand {
|
||||
case .apiStopChat: return "apiStopChat"
|
||||
case .apiActivateChat: return "apiActivateChat"
|
||||
case .apiSuspendChat: return "apiSuspendChat"
|
||||
case .apiSetAppFilePaths: return "apiSetAppFilePaths"
|
||||
case .setTempFolder: return "setTempFolder"
|
||||
case .setFilesFolder: return "setFilesFolder"
|
||||
case .apiSetEncryptLocalFiles: return "apiSetEncryptLocalFiles"
|
||||
case .apiExportArchive: return "apiExportArchive"
|
||||
case .apiImportArchive: return "apiImportArchive"
|
||||
@@ -403,8 +402,6 @@ public enum ChatCommand {
|
||||
case .apiSetContactPrefs: return "apiSetContactPrefs"
|
||||
case .apiSetContactAlias: return "apiSetContactAlias"
|
||||
case .apiSetConnectionAlias: return "apiSetConnectionAlias"
|
||||
case .apiSetUserUIThemes: return "apiSetUserUIThemes"
|
||||
case .apiSetChatUIThemes: return "apiSetChatUIThemes"
|
||||
case .apiCreateMyAddress: return "apiCreateMyAddress"
|
||||
case .apiDeleteMyAddress: return "apiDeleteMyAddress"
|
||||
case .apiShowMyAddress: return "apiShowMyAddress"
|
||||
@@ -625,8 +622,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case rcvStandaloneFileComplete(user: UserRef, targetPath: String, rcvFileTransfer: RcvFileTransfer)
|
||||
case rcvFileCancelled(user: UserRef, chatItem_: AChatItem?, rcvFileTransfer: RcvFileTransfer)
|
||||
case rcvFileSndCancelled(user: UserRef, chatItem: AChatItem, rcvFileTransfer: RcvFileTransfer)
|
||||
case rcvFileError(user: UserRef, chatItem_: AChatItem?, agentError: AgentErrorType, rcvFileTransfer: RcvFileTransfer)
|
||||
case rcvFileWarning(user: UserRef, chatItem_: AChatItem?, agentError: AgentErrorType, rcvFileTransfer: RcvFileTransfer)
|
||||
case rcvFileError(user: UserRef, chatItem_: AChatItem?, rcvFileTransfer: RcvFileTransfer)
|
||||
// sending file events
|
||||
case sndFileStart(user: UserRef, chatItem: AChatItem, sndFileTransfer: SndFileTransfer)
|
||||
case sndFileComplete(user: UserRef, chatItem: AChatItem, sndFileTransfer: SndFileTransfer)
|
||||
@@ -640,7 +636,6 @@ public enum ChatResponse: Decodable, Error {
|
||||
case sndStandaloneFileComplete(user: UserRef, fileTransferMeta: FileTransferMeta, rcvURIs: [String])
|
||||
case sndFileCancelledXFTP(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta)
|
||||
case sndFileError(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, errorMessage: String)
|
||||
case sndFileWarning(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, errorMessage: String)
|
||||
// call events
|
||||
case callInvitation(callInvitation: RcvCallInvitation)
|
||||
case callOffer(user: UserRef, contact: Contact, callType: CallType, offer: WebRTCSession, sharedKey: String?, askConfirmation: Bool)
|
||||
@@ -789,7 +784,6 @@ public enum ChatResponse: Decodable, Error {
|
||||
case .rcvFileCancelled: return "rcvFileCancelled"
|
||||
case .rcvFileSndCancelled: return "rcvFileSndCancelled"
|
||||
case .rcvFileError: return "rcvFileError"
|
||||
case .rcvFileWarning: return "rcvFileWarning"
|
||||
case .sndFileStart: return "sndFileStart"
|
||||
case .sndFileComplete: return "sndFileComplete"
|
||||
case .sndFileCancelled: return "sndFileCancelled"
|
||||
@@ -802,7 +796,6 @@ public enum ChatResponse: Decodable, Error {
|
||||
case .sndStandaloneFileComplete: return "sndStandaloneFileComplete"
|
||||
case .sndFileCancelledXFTP: return "sndFileCancelledXFTP"
|
||||
case .sndFileError: return "sndFileError"
|
||||
case .sndFileWarning: return "sndFileWarning"
|
||||
case .callInvitation: return "callInvitation"
|
||||
case .callOffer: return "callOffer"
|
||||
case .callAnswer: return "callAnswer"
|
||||
@@ -951,8 +944,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case let .rcvFileComplete(u, chatItem): return withUser(u, String(describing: chatItem))
|
||||
case let .rcvFileCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .rcvFileSndCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .rcvFileError(u, chatItem, agentError, _): return withUser(u, "agentError: \(String(describing: agentError))\nchatItem: \(String(describing: chatItem))")
|
||||
case let .rcvFileWarning(u, chatItem, agentError, _): return withUser(u, "agentError: \(String(describing: agentError))\nchatItem: \(String(describing: chatItem))")
|
||||
case let .rcvFileError(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileStart(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileComplete(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileCancelled(u, chatItem, _, _): return withUser(u, String(describing: chatItem))
|
||||
@@ -965,7 +957,6 @@ public enum ChatResponse: Decodable, Error {
|
||||
case let .sndStandaloneFileComplete(u, _, rcvURIs): return withUser(u, String(rcvURIs.count))
|
||||
case let .sndFileCancelledXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileError(u, chatItem, _, err): return withUser(u, "error: \(String(describing: err))\nchatItem: \(String(describing: chatItem))")
|
||||
case let .sndFileWarning(u, chatItem, _, err): return withUser(u, "error: \(String(describing: err))\nchatItem: \(String(describing: chatItem))")
|
||||
case let .callInvitation(inv): return String(describing: inv)
|
||||
case let .callOffer(u, contact, callType, offer, sharedKey, askConfirmation): return withUser(u, "contact: \(contact.id)\ncallType: \(String(describing: callType))\nsharedKey: \(sharedKey ?? "")\naskConfirmation: \(askConfirmation)\noffer: \(String(describing: offer))")
|
||||
case let .callAnswer(u, contact, answer): return withUser(u, "contact: \(contact.id)\nanswer: \(String(describing: answer))")
|
||||
@@ -983,7 +974,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case let .remoteCtrlConnecting(remoteCtrl_, ctrlAppInfo, appVersion): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nctrlAppInfo:\n\(String(describing: ctrlAppInfo))\nappVersion: \(appVersion)"
|
||||
case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nsessionCode: \(sessionCode)"
|
||||
case let .remoteCtrlConnected(remoteCtrl): return String(describing: remoteCtrl)
|
||||
case let .remoteCtrlStopped(rcsState, rcStopReason): return "rcsState: \(String(describing: rcsState))\nrcStopReason: \(String(describing: rcStopReason))"
|
||||
case .remoteCtrlStopped: return noDetails
|
||||
case let .contactPQEnabled(u, contact, pqEnabled): return withUser(u, "contact: \(String(describing: contact))\npqEnabled: \(pqEnabled)")
|
||||
case let .versionInfo(versionInfo, chatMigrations, agentMigrations): return "\(String(describing: versionInfo))\n\nchat migrations: \(chatMigrations.map(\.upName))\n\nagent migrations: \(agentMigrations.map(\.upName))"
|
||||
case .cmdOk: return noDetails
|
||||
@@ -1780,6 +1771,8 @@ public enum ChatErrorType: Decodable {
|
||||
case fileImageSize(filePath: String)
|
||||
case fileNotReceived(fileId: Int64)
|
||||
case fileNotApproved(fileId: Int64, unknownServers: [String])
|
||||
// case xFTPRcvFile
|
||||
// case xFTPSndFile
|
||||
case fallbackToSMPProhibited(fileId: Int64)
|
||||
case inlineFileProhibited(fileId: Int64)
|
||||
case invalidQuote
|
||||
@@ -2074,11 +2067,6 @@ public struct AppSettings: Codable, Equatable {
|
||||
public var androidCallOnLockScreen: AppSettingsLockScreenCalls? = nil
|
||||
public var iosCallKitEnabled: Bool? = nil
|
||||
public var iosCallKitCallsInRecents: Bool? = nil
|
||||
public var uiProfileImageCornerRadius: Float? = nil
|
||||
public var uiColorScheme: String? = nil
|
||||
public var uiDarkColorScheme: String? = nil
|
||||
public var uiCurrentThemeIds: [String: String]? = nil
|
||||
public var uiThemes: [ThemeOverrides]? = nil
|
||||
|
||||
public func prepareForExport() -> AppSettings {
|
||||
var empty = AppSettings()
|
||||
@@ -2103,11 +2091,6 @@ public struct AppSettings: Codable, Equatable {
|
||||
if androidCallOnLockScreen != def.androidCallOnLockScreen { empty.androidCallOnLockScreen = androidCallOnLockScreen }
|
||||
if iosCallKitEnabled != def.iosCallKitEnabled { empty.iosCallKitEnabled = iosCallKitEnabled }
|
||||
if iosCallKitCallsInRecents != def.iosCallKitCallsInRecents { empty.iosCallKitCallsInRecents = iosCallKitCallsInRecents }
|
||||
if uiProfileImageCornerRadius != def.uiProfileImageCornerRadius { empty.uiProfileImageCornerRadius = uiProfileImageCornerRadius }
|
||||
if uiColorScheme != def.uiColorScheme { empty.uiColorScheme = uiColorScheme }
|
||||
if uiDarkColorScheme != def.uiDarkColorScheme { empty.uiDarkColorScheme = uiDarkColorScheme }
|
||||
if uiCurrentThemeIds != def.uiCurrentThemeIds { empty.uiCurrentThemeIds = uiCurrentThemeIds }
|
||||
if uiThemes != def.uiThemes { empty.uiThemes = uiThemes }
|
||||
return empty
|
||||
}
|
||||
|
||||
@@ -2132,12 +2115,7 @@ public struct AppSettings: Codable, Equatable {
|
||||
confirmDBUpgrades: false,
|
||||
androidCallOnLockScreen: AppSettingsLockScreenCalls.show,
|
||||
iosCallKitEnabled: true,
|
||||
iosCallKitCallsInRecents: false,
|
||||
uiProfileImageCornerRadius: 22.5,
|
||||
uiColorScheme: DefaultTheme.SYSTEM_THEME_NAME,
|
||||
uiDarkColorScheme: DefaultTheme.SIMPLEX.themeName,
|
||||
uiCurrentThemeIds: nil as [String: String]?,
|
||||
uiThemes: nil as [ThemeOverrides]?
|
||||
iosCallKitCallsInRecents: false
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -2242,9 +2220,3 @@ public enum MsgType: String, Codable {
|
||||
case message
|
||||
case quota
|
||||
}
|
||||
|
||||
public struct AppFilePaths: Encodable {
|
||||
public let appFilesFolder: String
|
||||
public let appTempFolder: String
|
||||
public let appAssetsFolder: String
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ public struct User: Identifiable, Decodable, UserLike, NamedChat {
|
||||
public var sendRcptsContacts: Bool
|
||||
public var sendRcptsSmallGroups: Bool
|
||||
public var viewPwdHash: UserPwdHash?
|
||||
public var uiThemes: ThemeModeOverrides?
|
||||
|
||||
public var id: Int64 { userId }
|
||||
|
||||
@@ -1505,7 +1504,6 @@ public struct Contact: Identifiable, Decodable, NamedChat {
|
||||
var chatTs: Date?
|
||||
var contactGroupMemberId: Int64?
|
||||
var contactGrpInvSent: Bool
|
||||
public var uiThemes: ThemeModeOverrides?
|
||||
|
||||
public var id: ChatId { get { "@\(contactId)" } }
|
||||
public var apiId: Int64 { get { contactId } }
|
||||
@@ -1845,7 +1843,6 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat {
|
||||
var createdAt: Date
|
||||
var updatedAt: Date
|
||||
var chatTs: Date?
|
||||
public var uiThemes: ThemeModeOverrides?
|
||||
|
||||
public var id: ChatId { get { "#\(groupId)" } }
|
||||
public var apiId: Int64 { get { groupId } }
|
||||
@@ -2656,8 +2653,8 @@ public struct CIMeta: Decodable {
|
||||
return false
|
||||
}
|
||||
|
||||
public func statusIcon(_ metaColor: Color = .secondary, _ primaryColor: Color = .accentColor) -> (String, Color)? {
|
||||
itemStatus.statusIcon(metaColor, primaryColor)
|
||||
public func statusIcon(_ metaColor: Color = .secondary) -> (String, Color)? {
|
||||
itemStatus.statusIcon(metaColor)
|
||||
}
|
||||
|
||||
public static func getSample(_ id: Int64, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, deletable: Bool = true, editable: Bool = true) -> CIMeta {
|
||||
@@ -2731,7 +2728,7 @@ public enum CIStatus: Decodable {
|
||||
case rcvRead
|
||||
case invalid(text: String)
|
||||
|
||||
public var id: String {
|
||||
var id: String {
|
||||
switch self {
|
||||
case .sndNew: return "sndNew"
|
||||
case .sndSent: return "sndSent"
|
||||
@@ -2745,7 +2742,7 @@ public enum CIStatus: Decodable {
|
||||
}
|
||||
}
|
||||
|
||||
public func statusIcon(_ metaColor: Color = .secondary, _ primaryColor: Color = .accentColor) -> (String, Color)? {
|
||||
public func statusIcon(_ metaColor: Color = .secondary) -> (String, Color)? {
|
||||
switch self {
|
||||
case .sndNew: return nil
|
||||
case .sndSent: return ("checkmark", metaColor)
|
||||
@@ -2757,7 +2754,7 @@ public enum CIStatus: Decodable {
|
||||
case .sndErrorAuth: return ("multiply", .red)
|
||||
case .sndError: return ("multiply", .red)
|
||||
case .sndWarning: return ("exclamationmark.triangle.fill", .orange)
|
||||
case .rcvNew: return ("circlebadge.fill", primaryColor)
|
||||
case .rcvNew: return ("circlebadge.fill", Color.accentColor)
|
||||
case .rcvRead: return nil
|
||||
case .invalid: return ("questionmark", metaColor)
|
||||
}
|
||||
@@ -2812,19 +2809,11 @@ public enum SndError: Decodable {
|
||||
}
|
||||
}
|
||||
|
||||
public enum SrvError: Decodable, Equatable {
|
||||
public enum SrvError: Decodable {
|
||||
case host
|
||||
case version
|
||||
case other(srvError: String)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case .host: return "host"
|
||||
case .version: return "version"
|
||||
case let .other(srvError): return "other \(srvError)"
|
||||
}
|
||||
}
|
||||
|
||||
public var errorInfo: String {
|
||||
switch self {
|
||||
case .host: NSLocalizedString("Server address is incompatible with network settings.", comment: "srv error text.")
|
||||
@@ -3182,7 +3171,6 @@ public struct CIFile: Decodable {
|
||||
case .sndComplete: return true
|
||||
case .sndCancelled: return true
|
||||
case .sndError: return true
|
||||
case .sndWarning: return true
|
||||
case .rcvInvitation: return false
|
||||
case .rcvAccepted: return false
|
||||
case .rcvTransfer: return false
|
||||
@@ -3190,7 +3178,6 @@ public struct CIFile: Decodable {
|
||||
case .rcvCancelled: return false
|
||||
case .rcvComplete: return true
|
||||
case .rcvError: return false
|
||||
case .rcvWarning: return false
|
||||
case .invalid: return false
|
||||
}
|
||||
}
|
||||
@@ -3209,14 +3196,12 @@ public struct CIFile: Decodable {
|
||||
}
|
||||
case .sndCancelled: return nil
|
||||
case .sndError: return nil
|
||||
case .sndWarning: return sndCancelAction
|
||||
case .rcvInvitation: return nil
|
||||
case .rcvAccepted: return rcvCancelAction
|
||||
case .rcvTransfer: return rcvCancelAction
|
||||
case .rcvAborted: return nil
|
||||
case .rcvCancelled: return nil
|
||||
case .rcvComplete: return nil
|
||||
case .rcvWarning: return rcvCancelAction
|
||||
case .rcvError: return nil
|
||||
case .invalid: return nil
|
||||
}
|
||||
@@ -3325,64 +3310,35 @@ public enum CIFileStatus: Decodable, Equatable {
|
||||
case sndTransfer(sndProgress: Int64, sndTotal: Int64)
|
||||
case sndComplete
|
||||
case sndCancelled
|
||||
case sndError(sndFileError: FileError)
|
||||
case sndWarning(sndFileError: FileError)
|
||||
case sndError
|
||||
case rcvInvitation
|
||||
case rcvAccepted
|
||||
case rcvTransfer(rcvProgress: Int64, rcvTotal: Int64)
|
||||
case rcvAborted
|
||||
case rcvComplete
|
||||
case rcvCancelled
|
||||
case rcvError(rcvFileError: FileError)
|
||||
case rcvWarning(rcvFileError: FileError)
|
||||
case rcvError
|
||||
case invalid(text: String)
|
||||
|
||||
public var id: String {
|
||||
var id: String {
|
||||
switch self {
|
||||
case .sndStored: return "sndStored"
|
||||
case let .sndTransfer(sndProgress, sndTotal): return "sndTransfer \(sndProgress) \(sndTotal)"
|
||||
case .sndComplete: return "sndComplete"
|
||||
case .sndCancelled: return "sndCancelled"
|
||||
case let .sndError(sndFileError): return "sndError \(sndFileError)"
|
||||
case let .sndWarning(sndFileError): return "sndWarning \(sndFileError)"
|
||||
case .sndError: return "sndError"
|
||||
case .rcvInvitation: return "rcvInvitation"
|
||||
case .rcvAccepted: return "rcvAccepted"
|
||||
case let .rcvTransfer(rcvProgress, rcvTotal): return "rcvTransfer \(rcvProgress) \(rcvTotal)"
|
||||
case .rcvAborted: return "rcvAborted"
|
||||
case .rcvComplete: return "rcvComplete"
|
||||
case .rcvCancelled: return "rcvCancelled"
|
||||
case let .rcvError(rcvFileError): return "rcvError \(rcvFileError)"
|
||||
case let .rcvWarning(rcvFileError): return "rcvWarning \(rcvFileError)"
|
||||
case .rcvError: return "rcvError"
|
||||
case .invalid: return "invalid"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum FileError: Decodable, Equatable {
|
||||
case auth
|
||||
case noFile
|
||||
case relay(srvError: SrvError)
|
||||
case other(fileError: String)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case .auth: return "auth"
|
||||
case .noFile: return "noFile"
|
||||
case let .relay(srvError): return "relay \(srvError)"
|
||||
case let .other(fileError): return "other \(fileError)"
|
||||
}
|
||||
}
|
||||
|
||||
public var errorInfo: String {
|
||||
switch self {
|
||||
case .auth: NSLocalizedString("Wrong key or unknown file chunk address - most likely file is deleted.", comment: "file error text")
|
||||
case .noFile: NSLocalizedString("File not found - most likely file was deleted or cancelled.", comment: "file error text")
|
||||
case let .relay(srvError): String.localizedStringWithFormat(NSLocalizedString("File server error: %@", comment: "file error text"), srvError.errorInfo)
|
||||
case let .other(fileError): String.localizedStringWithFormat(NSLocalizedString("Error: %@", comment: "file error text"), fileError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum MsgContent: Equatable {
|
||||
case text(String)
|
||||
case link(text: String, preview: LinkPreview)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user