mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
Merge branch 'master' into master-ios
This commit is contained in:
@@ -95,6 +95,7 @@ kotlin {
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.7.1")
|
||||
implementation("com.github.Dansoftowner:jSystemThemeDetector:3.6")
|
||||
implementation("org.slf4j:slf4j-simple:2.0.7")
|
||||
}
|
||||
}
|
||||
val desktopTest by getting
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ val defaultLocale: Locale = Locale.getDefault()
|
||||
lateinit var androidAppContext: Context
|
||||
lateinit var mainActivity: WeakReference<FragmentActivity>
|
||||
|
||||
actual fun initHaskell() {
|
||||
fun initHaskell() {
|
||||
val socketName = "chat.simplex.app.local.socket.address.listen.native.cmd2" + Random.nextLong(100000)
|
||||
val s = Semaphore(0)
|
||||
thread(name="stdout/stderr pipe") {
|
||||
|
||||
+2
-1
@@ -47,7 +47,8 @@ actual fun PlatformTextField(
|
||||
textStyle: MutableState<TextStyle>,
|
||||
showDeleteTextButton: MutableState<Boolean>,
|
||||
userIsObserver: Boolean,
|
||||
onMessageChange: (String) -> Unit
|
||||
onMessageChange: (String) -> Unit,
|
||||
onDone: () -> Unit,
|
||||
) {
|
||||
val cs = composeState.value
|
||||
val textColor = MaterialTheme.colors.onBackground
|
||||
|
||||
+2
@@ -8,6 +8,8 @@ import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.PopupProperties
|
||||
|
||||
actual fun Modifier.onRightClick(action: () -> Unit): Modifier = this
|
||||
|
||||
actual interface DefaultExposedDropdownMenuBoxScope {
|
||||
@Composable
|
||||
actual fun DefaultExposedDropdownMenu(
|
||||
|
||||
@@ -19,6 +19,8 @@ project("app")
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(OS_LIB_PATH "linux")
|
||||
set(OS_LIB_EXT "so")
|
||||
# Makes ld search libs in the same dir as libapp-lib, not in system dirs
|
||||
set(CMAKE_BUILD_RPATH "$ORIGIN")
|
||||
elseif(WIN32)
|
||||
set(OS_LIB_PATH "windows")
|
||||
set(OS_LIB_EXT "dll")
|
||||
@@ -37,9 +39,6 @@ else()
|
||||
set(OS_LIB_ARCH "${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
||||
# Makes ld search libs in the same dir as libapp-lib, not in system dirs
|
||||
#set(CMAKE_BUILD_RPATH "$ORIGIN")
|
||||
|
||||
# Creates and names a library, sets it as either STATIC
|
||||
# or SHARED, and provides the relative paths to its source code.
|
||||
# You can define multiple libraries, and CMake builds them for you.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <jni.h>
|
||||
#include <string.h>
|
||||
|
||||
// from the RTS
|
||||
void hs_init(int * argc, char **argv[]);
|
||||
@@ -30,13 +31,35 @@ extern char *chat_parse_markdown(const char *str);
|
||||
extern char *chat_parse_server(const char *str);
|
||||
extern char *chat_password_hash(const char *pwd, const char *salt);
|
||||
|
||||
|
||||
// As a reference: https://stackoverflow.com/a/60002045
|
||||
jstring correct_string_utf8(JNIEnv *env, char *string) {
|
||||
jobject bb = (*env)->NewDirectByteBuffer(env, (void *)string, strlen(string));
|
||||
jclass cls_charset = (*env)->FindClass(env, "java/nio/charset/Charset");
|
||||
jmethodID mid_charset_forName = (*env)->GetStaticMethodID(env, cls_charset, "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;");
|
||||
jobject charset = (*env)->CallStaticObjectMethod(env, cls_charset, mid_charset_forName, (*env)->NewStringUTF(env, "UTF-8"));
|
||||
|
||||
jmethodID mid_decode = (*env)->GetMethodID(env, cls_charset, "decode", "(Ljava/nio/ByteBuffer;)Ljava/nio/CharBuffer;");
|
||||
jobject cb = (*env)->CallObjectMethod(env, charset, mid_decode, bb);
|
||||
|
||||
jclass cls_char_buffer = (*env)->FindClass(env, "java/nio/CharBuffer");
|
||||
jmethodID mid_to_string = (*env)->GetMethodID(env, cls_char_buffer, "toString", "()Ljava/lang/String;");
|
||||
jstring res = (*env)->CallObjectMethod(env, cb, mid_to_string);
|
||||
|
||||
(*env)->DeleteLocalRef(env, bb);
|
||||
(*env)->DeleteLocalRef(env, charset);
|
||||
(*env)->DeleteLocalRef(env, cb);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatMigrateInit(JNIEnv *env, jclass clazz, jstring dbPath, jstring dbKey, jstring confirm) {
|
||||
const char *_dbPath = (*env)->GetStringUTFChars(env, dbPath, JNI_FALSE);
|
||||
const char *_dbKey = (*env)->GetStringUTFChars(env, dbKey, JNI_FALSE);
|
||||
const char *_confirm = (*env)->GetStringUTFChars(env, confirm, JNI_FALSE);
|
||||
jlong _ctrl = (jlong) 0;
|
||||
jstring res = (*env)->NewStringUTF(env, chat_migrate_init(_dbPath, _dbKey, _confirm, &_ctrl));
|
||||
jstring res = correct_string_utf8(env, chat_migrate_init(_dbPath, _dbKey, _confirm, &_ctrl));
|
||||
(*env)->ReleaseStringUTFChars(env, dbPath, _dbPath);
|
||||
(*env)->ReleaseStringUTFChars(env, dbKey, _dbKey);
|
||||
(*env)->ReleaseStringUTFChars(env, dbKey, _confirm);
|
||||
@@ -56,25 +79,25 @@ Java_chat_simplex_common_platform_CoreKt_chatMigrateInit(JNIEnv *env, jclass cla
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatSendCmd(JNIEnv *env, jclass clazz, jlong controller, jstring msg) {
|
||||
const char *_msg = (*env)->GetStringUTFChars(env, msg, JNI_FALSE);
|
||||
jstring res = (*env)->NewStringUTF(env, chat_send_cmd((void*)controller, _msg));
|
||||
jstring res = correct_string_utf8(env, chat_send_cmd((void*)controller, _msg));
|
||||
(*env)->ReleaseStringUTFChars(env, msg, _msg);
|
||||
return res;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatRecvMsg(JNIEnv *env, jclass clazz, jlong controller) {
|
||||
return (*env)->NewStringUTF(env, chat_recv_msg((void*)controller));
|
||||
return correct_string_utf8(env, chat_recv_msg((void*)controller));
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatRecvMsgWait(JNIEnv *env, jclass clazz, jlong controller, jint wait) {
|
||||
return (*env)->NewStringUTF(env, chat_recv_msg_wait((void*)controller, wait));
|
||||
return correct_string_utf8(env, chat_recv_msg_wait((void*)controller, wait));
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatParseMarkdown(JNIEnv *env, jclass clazz, jstring str) {
|
||||
const char *_str = (*env)->GetStringUTFChars(env, str, JNI_FALSE);
|
||||
jstring res = (*env)->NewStringUTF(env, chat_parse_markdown(_str));
|
||||
jstring res = correct_string_utf8(env, chat_parse_markdown(_str));
|
||||
(*env)->ReleaseStringUTFChars(env, str, _str);
|
||||
return res;
|
||||
}
|
||||
@@ -82,7 +105,7 @@ Java_chat_simplex_common_platform_CoreKt_chatParseMarkdown(JNIEnv *env, jclass c
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatParseServer(JNIEnv *env, jclass clazz, jstring str) {
|
||||
const char *_str = (*env)->GetStringUTFChars(env, str, JNI_FALSE);
|
||||
jstring res = (*env)->NewStringUTF(env, chat_parse_server(_str));
|
||||
jstring res = correct_string_utf8(env, chat_parse_server(_str));
|
||||
(*env)->ReleaseStringUTFChars(env, str, _str);
|
||||
return res;
|
||||
}
|
||||
@@ -91,7 +114,7 @@ JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatPasswordHash(JNIEnv *env, jclass clazz, jstring pwd, jstring salt) {
|
||||
const char *_pwd = (*env)->GetStringUTFChars(env, pwd, JNI_FALSE);
|
||||
const char *_salt = (*env)->GetStringUTFChars(env, salt, JNI_FALSE);
|
||||
jstring res = (*env)->NewStringUTF(env, chat_password_hash(_pwd, _salt));
|
||||
jstring res = correct_string_utf8(env, chat_password_hash(_pwd, _salt));
|
||||
(*env)->ReleaseStringUTFChars(env, pwd, _pwd);
|
||||
(*env)->ReleaseStringUTFChars(env, salt, _salt);
|
||||
return res;
|
||||
|
||||
@@ -6,8 +6,7 @@ import androidx.compose.material.Surface
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.Log
|
||||
import chat.simplex.common.platform.TAG
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.localauth.SetAppPasscodeView
|
||||
import chat.simplex.common.views.usersettings.*
|
||||
@@ -41,17 +40,25 @@ object AppLock {
|
||||
text = generalGetString(MR.strings.la_notice_to_protect_your_information_turn_on_simplex_lock_you_will_be_prompted_to_complete_authentication_before_this_feature_is_enabled),
|
||||
confirmText = generalGetString(MR.strings.la_notice_turn_on),
|
||||
onConfirm = {
|
||||
laNoticeShown.set(true)
|
||||
withBGApi { // to remove this call, change ordering of onConfirm call in AlertManager
|
||||
showChooseLAMode(laNoticeShown)
|
||||
if (appPlatform.isAndroid) {
|
||||
showChooseLAMode()
|
||||
} else {
|
||||
AlertManager.shared.hideAlert()
|
||||
setPasscode()
|
||||
}
|
||||
}
|
||||
},
|
||||
onDismiss = {
|
||||
AlertManager.shared.hideAlert()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showChooseLAMode(laNoticeShown: SharedPreference<Boolean>) {
|
||||
private fun showChooseLAMode() {
|
||||
Log.d(TAG, "showLANotice")
|
||||
laNoticeShown.set(true)
|
||||
AlertManager.shared.showAlertDialogStacked(
|
||||
title = generalGetString(MR.strings.la_lock_mode),
|
||||
text = null,
|
||||
|
||||
+10
-8
@@ -56,7 +56,7 @@ object ChatModel {
|
||||
val chatItems = mutableStateListOf<ChatItem>()
|
||||
val groupMembers = mutableStateListOf<GroupMember>()
|
||||
|
||||
val terminalItems = mutableStateListOf<TerminalItem>()
|
||||
val terminalItems = mutableStateOf(emptyList<TerminalItem>())
|
||||
val userAddress = mutableStateOf<UserContactLinkRec?>(null)
|
||||
// Allows to temporary save servers that are being edited on multiple screens
|
||||
val userSMPServersUnsaved = mutableStateOf<(List<ServerCfg>)?>(null)
|
||||
@@ -130,10 +130,11 @@ object ChatModel {
|
||||
}
|
||||
}
|
||||
|
||||
fun hasChat(id: String): Boolean = chats.firstOrNull { it.id == id } != null
|
||||
fun getChat(id: String): Chat? = chats.firstOrNull { it.id == id }
|
||||
fun getContactChat(contactId: Long): Chat? = chats.firstOrNull { it.chatInfo is ChatInfo.Direct && it.chatInfo.apiId == contactId }
|
||||
private fun getChatIndex(id: String): Int = chats.indexOfFirst { it.id == id }
|
||||
// toList() here is to prevent ConcurrentModificationException that is rarely happens but happens
|
||||
fun hasChat(id: String): Boolean = chats.toList().firstOrNull { it.id == id } != null
|
||||
fun getChat(id: String): Chat? = chats.toList().firstOrNull { it.id == id }
|
||||
fun getContactChat(contactId: Long): Chat? = chats.toList().firstOrNull { it.chatInfo is ChatInfo.Direct && it.chatInfo.apiId == contactId }
|
||||
private fun getChatIndex(id: String): Int = chats.toList().indexOfFirst { it.id == id }
|
||||
fun addChat(chat: Chat) = chats.add(index = 0, chat)
|
||||
|
||||
fun updateChatInfo(cInfo: ChatInfo) {
|
||||
@@ -483,10 +484,11 @@ object ChatModel {
|
||||
networkStatuses[contact.activeConn.agentConnId] ?: NetworkStatus.Unknown()
|
||||
|
||||
fun addTerminalItem(item: TerminalItem) {
|
||||
if (terminalItems.size >= 500) {
|
||||
terminalItems.removeAt(0)
|
||||
if (terminalItems.value.size >= 500) {
|
||||
terminalItems.value = terminalItems.value.takeLast(499) + item
|
||||
} else {
|
||||
terminalItems.value += item
|
||||
}
|
||||
terminalItems.add(item)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-2
@@ -22,8 +22,6 @@ val appVersionInfo: Pair<String, Int?> = if (appPlatform == AppPlatform.ANDROID)
|
||||
else
|
||||
BuildConfigCommon.DESKTOP_VERSION_NAME to null
|
||||
|
||||
expect fun initHaskell()
|
||||
|
||||
class FifoQueue<E>(private var capacity: Int) : LinkedList<E>() {
|
||||
override fun add(element: E): Boolean {
|
||||
if(size > capacity) removeFirst()
|
||||
|
||||
+2
-1
@@ -11,5 +11,6 @@ expect fun PlatformTextField(
|
||||
textStyle: MutableState<TextStyle>,
|
||||
showDeleteTextButton: MutableState<Boolean>,
|
||||
userIsObserver: Boolean,
|
||||
onMessageChange: (String) -> Unit
|
||||
onMessageChange: (String) -> Unit,
|
||||
onDone: () -> Unit,
|
||||
)
|
||||
|
||||
+5
-5
@@ -34,7 +34,7 @@ fun TerminalView(chatModel: ChatModel, close: () -> Unit) {
|
||||
close()
|
||||
})
|
||||
TerminalLayout(
|
||||
remember { chatModel.terminalItems },
|
||||
chatModel.terminalItems,
|
||||
composeState,
|
||||
sendCommand = { sendCommand(chatModel, composeState) },
|
||||
close
|
||||
@@ -62,7 +62,7 @@ private fun sendCommand(chatModel: ChatModel, composeState: MutableState<Compose
|
||||
|
||||
@Composable
|
||||
fun TerminalLayout(
|
||||
terminalItems: List<TerminalItem>,
|
||||
terminalItems: MutableState<List<TerminalItem>>,
|
||||
composeState: MutableState<ComposeState>,
|
||||
sendCommand: () -> Unit,
|
||||
close: () -> Unit
|
||||
@@ -115,12 +115,12 @@ fun TerminalLayout(
|
||||
private var lazyListState = 0 to 0
|
||||
|
||||
@Composable
|
||||
fun TerminalLog(terminalItems: List<TerminalItem>) {
|
||||
fun TerminalLog(terminalItems: MutableState<List<TerminalItem>>) {
|
||||
val listState = rememberLazyListState(lazyListState.first, lazyListState.second)
|
||||
DisposableEffect(Unit) {
|
||||
onDispose { lazyListState = listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset }
|
||||
}
|
||||
val reversedTerminalItems by remember { derivedStateOf { terminalItems.reversed().toList() } }
|
||||
val reversedTerminalItems by remember { derivedStateOf { terminalItems.value.reversed().toList() } }
|
||||
val clipboard = LocalClipboardManager.current
|
||||
LazyColumn(state = listState, reverseLayout = true) {
|
||||
items(reversedTerminalItems) { item ->
|
||||
@@ -152,7 +152,7 @@ fun TerminalLog(terminalItems: List<TerminalItem>) {
|
||||
fun PreviewTerminalLayout() {
|
||||
SimpleXTheme {
|
||||
TerminalLayout(
|
||||
terminalItems = TerminalItem.sampleData,
|
||||
terminalItems = remember { mutableStateOf(TerminalItem.sampleData) },
|
||||
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = false)) },
|
||||
sendCommand = {},
|
||||
close = {}
|
||||
|
||||
+4
@@ -3,6 +3,7 @@ package chat.simplex.common.views.call
|
||||
import chat.simplex.common.model.ChatModel
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.helpers.withApi
|
||||
import chat.simplex.common.views.usersettings.showInDevelopingAlert
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
@@ -24,6 +25,9 @@ class CallManager(val chatModel: ChatModel) {
|
||||
}
|
||||
|
||||
fun acceptIncomingCall(invitation: RcvCallInvitation) {
|
||||
if (appPlatform.isDesktop) {
|
||||
return showInDevelopingAlert()
|
||||
}
|
||||
val call = chatModel.activeCall.value
|
||||
if (call == null) {
|
||||
justAcceptIncomingCall(invitation = invitation)
|
||||
|
||||
+2
@@ -70,6 +70,7 @@ fun ChatItemInfoView(ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) {
|
||||
Box(
|
||||
Modifier.clip(RoundedCornerShape(18.dp)).background(itemColor).padding(bottom = 3.dp)
|
||||
.combinedClickable(onLongClick = { showMenu.value = true }, onClick = {})
|
||||
.onRightClick { showMenu.value = true }
|
||||
) {
|
||||
Box(Modifier.padding(vertical = 6.dp, horizontal = 12.dp)) {
|
||||
TextBubble(text, ciVersion.formattedText, sender = null, showMenu)
|
||||
@@ -116,6 +117,7 @@ fun ChatItemInfoView(ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) {
|
||||
Box(
|
||||
Modifier.clip(RoundedCornerShape(18.dp)).background(quoteColor).padding(bottom = 3.dp)
|
||||
.combinedClickable(onLongClick = { showMenu.value = true }, onClick = {})
|
||||
.onRightClick { showMenu.value = true }
|
||||
) {
|
||||
Box(Modifier.padding(vertical = 6.dp, horizontal = 12.dp)) {
|
||||
TextBubble(text, qi.formattedText, sender = qi.sender(null), showMenu)
|
||||
|
||||
+5
-1
@@ -34,6 +34,7 @@ import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.model.GroupInfo
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.platform.AudioPlayer
|
||||
import chat.simplex.common.views.usersettings.showInDevelopingAlert
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
@@ -236,7 +237,10 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) {
|
||||
joinGroup = { groupId ->
|
||||
withApi { chatModel.controller.apiJoinGroup(groupId) }
|
||||
},
|
||||
startCall = { media ->
|
||||
startCall = out@ { media ->
|
||||
if (appPlatform.isDesktop) {
|
||||
return@out showInDevelopingAlert()
|
||||
}
|
||||
val cInfo = chat.chatInfo
|
||||
if (cInfo is ChatInfo.Direct) {
|
||||
chatModel.activeCall.value = Call(contact = cInfo.contact, callState = CallState.WaitCapabilities, localMedia = media)
|
||||
|
||||
+10
-3
@@ -24,6 +24,7 @@ import chat.simplex.common.views.chat.item.ItemAction
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.model.ChatItem
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.usersettings.showInDevelopingAlert
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
@@ -66,7 +67,9 @@ fun SendMsgView(
|
||||
val showVoiceButton = cs.message.isEmpty() && showVoiceRecordIcon && !composeState.value.editing &&
|
||||
cs.liveMessage == null && (cs.preview is ComposePreview.NoPreview || recState.value is RecordingState.Started)
|
||||
val showDeleteTextButton = rememberSaveable { mutableStateOf(false) }
|
||||
PlatformTextField(composeState, textStyle, showDeleteTextButton, userIsObserver, onMessageChange)
|
||||
PlatformTextField(composeState, textStyle, showDeleteTextButton, userIsObserver, onMessageChange) {
|
||||
sendMessage(null)
|
||||
}
|
||||
// Disable clicks on text field
|
||||
if (cs.preview is ComposePreview.VoicePreview || !userCanSend || cs.inProgress) {
|
||||
Box(
|
||||
@@ -320,7 +323,10 @@ private fun RecordVoiceView(recState: MutableState<RecordingState>, stopRecOnNex
|
||||
LockToCurrentOrientationUntilDispose()
|
||||
StopRecordButton(stopRecordingAndAddAudio)
|
||||
} else {
|
||||
val startRecording: () -> Unit = {
|
||||
val startRecording: () -> Unit = out@ {
|
||||
if (appPlatform.isDesktop) {
|
||||
return@out showInDevelopingAlert()
|
||||
}
|
||||
recState.value = RecordingState.Started(
|
||||
filePath = rec.start { progress: Int?, finished: Boolean ->
|
||||
val state = recState.value
|
||||
@@ -446,7 +452,8 @@ private fun SendMsgButton(
|
||||
role = Role.Button,
|
||||
interactionSource = interactionSource,
|
||||
indication = rememberRipple(bounded = false, radius = 24.dp)
|
||||
),
|
||||
)
|
||||
.onRightClick { onLongClick?.invoke() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
|
||||
+5
-4
@@ -1,7 +1,6 @@
|
||||
package chat.simplex.common.views.chat.item
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.CircularProgressIndicator
|
||||
import androidx.compose.material.Icon
|
||||
@@ -101,7 +100,8 @@ fun CIImageView(
|
||||
.combinedClickable(
|
||||
onLongClick = { showMenu.value = true },
|
||||
onClick = onClick
|
||||
),
|
||||
)
|
||||
.onRightClick { showMenu.value = true },
|
||||
contentScale = ContentScale.FillWidth,
|
||||
)
|
||||
}
|
||||
@@ -118,7 +118,8 @@ fun CIImageView(
|
||||
.combinedClickable(
|
||||
onLongClick = { showMenu.value = true },
|
||||
onClick = onClick
|
||||
),
|
||||
)
|
||||
.onRightClick { showMenu.value = true },
|
||||
contentScale = ContentScale.FillWidth,
|
||||
)
|
||||
}
|
||||
|
||||
+4
-2
@@ -142,7 +142,8 @@ private fun BoxScope.PlayButton(error: Boolean = false, onLongClick: () -> Unit,
|
||||
Box(
|
||||
Modifier
|
||||
.defaultMinSize(minWidth = 40.dp, minHeight = 40.dp)
|
||||
.combinedClickable(onClick = onClick, onLongClick = onLongClick),
|
||||
.combinedClickable(onClick = onClick, onLongClick = onLongClick)
|
||||
.onRightClick { onLongClick.invoke() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
@@ -211,7 +212,8 @@ private fun ImageView(preview: ImageBitmap, showMenu: MutableState<Boolean>, onC
|
||||
.combinedClickable(
|
||||
onLongClick = { showMenu.value = true },
|
||||
onClick = onClick
|
||||
),
|
||||
)
|
||||
.onRightClick { showMenu.value = true },
|
||||
contentScale = ContentScale.FillWidth,
|
||||
)
|
||||
}
|
||||
|
||||
+2
-1
@@ -222,7 +222,8 @@ private fun PlayPauseButton(
|
||||
.combinedClickable(
|
||||
onClick = { if (!audioPlaying) play() else pause() },
|
||||
onLongClick = longClick
|
||||
),
|
||||
)
|
||||
.onRightClick { longClick() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
|
||||
+2
-1
@@ -111,7 +111,8 @@ fun ChatItemView(
|
||||
Column(
|
||||
Modifier
|
||||
.clip(RoundedCornerShape(18.dp))
|
||||
.combinedClickable(onLongClick = { showMenu.value = true }, onClick = onClick),
|
||||
.combinedClickable(onLongClick = { showMenu.value = true }, onClick = onClick)
|
||||
.onRightClick { showMenu.value = true },
|
||||
) {
|
||||
@Composable
|
||||
fun framedItemView() {
|
||||
|
||||
+1
-3
@@ -10,7 +10,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.layout.*
|
||||
import androidx.compose.ui.platform.UriHandler
|
||||
@@ -23,10 +22,8 @@ import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.base64ToBitmap
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlin.math.min
|
||||
|
||||
@Composable
|
||||
@@ -110,6 +107,7 @@ fun FramedItemView(
|
||||
onLongClick = { showMenu.value = true },
|
||||
onClick = { scrollToItem(qi.itemId?: return@combinedClickable) }
|
||||
)
|
||||
.onRightClick { showMenu.value = true }
|
||||
) {
|
||||
when (qi.content) {
|
||||
is MsgContent.MCImage -> {
|
||||
|
||||
+9
-3
@@ -19,8 +19,7 @@ import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.Log
|
||||
import chat.simplex.common.platform.TAG
|
||||
import chat.simplex.common.ui.theme.CurrentColors
|
||||
import chat.simplex.common.views.helpers.DisposableEffectOnGone
|
||||
import chat.simplex.common.views.helpers.detectGesture
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
val reserveTimestampStyle = SpanStyle(color = Color.Transparent)
|
||||
@@ -149,7 +148,7 @@ fun MarkdownText (
|
||||
} else {
|
||||
ft.format.style
|
||||
}
|
||||
withAnnotation(tag = "URL", annotation = link) {
|
||||
withAnnotation(tag = if (ft.format is Format.SimplexLink && linkMode != SimplexLinkMode.BROWSER) "SIMPLEX_URL" else "URL", annotation = link) {
|
||||
withStyle(ftStyle) { append(ft.viewText(linkMode)) }
|
||||
}
|
||||
} else {
|
||||
@@ -170,6 +169,8 @@ fun MarkdownText (
|
||||
onLongClick = { offset ->
|
||||
annotatedText.getStringAnnotations(tag = "URL", start = offset, end = offset)
|
||||
.firstOrNull()?.let { annotation -> onLinkLongClick(annotation.item) }
|
||||
annotatedText.getStringAnnotations(tag = "SIMPLEX_URL", start = offset, end = offset)
|
||||
.firstOrNull()?.let { annotation -> onLinkLongClick(annotation.item) }
|
||||
},
|
||||
onClick = { offset ->
|
||||
annotatedText.getStringAnnotations(tag = "URL", start = offset, end = offset)
|
||||
@@ -182,9 +183,14 @@ fun MarkdownText (
|
||||
Log.e(TAG, "Open url: ${e.stackTraceToString()}")
|
||||
}
|
||||
}
|
||||
annotatedText.getStringAnnotations(tag = "SIMPLEX_URL", start = offset, end = offset)
|
||||
.firstOrNull()?.let { annotation ->
|
||||
uriHandler.openVerifiedSimplexUri(annotation.item)
|
||||
}
|
||||
},
|
||||
shouldConsumeEvent = { offset ->
|
||||
annotatedText.getStringAnnotations(tag = "URL", start = offset, end = offset).any()
|
||||
annotatedText.getStringAnnotations(tag = "SIMPLEX_URL", start = offset, end = offset).any()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ fun ChatHelpView(addContact: (() -> Unit)? = null) {
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
Text(stringResource(MR.strings.thank_you_for_installing_simplex), lineHeight = 22.sp)
|
||||
ReadableTextWithLink(MR.strings.you_can_connect_to_simplex_chat_founder, simplexTeamUri)
|
||||
ReadableTextWithLink(MR.strings.you_can_connect_to_simplex_chat_founder, simplexTeamUri, simplexLink = true)
|
||||
Column(
|
||||
Modifier.padding(top = 24.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
|
||||
+3
-2
@@ -24,7 +24,6 @@ import chat.simplex.common.views.chat.item.InvalidJSONView
|
||||
import chat.simplex.common.views.chat.item.ItemAction
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.newchat.ContactConnectionInfoView
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.appPlatform
|
||||
import chat.simplex.common.platform.ntfManager
|
||||
import chat.simplex.res.MR
|
||||
@@ -619,7 +618,9 @@ fun ChatListNavLinkLayout(
|
||||
stopped: Boolean
|
||||
) {
|
||||
var modifier = Modifier.fillMaxWidth()
|
||||
if (!stopped) modifier = modifier.combinedClickable(onClick = click, onLongClick = { showMenu.value = true })
|
||||
if (!stopped) modifier = modifier
|
||||
.combinedClickable(onClick = click, onLongClick = { showMenu.value = true })
|
||||
.onRightClick { showMenu.value = true }
|
||||
Box(modifier) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
|
||||
+11
-8
@@ -8,6 +8,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.*
|
||||
@@ -27,9 +28,8 @@ import chat.simplex.common.views.usersettings.simplexTeamUri
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.newchat.*
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import java.net.URI
|
||||
|
||||
@Composable
|
||||
@@ -130,7 +130,7 @@ private fun OnboardingButtons(openNewChatSheet: () -> Unit) {
|
||||
Column(Modifier.fillMaxSize().padding(DEFAULT_PADDING), horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.Bottom) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
ConnectButton(generalGetString(MR.strings.chat_with_developers)) {
|
||||
uriHandler.openUriCatching(simplexTeamUri)
|
||||
uriHandler.openVerifiedSimplexUri(simplexTeamUri)
|
||||
}
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
ConnectButton(generalGetString(MR.strings.tap_to_start_new_chat), openNewChatSheet)
|
||||
@@ -344,7 +344,11 @@ private fun ChatList(chatModel: ChatModel, search: String) {
|
||||
onDispose { lazyListState = listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset }
|
||||
}
|
||||
val showUnreadAndFavorites = remember { ChatController.appPrefs.showUnreadAndFavorites.state }.value
|
||||
val chats by remember(search, showUnreadAndFavorites) { derivedStateOf { filteredChats(showUnreadAndFavorites, search) } }
|
||||
val allChats = remember { chatModel.chats }
|
||||
// In some not always reproducible situations this code produce IndexOutOfBoundsException on Compose's side
|
||||
// which is related to [derivedStateOf]. Using safe alternative instead
|
||||
// val chats by remember(search, showUnreadAndFavorites) { derivedStateOf { filteredChats(showUnreadAndFavorites, search, allChats.toList()) } }
|
||||
val chats = filteredChats(showUnreadAndFavorites, search, allChats.toList())
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
listState
|
||||
@@ -360,13 +364,12 @@ private fun ChatList(chatModel: ChatModel, search: String) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun filteredChats(showUnreadAndFavorites: Boolean, searchText: String): List<Chat> {
|
||||
val chatModel = ChatModel
|
||||
private fun filteredChats(showUnreadAndFavorites: Boolean, searchText: String, chats: List<Chat>): List<Chat> {
|
||||
val s = searchText.trim().lowercase()
|
||||
return if (s.isEmpty() && !showUnreadAndFavorites)
|
||||
chatModel.chats
|
||||
chats
|
||||
else {
|
||||
chatModel.chats.filter { chat ->
|
||||
chats.filter { chat ->
|
||||
when (val cInfo = chat.chatInfo) {
|
||||
is ChatInfo.Direct -> if (s.isEmpty()) {
|
||||
filtered(chat)
|
||||
|
||||
+1
@@ -165,6 +165,7 @@ fun UserProfilePickerItem(u: User, unreadCount: Int = 0, padding: PaddingValues
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = if (!u.activeUser) LocalIndication.current else null
|
||||
)
|
||||
.onRightClick { onLongClick() }
|
||||
.padding(padding),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
|
||||
+2
@@ -11,6 +11,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
expect fun Modifier.onRightClick(action: () -> Unit): Modifier
|
||||
|
||||
expect interface DefaultExposedDropdownMenuBoxScope {
|
||||
@Composable
|
||||
open fun DefaultExposedDropdownMenu(
|
||||
|
||||
+5
-5
@@ -1,5 +1,4 @@
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.*
|
||||
@@ -15,8 +14,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.platform.screenWidth
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.ValueTitleDesc
|
||||
import chat.simplex.common.views.helpers.ValueTitle
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.usersettings.SettingsActionItemWithContent
|
||||
import chat.simplex.res.MR
|
||||
|
||||
@@ -132,7 +130,9 @@ fun SectionItemViewSpaceBetween(
|
||||
.fillMaxWidth()
|
||||
.sizeIn(minHeight = minHeight)
|
||||
Row(
|
||||
if (click == null || disabled) modifier.padding(padding) else modifier.combinedClickable(onClick = click, onLongClick = onLongClick).padding(padding),
|
||||
if (click == null || disabled) modifier.padding(padding) else modifier
|
||||
.combinedClickable(onClick = click, onLongClick = onLongClick).padding(padding)
|
||||
.onRightClick { onLongClick?.invoke() },
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
||||
+8
@@ -9,6 +9,7 @@ import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.ThemeOverrides
|
||||
import chat.simplex.common.views.chatlist.connectIfOpenedViaUri
|
||||
import chat.simplex.res.MR
|
||||
import com.charleskorn.kaml.decodeFromStream
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
@@ -281,6 +282,13 @@ inline fun <reified T> serializableSaver(): Saver<T, *> = Saver(
|
||||
restore = { json.decodeFromString(it) }
|
||||
)
|
||||
|
||||
fun UriHandler.openVerifiedSimplexUri(uri: String) {
|
||||
val URI = try { URI.create(uri) } catch (e: Exception) { null }
|
||||
if (URI != null) {
|
||||
connectIfOpenedViaUri(URI, ChatModel)
|
||||
}
|
||||
}
|
||||
|
||||
fun UriHandler.openUriCatching(uri: String) {
|
||||
try {
|
||||
openUri(uri)
|
||||
|
||||
+7
-2
@@ -57,8 +57,13 @@ fun CreateSimpleXAddress(m: ChatModel) {
|
||||
}
|
||||
},
|
||||
nextStep = {
|
||||
m.controller.appPrefs.onboardingStage.set(OnboardingStage.Step4_SetNotificationsMode)
|
||||
m.onboardingStage.value = OnboardingStage.Step4_SetNotificationsMode
|
||||
val next = if (appPlatform.isAndroid) {
|
||||
OnboardingStage.Step4_SetNotificationsMode
|
||||
} else {
|
||||
OnboardingStage.OnboardingComplete
|
||||
}
|
||||
m.controller.appPrefs.onboardingStage.set(next)
|
||||
m.onboardingStage.value = next
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
+2
-2
@@ -55,7 +55,7 @@ fun ReadableText(stringResId: StringResource, textAlign: TextAlign = TextAlign.S
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ReadableTextWithLink(stringResId: StringResource, link: String, textAlign: TextAlign = TextAlign.Start, padding: PaddingValues = PaddingValues(bottom = 12.dp)) {
|
||||
fun ReadableTextWithLink(stringResId: StringResource, link: String, textAlign: TextAlign = TextAlign.Start, padding: PaddingValues = PaddingValues(bottom = 12.dp), simplexLink: Boolean = false) {
|
||||
val annotated = annotatedStringResource(stringResId)
|
||||
val primary = MaterialTheme.colors.primary
|
||||
// This replaces links in text highlighted with specific color, e.g. SimplexBlue
|
||||
@@ -71,7 +71,7 @@ fun ReadableTextWithLink(stringResId: StringResource, link: String, textAlign: T
|
||||
newStyles
|
||||
}
|
||||
val uriHandler = LocalUriHandler.current
|
||||
Text(AnnotatedString(annotated.text, newStyles), modifier = Modifier.padding(padding).clickable { uriHandler.openUriCatching(link) }, textAlign = textAlign, lineHeight = 22.sp)
|
||||
Text(AnnotatedString(annotated.text, newStyles), modifier = Modifier.padding(padding).clickable { if (simplexLink) uriHandler.openVerifiedSimplexUri(link) else uriHandler.openUriCatching(link) }, textAlign = textAlign, lineHeight = 22.sp)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
+6
-3
@@ -41,6 +41,7 @@ fun SimpleXInfoLayout(
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(start = DEFAULT_PADDING , end = DEFAULT_PADDING, top = DEFAULT_PADDING),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Box(Modifier.fillMaxWidth().padding(top = 8.dp, bottom = 10.dp), contentAlignment = Alignment.Center) {
|
||||
SimpleXLogo()
|
||||
@@ -48,9 +49,11 @@ fun SimpleXInfoLayout(
|
||||
|
||||
Text(stringResource(MR.strings.next_generation_of_private_messaging), style = MaterialTheme.typography.h2, modifier = Modifier.padding(bottom = 48.dp).padding(horizontal = 36.dp), textAlign = TextAlign.Center)
|
||||
|
||||
InfoRow(painterResource(MR.images.privacy), MR.strings.privacy_redefined, MR.strings.first_platform_without_user_ids, width = 80.dp)
|
||||
InfoRow(painterResource(MR.images.shield), MR.strings.immune_to_spam_and_abuse, MR.strings.people_can_connect_only_via_links_you_share)
|
||||
InfoRow(painterResource(if (isInDarkTheme()) MR.images.decentralized_light else MR.images.decentralized), MR.strings.decentralized, MR.strings.opensource_protocol_and_code_anybody_can_run_servers)
|
||||
Column {
|
||||
InfoRow(painterResource(MR.images.privacy), MR.strings.privacy_redefined, MR.strings.first_platform_without_user_ids, width = 80.dp)
|
||||
InfoRow(painterResource(MR.images.shield), MR.strings.immune_to_spam_and_abuse, MR.strings.people_can_connect_only_via_links_you_share)
|
||||
InfoRow(painterResource(if (isInDarkTheme()) MR.images.decentralized_light else MR.images.decentralized), MR.strings.decentralized, MR.strings.opensource_protocol_and_code_anybody_can_run_servers)
|
||||
}
|
||||
|
||||
Spacer(Modifier.fillMaxHeight().weight(1f))
|
||||
|
||||
|
||||
+8
@@ -13,6 +13,7 @@ import androidx.compose.ui.platform.LocalUriHandler
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import chat.simplex.common.model.ChatModel
|
||||
import chat.simplex.common.platform.appPlatform
|
||||
import chat.simplex.common.views.TerminalView
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
@@ -41,3 +42,10 @@ fun DeveloperView(
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
|
||||
fun showInDevelopingAlert() {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.in_developing_title),
|
||||
text = generalGetString(MR.strings.in_developing_desc)
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -175,7 +175,7 @@ fun SettingsLayout(
|
||||
SettingsActionItem(painterResource(MR.images.ic_help), stringResource(MR.strings.how_to_use_simplex_chat), showModal { HelpView(userDisplayName) }, disabled = stopped, extraPadding = true)
|
||||
SettingsActionItem(painterResource(MR.images.ic_add), stringResource(MR.strings.whats_new), showCustomModal { _, close -> WhatsNewView(viaSettings = true, close) }, disabled = stopped, extraPadding = true)
|
||||
SettingsActionItem(painterResource(MR.images.ic_info), stringResource(MR.strings.about_simplex_chat), showModal { SimpleXInfo(it, onboarding = false) }, extraPadding = true)
|
||||
SettingsActionItem(painterResource(MR.images.ic_tag), stringResource(MR.strings.chat_with_the_founder), { uriHandler.openUriCatching(simplexTeamUri) }, textColor = MaterialTheme.colors.primary, disabled = stopped, extraPadding = true)
|
||||
SettingsActionItem(painterResource(MR.images.ic_tag), stringResource(MR.strings.chat_with_the_founder), { uriHandler.openVerifiedSimplexUri(simplexTeamUri) }, textColor = MaterialTheme.colors.primary, disabled = stopped, extraPadding = true)
|
||||
SettingsActionItem(painterResource(MR.images.ic_mail), stringResource(MR.strings.send_us_an_email), { uriHandler.openUriCatching("mailto:chat@simplex.chat") }, textColor = MaterialTheme.colors.primary, extraPadding = true)
|
||||
}
|
||||
SectionDividerSpaced()
|
||||
|
||||
@@ -1526,4 +1526,8 @@
|
||||
<string name="delivery_receipts_are_disabled">Delivery receipts are disabled!</string>
|
||||
<string name="you_can_enable_delivery_receipts_later_alert">You can enable them later via app Privacy & Security settings.</string>
|
||||
<string name="error_enabling_delivery_receipts">Error enabling delivery receipts!</string>
|
||||
|
||||
<!-- Under development -->
|
||||
<string name="in_developing_title">Coming soon!</string>
|
||||
<string name="in_developing_desc">This feature is not yet supported. Try the next release.</string>
|
||||
</resources>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
@@ -14,6 +14,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.*
|
||||
import chat.simplex.common.model.ChatController
|
||||
import chat.simplex.common.model.ChatModel
|
||||
import chat.simplex.common.platform.desktopPlatform
|
||||
import chat.simplex.common.ui.theme.SimpleXTheme
|
||||
import chat.simplex.common.views.helpers.FileDialogChooser
|
||||
import kotlinx.coroutines.*
|
||||
@@ -24,7 +25,10 @@ import java.io.File
|
||||
val simplexWindowState = SimplexWindowState()
|
||||
|
||||
fun showApp() = application {
|
||||
val windowState = rememberWindowState(placement = WindowPlacement.Floating, width = 1366.dp, height = 768.dp)
|
||||
// For some reason on Linux actual width will be 10.dp less after specifying it here. If we specify 1366,
|
||||
// it will show 1356. But after that we can still update it to 1366 by changing window state. Just making it +10 now here
|
||||
val width = if (desktopPlatform.isLinux()) 1376.dp else 1366.dp
|
||||
val windowState = rememberWindowState(placement = WindowPlacement.Floating, width = width, height = 768.dp)
|
||||
simplexWindowState.windowState = windowState
|
||||
Window(state = windowState, onCloseRequest = ::exitApplication, onKeyEvent = {
|
||||
if (it.key == Key.Escape && it.type == KeyEventType.KeyUp) {
|
||||
@@ -146,6 +150,3 @@ fun AppPreview() {
|
||||
AppScreen()
|
||||
}
|
||||
}
|
||||
|
||||
/** Needed for [chat.simplex.common.platform.Files] to get path to jar file */
|
||||
class DesktopApp()
|
||||
|
||||
-30
@@ -1,12 +1,8 @@
|
||||
package chat.simplex.common.platform
|
||||
|
||||
import chat.simplex.common.DesktopApp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.views.call.RcvCallInvitation
|
||||
import chat.simplex.common.views.helpers.withBGApi
|
||||
import java.io.*
|
||||
import java.nio.file.*
|
||||
import java.nio.file.attribute.BasicFileAttributes
|
||||
import java.util.*
|
||||
|
||||
actual val appPlatform = AppPlatform.DESKTOP
|
||||
@@ -39,29 +35,3 @@ private fun applyAppLocale() {
|
||||
if (lang == null || lang == Locale.getDefault().language) return
|
||||
Locale.setDefault(Locale.forLanguageTag(lang))
|
||||
}
|
||||
|
||||
@Suppress("UnsafeDynamicallyLoadedCode")
|
||||
actual fun initHaskell() {
|
||||
val libApp = "libapp-lib.${desktopPlatform.libExtension}"
|
||||
val libsTmpDir = File(tmpDir.absolutePath + File.separator + "libs")
|
||||
copyResources(desktopPlatform.libPath, libsTmpDir.toPath())
|
||||
System.load(File(libsTmpDir, libApp).absolutePath)
|
||||
libsTmpDir.deleteRecursively()
|
||||
initHS()
|
||||
}
|
||||
|
||||
private fun copyResources(from: String, to: Path) {
|
||||
val resource = DesktopApp::class.java.getResource("")!!.toURI()
|
||||
val fileSystem = FileSystems.newFileSystem(resource, emptyMap<String, String>())
|
||||
val resPath = fileSystem.getPath(from)
|
||||
Files.walkFileTree(resPath, object: SimpleFileVisitor<Path>() {
|
||||
override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult {
|
||||
Files.createDirectories(to.resolve(resPath.relativize(dir).toString()))
|
||||
return FileVisitResult.CONTINUE
|
||||
}
|
||||
override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
|
||||
Files.copy(file, to.resolve(resPath.relativize(file).toString()), StandardCopyOption.REPLACE_EXISTING)
|
||||
return FileVisitResult.CONTINUE
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
-9
@@ -7,15 +7,6 @@ import chat.simplex.res.MR
|
||||
import java.io.*
|
||||
import java.net.URI
|
||||
|
||||
private fun applicationParentPath(): String = try {
|
||||
DesktopApp::class.java.protectionDomain!!.codeSource.location.toURI().path
|
||||
.replaceAfterLast("/", "")
|
||||
.replaceAfterLast(File.separator, "")
|
||||
.replace("/", File.separator)
|
||||
} catch (e: Exception) {
|
||||
"./"
|
||||
}
|
||||
|
||||
actual val dataDir: File = File(desktopPlatform.dataPath)
|
||||
actual val tmpDir: File = File(System.getProperty("java.io.tmpdir") + File.separator + "simplex").also { it.deleteOnExit() }
|
||||
actual val filesDir: File = File(dataDir.absolutePath + File.separator + "simplex_v1_files")
|
||||
|
||||
+2
@@ -14,6 +14,8 @@ enum class DesktopPlatform(val libPath: String, val libExtension: String, val co
|
||||
WINDOWS_X86_64("/libs/windows-x86_64", "dll", System.getenv("AppData") + File.separator + "SimpleX", System.getenv("AppData") + File.separator + "SimpleX"),
|
||||
MAC_X86_64("/libs/mac-x86_64", "dylib", unixConfigPath, unixDataPath),
|
||||
MAC_AARCH64("/libs/mac-aarch64", "dylib", unixConfigPath, unixDataPath);
|
||||
|
||||
fun isLinux() = this == LINUX_X86_64 || this == LINUX_AARCH64
|
||||
}
|
||||
|
||||
private fun detectDesktopPlatform(): DesktopPlatform {
|
||||
|
||||
+29
-5
@@ -14,10 +14,13 @@ import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.input.key.*
|
||||
import androidx.compose.ui.platform.*
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.common.views.chat.*
|
||||
@@ -33,7 +36,8 @@ actual fun PlatformTextField(
|
||||
textStyle: MutableState<TextStyle>,
|
||||
showDeleteTextButton: MutableState<Boolean>,
|
||||
userIsObserver: Boolean,
|
||||
onMessageChange: (String) -> Unit
|
||||
onMessageChange: (String) -> Unit,
|
||||
onDone: () -> Unit,
|
||||
) {
|
||||
val cs = composeState.value
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
@@ -47,11 +51,14 @@ actual fun PlatformTextField(
|
||||
keyboard?.show()
|
||||
}
|
||||
val isRtl = remember(cs.message) { isRtl(cs.message.subSequence(0, min(50, cs.message.length))) }
|
||||
var textFieldValueState by remember { mutableStateOf(TextFieldValue(text = cs.message)) }
|
||||
val textFieldValue = textFieldValueState.copy(text = cs.message)
|
||||
BasicTextField(
|
||||
value = cs.message,
|
||||
value = textFieldValue,
|
||||
onValueChange = {
|
||||
if (!composeState.value.inProgress && !(composeState.value.preview is ComposePreview.VoicePreview && it != "")) {
|
||||
onMessageChange(it)
|
||||
if (!composeState.value.inProgress && !(composeState.value.preview is ComposePreview.VoicePreview && it.text != "")) {
|
||||
textFieldValueState = it
|
||||
onMessageChange(it.text)
|
||||
}
|
||||
},
|
||||
textStyle = textStyle.value,
|
||||
@@ -60,7 +67,24 @@ actual fun PlatformTextField(
|
||||
capitalization = KeyboardCapitalization.Sentences,
|
||||
autoCorrect = true
|
||||
),
|
||||
modifier = Modifier.padding(vertical = 4.dp).focusRequester(focusRequester),
|
||||
modifier = Modifier
|
||||
.padding(vertical = 4.dp)
|
||||
.focusRequester(focusRequester)
|
||||
.onPreviewKeyEvent {
|
||||
if (it.key == Key.Enter && it.type == KeyEventType.KeyDown) {
|
||||
if (it.isShiftPressed) {
|
||||
val newText = textFieldValue.text + "\n"
|
||||
textFieldValueState = textFieldValue.copy(
|
||||
text = newText,
|
||||
selection = TextRange(newText.length, newText.length)
|
||||
)
|
||||
onMessageChange(newText)
|
||||
} else if (cs.message.isNotEmpty()) {
|
||||
onDone()
|
||||
}
|
||||
true
|
||||
} else false
|
||||
},
|
||||
cursorBrush = SolidColor(MaterialTheme.colors.secondary),
|
||||
decorationBox = { innerTextField ->
|
||||
Surface(
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ package chat.simplex.common.platform
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import chat.simplex.common.model.ChatItem
|
||||
import chat.simplex.common.views.usersettings.showInDevelopingAlert
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
actual class RecorderNative: RecorderInterface {
|
||||
@@ -18,7 +19,7 @@ actual class RecorderNative: RecorderInterface {
|
||||
|
||||
actual object AudioPlayer: AudioPlayerInterface {
|
||||
override fun play(filePath: String?, audioPlaying: MutableState<Boolean>, progress: MutableState<Int>, duration: MutableState<Int>, resetOnEnd: Boolean) {
|
||||
/*LALAL*/
|
||||
showInDevelopingAlert()
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
|
||||
+22
-19
@@ -1,7 +1,9 @@
|
||||
package chat.simplex.common.platform
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import chat.simplex.common.views.usersettings.showInDevelopingAlert
|
||||
import java.net.URI
|
||||
|
||||
actual class VideoPlayer: VideoPlayerInterface {
|
||||
@@ -12,39 +14,40 @@ actual class VideoPlayer: VideoPlayerInterface {
|
||||
defaultPreview: ImageBitmap,
|
||||
defaultDuration: Long,
|
||||
soundEnabled: Boolean
|
||||
): VideoPlayer = VideoPlayer()
|
||||
actual fun enableSound(enable: Boolean, fileName: String?, gallery: Boolean): Boolean { TODO() }
|
||||
actual fun release(uri: URI, gallery: Boolean, remove: Boolean) { TODO() }
|
||||
): VideoPlayer = VideoPlayer().also {
|
||||
it.preview.value = defaultPreview
|
||||
it.duration.value = defaultDuration
|
||||
it.soundEnabled.value = soundEnabled
|
||||
}
|
||||
actual fun enableSound(enable: Boolean, fileName: String?, gallery: Boolean): Boolean { /*TODO*/ return false }
|
||||
actual fun release(uri: URI, gallery: Boolean, remove: Boolean) { /*TODO*/ }
|
||||
actual fun stopAll() { /*LALAL*/ }
|
||||
actual fun releaseAll() { /*LALAL*/ }
|
||||
}
|
||||
|
||||
override val soundEnabled: MutableState<Boolean>
|
||||
get() = TODO("Not yet implemented")
|
||||
override val brokenVideo: MutableState<Boolean>
|
||||
get() = TODO("Not yet implemented")
|
||||
override val videoPlaying: MutableState<Boolean>
|
||||
get() = TODO("Not yet implemented")
|
||||
override val progress: MutableState<Long>
|
||||
get() = TODO("Not yet implemented")
|
||||
override val duration: MutableState<Long>
|
||||
get() = TODO("Not yet implemented")
|
||||
override val preview: MutableState<ImageBitmap>
|
||||
get() = TODO("Not yet implemented")
|
||||
override val soundEnabled: MutableState<Boolean> = mutableStateOf(false)
|
||||
override val brokenVideo: MutableState<Boolean> = mutableStateOf(false)
|
||||
override val videoPlaying: MutableState<Boolean> = mutableStateOf(false)
|
||||
override val progress: MutableState<Long> = mutableStateOf(0L)
|
||||
override val duration: MutableState<Long> = mutableStateOf(0L)
|
||||
override val preview: MutableState<ImageBitmap> = mutableStateOf(ImageBitmap(0, 0))
|
||||
|
||||
override fun stop() {
|
||||
TODO("Not yet implemented")
|
||||
/*TODO*/
|
||||
}
|
||||
|
||||
override fun play(resetOnEnd: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
if (appPlatform.isDesktop) {
|
||||
showInDevelopingAlert()
|
||||
}
|
||||
}
|
||||
|
||||
override fun enableSound(enable: Boolean): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
/*TODO*/
|
||||
return false
|
||||
}
|
||||
|
||||
override fun release(remove: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
/*TODO*/
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
package chat.simplex.common.views.helpers
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.DropdownMenu
|
||||
@@ -9,7 +9,8 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlin.math.exp
|
||||
|
||||
actual fun Modifier.onRightClick(action: () -> Unit): Modifier = contextMenuOpenDetector { action() }
|
||||
|
||||
actual interface DefaultExposedDropdownMenuBoxScope {
|
||||
@Composable
|
||||
|
||||
+71
-5
@@ -1,22 +1,88 @@
|
||||
package chat.simplex.common.views.helpers
|
||||
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.Density
|
||||
import chat.simplex.common.model.CIFile
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.simplexWindowState
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.util.*
|
||||
import javax.imageio.ImageIO
|
||||
import kotlin.io.encoding.Base64
|
||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||
import kotlin.io.path.toPath
|
||||
|
||||
// LALAL MAKE REALLY ANNOTATED STRING FROM HTML
|
||||
actual fun escapedHtmlToAnnotatedString(text: String, density: Density): AnnotatedString {
|
||||
return AnnotatedString(text)
|
||||
private val bStyle = SpanStyle(fontWeight = FontWeight.Bold)
|
||||
private val iStyle = SpanStyle(fontStyle = FontStyle.Italic)
|
||||
private fun fontStyle(color: String) =
|
||||
SpanStyle(color = Color(color.replace("#", "ff").toLongOrNull(16) ?: Color.White.toArgb().toLong()))
|
||||
|
||||
actual fun escapedHtmlToAnnotatedString(text: String, density: Density): AnnotatedString = try {
|
||||
buildAnnotatedString {
|
||||
fun String.substringSafe(start: Int, len: Int): String =
|
||||
if (start < 0 || start >= this.length || start + len < 0 || start + len > this.length) ""
|
||||
else substring(start, start + len)
|
||||
|
||||
var skipTil = 0
|
||||
for (outerI in text.indices) {
|
||||
if (skipTil > outerI) continue
|
||||
if (text[outerI] == '<') {
|
||||
for (innerI in outerI + 1 until text.length) {
|
||||
when {
|
||||
text.substringSafe(innerI, 2) == "b>" -> {
|
||||
val textStart = innerI + 2
|
||||
for (insideTagI in textStart until text.length) {
|
||||
if (text[insideTagI] == '<') {
|
||||
withStyle(bStyle) { append(text.substring(textStart, insideTagI)) }
|
||||
skipTil = insideTagI + 4
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
text.substringSafe(innerI, 2) == "i>" -> {
|
||||
val textStart = innerI + 2
|
||||
for (insideTagI in textStart until text.length) {
|
||||
if (text[insideTagI] == '<') {
|
||||
withStyle(iStyle) { append(text.substring(textStart, insideTagI)) }
|
||||
skipTil = insideTagI + 4
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
text.substringSafe(innerI, 4) == "font" -> {
|
||||
var textStart = innerI + 5
|
||||
var color = "#000000"
|
||||
for (i in textStart until text.length) {
|
||||
if (text[i] == '#') {
|
||||
color = text.substring(i, i + 7)
|
||||
textStart = i + 9
|
||||
break
|
||||
}
|
||||
}
|
||||
for (insideTagI in textStart until text.length) {
|
||||
if (text[insideTagI] == '<') {
|
||||
withStyle(fontStyle(color)) { append(text.substring(textStart, insideTagI)) }
|
||||
skipTil = insideTagI + 7
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if (skipTil > innerI) continue
|
||||
}
|
||||
} else {
|
||||
append(text[outerI])
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
AnnotatedString(text)
|
||||
}
|
||||
|
||||
actual fun getAppFileUri(fileName: String): URI =
|
||||
|
||||
@@ -30,8 +30,16 @@ kotlin {
|
||||
compose {
|
||||
desktop {
|
||||
application {
|
||||
mainClass = "MainKt"
|
||||
// For debugging via VisualVM
|
||||
/*jvmArgs += listOf(
|
||||
"-Dcom.sun.management.jmxremote.port=8080",
|
||||
"-Dcom.sun.management.jmxremote.ssl=false",
|
||||
"-Dcom.sun.management.jmxremote.authenticate=false"
|
||||
)*/
|
||||
mainClass = "chat.simplex.desktop.MainKt"
|
||||
nativeDistributions {
|
||||
// For debugging via VisualVM
|
||||
//modules("jdk.zipfs", "jdk.management.agent")
|
||||
modules("jdk.zipfs")
|
||||
//includeAllModules = true
|
||||
outputBaseDir.set(project.file("../release"))
|
||||
@@ -40,19 +48,19 @@ compose {
|
||||
//, TargetFormat.AppImage // Gradle doesn't sync on Mac with it
|
||||
)
|
||||
linux {
|
||||
iconFile.set(project.file("../common/src/commonMain/resources/distribute/simplex.png"))
|
||||
iconFile.set(project.file("src/jvmMain/resources/distribute/simplex.png"))
|
||||
appCategory = "Messenger"
|
||||
}
|
||||
windows {
|
||||
// LALAL
|
||||
iconFile.set(project.file("../common/src/commonMain/resources/distribute/simplex.ico"))
|
||||
packageName = "SimpleX"
|
||||
iconFile.set(project.file("src/jvmMain/resources/distribute/simplex.ico"))
|
||||
console = true
|
||||
perUserInstall = true
|
||||
dirChooser = true
|
||||
}
|
||||
macOS {
|
||||
// LALAL
|
||||
//iconFile.set(project.file("../common/src/commonMain/resources/distribute/simplex.icns"))
|
||||
packageName = "SimpleX"
|
||||
iconFile.set(project.file("src/jvmMain/resources/distribute/simplex.icns"))
|
||||
appCategory = "public.app-category.social-networking"
|
||||
bundleID = "chat.simplex.app"
|
||||
}
|
||||
@@ -116,7 +124,7 @@ afterEvaluate {
|
||||
doLast {
|
||||
copy {
|
||||
from("${project(":desktop").buildDir}/cmake/main/linux-amd64", "$cppPath/desktop/libs/linux-x86_64", "$cppPath/desktop/libs/linux-x86_64/deps")
|
||||
into("../common/src/commonMain/resources/libs/linux-x86_64")
|
||||
into("src/jvmMain/resources/libs/linux-x86_64")
|
||||
include("*.so")
|
||||
eachFile {
|
||||
path = name
|
||||
@@ -126,7 +134,7 @@ afterEvaluate {
|
||||
}
|
||||
copy {
|
||||
from("${project(":desktop").buildDir}/cmake/main/linux-aarch64", "$cppPath/desktop/libs/linux-aarch64", "$cppPath/desktop/libs/linux-aarch64/deps")
|
||||
into("../common/src/commonMain/resources/libs/linux-aarch64")
|
||||
into("src/jvmMain/resources/libs/linux-aarch64")
|
||||
include("*.so")
|
||||
eachFile {
|
||||
path = name
|
||||
@@ -136,7 +144,7 @@ afterEvaluate {
|
||||
}
|
||||
copy {
|
||||
from("${project(":desktop").buildDir}/cmake/main/win-amd64", "$cppPath/desktop/libs/windows-x86_64", "$cppPath/desktop/libs/windows-x86_64/deps")
|
||||
into("../common/src/commonMain/resources/libs/windows-x86_64")
|
||||
into("src/jvmMain/resources/libs/windows-x86_64")
|
||||
include("*.dll")
|
||||
eachFile {
|
||||
path = name
|
||||
@@ -146,7 +154,7 @@ afterEvaluate {
|
||||
}
|
||||
copy {
|
||||
from("${project(":desktop").buildDir}/cmake/main/mac-x86_64", "$cppPath/desktop/libs/mac-x86_64", "$cppPath/desktop/libs/mac-x86_64/deps")
|
||||
into("../common/src/commonMain/resources/libs/mac-x86_64")
|
||||
into("src/jvmMain/resources/libs/mac-x86_64")
|
||||
include("*.dylib")
|
||||
eachFile {
|
||||
path = name
|
||||
@@ -156,7 +164,7 @@ afterEvaluate {
|
||||
}
|
||||
copy {
|
||||
from("${project(":desktop").buildDir}/cmake/main/mac-aarch64", "$cppPath/desktop/libs/mac-aarch64", "$cppPath/desktop/libs/mac-aarch64/deps")
|
||||
into("../common/src/commonMain/resources/libs/mac-aarch64")
|
||||
into("src/jvmMain/resources/libs/mac-aarch64")
|
||||
include("*.dylib")
|
||||
eachFile {
|
||||
path = name
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.showApp
|
||||
|
||||
fun main() {
|
||||
initHaskell()
|
||||
initApp()
|
||||
tmpDir.deleteRecursively()
|
||||
return showApp()
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package chat.simplex.desktop
|
||||
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.showApp
|
||||
import java.io.File
|
||||
import java.nio.file.*
|
||||
import java.nio.file.attribute.BasicFileAttributes
|
||||
|
||||
fun main() {
|
||||
initHaskell()
|
||||
initApp()
|
||||
tmpDir.deleteRecursively()
|
||||
return showApp()
|
||||
}
|
||||
|
||||
@Suppress("UnsafeDynamicallyLoadedCode")
|
||||
private fun initHaskell() {
|
||||
val libApp = "libapp-lib.${desktopPlatform.libExtension}"
|
||||
val libsTmpDir = File(tmpDir.absolutePath + File.separator + "libs")
|
||||
copyResources(desktopPlatform.libPath, libsTmpDir.toPath())
|
||||
System.load(File(libsTmpDir, libApp).absolutePath)
|
||||
libsTmpDir.deleteRecursively()
|
||||
initHS()
|
||||
}
|
||||
|
||||
private fun copyResources(from: String, to: Path) {
|
||||
val resource = Class.forName("chat.simplex.desktop.MainKt").getResource("")!!.toURI()
|
||||
val fileSystem = FileSystems.newFileSystem(resource, emptyMap<String, String>())
|
||||
val resPath = fileSystem.getPath(from)
|
||||
Files.walkFileTree(resPath, object: SimpleFileVisitor<Path>() {
|
||||
override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult {
|
||||
Files.createDirectories(to.resolve(resPath.relativize(dir).toString()))
|
||||
return FileVisitResult.CONTINUE
|
||||
}
|
||||
override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
|
||||
Files.copy(file, to.resolve(resPath.relativize(file).toString()), StandardCopyOption.REPLACE_EXISTING)
|
||||
return FileVisitResult.CONTINUE
|
||||
}
|
||||
})
|
||||
}
|
||||
+3
-3
@@ -2,6 +2,6 @@
|
||||
Type=Application
|
||||
Name=SimpleX Chat
|
||||
Comment=Private and secure open-source messenger - no user IDs (not even random numbers).
|
||||
Exec=simplex
|
||||
Icon=simplex.png
|
||||
Categories=Messenger;
|
||||
Exec=/opt/simplex/bin/simplex
|
||||
Icon=/opt/simplex/lib/simplex
|
||||
Categories=Network;Chat;
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
+1
-1
@@ -1,5 +1,5 @@
|
||||
name: simplex-chat
|
||||
version: 5.2.0.4
|
||||
version: 5.3.0.0
|
||||
#synopsis:
|
||||
#description:
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
|
||||
@@ -17,7 +17,7 @@ ldd libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so | grep "ghc" | cut -d' ' -f
|
||||
cd -
|
||||
|
||||
rm -rf apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
|
||||
rm -rf apps/multiplatform/common/src/commonMain/resources/libs/$OS-$ARCH/
|
||||
rm -rf apps/multiplatform/desktop/src/jvmMain/resources/libs/$OS-$ARCH/
|
||||
rm -rf apps/multiplatform/desktop/build/cmake
|
||||
|
||||
mkdir -p apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
|
||||
|
||||
@@ -64,7 +64,7 @@ rm deps/`basename $LIB`
|
||||
cd -
|
||||
|
||||
rm -rf apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
|
||||
rm -rf apps/multiplatform/common/src/commonMain/resources/libs/$OS-$ARCH/
|
||||
rm -rf apps/multiplatform/desktop/src/jvmMain/resources/libs/$OS-$ARCH/
|
||||
rm -rf apps/multiplatform/desktop/build/cmake
|
||||
|
||||
mkdir -p apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ cabal-version: 1.12
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: simplex-chat
|
||||
version: 5.2.0.4
|
||||
version: 5.3.0.0
|
||||
category: Web, System, Services, Cryptography
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
author: simplex.chat
|
||||
|
||||
Reference in New Issue
Block a user