mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
Compare commits
7 Commits
av/node-addon
...
stable
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d82afe602 | |||
| 591f74a8e3 | |||
| aede65db14 | |||
| 0bf82c08a1 | |||
| ddef5a122c | |||
| 0fdd2e04cc | |||
| d1e66386f5 |
@@ -440,6 +440,7 @@ struct ChatView: View {
|
|||||||
maxWidth: maxWidth,
|
maxWidth: maxWidth,
|
||||||
composeState: $composeState,
|
composeState: $composeState,
|
||||||
selectedMember: $selectedMember,
|
selectedMember: $selectedMember,
|
||||||
|
showChatInfoSheet: $showChatInfoSheet,
|
||||||
revealedChatItem: $revealedChatItem,
|
revealedChatItem: $revealedChatItem,
|
||||||
selectedChatItems: $selectedChatItems,
|
selectedChatItems: $selectedChatItems,
|
||||||
forwardedChatItems: $forwardedChatItems
|
forwardedChatItems: $forwardedChatItems
|
||||||
@@ -893,12 +894,14 @@ struct ChatView: View {
|
|||||||
private struct ChatItemWithMenu: View {
|
private struct ChatItemWithMenu: View {
|
||||||
@EnvironmentObject var m: ChatModel
|
@EnvironmentObject var m: ChatModel
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
|
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var profileRadius = defaultProfileImageCorner
|
||||||
@Binding @ObservedObject var chat: Chat
|
@Binding @ObservedObject var chat: Chat
|
||||||
@ObservedObject var dummyModel: ChatItemDummyModel = .shared
|
@ObservedObject var dummyModel: ChatItemDummyModel = .shared
|
||||||
let chatItem: ChatItem
|
let chatItem: ChatItem
|
||||||
let maxWidth: CGFloat
|
let maxWidth: CGFloat
|
||||||
@Binding var composeState: ComposeState
|
@Binding var composeState: ComposeState
|
||||||
@Binding var selectedMember: GMember?
|
@Binding var selectedMember: GMember?
|
||||||
|
@Binding var showChatInfoSheet: Bool
|
||||||
@Binding var revealedChatItem: ChatItem?
|
@Binding var revealedChatItem: ChatItem?
|
||||||
|
|
||||||
@State private var deletingItem: ChatItem? = nil
|
@State private var deletingItem: ChatItem? = nil
|
||||||
@@ -1255,16 +1258,22 @@ struct ChatView: View {
|
|||||||
setReaction(ci, add: !r.userReacted, reaction: r.reaction)
|
setReaction(ci, add: !r.userReacted, reaction: r.reaction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if case let .group(groupInfo) = chat.chatInfo {
|
switch chat.chatInfo {
|
||||||
|
case let .group(groupInfo):
|
||||||
v.contextMenu {
|
v.contextMenu {
|
||||||
ReactionContextMenu(
|
ReactionContextMenu(
|
||||||
groupInfo: groupInfo,
|
groupInfo: groupInfo,
|
||||||
itemId: ci.id,
|
itemId: ci.id,
|
||||||
reactionCount: r,
|
reactionCount: r,
|
||||||
selectedMember: $selectedMember
|
selectedMember: $selectedMember,
|
||||||
|
profileRadius: profileRadius
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
case let .direct(contact):
|
||||||
|
v.contextMenu {
|
||||||
|
contactReactionMenu(contact, r)
|
||||||
|
}
|
||||||
|
default:
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1767,6 +1776,20 @@ struct ChatView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ViewBuilder private func contactReactionMenu(_ contact: Contact, _ r: CIReactionCount) -> some View {
|
||||||
|
if !r.userReacted || r.totalReacted > 1 {
|
||||||
|
Button { showChatInfoSheet = true } label: {
|
||||||
|
profileMenuItem(Text(contact.displayName), contact.image, radius: profileRadius)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.userReacted {
|
||||||
|
Button {} label: {
|
||||||
|
profileMenuItem(Text("you"), m.currentUser?.profile.image, radius: profileRadius)
|
||||||
|
}
|
||||||
|
.disabled(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private struct SelectedChatItem: View {
|
private struct SelectedChatItem: View {
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@@ -1859,13 +1882,12 @@ struct ReactionContextMenu: View {
|
|||||||
var itemId: Int64
|
var itemId: Int64
|
||||||
var reactionCount: CIReactionCount
|
var reactionCount: CIReactionCount
|
||||||
@Binding var selectedMember: GMember?
|
@Binding var selectedMember: GMember?
|
||||||
|
var profileRadius: CGFloat
|
||||||
@State private var memberReactions: [MemberReaction] = []
|
@State private var memberReactions: [MemberReaction] = []
|
||||||
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var radius = defaultProfileImageCorner
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
groupMemberReactionList()
|
groupMemberReactionList()
|
||||||
.task {
|
.task {
|
||||||
logger.debug("ReactionContextMenu task \(radius)")
|
|
||||||
await loadChatItemReaction()
|
await loadChatItemReaction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1889,27 +1911,12 @@ struct ReactionContextMenu: View {
|
|||||||
selectedMember = member
|
selectedMember = member
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
profileMenuItem(Text(mem.displayName), mem.image, radius: profileRadius)
|
||||||
Text(mem.displayName)
|
|
||||||
if let img = cropImage(mem.image) {
|
|
||||||
Image(uiImage: img)
|
|
||||||
} else {
|
|
||||||
Image(systemName: "person.crop.circle")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.disabled(userMember)
|
.disabled(userMember)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func cropImage(_ img: String?) -> UIImage? {
|
|
||||||
return if let originalImage = imageFromBase64(img) {
|
|
||||||
maskToCustomShape(originalImage, size: 30, radius: radius)
|
|
||||||
} else {
|
|
||||||
nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loadChatItemReaction() async {
|
private func loadChatItemReaction() async {
|
||||||
do {
|
do {
|
||||||
@@ -1927,6 +1934,17 @@ struct ReactionContextMenu: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func profileMenuItem(_ nameText: Text, _ image: String?, radius: CGFloat) -> some View {
|
||||||
|
HStack {
|
||||||
|
nameText
|
||||||
|
if let image, let img = imageFromBase64(image) {
|
||||||
|
Image(uiImage: maskToCustomShape(img, size: 30, radius: radius))
|
||||||
|
} else {
|
||||||
|
Image(systemName: "person.crop.circle")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func maskToCustomShape(_ image: UIImage, size: CGFloat, radius: CGFloat) -> UIImage {
|
func maskToCustomShape(_ image: UIImage, size: CGFloat, radius: CGFloat) -> UIImage {
|
||||||
let path = Path { path in
|
let path = Path { path in
|
||||||
if radius >= 50 {
|
if radius >= 50 {
|
||||||
|
|||||||
@@ -1931,7 +1931,7 @@
|
|||||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 254;
|
CURRENT_PROJECT_VERSION = 255;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@@ -1956,7 +1956,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES_THIN;
|
LLVM_LTO = YES_THIN;
|
||||||
MARKETING_VERSION = 6.2;
|
MARKETING_VERSION = 6.2.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||||
PRODUCT_NAME = SimpleX;
|
PRODUCT_NAME = SimpleX;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -1980,7 +1980,7 @@
|
|||||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 254;
|
CURRENT_PROJECT_VERSION = 255;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@@ -2005,7 +2005,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 6.2;
|
MARKETING_VERSION = 6.2.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||||
PRODUCT_NAME = SimpleX;
|
PRODUCT_NAME = SimpleX;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -2021,11 +2021,11 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 254;
|
CURRENT_PROJECT_VERSION = 255;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
MARKETING_VERSION = 6.2;
|
MARKETING_VERSION = 6.2.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -2041,11 +2041,11 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 254;
|
CURRENT_PROJECT_VERSION = 255;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
MARKETING_VERSION = 6.2;
|
MARKETING_VERSION = 6.2.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -2066,7 +2066,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 254;
|
CURRENT_PROJECT_VERSION = 255;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
GCC_OPTIMIZATION_LEVEL = s;
|
GCC_OPTIMIZATION_LEVEL = s;
|
||||||
@@ -2081,7 +2081,7 @@
|
|||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 6.2;
|
MARKETING_VERSION = 6.2.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -2103,7 +2103,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 254;
|
CURRENT_PROJECT_VERSION = 255;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
ENABLE_CODE_COVERAGE = NO;
|
ENABLE_CODE_COVERAGE = NO;
|
||||||
@@ -2118,7 +2118,7 @@
|
|||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 6.2;
|
MARKETING_VERSION = 6.2.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -2140,7 +2140,7 @@
|
|||||||
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
||||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 254;
|
CURRENT_PROJECT_VERSION = 255;
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
@@ -2166,7 +2166,7 @@
|
|||||||
"$(PROJECT_DIR)/Libraries/sim",
|
"$(PROJECT_DIR)/Libraries/sim",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 6.2;
|
MARKETING_VERSION = 6.2.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -2191,7 +2191,7 @@
|
|||||||
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
||||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 254;
|
CURRENT_PROJECT_VERSION = 255;
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
@@ -2217,7 +2217,7 @@
|
|||||||
"$(PROJECT_DIR)/Libraries/sim",
|
"$(PROJECT_DIR)/Libraries/sim",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 6.2;
|
MARKETING_VERSION = 6.2.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -2242,7 +2242,7 @@
|
|||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 254;
|
CURRENT_PROJECT_VERSION = 255;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
@@ -2257,7 +2257,7 @@
|
|||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MARKETING_VERSION = 6.2;
|
MARKETING_VERSION = 6.2.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -2276,7 +2276,7 @@
|
|||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 254;
|
CURRENT_PROJECT_VERSION = 255;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
@@ -2291,7 +2291,7 @@
|
|||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MARKETING_VERSION = 6.2;
|
MARKETING_VERSION = 6.2.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
|||||||
+5
-4
@@ -1,10 +1,11 @@
|
|||||||
package chat.simplex.common.platform
|
package chat.simplex.common.platform
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import chat.simplex.common.model.ChatController.appPrefs
|
||||||
|
|
||||||
actual object Log {
|
actual object Log {
|
||||||
actual fun d(tag: String, text: String) = Log.d(tag, text).run{}
|
actual fun d(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.DEBUG && appPrefs.developerTools.get()) Log.d(tag, text) }
|
||||||
actual fun e(tag: String, text: String) = Log.e(tag, text).run{}
|
actual fun e(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.ERROR || !appPrefs.developerTools.get()) Log.e(tag, text) }
|
||||||
actual fun i(tag: String, text: String) = Log.i(tag, text).run{}
|
actual fun i(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.INFO && appPrefs.developerTools.get()) Log.i(tag, text) }
|
||||||
actual fun w(tag: String, text: String) = Log.w(tag, text).run{}
|
actual fun w(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.WARNING || !appPrefs.developerTools.get()) Log.w(tag, text) }
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -132,6 +132,7 @@ class AppPreferences {
|
|||||||
val chatLastStart = mkDatePreference(SHARED_PREFS_CHAT_LAST_START, null)
|
val chatLastStart = mkDatePreference(SHARED_PREFS_CHAT_LAST_START, null)
|
||||||
val chatStopped = mkBoolPreference(SHARED_PREFS_CHAT_STOPPED, false)
|
val chatStopped = mkBoolPreference(SHARED_PREFS_CHAT_STOPPED, false)
|
||||||
val developerTools = mkBoolPreference(SHARED_PREFS_DEVELOPER_TOOLS, false)
|
val developerTools = mkBoolPreference(SHARED_PREFS_DEVELOPER_TOOLS, false)
|
||||||
|
val logLevel = mkEnumPreference(SHARED_PREFS_LOG_LEVEL, LogLevel.WARNING) { LogLevel.entries.firstOrNull { it.name == this } }
|
||||||
val showInternalErrors = mkBoolPreference(SHARED_PREFS_SHOW_INTERNAL_ERRORS, false)
|
val showInternalErrors = mkBoolPreference(SHARED_PREFS_SHOW_INTERNAL_ERRORS, false)
|
||||||
val showSlowApiCalls = mkBoolPreference(SHARED_PREFS_SHOW_SLOW_API_CALLS, false)
|
val showSlowApiCalls = mkBoolPreference(SHARED_PREFS_SHOW_SLOW_API_CALLS, false)
|
||||||
val terminalAlwaysVisible = mkBoolPreference(SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE, false)
|
val terminalAlwaysVisible = mkBoolPreference(SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE, false)
|
||||||
@@ -393,6 +394,7 @@ class AppPreferences {
|
|||||||
private const val SHARED_PREFS_CHAT_LAST_START = "ChatLastStart"
|
private const val SHARED_PREFS_CHAT_LAST_START = "ChatLastStart"
|
||||||
private const val SHARED_PREFS_CHAT_STOPPED = "ChatStopped"
|
private const val SHARED_PREFS_CHAT_STOPPED = "ChatStopped"
|
||||||
private const val SHARED_PREFS_DEVELOPER_TOOLS = "DeveloperTools"
|
private const val SHARED_PREFS_DEVELOPER_TOOLS = "DeveloperTools"
|
||||||
|
private const val SHARED_PREFS_LOG_LEVEL = "LogLevel"
|
||||||
private const val SHARED_PREFS_SHOW_INTERNAL_ERRORS = "ShowInternalErrors"
|
private const val SHARED_PREFS_SHOW_INTERNAL_ERRORS = "ShowInternalErrors"
|
||||||
private const val SHARED_PREFS_SHOW_SLOW_API_CALLS = "ShowSlowApiCalls"
|
private const val SHARED_PREFS_SHOW_SLOW_API_CALLS = "ShowSlowApiCalls"
|
||||||
private const val SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE = "TerminalAlwaysVisible"
|
private const val SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE = "TerminalAlwaysVisible"
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ package chat.simplex.common.platform
|
|||||||
|
|
||||||
const val TAG = "SIMPLEX"
|
const val TAG = "SIMPLEX"
|
||||||
|
|
||||||
|
enum class LogLevel {
|
||||||
|
DEBUG, INFO, WARNING, ERROR
|
||||||
|
}
|
||||||
|
|
||||||
expect object Log {
|
expect object Log {
|
||||||
fun d(tag: String, text: String)
|
fun d(tag: String, text: String)
|
||||||
fun e(tag: String, text: String)
|
fun e(tag: String, text: String)
|
||||||
|
|||||||
+8
-5
@@ -296,6 +296,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SectionBottomSpacer()
|
SectionBottomSpacer()
|
||||||
|
SectionBottomSpacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,6 +310,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
|||||||
QuotedMsgView(qi)
|
QuotedMsgView(qi)
|
||||||
}
|
}
|
||||||
SectionBottomSpacer()
|
SectionBottomSpacer()
|
||||||
|
SectionBottomSpacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,6 +326,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
|||||||
ForwardedFromView(forwardedFromItem)
|
ForwardedFromView(forwardedFromItem)
|
||||||
}
|
}
|
||||||
SectionBottomSpacer()
|
SectionBottomSpacer()
|
||||||
|
SectionBottomSpacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,6 +398,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SectionBottomSpacer()
|
SectionBottomSpacer()
|
||||||
|
SectionBottomSpacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,12 +437,11 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
|||||||
|
|
||||||
Column {
|
Column {
|
||||||
if (numTabs() > 1) {
|
if (numTabs() > 1) {
|
||||||
Column(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxHeight(),
|
.fillMaxHeight()
|
||||||
verticalArrangement = Arrangement.SpaceBetween
|
|
||||||
) {
|
) {
|
||||||
Column(Modifier.weight(1f)) {
|
Column {
|
||||||
when (val sel = selection.value) {
|
when (val sel = selection.value) {
|
||||||
is CIInfoTab.Delivery -> {
|
is CIInfoTab.Delivery -> {
|
||||||
DeliveryTab(sel.memberDeliveryStatuses)
|
DeliveryTab(sel.memberDeliveryStatuses)
|
||||||
@@ -479,7 +482,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val oneHandUI = remember { appPrefs.oneHandUI.state }
|
val oneHandUI = remember { appPrefs.oneHandUI.state }
|
||||||
Box(Modifier.offset(x = 0.dp, y = if (oneHandUI.value) -AppBarHeight * fontSizeSqrtMultiplier else 0.dp)) {
|
Box(Modifier.align(Alignment.BottomCenter).navigationBarsPadding().offset(x = 0.dp, y = if (oneHandUI.value) -AppBarHeight * fontSizeSqrtMultiplier else 0.dp)) {
|
||||||
TabRow(
|
TabRow(
|
||||||
selectedTabIndex = availableTabs.indexOfFirst { it::class == selection.value::class },
|
selectedTabIndex = availableTabs.indexOfFirst { it::class == selection.value::class },
|
||||||
Modifier.height(AppBarHeight * fontSizeSqrtMultiplier),
|
Modifier.height(AppBarHeight * fontSizeSqrtMultiplier),
|
||||||
|
|||||||
+14
-8
@@ -665,13 +665,18 @@ fun ChatLayout(
|
|||||||
AdaptingBottomPaddingLayout(Modifier, CHAT_COMPOSE_LAYOUT_ID, composeViewHeight) {
|
AdaptingBottomPaddingLayout(Modifier, CHAT_COMPOSE_LAYOUT_ID, composeViewHeight) {
|
||||||
if (chatInfo != null) {
|
if (chatInfo != null) {
|
||||||
Box(Modifier.fillMaxSize()) {
|
Box(Modifier.fillMaxSize()) {
|
||||||
ChatItemsList(
|
// disables scrolling to top of chat item on click inside the bubble
|
||||||
remoteHostId, chatInfo, unreadCount, composeState, composeViewHeight, searchValue,
|
CompositionLocalProvider(LocalBringIntoViewSpec provides object : BringIntoViewSpec {
|
||||||
useLinkPreviews, linkMode, selectedChatItems, showMemberInfo, loadMessages, deleteMessage, deleteMessages,
|
override fun calculateScrollDistance(offset: Float, size: Float, containerSize: Float): Float = 0f
|
||||||
receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature, openDirectChat, forwardItem,
|
}) {
|
||||||
updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember,
|
ChatItemsList(
|
||||||
setReaction, showItemDetails, markItemsRead, markChatRead, remember { { onComposed(it) } }, developerTools, showViaProxy,
|
remoteHostId, chatInfo, unreadCount, composeState, composeViewHeight, searchValue,
|
||||||
)
|
useLinkPreviews, linkMode, selectedChatItems, showMemberInfo, showChatInfo = info, loadMessages, deleteMessage, deleteMessages,
|
||||||
|
receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature, openDirectChat, forwardItem,
|
||||||
|
updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember,
|
||||||
|
setReaction, showItemDetails, markItemsRead, markChatRead, remember { { onComposed(it) } }, developerTools, showViaProxy,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Box(
|
Box(
|
||||||
@@ -939,6 +944,7 @@ fun BoxScope.ChatItemsList(
|
|||||||
linkMode: SimplexLinkMode,
|
linkMode: SimplexLinkMode,
|
||||||
selectedChatItems: MutableState<Set<Long>?>,
|
selectedChatItems: MutableState<Set<Long>?>,
|
||||||
showMemberInfo: (GroupInfo, GroupMember) -> Unit,
|
showMemberInfo: (GroupInfo, GroupMember) -> Unit,
|
||||||
|
showChatInfo: () -> Unit,
|
||||||
loadMessages: suspend (ChatId, ChatPagination, ActiveChatState, visibleItemIndexesNonReversed: () -> IntRange) -> Unit,
|
loadMessages: suspend (ChatId, ChatPagination, ActiveChatState, visibleItemIndexesNonReversed: () -> IntRange) -> Unit,
|
||||||
deleteMessage: (Long, CIDeleteMode) -> Unit,
|
deleteMessage: (Long, CIDeleteMode) -> Unit,
|
||||||
deleteMessages: (List<Long>) -> Unit,
|
deleteMessages: (List<Long>) -> Unit,
|
||||||
@@ -1066,7 +1072,7 @@ fun BoxScope.ChatItemsList(
|
|||||||
highlightedItems.value = setOf()
|
highlightedItems.value = setOf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ChatItemView(remoteHostId, chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, highlighted = highlighted, range = range, fillMaxWidth = fillMaxWidth, selectedChatItems = selectedChatItems, selectChatItem = { selectUnselectChatItem(true, cItem, revealed, selectedChatItems) }, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, forwardItem = forwardItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, scrollToQuotedItemFromItem = scrollToQuotedItemFromItem, setReaction = setReaction, showItemDetails = showItemDetails, reveal = reveal, showMemberInfo = showMemberInfo, developerTools = developerTools, showViaProxy = showViaProxy, itemSeparation = itemSeparation, showTimestamp = itemSeparation.timestamp)
|
ChatItemView(remoteHostId, chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, highlighted = highlighted, range = range, fillMaxWidth = fillMaxWidth, selectedChatItems = selectedChatItems, selectChatItem = { selectUnselectChatItem(true, cItem, revealed, selectedChatItems) }, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, forwardItem = forwardItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, scrollToQuotedItemFromItem = scrollToQuotedItemFromItem, setReaction = setReaction, showItemDetails = showItemDetails, reveal = reveal, showMemberInfo = showMemberInfo, showChatInfo = showChatInfo, developerTools = developerTools, showViaProxy = showViaProxy, itemSeparation = itemSeparation, showTimestamp = itemSeparation.timestamp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+49
-19
@@ -24,12 +24,12 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
import androidx.compose.ui.unit.*
|
import androidx.compose.ui.unit.*
|
||||||
import chat.simplex.common.model.*
|
import chat.simplex.common.model.*
|
||||||
import chat.simplex.common.model.ChatModel.controller
|
import chat.simplex.common.model.ChatModel.controller
|
||||||
|
import chat.simplex.common.model.ChatModel.currentUser
|
||||||
import chat.simplex.common.platform.*
|
import chat.simplex.common.platform.*
|
||||||
import chat.simplex.common.ui.theme.*
|
import chat.simplex.common.ui.theme.*
|
||||||
import chat.simplex.common.views.chat.*
|
import chat.simplex.common.views.chat.*
|
||||||
import chat.simplex.common.views.helpers.*
|
import chat.simplex.common.views.helpers.*
|
||||||
import chat.simplex.res.MR
|
import chat.simplex.res.MR
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
@@ -51,6 +51,12 @@ fun chatEventText(eventText: String, ts: String): AnnotatedString =
|
|||||||
withStyle(chatEventStyle) { append("$eventText $ts") }
|
withStyle(chatEventStyle) { append("$eventText $ts") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class ChatItemReactionMenuItem (
|
||||||
|
val name: String,
|
||||||
|
val image: String?,
|
||||||
|
val onClick: (() -> Unit)?
|
||||||
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatItemView(
|
fun ChatItemView(
|
||||||
rhId: Long?,
|
rhId: Long?,
|
||||||
@@ -87,6 +93,7 @@ fun ChatItemView(
|
|||||||
showItemDetails: (ChatInfo, ChatItem) -> Unit,
|
showItemDetails: (ChatInfo, ChatItem) -> Unit,
|
||||||
reveal: (Boolean) -> Unit,
|
reveal: (Boolean) -> Unit,
|
||||||
showMemberInfo: (GroupInfo, GroupMember) -> Unit,
|
showMemberInfo: (GroupInfo, GroupMember) -> Unit,
|
||||||
|
showChatInfo: () -> Unit,
|
||||||
developerTools: Boolean,
|
developerTools: Boolean,
|
||||||
showViaProxy: Boolean,
|
showViaProxy: Boolean,
|
||||||
showTimestamp: Boolean,
|
showTimestamp: Boolean,
|
||||||
@@ -120,7 +127,7 @@ fun ChatItemView(
|
|||||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.chatItemOffset(cItem, itemSeparation.largeGap, inverted = true, revealed = true)) {
|
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.chatItemOffset(cItem, itemSeparation.largeGap, inverted = true, revealed = true)) {
|
||||||
cItem.reactions.forEach { r ->
|
cItem.reactions.forEach { r ->
|
||||||
val showReactionMenu = remember { mutableStateOf(false) }
|
val showReactionMenu = remember { mutableStateOf(false) }
|
||||||
val reactionMembers = remember { mutableStateOf(emptyList<MemberReaction>()) }
|
val reactionMenuItems = remember { mutableStateOf(emptyList<ChatItemReactionMenuItem>()) }
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val enterInteraction = remember { HoverInteraction.Enter() }
|
val enterInteraction = remember { HoverInteraction.Enter() }
|
||||||
KeyChangeEffect(highlighted.value) {
|
KeyChangeEffect(highlighted.value) {
|
||||||
@@ -134,18 +141,39 @@ fun ChatItemView(
|
|||||||
var modifier = Modifier.padding(horizontal = 5.dp, vertical = 2.dp).clip(RoundedCornerShape(8.dp))
|
var modifier = Modifier.padding(horizontal = 5.dp, vertical = 2.dp).clip(RoundedCornerShape(8.dp))
|
||||||
if (cInfo.featureEnabled(ChatFeature.Reactions)) {
|
if (cInfo.featureEnabled(ChatFeature.Reactions)) {
|
||||||
fun showReactionsMenu() {
|
fun showReactionsMenu() {
|
||||||
if (cInfo is ChatInfo.Group) {
|
when (cInfo) {
|
||||||
withBGApi {
|
is ChatInfo.Group -> {
|
||||||
try {
|
withBGApi {
|
||||||
val members = controller.apiGetReactionMembers(rhId, cInfo.groupInfo.groupId, cItem.id, r.reaction)
|
try {
|
||||||
if (members != null) {
|
val members = controller.apiGetReactionMembers(rhId, cInfo.groupInfo.groupId, cItem.id, r.reaction)
|
||||||
showReactionMenu.value = true
|
if (members != null) {
|
||||||
reactionMembers.value = members
|
showReactionMenu.value = true
|
||||||
|
reactionMenuItems.value = members.map {
|
||||||
|
val enabled = cInfo.groupInfo.membership.groupMemberId != it.groupMember.groupMemberId
|
||||||
|
val click = if (enabled) ({ showMemberInfo(cInfo.groupInfo, it.groupMember) }) else null
|
||||||
|
ChatItemReactionMenuItem(it.groupMember.displayName, it.groupMember.image, click)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.d(TAG, "chatItemView ChatItemReactions onLongClick: unexpected exception: ${e.stackTraceToString()}")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.d(TAG, "hatItemView ChatItemReactions onLongClick: unexpected exception: ${e.stackTraceToString()}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is ChatInfo.Direct -> {
|
||||||
|
showReactionMenu.value = true
|
||||||
|
val reactions = mutableListOf<ChatItemReactionMenuItem>()
|
||||||
|
|
||||||
|
if (!r.userReacted || r.totalReacted > 1) {
|
||||||
|
val contact = cInfo.contact
|
||||||
|
reactions.add(ChatItemReactionMenuItem(contact.displayName, contact.image, showChatInfo))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r.userReacted) {
|
||||||
|
reactions.add(ChatItemReactionMenuItem(generalGetString(MR.strings.sender_you_pronoun), currentUser.value?.image, null))
|
||||||
|
}
|
||||||
|
reactionMenuItems.value = reactions
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
@@ -166,19 +194,19 @@ fun ChatItemView(
|
|||||||
Row(modifier.padding(2.dp), verticalAlignment = Alignment.CenterVertically) {
|
Row(modifier.padding(2.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||||
ReactionIcon(r.reaction.text, fontSize = 12.sp)
|
ReactionIcon(r.reaction.text, fontSize = 12.sp)
|
||||||
DefaultDropdownMenu(showMenu = showReactionMenu) {
|
DefaultDropdownMenu(showMenu = showReactionMenu) {
|
||||||
reactionMembers.value.forEach { m ->
|
reactionMenuItems.value.forEach { m ->
|
||||||
ItemAction(
|
ItemAction(
|
||||||
text = m.groupMember.displayName,
|
text = m.name,
|
||||||
composable = { ProfileImage(44.dp, m.groupMember.image) },
|
composable = { ProfileImage(44.dp, m.image) },
|
||||||
onClick = {
|
onClick = {
|
||||||
if (cInfo is ChatInfo.Group && cInfo.groupInfo.membership.groupMemberId != m.groupMember.groupMemberId) {
|
val click = m.onClick
|
||||||
showMemberInfo(cInfo.groupInfo, m.groupMember)
|
if (click != null) {
|
||||||
showReactionMenu.value = false
|
click()
|
||||||
} else {
|
|
||||||
showReactionMenu.value = false
|
showReactionMenu.value = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
lineLimit = 1
|
lineLimit = 1,
|
||||||
|
color = if (m.onClick == null) MaterialTheme.colors.secondary else MenuTextColor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1188,6 +1216,7 @@ fun PreviewChatItemView(
|
|||||||
showItemDetails = { _, _ -> },
|
showItemDetails = { _, _ -> },
|
||||||
reveal = {},
|
reveal = {},
|
||||||
showMemberInfo = { _, _ ->},
|
showMemberInfo = { _, _ ->},
|
||||||
|
showChatInfo = {},
|
||||||
developerTools = false,
|
developerTools = false,
|
||||||
showViaProxy = false,
|
showViaProxy = false,
|
||||||
showTimestamp = true,
|
showTimestamp = true,
|
||||||
@@ -1233,6 +1262,7 @@ fun PreviewChatItemViewDeletedContent() {
|
|||||||
showItemDetails = { _, _ -> },
|
showItemDetails = { _, _ -> },
|
||||||
reveal = {},
|
reveal = {},
|
||||||
showMemberInfo = { _, _ ->},
|
showMemberInfo = { _, _ ->},
|
||||||
|
showChatInfo = {},
|
||||||
developerTools = false,
|
developerTools = false,
|
||||||
showViaProxy = false,
|
showViaProxy = false,
|
||||||
preview = true,
|
preview = true,
|
||||||
|
|||||||
+14
-7
@@ -30,6 +30,7 @@ import kotlinx.datetime.*
|
|||||||
import java.io.*
|
import java.io.*
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.StandardCopyOption
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
@@ -44,11 +45,14 @@ fun DatabaseView() {
|
|||||||
val chatArchiveFile = remember { mutableStateOf<String?>(null) }
|
val chatArchiveFile = remember { mutableStateOf<String?>(null) }
|
||||||
val stopped = remember { m.chatRunning }.value == false
|
val stopped = remember { m.chatRunning }.value == false
|
||||||
val saveArchiveLauncher = rememberFileChooserLauncher(false) { to: URI? ->
|
val saveArchiveLauncher = rememberFileChooserLauncher(false) { to: URI? ->
|
||||||
val file = chatArchiveFile.value
|
val archive = chatArchiveFile.value
|
||||||
if (file != null && to != null) {
|
if (archive != null && to != null) {
|
||||||
copyFileToFile(File(file), to) {
|
copyFileToFile(File(archive), to) {}
|
||||||
chatArchiveFile.value = null
|
}
|
||||||
}
|
// delete no matter the database was exported or canceled the export process
|
||||||
|
if (archive != null) {
|
||||||
|
File(archive).delete()
|
||||||
|
chatArchiveFile.value = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val appFilesCountAndSize = remember { mutableStateOf(directoryFileCountAndSize(appFilesDir.absolutePath)) }
|
val appFilesCountAndSize = remember { mutableStateOf(directoryFileCountAndSize(appFilesDir.absolutePath)) }
|
||||||
@@ -680,6 +684,8 @@ suspend fun importArchive(
|
|||||||
} finally {
|
} finally {
|
||||||
File(archivePath).delete()
|
File(archivePath).delete()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
progressIndicator.value = false
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -691,14 +697,15 @@ private fun saveArchiveFromURI(importedArchiveURI: URI): String? {
|
|||||||
if (inputStream != null && archiveName != null) {
|
if (inputStream != null && archiveName != null) {
|
||||||
val archivePath = "$databaseExportDir${File.separator}$archiveName"
|
val archivePath = "$databaseExportDir${File.separator}$archiveName"
|
||||||
val destFile = File(archivePath)
|
val destFile = File(archivePath)
|
||||||
Files.copy(inputStream, destFile.toPath())
|
Files.copy(inputStream, destFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||||
archivePath
|
archivePath
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "saveArchiveFromURI null inputStream")
|
Log.e(TAG, "saveArchiveFromURI null inputStream")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "saveArchiveFromURI error: ${e.message}")
|
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.error_saving_database), e.stackTraceToString())
|
||||||
|
Log.e(TAG, "saveArchiveFromURI error: ${e.stackTraceToString()}")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -14,6 +14,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import chat.simplex.common.model.*
|
import chat.simplex.common.model.*
|
||||||
|
import chat.simplex.common.model.ChatController.appPrefs
|
||||||
import chat.simplex.common.platform.*
|
import chat.simplex.common.platform.*
|
||||||
import dev.icerock.moko.resources.compose.painterResource
|
import dev.icerock.moko.resources.compose.painterResource
|
||||||
import dev.icerock.moko.resources.compose.stringResource
|
import dev.icerock.moko.resources.compose.stringResource
|
||||||
@@ -44,6 +45,12 @@ fun DeveloperView(withAuth: (title: String, desc: String, block: () -> Unit) ->
|
|||||||
if (devTools.value) {
|
if (devTools.value) {
|
||||||
SectionDividerSpaced(maxTopPadding = true)
|
SectionDividerSpaced(maxTopPadding = true)
|
||||||
SectionView(stringResource(MR.strings.developer_options_section).uppercase()) {
|
SectionView(stringResource(MR.strings.developer_options_section).uppercase()) {
|
||||||
|
SettingsActionItemWithContent(painterResource(MR.images.ic_breaking_news), stringResource(MR.strings.debug_logs)) {
|
||||||
|
DefaultSwitch(
|
||||||
|
checked = remember { appPrefs.logLevel.state }.value <= LogLevel.DEBUG,
|
||||||
|
onCheckedChange = { appPrefs.logLevel.set(if (it) LogLevel.DEBUG else LogLevel.WARNING) }
|
||||||
|
)
|
||||||
|
}
|
||||||
SettingsPreferenceItem(painterResource(MR.images.ic_drive_folder_upload), stringResource(MR.strings.confirm_database_upgrades), m.controller.appPrefs.confirmDBUpgrades)
|
SettingsPreferenceItem(painterResource(MR.images.ic_drive_folder_upload), stringResource(MR.strings.confirm_database_upgrades), m.controller.appPrefs.confirmDBUpgrades)
|
||||||
if (appPlatform.isDesktop) {
|
if (appPlatform.isDesktop) {
|
||||||
TerminalAlwaysVisibleItem(m.controller.appPrefs.terminalAlwaysVisible) { checked ->
|
TerminalAlwaysVisibleItem(m.controller.appPrefs.terminalAlwaysVisible) { checked ->
|
||||||
|
|||||||
@@ -907,6 +907,7 @@
|
|||||||
<string name="show_dev_options">Show:</string>
|
<string name="show_dev_options">Show:</string>
|
||||||
<string name="hide_dev_options">Hide:</string>
|
<string name="hide_dev_options">Hide:</string>
|
||||||
<string name="show_developer_options">Show developer options</string>
|
<string name="show_developer_options">Show developer options</string>
|
||||||
|
<string name="debug_logs">Enable logs</string>
|
||||||
<string name="developer_options">Database IDs and Transport isolation option.</string>
|
<string name="developer_options">Database IDs and Transport isolation option.</string>
|
||||||
<string name="developer_options_section">Developer options</string>
|
<string name="developer_options_section">Developer options</string>
|
||||||
<string name="show_internal_errors">Show internal errors</string>
|
<string name="show_internal_errors">Show internal errors</string>
|
||||||
@@ -1338,6 +1339,7 @@
|
|||||||
<string name="chat_database_exported_migrate">You may migrate the exported database.</string>
|
<string name="chat_database_exported_migrate">You may migrate the exported database.</string>
|
||||||
<string name="chat_database_exported_not_all_files">Some file(s) were not exported</string>
|
<string name="chat_database_exported_not_all_files">Some file(s) were not exported</string>
|
||||||
<string name="chat_database_exported_continue">Continue</string>
|
<string name="chat_database_exported_continue">Continue</string>
|
||||||
|
<string name="error_saving_database">Error saving database</string>
|
||||||
|
|
||||||
<!-- DatabaseEncryptionView.kt -->
|
<!-- DatabaseEncryptionView.kt -->
|
||||||
<string name="save_passphrase_in_keychain">Save passphrase in Keystore</string>
|
<string name="save_passphrase_in_keychain">Save passphrase in Keystore</string>
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="22px" viewBox="0 -960 960 960" width="22px" fill="#5f6368"><path d="M262.5-285q11.5 0 20.25-8.5t8.75-20q0-11.5-8.75-20.25t-20.25-8.75q-11.5 0-20 8.75T234-313.5q0 11.5 8.5 20t20 8.5Zm-29-168.5H291v-227h-57.5v227Zm217.5 174h275.5V-337H451v57.5Zm0-174h275.5V-511H451v57.5Zm0-169.5h275.5v-57.5H451v57.5ZM134.5-124.5q-22.97 0-40.23-17.27Q77-159.03 77-182v-596q0-22.97 17.27-40.23 17.26-17.27 40.23-17.27h691q22.97 0 40.23 17.27Q883-800.97 883-778v596q0 22.97-17.27 40.23-17.26 17.27-40.23 17.27h-691Zm0-57.5h691v-596h-691v596Zm0 0v-596 596Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 592 B |
+4
-3
@@ -67,7 +67,7 @@ object NtfManager {
|
|||||||
ntf.second.close()
|
ntf.second.close()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// Can be java.lang.UnsupportedOperationException, for example. May do nothing
|
// Can be java.lang.UnsupportedOperationException, for example. May do nothing
|
||||||
println("Failed to close notification: ${e.stackTraceToString()}")
|
Log.e(TAG, "Failed to close notification: ${e.stackTraceToString()}")
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,8 @@ object NtfManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun cancelAllNotifications() {
|
fun cancelAllNotifications() {
|
||||||
// prevNtfs.forEach { try { it.second.close() } catch (e: Exception) { println("Failed to close notification: ${e.stackTraceToString()}") } }
|
// prevNtfs.forEach { try { it.second.close() } catch (e: Exception) { Log.e(TAG, "Failed to close notification: ${e
|
||||||
|
// .stackTraceToString()}") } }
|
||||||
withBGApi {
|
withBGApi {
|
||||||
prevNtfsMutex.withLock {
|
prevNtfsMutex.withLock {
|
||||||
prevNtfs.clear()
|
prevNtfs.clear()
|
||||||
@@ -153,7 +154,7 @@ object NtfManager {
|
|||||||
ImageIO.write(icon.toAwtImage(), "PNG", newFile.outputStream())
|
ImageIO.write(icon.toAwtImage(), "PNG", newFile.outputStream())
|
||||||
newFile.absolutePath
|
newFile.absolutePath
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
println("Failed to write an icon to tmpDir: ${e.stackTraceToString()}")
|
Log.e(TAG, "Failed to write an icon to tmpDir: ${e.stackTraceToString()}")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
} else null
|
} else null
|
||||||
|
|||||||
+6
-4
@@ -1,8 +1,10 @@
|
|||||||
package chat.simplex.common.platform
|
package chat.simplex.common.platform
|
||||||
|
|
||||||
|
import chat.simplex.common.model.ChatController.appPrefs
|
||||||
|
|
||||||
actual object Log {
|
actual object Log {
|
||||||
actual fun d(tag: String, text: String) = println("D: $text")
|
actual fun d(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.DEBUG && appPrefs.developerTools.get()) println("D: $text") }
|
||||||
actual fun e(tag: String, text: String) = println("E: $text")
|
actual fun e(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.ERROR || !appPrefs.developerTools.get()) println("E: $text") }
|
||||||
actual fun i(tag: String, text: String) = println("I: $text")
|
actual fun i(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.INFO && appPrefs.developerTools.get()) println("I: $text") }
|
||||||
actual fun w(tag: String, text: String) = println("W: $text")
|
actual fun w(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.WARNING || !appPrefs.developerTools.get()) println("W: $text") }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ android.nonTransitiveRClass=true
|
|||||||
kotlin.mpp.androidSourceSetLayoutVersion=2
|
kotlin.mpp.androidSourceSetLayoutVersion=2
|
||||||
kotlin.jvm.target=11
|
kotlin.jvm.target=11
|
||||||
|
|
||||||
android.version_name=6.2
|
android.version_name=6.2.1
|
||||||
android.version_code=259
|
android.version_code=261
|
||||||
|
|
||||||
desktop.version_name=6.2
|
desktop.version_name=6.2.1
|
||||||
desktop.version_code=82
|
desktop.version_code=83
|
||||||
|
|
||||||
kotlin.version=1.9.23
|
kotlin.version=1.9.23
|
||||||
gradle.plugin.version=8.2.0
|
gradle.plugin.version=8.2.0
|
||||||
|
|||||||
Reference in New Issue
Block a user