Compare commits

..

8 Commits

Author SHA1 Message Date
Avently bfea71342d mac no alert 2024-12-13 16:31:17 +07:00
Evgeny Poberezkin a2048b6ade Merge branch 'master' into av/desktop-saving-file-with-prompt 2024-12-12 17:48:21 +00:00
Stanislav Dmitrenko 0bf82c08a1 android, desktop: fix importing the same database after exporting it (#5372)
* android, desktop: fix importing the same database after exporting it

* delete archive after export or cancel

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-12-12 17:07:08 +00:00
Stanislav Dmitrenko ddef5a122c android, desktop: disable scroll to top of the message when chat bubble was clicked (#5365) 2024-12-12 16:25:51 +00:00
Stanislav Dmitrenko 0fdd2e04cc android, desktop: hide debug logs by default (#5362)
* android, desktop: hide debug logs by default

* string

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-12-12 16:14:55 +00:00
Stanislav Dmitrenko d1e66386f5 android: fix bottom bar positon in chat item info page (#5373)
* android: fix bottom bar positon in chat item info page

* change
2024-12-12 15:56:59 +00:00
Avently 63406bd806 one button 2024-12-10 18:48:37 +07:00
Avently 9b5e650af0 desktop: offer rename or overwrite file on save 2024-12-10 14:09:11 +07:00
21 changed files with 122 additions and 350 deletions
-3
View File
@@ -79,6 +79,3 @@ website/package-lock.json
website/.cache
website/test/stubs-layout-cache/_includes/*.js
apps/android/app/release
packages/simplex-chat-nodejs/build
packages/simplex-chat-nodejs/.vscode
packages/simplex-chat-nodejs/libs
@@ -1,10 +1,11 @@
package chat.simplex.common.platform
import android.util.Log
import chat.simplex.common.model.ChatController.appPrefs
actual object Log {
actual fun d(tag: String, text: String) = Log.d(tag, text).run{}
actual fun e(tag: String, text: String) = Log.e(tag, text).run{}
actual fun i(tag: String, text: String) = Log.i(tag, text).run{}
actual fun w(tag: String, text: String) = Log.w(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) { if (appPrefs.logLevel.get() <= LogLevel.ERROR || !appPrefs.developerTools.get()) Log.e(tag, text) }
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) { if (appPrefs.logLevel.get() <= LogLevel.WARNING || !appPrefs.developerTools.get()) Log.w(tag, text) }
}
@@ -54,10 +54,11 @@ add_library( # Sets the name of the library.
simplex-api.c)
add_library( simplex SHARED IMPORTED )
FILE(GLOB SIMPLEXLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/libsimplex.${OS_LIB_EXT})
if(WIN32)
FILE(GLOB SIMPLEXLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/lib*simplex*.${OS_LIB_EXT})
set_target_properties( simplex PROPERTIES IMPORTED_IMPLIB ${SIMPLEXLIB})
else()
FILE(GLOB SIMPLEXLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/lib*simplex-chat*.${OS_LIB_EXT})
set_target_properties( simplex PROPERTIES IMPORTED_LOCATION ${SIMPLEXLIB})
endif()
@@ -132,6 +132,7 @@ class AppPreferences {
val chatLastStart = mkDatePreference(SHARED_PREFS_CHAT_LAST_START, null)
val chatStopped = mkBoolPreference(SHARED_PREFS_CHAT_STOPPED, 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 showSlowApiCalls = mkBoolPreference(SHARED_PREFS_SHOW_SLOW_API_CALLS, 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_STOPPED = "ChatStopped"
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_SLOW_API_CALLS = "ShowSlowApiCalls"
private const val SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE = "TerminalAlwaysVisible"
@@ -2,6 +2,10 @@ package chat.simplex.common.platform
const val TAG = "SIMPLEX"
enum class LogLevel {
DEBUG, INFO, WARNING, ERROR
}
expect object Log {
fun d(tag: String, text: String)
fun e(tag: String, text: String)
@@ -296,6 +296,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
}
}
SectionBottomSpacer()
SectionBottomSpacer()
}
}
@@ -309,6 +310,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
QuotedMsgView(qi)
}
SectionBottomSpacer()
SectionBottomSpacer()
}
}
@@ -324,6 +326,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
ForwardedFromView(forwardedFromItem)
}
SectionBottomSpacer()
SectionBottomSpacer()
}
}
@@ -395,6 +398,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
}
}
SectionBottomSpacer()
SectionBottomSpacer()
}
}
@@ -433,12 +437,11 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
Column {
if (numTabs() > 1) {
Column(
Box(
Modifier
.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween
.fillMaxHeight()
) {
Column(Modifier.weight(1f)) {
Column {
when (val sel = selection.value) {
is CIInfoTab.Delivery -> {
DeliveryTab(sel.memberDeliveryStatuses)
@@ -479,7 +482,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
}
}
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(
selectedTabIndex = availableTabs.indexOfFirst { it::class == selection.value::class },
Modifier.height(AppBarHeight * fontSizeSqrtMultiplier),
@@ -665,13 +665,18 @@ fun ChatLayout(
AdaptingBottomPaddingLayout(Modifier, CHAT_COMPOSE_LAYOUT_ID, composeViewHeight) {
if (chatInfo != null) {
Box(Modifier.fillMaxSize()) {
ChatItemsList(
remoteHostId, chatInfo, unreadCount, composeState, composeViewHeight, searchValue,
useLinkPreviews, linkMode, selectedChatItems, showMemberInfo, 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,
)
// disables scrolling to top of chat item on click inside the bubble
CompositionLocalProvider(LocalBringIntoViewSpec provides object : BringIntoViewSpec {
override fun calculateScrollDistance(offset: Float, size: Float, containerSize: Float): Float = 0f
}) {
ChatItemsList(
remoteHostId, chatInfo, unreadCount, composeState, composeViewHeight, searchValue,
useLinkPreviews, linkMode, selectedChatItems, showMemberInfo, 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(
@@ -30,6 +30,7 @@ import kotlinx.datetime.*
import java.io.*
import java.net.URI
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList
@@ -44,11 +45,14 @@ fun DatabaseView() {
val chatArchiveFile = remember { mutableStateOf<String?>(null) }
val stopped = remember { m.chatRunning }.value == false
val saveArchiveLauncher = rememberFileChooserLauncher(false) { to: URI? ->
val file = chatArchiveFile.value
if (file != null && to != null) {
copyFileToFile(File(file), to) {
chatArchiveFile.value = null
}
val archive = chatArchiveFile.value
if (archive != null && to != null) {
copyFileToFile(File(archive), to) {}
}
// 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)) }
@@ -680,6 +684,8 @@ suspend fun importArchive(
} finally {
File(archivePath).delete()
}
} else {
progressIndicator.value = false
}
return false
}
@@ -691,14 +697,15 @@ private fun saveArchiveFromURI(importedArchiveURI: URI): String? {
if (inputStream != null && archiveName != null) {
val archivePath = "$databaseExportDir${File.separator}$archiveName"
val destFile = File(archivePath)
Files.copy(inputStream, destFile.toPath())
Files.copy(inputStream, destFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
archivePath
} else {
Log.e(TAG, "saveArchiveFromURI null inputStream")
null
}
} 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
}
}
@@ -14,6 +14,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.platform.*
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
@@ -44,6 +45,12 @@ fun DeveloperView(withAuth: (title: String, desc: String, block: () -> Unit) ->
if (devTools.value) {
SectionDividerSpaced(maxTopPadding = true)
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)
if (appPlatform.isDesktop) {
TerminalAlwaysVisibleItem(m.controller.appPrefs.terminalAlwaysVisible) { checked ->
@@ -482,6 +482,9 @@
<string name="loading_remote_file_desc">Please, wait while the file is being loaded from the linked mobile</string>
<string name="file_error">File error</string>
<string name="temporary_file_error">Temporary file error</string>
<string name="file_already_exists">File already exists</string>
<string name="what_would_you_like_to_do_with_file">What would you like to do?</string>
<string name="overwrite_file">Overwrite file</string>
<!-- Voice messages -->
<string name="voice_message">Voice message</string>
@@ -907,6 +910,7 @@
<string name="show_dev_options">Show:</string>
<string name="hide_dev_options">Hide:</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_section">Developer options</string>
<string name="show_internal_errors">Show internal errors</string>
@@ -1338,6 +1342,7 @@
<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_continue">Continue</string>
<string name="error_saving_database">Error saving database</string>
<!-- DatabaseEncryptionView.kt -->
<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

@@ -67,7 +67,7 @@ object NtfManager {
ntf.second.close()
} catch (e: Exception) {
// 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() {
// 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 {
prevNtfsMutex.withLock {
prevNtfs.clear()
@@ -153,7 +154,7 @@ object NtfManager {
ImageIO.write(icon.toAwtImage(), "PNG", newFile.outputStream())
newFile.absolutePath
} 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
}
} else null
@@ -1,9 +1,16 @@
package chat.simplex.common.platform
import SectionItemView
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import chat.simplex.common.*
import chat.simplex.common.views.helpers.AlertManager
import chat.simplex.common.views.helpers.generalGetString
import chat.simplex.common.views.chatlist.acceptContactRequest
import chat.simplex.common.views.helpers.*
import chat.simplex.res.MR
import java.awt.Desktop
import java.io.*
@@ -75,10 +82,42 @@ actual class FileChooserLauncher actual constructor() {
res = File(res, input)
}
}
onResult(res?.toURI())
if (res == null) {
onResult(null)
} else if (desktopPlatform.isMac() || !res.exists()) { // mac has an alert confirmation if the file exists
onResult(res.toURI())
} else {
showSaveRenameFileAlert(res, onResult)
}
}
}
private fun showSaveRenameFileAlert(file: File, onResult: (URI?) -> Unit) {
AlertManager.shared.showAlertDialogButtonsColumn(
title = generalGetString(MR.strings.file_already_exists),
text = generalGetString(MR.strings.what_would_you_like_to_do_with_file),
buttons = {
Column {
SectionItemView({
AlertManager.shared.hideAlert()
onResult(file.toURI())
}) {
Text(generalGetString(MR.strings.overwrite_file), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.error)
}
SectionItemView({
AlertManager.shared.hideAlert()
onResult(null)
}) {
Text(generalGetString(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
}
},
onDismissRequest = {
onResult(null)
}
)
}
actual class FileChooserMultipleLauncher actual constructor() {
lateinit var onResult: (List<URI>) -> Unit
@@ -1,8 +1,10 @@
package chat.simplex.common.platform
import chat.simplex.common.model.ChatController.appPrefs
actual object Log {
actual fun d(tag: String, text: String) = println("D: $text")
actual fun e(tag: String, text: String) = println("E: $text")
actual fun i(tag: String, text: String) = println("I: $text")
actual fun w(tag: String, text: String) = println("W: $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) { if (appPrefs.logLevel.get() <= LogLevel.ERROR || !appPrefs.developerTools.get()) println("E: $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) { if (appPrefs.logLevel.get() <= LogLevel.WARNING || !appPrefs.developerTools.get()) println("W: $text") }
}
-14
View File
@@ -1,14 +0,0 @@
{
"targets": [
{
"target_name": "addon",
"sources": [ "cpp/simplex.cc" ],
"libraries": [
"-Wl,-rpath,libs",
"-L<(module_root_dir)/build/Release/",
"-L<(module_root_dir)/libs",
"-lsimplex"
]
}
]
}
-188
View File
@@ -1,188 +0,0 @@
#include <sstream>
#include <string>
#include <node.h>
#include "simplex.h"
namespace simplex
{
using v8::Array;
using v8::ArrayBuffer;
using v8::Context;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Maybe;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Value;
void haskell_init()
{
int argc = 5;
char *argv[] = {
"simplex",
"+RTS", // requires `hs_init_with_rtsopts`
"-A64m", // chunk size for new allocations
"-H64m", // initial heap size
"-xn", // non-moving GC
0};
char **pargv = argv;
hs_init_with_rtsopts(&argc, &pargv);
}
void ChatMigrateInit(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
std::string path(*(v8::String::Utf8Value(isolate, args[0])));
std::string key(*(v8::String::Utf8Value(isolate, args[1])));
std::string confirm(*(v8::String::Utf8Value(isolate, args[2])));
long *ctrl(0);
std::string res = chat_migrate_init(path.c_str(), key.c_str(), confirm.c_str(), &ctrl);
std::stringstream ss;
ss << ctrl
<< "\n"
<< res;
// printf("ChatCtrl after init %d", cChatCtrl);
// v8::Local<v8::String> ctrl = v8::String::NewFromUtf8(isolate, cppChatCtrl.c_str(), v8::String::kNormalString);
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, ss.str().c_str())
.ToLocalChecked());
}
void ChatCloseStore(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
long *ctrl = (long *)((long)args[0].As<Number>()->Value());
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, chat_close_store(ctrl))
.ToLocalChecked());
}
void ChatSendCmd(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
long *ctrl = (long *)((long)args[0].As<Number>()->Value());
std::string cmd(*(v8::String::Utf8Value(isolate, args[1])));
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, chat_send_cmd(ctrl, cmd.c_str()))
.ToLocalChecked());
}
void ChatRecvMsgWait(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
long *ctrl = (long *)((long)args[0].As<Number>()->Value());
long wait = ((long)args[1].As<Number>()->Value());
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, chat_recv_msg_wait(ctrl, wait))
.ToLocalChecked());
}
void ChatWriteFile(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
long *ctrl = (long *)((long)args[0].As<Number>()->Value());
std::string path(*(v8::String::Utf8Value(isolate, args[1])));
Local<v8::ArrayBuffer> arr = args[2].As<ArrayBuffer>();
char *buffer = (char *)arr->Data();
int len(arr->ByteLength());
// std::array address(*arr);
// v8::Array a = *buffer;
// std::string data(*((isolate, args[1])));
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, chat_write_file(ctrl, path.c_str(), buffer, len))
.ToLocalChecked());
}
void ChatReadFile(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
long *ctrl = (long *)((long)args[0].As<Number>()->Value());
std::string path(*(v8::String::Utf8Value(isolate, args[1])));
Local<v8::ArrayBuffer> arr = args[2].As<ArrayBuffer>();
char *buffer = (char *)arr->Data();
int len(arr->ByteLength());
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, chat_write_file(ctrl, path.c_str(), buffer, len))
.ToLocalChecked());
}
void ChatEncryptFile(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
long *ctrl = (long *)((long)args[0].As<Number>()->Value());
std::string fromPath(*(v8::String::Utf8Value(isolate, args[1])));
std::string toPath(*(v8::String::Utf8Value(isolate, args[2])));
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, chat_encrypt_file(ctrl, fromPath.c_str(), toPath.c_str()))
.ToLocalChecked());
}
void ChatDecryptFile(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
std::string fromPath(*(v8::String::Utf8Value(isolate, args[0])));
std::string key(*(v8::String::Utf8Value(isolate, args[1])));
std::string nonce(*(v8::String::Utf8Value(isolate, args[2])));
std::string toPath(*(v8::String::Utf8Value(isolate, args[3])));
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, chat_decrypt_file(fromPath.c_str(), key.c_str(), nonce.c_str(), toPath.c_str()))
.ToLocalChecked());
}
void Initialize(Local<Object> exports)
{
haskell_init();
NODE_SET_METHOD(exports, "chat_migrate_init", ChatMigrateInit);
NODE_SET_METHOD(exports, "chat_close_store", ChatCloseStore);
NODE_SET_METHOD(exports, "chat_send_cmd", ChatSendCmd);
NODE_SET_METHOD(exports, "chat_recv_msg_wait", ChatRecvMsgWait);
NODE_SET_METHOD(exports, "chat_write_file", ChatWriteFile);
NODE_SET_METHOD(exports, "chat_read_file", ChatReadFile);
NODE_SET_METHOD(exports, "chat_encrypt_file", ChatEncryptFile);
NODE_SET_METHOD(exports, "chat_decrypt_file", ChatDecryptFile);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
} // namespace simplex
@@ -1,46 +0,0 @@
//
// simplex.h
// SimpleX
//
// Created by Evgeny on 30/05/2022.
// Copyright © 2022 SimpleX Chat. All rights reserved.
//
#ifndef SimpleX_h
#define SimpleX_h
extern "C" void hs_init(int argc, char **argv[]);
extern "C" void hs_init_with_rtsopts(int * argc, char **argv[]);
typedef long* chat_ctrl;
// the last parameter is used to return the pointer to chat controller
extern "C" char *chat_migrate_init(const char *path, const char *key, const char *confirm, chat_ctrl *ctrl);
extern "C" char *chat_close_store(chat_ctrl ctrl);
extern "C" char *chat_reopen_store(chat_ctrl ctrl);
extern "C" char *chat_send_cmd(chat_ctrl ctrl, const char *cmd);
extern "C" char *chat_recv_msg_wait(chat_ctrl ctrl, const int wait);
extern "C" char *chat_parse_markdown(const char *str);
extern "C" char *chat_parse_server(const char *str);
extern "C" char *chat_password_hash(const char *pwd, const char *salt);
extern "C" char *chat_valid_name(const char *name);
extern "C" int chat_json_length(const char *str);
extern "C" char *chat_encrypt_media(chat_ctrl ctrl, const char *key, const char *frame, const int len);
extern "C" char *chat_decrypt_media(const char *key, const char *frame, const int len);
// chat_write_file returns null-terminated string with JSON of WriteFileResult
extern "C" char *chat_write_file(chat_ctrl ctrl, const char *path, const char *data, const int len);
// chat_read_file returns a buffer with:
// result status (1 byte), then if
// status == 0 (success): buffer length (uint32, 4 bytes), buffer of specified length.
// status == 1 (error): null-terminated error message string.
extern "C" char *chat_read_file(const char *path, const char *key, const char *nonce);
// chat_encrypt_file returns null-terminated string with JSON of WriteFileResult
extern "C" char *chat_encrypt_file(chat_ctrl ctrl, const char *fromPath, const char *toPath);
// chat_decrypt_file returns null-terminated string with the error message
extern "C" char *chat_decrypt_file(const char *fromPath, const char *key, const char *nonce, const char *toPath);
#endif /* simplex_h */
-31
View File
@@ -1,31 +0,0 @@
{
"name": "simplexjs",
"version": "1.0.0",
"main": "src/index.js",
"scripts": {
"install-tools": "npm install -g node-gyp",
"configure": "node-gyp configure; mkdir libs 2> /dev/null | true",
"rebuild": "node-gyp rebuild",
"build": "node-gyp build",
"run": "node src/index.js",
"build-run": "node-gyp build && node src/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/simplex-chat/simplex-chat.git"
},
"keywords": [
"messenger",
"chat",
"privacy",
"security"
],
"author": "SimpleX Chat",
"license": "AGPL-3.0",
"bugs": {
"url": "https://github.com/simplex-chat/simplex-chat/issues"
},
"homepage": "https://github.com/simplex-chat/simplex-chat/packages/simplex-chat-nodejs#readme",
"description": ""
}
-22
View File
@@ -1,22 +0,0 @@
const addon = require('../build/Release/addon');
const fs = require('fs');
const ctrlAndRes = addon.chat_migrate_init("db", "a", "yesUp")
const ctrl = Number(ctrlAndRes.split("\n")[0])
const res = ctrlAndRes.split("\n")[1]
console.log("Migrate ctrl:", ctrl, "res:", res)
console.log(addon.chat_send_cmd(ctrl, "/v"))
// const wait = 15_000_000
// console.log(ctrl, addon.chat_recv_msg_wait(ctrl, wait))
const path = "/tmp/write_file.txt"
const data = [0, 1, 2]
console.log(addon.chat_write_file(ctrl, path, new ArrayBuffer(data)))
fs.writeFileSync("/tmp/file_unencrypted.txt", "unencrypted")
const encRes = JSON.parse(addon.chat_encrypt_file(ctrl, "/tmp/file_unencrypted.txt", "/tmp/file_encrypted.txt"))
const key = encRes.cryptoArgs.fileKey
const nonce = encRes.cryptoArgs.fileNonce
console.log(encRes)
console.log(addon.chat_decrypt_file("/tmp/file_encrypted.txt", key, nonce, "/tmp/file_decrypted.txt"))
+7 -8
View File
@@ -24,19 +24,18 @@ exports=( $(sed 's/foreign export ccall "chat_migrate_init_key"//' src/Simplex/C
for elem in "${exports[@]}"; do count=$(grep -R "$elem$" libsimplex.dll.def | wc -l); if [ $count -ne 1 ]; then echo Wrong exports in libsimplex.dll.def. Add \"$elem\" to that file; exit 1; fi ; done
for elem in "${exports[@]}"; do count=$(grep -R "\"$elem\"" flake.nix | wc -l); if [ $count -ne 2 ]; then echo Wrong exports in flake.nix. Add \"$elem\" in two places of the file; exit 1; fi ; done
#rm -rf $BUILD_DIR
cabal build lib:simplex-chat --ghc-options='-optl-Wl,-rpath,$ORIGIN -optl-Wl,-soname,libsimplex.so -flink-rts -threaded' --constraint 'simplexmq +client_library'
rm -rf $BUILD_DIR
cabal build lib:simplex-chat --ghc-options='-optl-Wl,-rpath,$ORIGIN -flink-rts -threaded' --constraint 'simplexmq +client_library'
cd $BUILD_DIR/build
mv libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so libsimplex.so 2> /dev/null || true
#patchelf --add-needed libHSrts_thr-ghc${GHC_VERSION}.so libsimplex.so
#patchelf --add-rpath '$ORIGIN' libsimplex.so
#patchelf --add-needed libHSrts_thr-ghc${GHC_VERSION}.so libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so
#patchelf --add-rpath '$ORIGIN' libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so
# GitHub's Ubuntu 20.04 runner started to set libffi.so.7 as a dependency while Ubuntu 20.04 on user's devices may not have it
# but libffi.so.8 is shipped as an external library with other libs
patchelf --replace-needed "libffi.so.7" "libffi.so.8" libsimplex.so
patchelf --replace-needed "libffi.so.7" "libffi.so.8" libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so
mkdir deps 2> /dev/null || true
ldd libsimplex.so | grep "ghc" | cut -d' ' -f 3 | xargs -I {} cp {} ./deps/
ldd libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so | grep "ghc" | cut -d' ' -f 3 | xargs -I {} cp {} ./deps/
cd -
@@ -45,7 +44,7 @@ rm -rf apps/multiplatform/desktop/build/cmake
mkdir -p apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
cp -r $BUILD_DIR/build/deps/* apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
cp $BUILD_DIR/build/libsimplex.so apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
cp $BUILD_DIR/build/libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
scripts/desktop/prepare-vlc-linux.sh
links_dir=apps/multiplatform/build/links
+3 -4
View File
@@ -14,7 +14,7 @@ else
fi
LIB_EXT=dylib
LIB=libsimplex.$LIB_EXT
LIB=libHSsimplex-chat-*-inplace-ghc*.$LIB_EXT
GHC_LIBS_DIR=$(ghc --print-libdir)
BUILD_DIR=dist-newstyle/build/$ARCH-*/ghc-*/simplex-chat-*
@@ -24,10 +24,9 @@ for elem in "${exports[@]}"; do count=$(grep -R "$elem$" libsimplex.dll.def | wc
for elem in "${exports[@]}"; do count=$(grep -R "\"$elem\"" flake.nix | wc -l); if [ $count -ne 2 ]; then echo Wrong exports in flake.nix. Add \"$elem\" in two places of the file; exit 1; fi ; done
rm -rf $BUILD_DIR
cabal build lib:simplex-chat lib:simplex-chat --ghc-options="-optl-Wl,-rpath,@loader_path -optl-Wl,-install_name,@rpath/$LIB -optl-Wl,-L$GHC_LIBS_DIR/$ARCH-osx-ghc-$GHC_VERSION -optl-lHSrts_thr-ghc$GHC_VERSION -optl-lffi" --constraint 'simplexmq +client_library'
cabal build lib:simplex-chat lib:simplex-chat --ghc-options="-optl-Wl,-rpath,@loader_path -optl-Wl,-L$GHC_LIBS_DIR/$ARCH-osx-ghc-$GHC_VERSION -optl-lHSrts_thr-ghc$GHC_VERSION -optl-lffi" --constraint 'simplexmq +client_library'
cd $BUILD_DIR/build
mv libHSsimplex-chat-*-inplace-ghc*.$LIB_EXT libsimplex.dylib 2> /dev/null || true
mkdir deps 2> /dev/null || true
# It's not included by default for some reason. Compiled lib tries to find system one but it's not always available
@@ -96,7 +95,7 @@ rm -rf apps/multiplatform/desktop/build/cmake
mkdir -p apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
cp -r $BUILD_DIR/build/deps/* apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
cp $BUILD_DIR/build/$LIB apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
cp $BUILD_DIR/build/libHSsimplex-chat-*-inplace-ghc*.$LIB_EXT apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
cd apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/