mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-29 04:39:53 +00:00
Merge branch 'master' into sq/web-glossary-fix
This commit is contained in:
commit
912d35b61a
53 changed files with 549 additions and 250 deletions
|
@ -1957,12 +1957,28 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||||
let state = UIRemoteCtrlSessionState.connected(remoteCtrl: remoteCtrl, sessionCode: m.remoteCtrlSession?.sessionCode ?? "")
|
let state = UIRemoteCtrlSessionState.connected(remoteCtrl: remoteCtrl, sessionCode: m.remoteCtrlSession?.sessionCode ?? "")
|
||||||
m.remoteCtrlSession = m.remoteCtrlSession?.updateState(state)
|
m.remoteCtrlSession = m.remoteCtrlSession?.updateState(state)
|
||||||
}
|
}
|
||||||
case .remoteCtrlStopped:
|
case let .remoteCtrlStopped(_, rcStopReason):
|
||||||
// This delay is needed to cancel the session that fails on network failure,
|
// This delay is needed to cancel the session that fails on network failure,
|
||||||
// e.g. when user did not grant permission to access local network yet.
|
// e.g. when user did not grant permission to access local network yet.
|
||||||
if let sess = m.remoteCtrlSession {
|
if let sess = m.remoteCtrlSession {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
m.remoteCtrlSession = nil
|
m.remoteCtrlSession = nil
|
||||||
|
dismissAllSheets() {
|
||||||
|
switch rcStopReason {
|
||||||
|
case .connectionFailed(.errorAgent(.RCP(.identity))):
|
||||||
|
AlertManager.shared.showAlertMsg(
|
||||||
|
title: "Connection with desktop stopped",
|
||||||
|
message: "This link was used with another mobile device, please create a new link on the desktop."
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
AlertManager.shared.showAlert(Alert(
|
||||||
|
title: Text("Connection with desktop stopped"),
|
||||||
|
message: Text("Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers."),
|
||||||
|
primaryButton: .default(Text("Ok")),
|
||||||
|
secondaryButton: .default(Text("Copy error")) { UIPasteboard.general.string = String(describing: rcStopReason) }
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if case .connected = sess.sessionState {
|
if case .connected = sess.sessionState {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
|
|
@ -19,6 +19,9 @@ struct ChatItemForwardingView: View {
|
||||||
|
|
||||||
@State private var searchText: String = ""
|
@State private var searchText: String = ""
|
||||||
@FocusState private var searchFocused
|
@FocusState private var searchFocused
|
||||||
|
@State private var alert: SomeAlert?
|
||||||
|
@State private var hasSimplexLink_: Bool?
|
||||||
|
private let chatsToForwardTo = filterChatsToForwardTo()
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
|
@ -35,47 +38,29 @@ struct ChatItemForwardingView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.alert(item: $alert) { $0.alert }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder private func forwardListView() -> some View {
|
@ViewBuilder private func forwardListView() -> some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
let chatsToForwardTo = filterChatsToForwardTo()
|
|
||||||
if !chatsToForwardTo.isEmpty {
|
if !chatsToForwardTo.isEmpty {
|
||||||
ScrollView {
|
List {
|
||||||
LazyVStack(alignment: .leading, spacing: 8) {
|
searchFieldView(text: $searchText, focussed: $searchFocused)
|
||||||
searchFieldView(text: $searchText, focussed: $searchFocused)
|
.padding(.leading, 2)
|
||||||
.padding(.leading, 2)
|
let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase
|
||||||
let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase
|
let chats = s == "" ? chatsToForwardTo : chatsToForwardTo.filter { foundChat($0, s) }
|
||||||
let chats = s == "" ? chatsToForwardTo : chatsToForwardTo.filter { filterChatSearched($0, s) }
|
ForEach(chats) { chat in
|
||||||
ForEach(chats) { chat in
|
forwardListChatView(chat)
|
||||||
Divider()
|
.disabled(chatModel.deletedChats.contains(chat.chatInfo.id))
|
||||||
forwardListNavLinkView(chat)
|
|
||||||
.disabled(chatModel.deletedChats.contains(chat.chatInfo.id))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
|
||||||
.padding(.vertical, 8)
|
|
||||||
.background(Color(uiColor: .systemBackground))
|
|
||||||
.cornerRadius(12)
|
|
||||||
.padding(.horizontal)
|
|
||||||
}
|
}
|
||||||
.background(Color(.systemGroupedBackground))
|
|
||||||
} else {
|
} else {
|
||||||
emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func filterChatsToForwardTo() -> [Chat] {
|
private func foundChat(_ chat: Chat, _ searchStr: String) -> Bool {
|
||||||
var filteredChats = chatModel.chats.filter({ canForwardToChat($0) })
|
|
||||||
if let index = filteredChats.firstIndex(where: { $0.chatInfo.chatType == .local }) {
|
|
||||||
let privateNotes = filteredChats.remove(at: index)
|
|
||||||
filteredChats.insert(privateNotes, at: 0)
|
|
||||||
}
|
|
||||||
return filteredChats
|
|
||||||
}
|
|
||||||
|
|
||||||
private func filterChatSearched(_ chat: Chat, _ searchStr: String) -> Bool {
|
|
||||||
let cInfo = chat.chatInfo
|
let cInfo = chat.chatInfo
|
||||||
return switch cInfo {
|
return switch cInfo {
|
||||||
case let .direct(contact):
|
case let .direct(contact):
|
||||||
|
@ -91,42 +76,70 @@ struct ChatItemForwardingView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func canForwardToChat(_ chat: Chat) -> Bool {
|
private func prohibitedByPref(_ chat: Chat) -> Bool {
|
||||||
switch chat.chatInfo {
|
// preference checks should match checks in compose view
|
||||||
case let .direct(contact): contact.sendMsgEnabled && !contact.nextSendGrpInv
|
let simplexLinkProhibited = hasSimplexLink && !chat.groupFeatureEnabled(.simplexLinks)
|
||||||
case let .group(groupInfo): groupInfo.sendMsgEnabled
|
let fileProhibited = (ci.content.msgContent?.isMediaOrFileAttachment ?? false) && !chat.groupFeatureEnabled(.files)
|
||||||
case let .local(noteFolder): noteFolder.sendMsgEnabled
|
let voiceProhibited = (ci.content.msgContent?.isVoice ?? false) && !chat.chatInfo.featureEnabled(.voice)
|
||||||
|
return switch chat.chatInfo {
|
||||||
|
case .direct: voiceProhibited
|
||||||
|
case .group: simplexLinkProhibited || fileProhibited || voiceProhibited
|
||||||
|
case .local: false
|
||||||
case .contactRequest: false
|
case .contactRequest: false
|
||||||
case .contactConnection: false
|
case .contactConnection: false
|
||||||
case .invalidJSON: false
|
case .invalidJSON: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var hasSimplexLink: Bool {
|
||||||
|
if let hasSimplexLink_ { return hasSimplexLink_ }
|
||||||
|
let r =
|
||||||
|
if let mcText = ci.content.msgContent?.text,
|
||||||
|
let parsedMsg = parseSimpleXMarkdown(mcText) {
|
||||||
|
parsedMsgHasSimplexLink(parsedMsg)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
hasSimplexLink_ = r
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
private func emptyList() -> some View {
|
private func emptyList() -> some View {
|
||||||
Text("No filtered chats")
|
Text("No filtered chats")
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder private func forwardListNavLinkView(_ chat: Chat) -> some View {
|
@ViewBuilder private func forwardListChatView(_ chat: Chat) -> some View {
|
||||||
|
let prohibited = prohibitedByPref(chat)
|
||||||
Button {
|
Button {
|
||||||
dismiss()
|
if prohibited {
|
||||||
if chat.id == fromChatInfo.id {
|
alert = SomeAlert(
|
||||||
composeState = ComposeState(
|
alert: mkAlert(
|
||||||
message: composeState.message,
|
title: "Cannot forward message",
|
||||||
preview: composeState.linkPreview != nil ? composeState.preview : .noPreview,
|
message: "Selected chat preferences prohibit this message."
|
||||||
contextItem: .forwardingItem(chatItem: ci, fromChatInfo: fromChatInfo)
|
),
|
||||||
)
|
id: "forward prohibited by preferences"
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
composeState = ComposeState.init(forwardingItem: ci, fromChatInfo: fromChatInfo)
|
dismiss()
|
||||||
chatModel.chatId = chat.id
|
if chat.id == fromChatInfo.id {
|
||||||
|
composeState = ComposeState(
|
||||||
|
message: composeState.message,
|
||||||
|
preview: composeState.linkPreview != nil ? composeState.preview : .noPreview,
|
||||||
|
contextItem: .forwardingItem(chatItem: ci, fromChatInfo: fromChatInfo)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
composeState = ComposeState.init(forwardingItem: ci, fromChatInfo: fromChatInfo)
|
||||||
|
chatModel.chatId = chat.id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
ChatInfoImage(chat: chat, size: 30)
|
ChatInfoImage(chat: chat, size: 30)
|
||||||
.padding(.trailing, 2)
|
.padding(.trailing, 2)
|
||||||
Text(chat.chatInfo.chatViewName)
|
Text(chat.chatInfo.chatViewName)
|
||||||
.foregroundColor(.primary)
|
.foregroundColor(prohibited ? .secondary : .primary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
if chat.chatInfo.incognito {
|
if chat.chatInfo.incognito {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
@ -142,6 +155,27 @@ struct ChatItemForwardingView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func filterChatsToForwardTo() -> [Chat] {
|
||||||
|
var filteredChats = ChatModel.shared.chats.filter { c in
|
||||||
|
c.chatInfo.chatType != .local && canForwardToChat(c)
|
||||||
|
}
|
||||||
|
if let privateNotes = ChatModel.shared.chats.first(where: { $0.chatInfo.chatType == .local }) {
|
||||||
|
filteredChats.insert(privateNotes, at: 0)
|
||||||
|
}
|
||||||
|
return filteredChats
|
||||||
|
}
|
||||||
|
|
||||||
|
private func canForwardToChat(_ chat: Chat) -> Bool {
|
||||||
|
switch chat.chatInfo {
|
||||||
|
case let .direct(contact): contact.sendMsgEnabled && !contact.nextSendGrpInv
|
||||||
|
case let .group(groupInfo): groupInfo.sendMsgEnabled
|
||||||
|
case let .local(noteFolder): noteFolder.sendMsgEnabled
|
||||||
|
case .contactRequest: false
|
||||||
|
case .contactConnection: false
|
||||||
|
case .invalidJSON: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
ChatItemForwardingView(
|
ChatItemForwardingView(
|
||||||
ci: ChatItem.getSample(1, .directSnd, .now, "hello"),
|
ci: ChatItem.getSample(1, .directSnd, .now, "hello"),
|
||||||
|
|
|
@ -286,6 +286,7 @@ struct ComposeView: View {
|
||||||
if chat.chatInfo.contact?.nextSendGrpInv ?? false {
|
if chat.chatInfo.contact?.nextSendGrpInv ?? false {
|
||||||
ContextInvitingContactMemberView()
|
ContextInvitingContactMemberView()
|
||||||
}
|
}
|
||||||
|
// preference checks should match checks in forwarding list
|
||||||
let simplexLinkProhibited = hasSimplexLink && !chat.groupFeatureEnabled(.simplexLinks)
|
let simplexLinkProhibited = hasSimplexLink && !chat.groupFeatureEnabled(.simplexLinks)
|
||||||
let fileProhibited = composeState.attachmentPreview && !chat.groupFeatureEnabled(.files)
|
let fileProhibited = composeState.attachmentPreview && !chat.groupFeatureEnabled(.files)
|
||||||
let voiceProhibited = composeState.voicePreview && !chat.chatInfo.featureEnabled(.voice)
|
let voiceProhibited = composeState.voicePreview && !chat.chatInfo.featureEnabled(.voice)
|
||||||
|
@ -1065,7 +1066,7 @@ struct ComposeView: View {
|
||||||
} else {
|
} else {
|
||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
let simplexLink = parsedMsg.contains(where: { ft in ft.format?.isSimplexLink ?? false })
|
let simplexLink = parsedMsgHasSimplexLink(parsedMsg)
|
||||||
return (url, simplexLink)
|
return (url, simplexLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1105,6 +1106,10 @@ struct ComposeView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parsedMsgHasSimplexLink(_ parsedMsg: [FormattedText]) -> Bool {
|
||||||
|
parsedMsg.contains(where: { ft in ft.format?.isSimplexLink ?? false })
|
||||||
|
}
|
||||||
|
|
||||||
struct ComposeView_Previews: PreviewProvider {
|
struct ComposeView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let chat = Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])
|
let chat = Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])
|
||||||
|
|
|
@ -11,14 +11,9 @@ import SimpleXChat
|
||||||
import CodeScanner
|
import CodeScanner
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
|
||||||
enum SomeAlert: Identifiable {
|
struct SomeAlert: Identifiable {
|
||||||
case someAlert(alert: Alert, id: String)
|
var alert: Alert
|
||||||
|
var id: String
|
||||||
var id: String {
|
|
||||||
switch self {
|
|
||||||
case let .someAlert(_, id): return id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum NewChatViewAlert: Identifiable {
|
private enum NewChatViewAlert: Identifiable {
|
||||||
|
@ -142,8 +137,8 @@ struct NewChatView: View {
|
||||||
switch(a) {
|
switch(a) {
|
||||||
case let .planAndConnectAlert(alert):
|
case let .planAndConnectAlert(alert):
|
||||||
return planAndConnectAlert(alert, dismiss: true, cleanup: { pastedLink = "" })
|
return planAndConnectAlert(alert, dismiss: true, cleanup: { pastedLink = "" })
|
||||||
case let .newChatSomeAlert(.someAlert(alert, _)):
|
case let .newChatSomeAlert(a):
|
||||||
return alert
|
return a.alert
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,7 +176,7 @@ struct NewChatView: View {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
creatingConnReq = false
|
creatingConnReq = false
|
||||||
if let apiAlert = apiAlert {
|
if let apiAlert = apiAlert {
|
||||||
alert = .newChatSomeAlert(alert: .someAlert(alert: apiAlert, id: "createInvitation error"))
|
alert = .newChatSomeAlert(alert: SomeAlert(alert: apiAlert, id: "createInvitation error"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -315,7 +310,7 @@ private struct ConnectView: View {
|
||||||
// showQRCodeScanner = false
|
// showQRCodeScanner = false
|
||||||
connect(pastedLink)
|
connect(pastedLink)
|
||||||
} else {
|
} else {
|
||||||
alert = .newChatSomeAlert(alert: .someAlert(
|
alert = .newChatSomeAlert(alert: SomeAlert(
|
||||||
alert: mkAlert(title: "Invalid link", message: "The text you pasted is not a SimpleX link."),
|
alert: mkAlert(title: "Invalid link", message: "The text you pasted is not a SimpleX link."),
|
||||||
id: "pasteLinkView: code is not a SimpleX link"
|
id: "pasteLinkView: code is not a SimpleX link"
|
||||||
))
|
))
|
||||||
|
@ -338,14 +333,14 @@ private struct ConnectView: View {
|
||||||
if strIsSimplexLink(r.string) {
|
if strIsSimplexLink(r.string) {
|
||||||
connect(link)
|
connect(link)
|
||||||
} else {
|
} else {
|
||||||
alert = .newChatSomeAlert(alert: .someAlert(
|
alert = .newChatSomeAlert(alert: SomeAlert(
|
||||||
alert: mkAlert(title: "Invalid QR code", message: "The code you scanned is not a SimpleX link QR code."),
|
alert: mkAlert(title: "Invalid QR code", message: "The code you scanned is not a SimpleX link QR code."),
|
||||||
id: "processQRCode: code is not a SimpleX link"
|
id: "processQRCode: code is not a SimpleX link"
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
case let .failure(e):
|
case let .failure(e):
|
||||||
logger.error("processQRCode QR code error: \(e.localizedDescription)")
|
logger.error("processQRCode QR code error: \(e.localizedDescription)")
|
||||||
alert = .newChatSomeAlert(alert: .someAlert(
|
alert = .newChatSomeAlert(alert: SomeAlert(
|
||||||
alert: mkAlert(title: "Invalid QR code", message: "Error scanning code: \(e.localizedDescription)"),
|
alert: mkAlert(title: "Invalid QR code", message: "Error scanning code: \(e.localizedDescription)"),
|
||||||
id: "processQRCode: failure"
|
id: "processQRCode: failure"
|
||||||
))
|
))
|
||||||
|
@ -367,11 +362,12 @@ struct ScannerInView: View {
|
||||||
@Binding var showQRCodeScanner: Bool
|
@Binding var showQRCodeScanner: Bool
|
||||||
let processQRCode: (_ resp: Result<ScanResult, ScanError>) -> Void
|
let processQRCode: (_ resp: Result<ScanResult, ScanError>) -> Void
|
||||||
@State private var cameraAuthorizationStatus: AVAuthorizationStatus?
|
@State private var cameraAuthorizationStatus: AVAuthorizationStatus?
|
||||||
|
var scanMode: ScanMode = .continuous
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
if showQRCodeScanner, case .authorized = cameraAuthorizationStatus {
|
if showQRCodeScanner, case .authorized = cameraAuthorizationStatus {
|
||||||
CodeScannerView(codeTypes: [.qr], scanMode: .continuous, completion: processQRCode)
|
CodeScannerView(codeTypes: [.qr], scanMode: scanMode, completion: processQRCode)
|
||||||
.aspectRatio(1, contentMode: .fit)
|
.aspectRatio(1, contentMode: .fit)
|
||||||
.cornerRadius(12)
|
.cornerRadius(12)
|
||||||
.listRowBackground(Color.clear)
|
.listRowBackground(Color.clear)
|
||||||
|
@ -436,6 +432,7 @@ struct ScannerInView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private func linkTextView(_ link: String) -> some View {
|
private func linkTextView(_ link: String) -> some View {
|
||||||
Text(link)
|
Text(link)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
|
|
|
@ -181,23 +181,27 @@ struct ConnectDesktopView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func connectingDesktopView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?) -> some View {
|
private func connectingDesktopView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?) -> some View {
|
||||||
List {
|
ZStack {
|
||||||
Section("Connecting to desktop") {
|
List {
|
||||||
ctrlDeviceNameText(session, rc)
|
Section("Connecting to desktop") {
|
||||||
ctrlDeviceVersionText(session)
|
ctrlDeviceNameText(session, rc)
|
||||||
}
|
ctrlDeviceVersionText(session)
|
||||||
|
}
|
||||||
|
|
||||||
if let sessCode = session.sessionCode {
|
if let sessCode = session.sessionCode {
|
||||||
Section("Session code") {
|
Section("Session code") {
|
||||||
sessionCodeText(sessCode)
|
sessionCodeText(sessCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
disconnectButton()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.navigationTitle("Connecting to desktop")
|
||||||
|
|
||||||
Section {
|
ProgressView().scaleEffect(2)
|
||||||
disconnectButton()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.navigationTitle("Connecting to desktop")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func searchingDesktopView() -> some View {
|
private func searchingDesktopView() -> some View {
|
||||||
|
@ -329,16 +333,10 @@ struct ConnectDesktopView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func scanDesctopAddressView() -> some View {
|
private func scanDesctopAddressView() -> some View {
|
||||||
Section("Scan QR code from desktop") {
|
Section("Scan QR code from desktop") {
|
||||||
CodeScannerView(codeTypes: [.qr], scanMode: .oncePerCode, completion: processDesktopQRCode)
|
ScannerInView(showQRCodeScanner: $showQRCodeScanner, processQRCode: processDesktopQRCode, scanMode: .oncePerCode)
|
||||||
.aspectRatio(1, contentMode: .fit)
|
|
||||||
.cornerRadius(12)
|
|
||||||
.listRowBackground(Color.clear)
|
|
||||||
.listRowSeparator(.hidden)
|
|
||||||
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
|
|
||||||
.padding(.horizontal)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,6 @@
|
||||||
5C029EAA283942EA004A9677 /* CallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C029EA9283942EA004A9677 /* CallController.swift */; };
|
5C029EAA283942EA004A9677 /* CallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C029EA9283942EA004A9677 /* CallController.swift */; };
|
||||||
5C05DF532840AA1D00C683F9 /* CallSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C05DF522840AA1D00C683F9 /* CallSettings.swift */; };
|
5C05DF532840AA1D00C683F9 /* CallSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C05DF522840AA1D00C683F9 /* CallSettings.swift */; };
|
||||||
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; };
|
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; };
|
||||||
5C0EA13B2C0B176B00AD2E5E /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C0EA1362C0B176B00AD2E5E /* libgmp.a */; };
|
|
||||||
5C0EA13C2C0B176B00AD2E5E /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C0EA1372C0B176B00AD2E5E /* libgmpxx.a */; };
|
|
||||||
5C0EA13D2C0B176B00AD2E5E /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C0EA1382C0B176B00AD2E5E /* libffi.a */; };
|
|
||||||
5C0EA13E2C0B176B00AD2E5E /* libHSsimplex-chat-5.8.0.5-Idqi6HXqzzs2zrnyZtMyhc-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C0EA1392C0B176B00AD2E5E /* libHSsimplex-chat-5.8.0.5-Idqi6HXqzzs2zrnyZtMyhc-ghc9.6.3.a */; };
|
|
||||||
5C0EA13F2C0B176B00AD2E5E /* libHSsimplex-chat-5.8.0.5-Idqi6HXqzzs2zrnyZtMyhc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C0EA13A2C0B176B00AD2E5E /* libHSsimplex-chat-5.8.0.5-Idqi6HXqzzs2zrnyZtMyhc.a */; };
|
|
||||||
5C10D88828EED12E00E58BF0 /* ContactConnectionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C10D88728EED12E00E58BF0 /* ContactConnectionInfo.swift */; };
|
5C10D88828EED12E00E58BF0 /* ContactConnectionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C10D88728EED12E00E58BF0 /* ContactConnectionInfo.swift */; };
|
||||||
5C10D88A28F187F300E58BF0 /* FullScreenMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C10D88928F187F300E58BF0 /* FullScreenMediaView.swift */; };
|
5C10D88A28F187F300E58BF0 /* FullScreenMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C10D88928F187F300E58BF0 /* FullScreenMediaView.swift */; };
|
||||||
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
|
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
|
||||||
|
@ -197,6 +192,11 @@
|
||||||
D741547A29AF90B00022400A /* PushKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547929AF90B00022400A /* PushKit.framework */; };
|
D741547A29AF90B00022400A /* PushKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547929AF90B00022400A /* PushKit.framework */; };
|
||||||
D77B92DC2952372200A5A1CC /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = D77B92DB2952372200A5A1CC /* SwiftyGif */; };
|
D77B92DC2952372200A5A1CC /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = D77B92DB2952372200A5A1CC /* SwiftyGif */; };
|
||||||
D7F0E33929964E7E0068AF69 /* LZString in Frameworks */ = {isa = PBXBuildFile; productRef = D7F0E33829964E7E0068AF69 /* LZString */; };
|
D7F0E33929964E7E0068AF69 /* LZString in Frameworks */ = {isa = PBXBuildFile; productRef = D7F0E33829964E7E0068AF69 /* LZString */; };
|
||||||
|
E5D68D3F2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5D68D3A2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c.a */; };
|
||||||
|
E5D68D402C22D78C00CBA347 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5D68D3B2C22D78C00CBA347 /* libffi.a */; };
|
||||||
|
E5D68D412C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5D68D3C2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c-ghc9.6.3.a */; };
|
||||||
|
E5D68D422C22D78C00CBA347 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5D68D3D2C22D78C00CBA347 /* libgmp.a */; };
|
||||||
|
E5D68D432C22D78C00CBA347 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5D68D3E2C22D78C00CBA347 /* libgmpxx.a */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -273,11 +273,6 @@
|
||||||
5C029EA9283942EA004A9677 /* CallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallController.swift; sourceTree = "<group>"; };
|
5C029EA9283942EA004A9677 /* CallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallController.swift; sourceTree = "<group>"; };
|
||||||
5C05DF522840AA1D00C683F9 /* CallSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallSettings.swift; sourceTree = "<group>"; };
|
5C05DF522840AA1D00C683F9 /* CallSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallSettings.swift; sourceTree = "<group>"; };
|
||||||
5C063D2627A4564100AEC577 /* ChatPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatPreviewView.swift; sourceTree = "<group>"; };
|
5C063D2627A4564100AEC577 /* ChatPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatPreviewView.swift; sourceTree = "<group>"; };
|
||||||
5C0EA1362C0B176B00AD2E5E /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
|
||||||
5C0EA1372C0B176B00AD2E5E /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
|
||||||
5C0EA1382C0B176B00AD2E5E /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
|
||||||
5C0EA1392C0B176B00AD2E5E /* libHSsimplex-chat-5.8.0.5-Idqi6HXqzzs2zrnyZtMyhc-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.0.5-Idqi6HXqzzs2zrnyZtMyhc-ghc9.6.3.a"; sourceTree = "<group>"; };
|
|
||||||
5C0EA13A2C0B176B00AD2E5E /* libHSsimplex-chat-5.8.0.5-Idqi6HXqzzs2zrnyZtMyhc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.0.5-Idqi6HXqzzs2zrnyZtMyhc.a"; sourceTree = "<group>"; };
|
|
||||||
5C10D88728EED12E00E58BF0 /* ContactConnectionInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionInfo.swift; sourceTree = "<group>"; };
|
5C10D88728EED12E00E58BF0 /* ContactConnectionInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionInfo.swift; sourceTree = "<group>"; };
|
||||||
5C10D88928F187F300E58BF0 /* FullScreenMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenMediaView.swift; sourceTree = "<group>"; };
|
5C10D88928F187F300E58BF0 /* FullScreenMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenMediaView.swift; sourceTree = "<group>"; };
|
||||||
5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactRequestView.swift; sourceTree = "<group>"; };
|
5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactRequestView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -492,6 +487,11 @@
|
||||||
D741547729AF89AF0022400A /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; };
|
D741547729AF89AF0022400A /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
D741547929AF90B00022400A /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/PushKit.framework; sourceTree = DEVELOPER_DIR; };
|
D741547929AF90B00022400A /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/PushKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
D7AA2C3429A936B400737B40 /* MediaEncryption.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = MediaEncryption.playground; path = Shared/MediaEncryption.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
D7AA2C3429A936B400737B40 /* MediaEncryption.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = MediaEncryption.playground; path = Shared/MediaEncryption.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||||
|
E5D68D3A2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c.a"; sourceTree = "<group>"; };
|
||||||
|
E5D68D3B2C22D78C00CBA347 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||||
|
E5D68D3C2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||||
|
E5D68D3D2C22D78C00CBA347 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||||
|
E5D68D3E2C22D78C00CBA347 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -529,13 +529,13 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
5C0EA13C2C0B176B00AD2E5E /* libgmpxx.a in Frameworks */,
|
E5D68D412C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c-ghc9.6.3.a in Frameworks */,
|
||||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||||
5C0EA13B2C0B176B00AD2E5E /* libgmp.a in Frameworks */,
|
|
||||||
5C0EA13D2C0B176B00AD2E5E /* libffi.a in Frameworks */,
|
|
||||||
5C0EA13F2C0B176B00AD2E5E /* libHSsimplex-chat-5.8.0.5-Idqi6HXqzzs2zrnyZtMyhc.a in Frameworks */,
|
|
||||||
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
||||||
5C0EA13E2C0B176B00AD2E5E /* libHSsimplex-chat-5.8.0.5-Idqi6HXqzzs2zrnyZtMyhc-ghc9.6.3.a in Frameworks */,
|
E5D68D3F2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c.a in Frameworks */,
|
||||||
|
E5D68D422C22D78C00CBA347 /* libgmp.a in Frameworks */,
|
||||||
|
E5D68D402C22D78C00CBA347 /* libffi.a in Frameworks */,
|
||||||
|
E5D68D432C22D78C00CBA347 /* libgmpxx.a in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -601,11 +601,11 @@
|
||||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
5C0EA1382C0B176B00AD2E5E /* libffi.a */,
|
E5D68D3B2C22D78C00CBA347 /* libffi.a */,
|
||||||
5C0EA1362C0B176B00AD2E5E /* libgmp.a */,
|
E5D68D3D2C22D78C00CBA347 /* libgmp.a */,
|
||||||
5C0EA1372C0B176B00AD2E5E /* libgmpxx.a */,
|
E5D68D3E2C22D78C00CBA347 /* libgmpxx.a */,
|
||||||
5C0EA1392C0B176B00AD2E5E /* libHSsimplex-chat-5.8.0.5-Idqi6HXqzzs2zrnyZtMyhc-ghc9.6.3.a */,
|
E5D68D3C2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c-ghc9.6.3.a */,
|
||||||
5C0EA13A2C0B176B00AD2E5E /* libHSsimplex-chat-5.8.0.5-Idqi6HXqzzs2zrnyZtMyhc.a */,
|
E5D68D3A2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c.a */,
|
||||||
);
|
);
|
||||||
path = Libraries;
|
path = Libraries;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1552,7 +1552,7 @@
|
||||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 224;
|
CURRENT_PROJECT_VERSION = 225;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
|
@ -1577,7 +1577,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES_THIN;
|
LLVM_LTO = YES_THIN;
|
||||||
MARKETING_VERSION = 5.8;
|
MARKETING_VERSION = 5.8.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||||
PRODUCT_NAME = SimpleX;
|
PRODUCT_NAME = SimpleX;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
@ -1601,7 +1601,7 @@
|
||||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 224;
|
CURRENT_PROJECT_VERSION = 225;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
|
@ -1626,7 +1626,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 5.8;
|
MARKETING_VERSION = 5.8.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||||
PRODUCT_NAME = SimpleX;
|
PRODUCT_NAME = SimpleX;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
@ -1687,7 +1687,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 224;
|
CURRENT_PROJECT_VERSION = 225;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
GCC_OPTIMIZATION_LEVEL = s;
|
GCC_OPTIMIZATION_LEVEL = s;
|
||||||
|
@ -1702,7 +1702,7 @@
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 5.8;
|
MARKETING_VERSION = 5.8.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
@ -1724,7 +1724,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 224;
|
CURRENT_PROJECT_VERSION = 225;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
ENABLE_CODE_COVERAGE = NO;
|
ENABLE_CODE_COVERAGE = NO;
|
||||||
|
@ -1739,7 +1739,7 @@
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 5.8;
|
MARKETING_VERSION = 5.8.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
@ -1761,7 +1761,7 @@
|
||||||
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
||||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 224;
|
CURRENT_PROJECT_VERSION = 225;
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
|
@ -1787,7 +1787,7 @@
|
||||||
"$(PROJECT_DIR)/Libraries/sim",
|
"$(PROJECT_DIR)/Libraries/sim",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 5.8;
|
MARKETING_VERSION = 5.8.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
@ -1812,7 +1812,7 @@
|
||||||
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
||||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 224;
|
CURRENT_PROJECT_VERSION = 225;
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
|
@ -1838,7 +1838,7 @@
|
||||||
"$(PROJECT_DIR)/Libraries/sim",
|
"$(PROJECT_DIR)/Libraries/sim",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 5.8;
|
MARKETING_VERSION = 5.8.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
|
|
@ -980,7 +980,7 @@ public enum ChatResponse: Decodable, Error {
|
||||||
case let .remoteCtrlConnecting(remoteCtrl_, ctrlAppInfo, appVersion): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nctrlAppInfo:\n\(String(describing: ctrlAppInfo))\nappVersion: \(appVersion)"
|
case let .remoteCtrlConnecting(remoteCtrl_, ctrlAppInfo, appVersion): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nctrlAppInfo:\n\(String(describing: ctrlAppInfo))\nappVersion: \(appVersion)"
|
||||||
case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nsessionCode: \(sessionCode)"
|
case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nsessionCode: \(sessionCode)"
|
||||||
case let .remoteCtrlConnected(remoteCtrl): return String(describing: remoteCtrl)
|
case let .remoteCtrlConnected(remoteCtrl): return String(describing: remoteCtrl)
|
||||||
case .remoteCtrlStopped: return noDetails
|
case let .remoteCtrlStopped(rcsState, rcStopReason): return "rcsState: \(String(describing: rcsState))\nrcStopReason: \(String(describing: rcStopReason))"
|
||||||
case let .contactPQEnabled(u, contact, pqEnabled): return withUser(u, "contact: \(String(describing: contact))\npqEnabled: \(pqEnabled)")
|
case let .contactPQEnabled(u, contact, pqEnabled): return withUser(u, "contact: \(String(describing: contact))\npqEnabled: \(pqEnabled)")
|
||||||
case let .versionInfo(versionInfo, chatMigrations, agentMigrations): return "\(String(describing: versionInfo))\n\nchat migrations: \(chatMigrations.map(\.upName))\n\nagent migrations: \(agentMigrations.map(\.upName))"
|
case let .versionInfo(versionInfo, chatMigrations, agentMigrations): return "\(String(describing: versionInfo))\n\nchat migrations: \(chatMigrations.map(\.upName))\n\nagent migrations: \(agentMigrations.map(\.upName))"
|
||||||
case .cmdOk: return noDetails
|
case .cmdOk: return noDetails
|
||||||
|
|
|
@ -3438,6 +3438,15 @@ public enum MsgContent: Equatable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var isMediaOrFileAttachment: Bool {
|
||||||
|
switch self {
|
||||||
|
case .image: true
|
||||||
|
case .video: true
|
||||||
|
case .file: true
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var cmdString: String {
|
var cmdString: String {
|
||||||
"json \(encodeJSON(self))"
|
"json \(encodeJSON(self))"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2925,6 +2925,20 @@ sealed class MsgContent {
|
||||||
@Serializable(with = MsgContentSerializer::class) class MCFile(override val text: String): MsgContent()
|
@Serializable(with = MsgContentSerializer::class) class MCFile(override val text: String): MsgContent()
|
||||||
@Serializable(with = MsgContentSerializer::class) class MCUnknown(val type: String? = null, override val text: String, val json: JsonElement): MsgContent()
|
@Serializable(with = MsgContentSerializer::class) class MCUnknown(val type: String? = null, override val text: String, val json: JsonElement): MsgContent()
|
||||||
|
|
||||||
|
val isVoice: Boolean get() =
|
||||||
|
when (this) {
|
||||||
|
is MCVoice -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
val isMediaOrFileAttachment: Boolean get() =
|
||||||
|
when (this) {
|
||||||
|
is MCImage -> true
|
||||||
|
is MCVideo -> true
|
||||||
|
is MCFile -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
val cmdString: String get() =
|
val cmdString: String get() =
|
||||||
if (this is MCUnknown) "json $json" else "json ${json.encodeToString(this)}"
|
if (this is MCUnknown) "json $json" else "json ${json.encodeToString(this)}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
package chat.simplex.common.model
|
package chat.simplex.common.model
|
||||||
|
|
||||||
|
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 chat.simplex.common.views.helpers.*
|
import chat.simplex.common.views.helpers.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
|
import androidx.compose.ui.platform.LocalClipboardManager
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import chat.simplex.common.model.ChatController.getNetCfg
|
import chat.simplex.common.model.ChatController.getNetCfg
|
||||||
import chat.simplex.common.model.ChatController.setNetCfg
|
import chat.simplex.common.model.ChatController.setNetCfg
|
||||||
import chat.simplex.common.model.ChatModel.updatingChatsMutex
|
import chat.simplex.common.model.ChatModel.updatingChatsMutex
|
||||||
|
@ -12,7 +21,6 @@ import dev.icerock.moko.resources.compose.painterResource
|
||||||
import chat.simplex.common.platform.*
|
import chat.simplex.common.platform.*
|
||||||
import chat.simplex.common.ui.theme.*
|
import chat.simplex.common.ui.theme.*
|
||||||
import chat.simplex.common.views.call.*
|
import chat.simplex.common.views.call.*
|
||||||
import chat.simplex.common.views.chat.group.toggleShowMemberMessages
|
|
||||||
import chat.simplex.common.views.migration.MigrationFileLinkData
|
import chat.simplex.common.views.migration.MigrationFileLinkData
|
||||||
import chat.simplex.common.views.onboarding.OnboardingStage
|
import chat.simplex.common.views.onboarding.OnboardingStage
|
||||||
import chat.simplex.common.views.usersettings.*
|
import chat.simplex.common.views.usersettings.*
|
||||||
|
@ -20,6 +28,7 @@ import com.charleskorn.kaml.Yaml
|
||||||
import com.charleskorn.kaml.YamlConfiguration
|
import com.charleskorn.kaml.YamlConfiguration
|
||||||
import chat.simplex.res.MR
|
import chat.simplex.res.MR
|
||||||
import com.russhwolf.settings.Settings
|
import com.russhwolf.settings.Settings
|
||||||
|
import dev.icerock.moko.resources.compose.stringResource
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
@ -2194,15 +2203,43 @@ object ChatController {
|
||||||
val sess = chatModel.remoteCtrlSession.value
|
val sess = chatModel.remoteCtrlSession.value
|
||||||
if (sess != null) {
|
if (sess != null) {
|
||||||
chatModel.remoteCtrlSession.value = null
|
chatModel.remoteCtrlSession.value = null
|
||||||
|
ModalManager.fullscreen.closeModals()
|
||||||
fun showAlert(chatError: ChatError) {
|
fun showAlert(chatError: ChatError) {
|
||||||
AlertManager.shared.showAlertMsg(
|
when {
|
||||||
generalGetString(MR.strings.remote_ctrl_was_disconnected_title),
|
r.rcStopReason is RemoteCtrlStopReason.ConnectionFailed
|
||||||
if (chatError is ChatError.ChatErrorRemoteCtrl) {
|
&& r.rcStopReason.chatError is ChatError.ChatErrorAgent
|
||||||
chatError.remoteCtrlError.localizedString
|
&& r.rcStopReason.chatError.agentError is AgentErrorType.RCP
|
||||||
} else {
|
&& r.rcStopReason.chatError.agentError.rcpErr is RCErrorType.IDENTITY ->
|
||||||
generalGetString(MR.strings.remote_ctrl_disconnected_with_reason).format(chatError.string)
|
AlertManager.shared.showAlertMsg(
|
||||||
}
|
title = generalGetString(MR.strings.remote_ctrl_was_disconnected_title),
|
||||||
)
|
text = generalGetString(MR.strings.remote_ctrl_connection_stopped_identity_desc)
|
||||||
|
)
|
||||||
|
else ->
|
||||||
|
AlertManager.shared.showAlertDialogButtonsColumn(
|
||||||
|
title = generalGetString(MR.strings.remote_ctrl_was_disconnected_title),
|
||||||
|
text = if (chatError is ChatError.ChatErrorRemoteCtrl) {
|
||||||
|
chatError.remoteCtrlError.localizedString
|
||||||
|
} else {
|
||||||
|
generalGetString(MR.strings.remote_ctrl_connection_stopped_desc)
|
||||||
|
},
|
||||||
|
buttons = {
|
||||||
|
Column {
|
||||||
|
SectionItemView({
|
||||||
|
AlertManager.shared.hideAlert()
|
||||||
|
}) {
|
||||||
|
Text(stringResource(MR.strings.ok), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
|
||||||
|
}
|
||||||
|
val clipboard = LocalClipboardManager.current
|
||||||
|
SectionItemView({
|
||||||
|
clipboard.setText(AnnotatedString(json.encodeToString(r.rcStopReason)))
|
||||||
|
AlertManager.shared.hideAlert()
|
||||||
|
}) {
|
||||||
|
Text(stringResource(MR.strings.copy_error), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
when (r.rcStopReason) {
|
when (r.rcStopReason) {
|
||||||
is RemoteCtrlStopReason.DiscoveryFailed -> showAlert(r.rcStopReason.chatError)
|
is RemoteCtrlStopReason.DiscoveryFailed -> showAlert(r.rcStopReason.chatError)
|
||||||
|
@ -4716,7 +4753,7 @@ sealed class CR {
|
||||||
(if (remoteCtrl_ == null) "null" else json.encodeToString(remoteCtrl_)) +
|
(if (remoteCtrl_ == null) "null" else json.encodeToString(remoteCtrl_)) +
|
||||||
"\nsessionCode: $sessionCode"
|
"\nsessionCode: $sessionCode"
|
||||||
is RemoteCtrlConnected -> json.encodeToString(remoteCtrl)
|
is RemoteCtrlConnected -> json.encodeToString(remoteCtrl)
|
||||||
is RemoteCtrlStopped -> noDetails()
|
is RemoteCtrlStopped -> "rcsState: $rcsState\nrcsStopReason: $rcStopReason"
|
||||||
is ContactPQAllowed -> withUser(user, "contact: ${contact.id}\npqEncryption: $pqEncryption")
|
is ContactPQAllowed -> withUser(user, "contact: ${contact.id}\npqEncryption: $pqEncryption")
|
||||||
is ContactPQEnabled -> withUser(user, "contact: ${contact.id}\npqEnabled: $pqEnabled")
|
is ContactPQEnabled -> withUser(user, "contact: ${contact.id}\npqEnabled: $pqEnabled")
|
||||||
is VersionInfo -> "version ${json.encodeToString(versionInfo)}\n\n" +
|
is VersionInfo -> "version ${json.encodeToString(versionInfo)}\n\n" +
|
||||||
|
|
|
@ -9,30 +9,55 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import chat.simplex.common.views.helpers.ProfileImage
|
|
||||||
import chat.simplex.common.model.*
|
import chat.simplex.common.model.*
|
||||||
import chat.simplex.common.ui.theme.*
|
import chat.simplex.common.ui.theme.*
|
||||||
|
import chat.simplex.common.views.helpers.*
|
||||||
import chat.simplex.res.MR
|
import chat.simplex.res.MR
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ShareListNavLinkView(chat: Chat, chatModel: ChatModel) {
|
fun ShareListNavLinkView(
|
||||||
|
chat: Chat,
|
||||||
|
chatModel: ChatModel,
|
||||||
|
isMediaOrFileAttachment: Boolean,
|
||||||
|
isVoice: Boolean,
|
||||||
|
hasSimplexLink: Boolean
|
||||||
|
) {
|
||||||
val stopped = chatModel.chatRunning.value == false
|
val stopped = chatModel.chatRunning.value == false
|
||||||
when (chat.chatInfo) {
|
when (chat.chatInfo) {
|
||||||
is ChatInfo.Direct ->
|
is ChatInfo.Direct -> {
|
||||||
|
val voiceProhibited = isVoice && !chat.chatInfo.featureEnabled(ChatFeature.Voice)
|
||||||
ShareListNavLinkLayout(
|
ShareListNavLinkLayout(
|
||||||
chatLinkPreview = { SharePreviewView(chat) },
|
chatLinkPreview = { SharePreviewView(chat, disabled = voiceProhibited) },
|
||||||
click = { directChatAction(chat.remoteHostId, chat.chatInfo.contact, chatModel) },
|
click = {
|
||||||
|
if (voiceProhibited) {
|
||||||
|
showForwardProhibitedByPrefAlert()
|
||||||
|
} else {
|
||||||
|
directChatAction(chat.remoteHostId, chat.chatInfo.contact, chatModel)
|
||||||
|
}
|
||||||
|
},
|
||||||
stopped
|
stopped
|
||||||
)
|
)
|
||||||
is ChatInfo.Group ->
|
}
|
||||||
|
is ChatInfo.Group -> {
|
||||||
|
val simplexLinkProhibited = hasSimplexLink && !chat.groupFeatureEnabled(GroupFeature.SimplexLinks)
|
||||||
|
val fileProhibited = isMediaOrFileAttachment && !chat.groupFeatureEnabled(GroupFeature.Files)
|
||||||
|
val voiceProhibited = isVoice && !chat.chatInfo.featureEnabled(ChatFeature.Voice)
|
||||||
|
val prohibitedByPref = simplexLinkProhibited || fileProhibited || voiceProhibited
|
||||||
ShareListNavLinkLayout(
|
ShareListNavLinkLayout(
|
||||||
chatLinkPreview = { SharePreviewView(chat) },
|
chatLinkPreview = { SharePreviewView(chat, disabled = prohibitedByPref) },
|
||||||
click = { groupChatAction(chat.remoteHostId, chat.chatInfo.groupInfo, chatModel) },
|
click = {
|
||||||
|
if (prohibitedByPref) {
|
||||||
|
showForwardProhibitedByPrefAlert()
|
||||||
|
} else {
|
||||||
|
groupChatAction(chat.remoteHostId, chat.chatInfo.groupInfo, chatModel)
|
||||||
|
}
|
||||||
|
},
|
||||||
stopped
|
stopped
|
||||||
)
|
)
|
||||||
|
}
|
||||||
is ChatInfo.Local ->
|
is ChatInfo.Local ->
|
||||||
ShareListNavLinkLayout(
|
ShareListNavLinkLayout(
|
||||||
chatLinkPreview = { SharePreviewView(chat) },
|
chatLinkPreview = { SharePreviewView(chat, disabled = false) },
|
||||||
click = { noteFolderChatAction(chat.remoteHostId, chat.chatInfo.noteFolder) },
|
click = { noteFolderChatAction(chat.remoteHostId, chat.chatInfo.noteFolder) },
|
||||||
stopped
|
stopped
|
||||||
)
|
)
|
||||||
|
@ -40,6 +65,13 @@ fun ShareListNavLinkView(chat: Chat, chatModel: ChatModel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showForwardProhibitedByPrefAlert() {
|
||||||
|
AlertManager.shared.showAlertMsg(
|
||||||
|
title = generalGetString(MR.strings.cannot_share_message_alert_title),
|
||||||
|
text = generalGetString(MR.strings.cannot_share_message_alert_text),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ShareListNavLinkLayout(
|
private fun ShareListNavLinkLayout(
|
||||||
chatLinkPreview: @Composable () -> Unit,
|
chatLinkPreview: @Composable () -> Unit,
|
||||||
|
@ -53,7 +85,7 @@ private fun ShareListNavLinkLayout(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SharePreviewView(chat: Chat) {
|
private fun SharePreviewView(chat: Chat, disabled: Boolean) {
|
||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxSize(),
|
Modifier.fillMaxSize(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
@ -70,7 +102,7 @@ private fun SharePreviewView(chat: Chat) {
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
chat.chatInfo.chatViewName, maxLines = 1, overflow = TextOverflow.Ellipsis,
|
chat.chatInfo.chatViewName, maxLines = 1, overflow = TextOverflow.Ellipsis,
|
||||||
color = if (chat.chatInfo.incognito) Indigo else Color.Unspecified
|
color = if (disabled) MaterialTheme.colors.secondary else if (chat.chatInfo.incognito) Indigo else Color.Unspecified
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,13 +31,44 @@ fun ShareListView(chatModel: ChatModel, settingsState: SettingsViewState, stoppe
|
||||||
scaffoldState = scaffoldState,
|
scaffoldState = scaffoldState,
|
||||||
topBar = { Column { ShareListToolbar(chatModel, userPickerState, stopped) { searchInList = it.trim() } } },
|
topBar = { Column { ShareListToolbar(chatModel, userPickerState, stopped) { searchInList = it.trim() } } },
|
||||||
) {
|
) {
|
||||||
|
val sharedContent = chatModel.sharedContent.value
|
||||||
|
var isMediaOrFileAttachment = false
|
||||||
|
var isVoice = false
|
||||||
|
var hasSimplexLink = false
|
||||||
|
when (sharedContent) {
|
||||||
|
is SharedContent.Text ->
|
||||||
|
hasSimplexLink = hasSimplexLink(sharedContent.text)
|
||||||
|
is SharedContent.Media -> {
|
||||||
|
isMediaOrFileAttachment = true
|
||||||
|
hasSimplexLink = hasSimplexLink(sharedContent.text)
|
||||||
|
}
|
||||||
|
is SharedContent.File -> {
|
||||||
|
isMediaOrFileAttachment = true
|
||||||
|
hasSimplexLink = hasSimplexLink(sharedContent.text)
|
||||||
|
}
|
||||||
|
is SharedContent.Forward -> {
|
||||||
|
val mc = sharedContent.chatItem.content.msgContent
|
||||||
|
if (mc != null) {
|
||||||
|
isMediaOrFileAttachment = mc.isMediaOrFileAttachment
|
||||||
|
isVoice = mc.isVoice
|
||||||
|
hasSimplexLink = hasSimplexLink(mc.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
null -> {}
|
||||||
|
}
|
||||||
Box(Modifier.padding(it)) {
|
Box(Modifier.padding(it)) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
if (chatModel.chats.isNotEmpty()) {
|
if (chatModel.chats.isNotEmpty()) {
|
||||||
ShareList(chatModel, search = searchInList)
|
ShareList(
|
||||||
|
chatModel,
|
||||||
|
search = searchInList,
|
||||||
|
isMediaOrFileAttachment = isMediaOrFileAttachment,
|
||||||
|
isVoice = isVoice,
|
||||||
|
hasSimplexLink = hasSimplexLink
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
EmptyList()
|
EmptyList()
|
||||||
}
|
}
|
||||||
|
@ -54,6 +85,11 @@ fun ShareListView(chatModel: ChatModel, settingsState: SettingsViewState, stoppe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun hasSimplexLink(msg: String): Boolean {
|
||||||
|
val parsedMsg = parseToMarkdown(msg) ?: return false
|
||||||
|
return parsedMsg.any { ft -> ft.format is Format.SimplexLink }
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun EmptyList() {
|
private fun EmptyList() {
|
||||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
|
@ -141,7 +177,13 @@ private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableState
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ShareList(chatModel: ChatModel, search: String) {
|
private fun ShareList(
|
||||||
|
chatModel: ChatModel,
|
||||||
|
search: String,
|
||||||
|
isMediaOrFileAttachment: Boolean,
|
||||||
|
isVoice: Boolean,
|
||||||
|
hasSimplexLink: Boolean
|
||||||
|
) {
|
||||||
val chats by remember(search) {
|
val chats by remember(search) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
val sorted = chatModel.chats.toList().sortedByDescending { it.chatInfo is ChatInfo.Local }
|
val sorted = chatModel.chats.toList().sortedByDescending { it.chatInfo is ChatInfo.Local }
|
||||||
|
@ -156,7 +198,13 @@ private fun ShareList(chatModel: ChatModel, search: String) {
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
items(chats) { chat ->
|
items(chats) { chat ->
|
||||||
ShareListNavLinkView(chat, chatModel)
|
ShareListNavLinkView(
|
||||||
|
chat,
|
||||||
|
chatModel,
|
||||||
|
isMediaOrFileAttachment = isMediaOrFileAttachment,
|
||||||
|
isVoice = isVoice,
|
||||||
|
hasSimplexLink = hasSimplexLink
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -448,6 +448,9 @@ private fun stopChat(m: ChatModel, progressIndicator: MutableState<Boolean>? = n
|
||||||
progressIndicator?.value = true
|
progressIndicator?.value = true
|
||||||
stopChatAsync(m)
|
stopChatAsync(m)
|
||||||
platform.androidChatStopped()
|
platform.androidChatStopped()
|
||||||
|
// close chat view for desktop
|
||||||
|
chatModel.chatId.value = null
|
||||||
|
ModalManager.end.closeModals()
|
||||||
onStop?.invoke()
|
onStop?.invoke()
|
||||||
} catch (e: Error) {
|
} catch (e: Error) {
|
||||||
m.chatRunning.value = true
|
m.chatRunning.value = true
|
||||||
|
|
|
@ -15,6 +15,7 @@ import androidx.compose.material.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalClipboardManager
|
import androidx.compose.ui.platform.LocalClipboardManager
|
||||||
|
@ -166,6 +167,24 @@ private fun ConnectingDesktop(session: RemoteCtrlSession, rc: RemoteCtrlInfo?) {
|
||||||
SectionView {
|
SectionView {
|
||||||
DisconnectButton(onClick = ::disconnectDesktop)
|
DisconnectButton(onClick = ::disconnectDesktop)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProgressIndicator()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ProgressIndicator() {
|
||||||
|
Box(
|
||||||
|
Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
Modifier
|
||||||
|
.padding(horizontal = 2.dp)
|
||||||
|
.size(30.dp),
|
||||||
|
color = MaterialTheme.colors.secondary,
|
||||||
|
strokeWidth = 3.dp
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
@ -28,6 +28,7 @@ import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import chat.simplex.common.model.*
|
import chat.simplex.common.model.*
|
||||||
|
import chat.simplex.common.model.ChatModel.controller
|
||||||
import chat.simplex.common.platform.*
|
import chat.simplex.common.platform.*
|
||||||
import chat.simplex.common.ui.theme.*
|
import chat.simplex.common.ui.theme.*
|
||||||
import chat.simplex.common.views.chat.item.ClickableText
|
import chat.simplex.common.views.chat.item.ClickableText
|
||||||
|
@ -58,6 +59,8 @@ fun NetworkAndServersView() {
|
||||||
smpProxyFallback = smpProxyFallback,
|
smpProxyFallback = smpProxyFallback,
|
||||||
proxyPort = proxyPort,
|
proxyPort = proxyPort,
|
||||||
toggleSocksProxy = { enable ->
|
toggleSocksProxy = { enable ->
|
||||||
|
val def = NetCfg.defaults
|
||||||
|
val proxyDef = NetCfg.proxyDefaults
|
||||||
if (enable) {
|
if (enable) {
|
||||||
AlertManager.shared.showAlertDialog(
|
AlertManager.shared.showAlertDialog(
|
||||||
title = generalGetString(MR.strings.network_enable_socks),
|
title = generalGetString(MR.strings.network_enable_socks),
|
||||||
|
@ -65,7 +68,19 @@ fun NetworkAndServersView() {
|
||||||
confirmText = generalGetString(MR.strings.confirm_verb),
|
confirmText = generalGetString(MR.strings.confirm_verb),
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
withBGApi {
|
withBGApi {
|
||||||
val conf = NetCfg.proxyDefaults.withHostPort(chatModel.controller.appPrefs.networkProxyHostPort.get())
|
var conf = controller.getNetCfg().withHostPort(controller.appPrefs.networkProxyHostPort.get())
|
||||||
|
if (conf.tcpConnectTimeout == def.tcpConnectTimeout) {
|
||||||
|
conf = conf.copy(tcpConnectTimeout = proxyDef.tcpConnectTimeout)
|
||||||
|
}
|
||||||
|
if (conf.tcpTimeout == def.tcpTimeout) {
|
||||||
|
conf = conf.copy(tcpTimeout = proxyDef.tcpTimeout)
|
||||||
|
}
|
||||||
|
if (conf.tcpTimeoutPerKb == def.tcpTimeoutPerKb) {
|
||||||
|
conf = conf.copy(tcpTimeoutPerKb = proxyDef.tcpTimeoutPerKb)
|
||||||
|
}
|
||||||
|
if (conf.rcvConcurrency == def.rcvConcurrency) {
|
||||||
|
conf = conf.copy(rcvConcurrency = proxyDef.rcvConcurrency)
|
||||||
|
}
|
||||||
chatModel.controller.apiSetNetworkConfig(conf)
|
chatModel.controller.apiSetNetworkConfig(conf)
|
||||||
chatModel.controller.setNetCfg(conf)
|
chatModel.controller.setNetCfg(conf)
|
||||||
networkUseSocksProxy.value = true
|
networkUseSocksProxy.value = true
|
||||||
|
@ -80,7 +95,19 @@ fun NetworkAndServersView() {
|
||||||
confirmText = generalGetString(MR.strings.confirm_verb),
|
confirmText = generalGetString(MR.strings.confirm_verb),
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
withBGApi {
|
withBGApi {
|
||||||
val conf = NetCfg.defaults
|
var conf = controller.getNetCfg().copy(socksProxy = null)
|
||||||
|
if (conf.tcpConnectTimeout == proxyDef.tcpConnectTimeout) {
|
||||||
|
conf = conf.copy(tcpConnectTimeout = def.tcpConnectTimeout)
|
||||||
|
}
|
||||||
|
if (conf.tcpTimeout == proxyDef.tcpTimeout) {
|
||||||
|
conf = conf.copy(tcpTimeout = def.tcpTimeout)
|
||||||
|
}
|
||||||
|
if (conf.tcpTimeoutPerKb == proxyDef.tcpTimeoutPerKb) {
|
||||||
|
conf = conf.copy(tcpTimeoutPerKb = def.tcpTimeoutPerKb)
|
||||||
|
}
|
||||||
|
if (conf.rcvConcurrency == proxyDef.rcvConcurrency) {
|
||||||
|
conf = conf.copy(rcvConcurrency = def.rcvConcurrency)
|
||||||
|
}
|
||||||
chatModel.controller.apiSetNetworkConfig(conf)
|
chatModel.controller.apiSetNetworkConfig(conf)
|
||||||
chatModel.controller.setNetCfg(conf)
|
chatModel.controller.setNetCfg(conf)
|
||||||
networkUseSocksProxy.value = false
|
networkUseSocksProxy.value = false
|
||||||
|
|
|
@ -358,6 +358,8 @@
|
||||||
<string name="share_image">Share media…</string>
|
<string name="share_image">Share media…</string>
|
||||||
<string name="share_file">Share file…</string>
|
<string name="share_file">Share file…</string>
|
||||||
<string name="forward_message">Forward message…</string>
|
<string name="forward_message">Forward message…</string>
|
||||||
|
<string name="cannot_share_message_alert_title">Cannot send message</string>
|
||||||
|
<string name="cannot_share_message_alert_text">Selected chat preferences prohibit this message.</string>
|
||||||
|
|
||||||
<!-- ComposeView.kt, helpers -->
|
<!-- ComposeView.kt, helpers -->
|
||||||
<string name="attach">Attach</string>
|
<string name="attach">Attach</string>
|
||||||
|
@ -1921,6 +1923,9 @@
|
||||||
<string name="remote_ctrl_was_disconnected_title">Connection stopped</string>
|
<string name="remote_ctrl_was_disconnected_title">Connection stopped</string>
|
||||||
<string name="remote_host_disconnected_from"><![CDATA[Disconnected from mobile <b>%s</b> with the reason: %s]]></string>
|
<string name="remote_host_disconnected_from"><![CDATA[Disconnected from mobile <b>%s</b> with the reason: %s]]></string>
|
||||||
<string name="remote_ctrl_disconnected_with_reason">Disconnected with the reason: %s</string>
|
<string name="remote_ctrl_disconnected_with_reason">Disconnected with the reason: %s</string>
|
||||||
|
<string name="remote_ctrl_connection_stopped_desc">Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers.</string>
|
||||||
|
<string name="remote_ctrl_connection_stopped_identity_desc">This link was used with another mobile device, please create a new link on the desktop.</string>
|
||||||
|
<string name="copy_error">Copy error</string>
|
||||||
<string name="disconnect_desktop_question">Disconnect desktop?</string>
|
<string name="disconnect_desktop_question">Disconnect desktop?</string>
|
||||||
<string name="only_one_device_can_work_at_the_same_time">Only one device can work at the same time</string>
|
<string name="only_one_device_can_work_at_the_same_time">Only one device can work at the same time</string>
|
||||||
<string name="open_on_mobile_and_scan_qr_code"><![CDATA[Open <i>Use from desktop</i> in mobile app and scan QR code.]]></string>
|
<string name="open_on_mobile_and_scan_qr_code"><![CDATA[Open <i>Use from desktop</i> in mobile app and scan QR code.]]></string>
|
||||||
|
|
|
@ -14,8 +14,10 @@ import com.russhwolf.settings.*
|
||||||
import dev.icerock.moko.resources.ImageResource
|
import dev.icerock.moko.resources.ImageResource
|
||||||
import dev.icerock.moko.resources.StringResource
|
import dev.icerock.moko.resources.StringResource
|
||||||
import dev.icerock.moko.resources.desc.desc
|
import dev.icerock.moko.resources.desc.desc
|
||||||
|
import kotlinx.coroutines.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
actual fun font(name: String, res: String, weight: FontWeight, style: FontStyle): Font =
|
actual fun font(name: String, res: String, weight: FontWeight, style: FontStyle): Font =
|
||||||
|
@ -59,8 +61,11 @@ private val settingsThemesProps =
|
||||||
Properties()
|
Properties()
|
||||||
.also { props -> try { settingsThemesFile.reader().use { props.load(it) } } catch (e: Exception) { /**/ } }
|
.also { props -> try { settingsThemesFile.reader().use { props.load(it) } } catch (e: Exception) { /**/ } }
|
||||||
|
|
||||||
actual val settings: Settings = PropertiesSettings(settingsProps) { withApi { settingsFile.writer().use { settingsProps.store(it, "") } } }
|
|
||||||
actual val settingsThemes: Settings = PropertiesSettings(settingsThemesProps) { withApi { settingsThemesFile.writer().use { settingsThemesProps.store(it, "") } } }
|
private val settingsWriterThread = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||||
|
|
||||||
|
actual val settings: Settings = PropertiesSettings(settingsProps) { CoroutineScope(settingsWriterThread).launch { settingsFile.writer().use { settingsProps.store(it, "") } } }
|
||||||
|
actual val settingsThemes: Settings = PropertiesSettings(settingsThemesProps) { CoroutineScope(settingsWriterThread).launch { settingsThemesFile.writer().use { settingsThemesProps.store(it, "") } } }
|
||||||
|
|
||||||
actual fun windowOrientation(): WindowOrientation =
|
actual fun windowOrientation(): WindowOrientation =
|
||||||
if (simplexWindowState.windowState.size.width > simplexWindowState.windowState.size.height) {
|
if (simplexWindowState.windowState.size.width > simplexWindowState.windowState.size.height) {
|
||||||
|
|
|
@ -26,11 +26,11 @@ android.enableJetifier=true
|
||||||
kotlin.mpp.androidSourceSetLayoutVersion=2
|
kotlin.mpp.androidSourceSetLayoutVersion=2
|
||||||
kotlin.jvm.target=11
|
kotlin.jvm.target=11
|
||||||
|
|
||||||
android.version_name=5.8
|
android.version_name=5.8.1
|
||||||
android.version_code=219
|
android.version_code=221
|
||||||
|
|
||||||
desktop.version_name=5.8
|
desktop.version_name=5.8.1
|
||||||
desktop.version_code=53
|
desktop.version_code=54
|
||||||
|
|
||||||
kotlin.version=1.9.23
|
kotlin.version=1.9.23
|
||||||
gradle.plugin.version=8.2.0
|
gradle.plugin.version=8.2.0
|
||||||
|
|
|
@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
|
||||||
source-repository-package
|
source-repository-package
|
||||||
type: git
|
type: git
|
||||||
location: https://github.com/simplex-chat/simplexmq.git
|
location: https://github.com/simplex-chat/simplexmq.git
|
||||||
tag: 3c0cd7efcc3d3058d940c7a9667faef2dc6de6cc
|
tag: 8a3b72458f917e9867f4e3640dda0fa1827ff6cf
|
||||||
|
|
||||||
source-repository-package
|
source-repository-package
|
||||||
type: git
|
type: git
|
||||||
|
|
|
@ -7,7 +7,7 @@ revision: 11.02.2024
|
||||||
| Updated 23.03.2024 | Languages: EN |
|
| Updated 23.03.2024 | Languages: EN |
|
||||||
# Download SimpleX apps
|
# Download SimpleX apps
|
||||||
|
|
||||||
The latest stable version is v5.6.
|
The latest stable version is v5.8.
|
||||||
|
|
||||||
You can get the latest beta releases from [GitHub](https://github.com/simplex-chat/simplex-chat/releases).
|
You can get the latest beta releases from [GitHub](https://github.com/simplex-chat/simplex-chat/releases).
|
||||||
|
|
||||||
|
|
|
@ -331,7 +331,7 @@ disconnect: off
|
||||||
|
|
||||||
[WEB]
|
[WEB]
|
||||||
# Set path to generate static mini-site for server information and qr codes/links
|
# Set path to generate static mini-site for server information and qr codes/links
|
||||||
static_path: <WRITABLE_PATH_TO_STORE_WEBSITE>
|
static_path: /var/opt/simplex/www
|
||||||
|
|
||||||
# Run an embedded server on this port
|
# Run an embedded server on this port
|
||||||
# Onion sites can use any port and register it in the hidden service config.
|
# Onion sites can use any port and register it in the hidden service config.
|
||||||
|
@ -601,13 +601,7 @@ SMP-server versions starting from `v5.8.0-beta.0` can be configured to PROXY smp
|
||||||
|
|
||||||
SMP-server versions starting from `v5.8.0` can be configured to serve Web page with server information that can include admin info, server info, provider info, etc. Run the following commands as `root` user.
|
SMP-server versions starting from `v5.8.0` can be configured to serve Web page with server information that can include admin info, server info, provider info, etc. Run the following commands as `root` user.
|
||||||
|
|
||||||
1. Create folder to store webserver static files and assign correct permissions:
|
1. Add the following to your smp-server configuration (please modify fields in [INFORMATION] section to include relevant information):
|
||||||
|
|
||||||
```sh
|
|
||||||
mkdir -p /var/www/smp-server-web && chown smp:smp /var/www/smp-server-web
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Add the following to your smp-server configuration (please modify fields in [INFORMATION] section to include relevant information):
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
vim /etc/opt/simplex/smp-server.ini
|
vim /etc/opt/simplex/smp-server.ini
|
||||||
|
@ -615,7 +609,7 @@ SMP-server versions starting from `v5.8.0` can be configured to serve Web page w
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
[WEB]
|
[WEB]
|
||||||
static_path: /var/www/smp-server-web
|
static_path: /var/opt/simplex/www
|
||||||
|
|
||||||
[INFORMATION]
|
[INFORMATION]
|
||||||
# AGPLv3 license requires that you make any source code modifications
|
# AGPLv3 license requires that you make any source code modifications
|
||||||
|
@ -656,7 +650,7 @@ SMP-server versions starting from `v5.8.0` can be configured to serve Web page w
|
||||||
hosting_country: <HOSTING_PROVIDER_LOCATION>
|
hosting_country: <HOSTING_PROVIDER_LOCATION>
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Install the webserver. For easy deployment we'll describe the installtion process of [Caddy](https://caddyserver.com) webserver on Ubuntu server:
|
2. Install the webserver. For easy deployment we'll describe the installtion process of [Caddy](https://caddyserver.com) webserver on Ubuntu server:
|
||||||
|
|
||||||
1. Install the packages:
|
1. Install the packages:
|
||||||
|
|
||||||
|
@ -684,7 +678,7 @@ SMP-server versions starting from `v5.8.0` can be configured to serve Web page w
|
||||||
|
|
||||||
[Full Caddy instllation instructions](https://caddyserver.com/docs/install)
|
[Full Caddy instllation instructions](https://caddyserver.com/docs/install)
|
||||||
|
|
||||||
4. Replace Caddy configuration with the following (don't forget to replace `<YOUR_DOMAIN>`):
|
3. Replace Caddy configuration with the following (don't forget to replace `<YOUR_DOMAIN>`):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
vim /etc/caddy/Caddyfile
|
vim /etc/caddy/Caddyfile
|
||||||
|
@ -692,20 +686,20 @@ SMP-server versions starting from `v5.8.0` can be configured to serve Web page w
|
||||||
|
|
||||||
```caddy
|
```caddy
|
||||||
<YOUR_DOMAIN> {
|
<YOUR_DOMAIN> {
|
||||||
root * /var/www/simplex
|
root * /var/opt/simplex/www
|
||||||
file_server
|
file_server
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Enable and start Caddy service:
|
4. Enable and start Caddy service:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
systemctl enable --now caddy
|
systemctl enable --now caddy
|
||||||
```
|
```
|
||||||
|
|
||||||
6. Upgrade your smp-server to latest version - [Updating your smp server](#updating-your-smp-server)
|
5. Upgrade your smp-server to latest version - [Updating your smp server](#updating-your-smp-server)
|
||||||
|
|
||||||
7. Access the webpage you've deployed from your browser. You should see the smp-server information that you've provided in your ini file.
|
6. Access the webpage you've deployed from your browser. You should see the smp-server information that you've provided in your ini file.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: simplex-chat
|
name: simplex-chat
|
||||||
version: 5.8.0.5
|
version: 5.8.1.0
|
||||||
#synopsis:
|
#synopsis:
|
||||||
#description:
|
#description:
|
||||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||||
|
|
|
@ -47,7 +47,7 @@ for ORIG_NAME in "${ORIG_NAMES[@]}"; do
|
||||||
#(cd apk && 7z a -r -mx=0 -tzip ../$ORIG_NAME resources.arsc)
|
#(cd apk && 7z a -r -mx=0 -tzip ../$ORIG_NAME resources.arsc)
|
||||||
|
|
||||||
ALL_TOOLS=("$sdk_dir"/build-tools/*/)
|
ALL_TOOLS=("$sdk_dir"/build-tools/*/)
|
||||||
BIN_DIR="${ALL_TOOLS[1]}"
|
BIN_DIR="${ALL_TOOLS[${#ALL_TOOLS[@]}-1]}"
|
||||||
|
|
||||||
"$BIN_DIR"/zipalign -p -f 4 "$ORIG_NAME" "$ORIG_NAME"-2
|
"$BIN_DIR"/zipalign -p -f 4 "$ORIG_NAME" "$ORIG_NAME"-2
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"https://github.com/simplex-chat/simplexmq.git"."3c0cd7efcc3d3058d940c7a9667faef2dc6de6cc" = "09fx6bj5f25v6a34lkfggj3a1yqrg1xz9fv0dg9vb87pcajhkrq0";
|
"https://github.com/simplex-chat/simplexmq.git"."8a3b72458f917e9867f4e3640dda0fa1827ff6cf" = "1mmxdaj563kjmlkacxdnq62n6mzw9khampzaqghnk6iiwzdig0qy";
|
||||||
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
|
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
|
||||||
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
|
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
|
||||||
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
|
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
|
||||||
|
|
|
@ -5,7 +5,7 @@ cabal-version: 1.12
|
||||||
-- see: https://github.com/sol/hpack
|
-- see: https://github.com/sol/hpack
|
||||||
|
|
||||||
name: simplex-chat
|
name: simplex-chat
|
||||||
version: 5.8.0.5
|
version: 5.8.1.0
|
||||||
category: Web, System, Services, Cryptography
|
category: Web, System, Services, Cryptography
|
||||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||||
author: simplex.chat
|
author: simplex.chat
|
||||||
|
|
|
@ -104,7 +104,7 @@ import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), Migrati
|
||||||
import Simplex.Messaging.Agent.Store.SQLite.DB (SlowQueryStats (..))
|
import Simplex.Messaging.Agent.Store.SQLite.DB (SlowQueryStats (..))
|
||||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||||
import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations
|
import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations
|
||||||
import Simplex.Messaging.Client (ProxyClientError (..), NetworkConfig (..), defaultNetworkConfig)
|
import Simplex.Messaging.Client (NetworkConfig (..), ProxyClientError (..), defaultNetworkConfig)
|
||||||
import qualified Simplex.Messaging.Crypto as C
|
import qualified Simplex.Messaging.Crypto as C
|
||||||
import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
|
import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
|
||||||
import qualified Simplex.Messaging.Crypto.File as CF
|
import qualified Simplex.Messaging.Crypto.File as CF
|
||||||
|
@ -218,11 +218,12 @@ newChatController :: ChatDatabase -> Maybe User -> ChatConfig -> ChatOpts -> Boo
|
||||||
newChatController
|
newChatController
|
||||||
ChatDatabase {chatStore, agentStore}
|
ChatDatabase {chatStore, agentStore}
|
||||||
user
|
user
|
||||||
cfg@ChatConfig {agentConfig = aCfg, defaultServers, inlineFiles, deviceNameForRemote}
|
cfg@ChatConfig {agentConfig = aCfg, defaultServers, inlineFiles, deviceNameForRemote, confirmMigrations}
|
||||||
ChatOpts {coreOptions = CoreChatOpts {smpServers, xftpServers, simpleNetCfg, logLevel, logConnections, logServerHosts, logFile, tbqSize, highlyAvailable}, deviceName, optFilesFolder, optTempDirectory, showReactions, allowInstantFiles, autoAcceptFileSize}
|
ChatOpts {coreOptions = CoreChatOpts {smpServers, xftpServers, simpleNetCfg, logLevel, logConnections, logServerHosts, logFile, tbqSize, highlyAvailable, yesToUpMigrations}, deviceName, optFilesFolder, optTempDirectory, showReactions, allowInstantFiles, autoAcceptFileSize}
|
||||||
backgroundMode = do
|
backgroundMode = do
|
||||||
let inlineFiles' = if allowInstantFiles || autoAcceptFileSize > 0 then inlineFiles else inlineFiles {sendChunks = 0, receiveInstant = False}
|
let inlineFiles' = if allowInstantFiles || autoAcceptFileSize > 0 then inlineFiles else inlineFiles {sendChunks = 0, receiveInstant = False}
|
||||||
config = cfg {logLevel, showReactions, tbqSize, subscriptionEvents = logConnections, hostEvents = logServerHosts, defaultServers = configServers, inlineFiles = inlineFiles', autoAcceptFileSize, highlyAvailable}
|
confirmMigrations' = if confirmMigrations == MCConsole && yesToUpMigrations then MCYesUp else confirmMigrations
|
||||||
|
config = cfg {logLevel, showReactions, tbqSize, subscriptionEvents = logConnections, hostEvents = logServerHosts, defaultServers = configServers, inlineFiles = inlineFiles', autoAcceptFileSize, highlyAvailable, confirmMigrations = confirmMigrations'}
|
||||||
firstTime = dbNew chatStore
|
firstTime = dbNew chatStore
|
||||||
currentUser <- newTVarIO user
|
currentUser <- newTVarIO user
|
||||||
currentRemoteHost <- newTVarIO Nothing
|
currentRemoteHost <- newTVarIO Nothing
|
||||||
|
@ -762,28 +763,31 @@ processChatCommand' vr = \case
|
||||||
_ -> throwChatError CEInvalidChatItemUpdate
|
_ -> throwChatError CEInvalidChatItemUpdate
|
||||||
CChatItem SMDRcv _ -> throwChatError CEInvalidChatItemUpdate
|
CChatItem SMDRcv _ -> throwChatError CEInvalidChatItemUpdate
|
||||||
CTGroup -> withGroupLock "updateChatItem" chatId $ do
|
CTGroup -> withGroupLock "updateChatItem" chatId $ do
|
||||||
Group gInfo@GroupInfo {groupId} ms <- withStore $ \db -> getGroup db vr user chatId
|
Group gInfo@GroupInfo {groupId, membership} ms <- withStore $ \db -> getGroup db vr user chatId
|
||||||
assertUserGroupRole gInfo GRAuthor
|
assertUserGroupRole gInfo GRAuthor
|
||||||
cci <- withStore $ \db -> getGroupCIWithReactions db user gInfo itemId
|
if prohibitedSimplexLinks gInfo membership mc
|
||||||
case cci of
|
then pure $ chatCmdError (Just user) ("feature not allowed " <> T.unpack (groupFeatureNameText GFSimplexLinks))
|
||||||
CChatItem SMDSnd ci@ChatItem {meta = CIMeta {itemSharedMsgId, itemTimed, itemLive, editable}, content = ciContent} -> do
|
else do
|
||||||
case (ciContent, itemSharedMsgId, editable) of
|
cci <- withStore $ \db -> getGroupCIWithReactions db user gInfo itemId
|
||||||
(CISndMsgContent oldMC, Just itemSharedMId, True) -> do
|
case cci of
|
||||||
let changed = mc /= oldMC
|
CChatItem SMDSnd ci@ChatItem {meta = CIMeta {itemSharedMsgId, itemTimed, itemLive, editable}, content = ciContent} -> do
|
||||||
if changed || fromMaybe False itemLive
|
case (ciContent, itemSharedMsgId, editable) of
|
||||||
then do
|
(CISndMsgContent oldMC, Just itemSharedMId, True) -> do
|
||||||
(SndMessage {msgId}, _) <- sendGroupMessage user gInfo ms (XMsgUpdate itemSharedMId mc (ttl' <$> itemTimed) (justTrue . (live &&) =<< itemLive))
|
let changed = mc /= oldMC
|
||||||
ci' <- withStore' $ \db -> do
|
if changed || fromMaybe False itemLive
|
||||||
currentTs <- liftIO getCurrentTime
|
then do
|
||||||
when changed $
|
(SndMessage {msgId}, _) <- sendGroupMessage user gInfo ms (XMsgUpdate itemSharedMId mc (ttl' <$> itemTimed) (justTrue . (live &&) =<< itemLive))
|
||||||
addInitialAndNewCIVersions db itemId (chatItemTs' ci, oldMC) (currentTs, mc)
|
ci' <- withStore' $ \db -> do
|
||||||
let edited = itemLive /= Just True
|
currentTs <- liftIO getCurrentTime
|
||||||
updateGroupChatItem db user groupId ci (CISndMsgContent mc) edited live $ Just msgId
|
when changed $
|
||||||
startUpdatedTimedItemThread user (ChatRef CTGroup groupId) ci ci'
|
addInitialAndNewCIVersions db itemId (chatItemTs' ci, oldMC) (currentTs, mc)
|
||||||
pure $ CRChatItemUpdated user (AChatItem SCTGroup SMDSnd (GroupChat gInfo) ci')
|
let edited = itemLive /= Just True
|
||||||
else pure $ CRChatItemNotChanged user (AChatItem SCTGroup SMDSnd (GroupChat gInfo) ci)
|
updateGroupChatItem db user groupId ci (CISndMsgContent mc) edited live $ Just msgId
|
||||||
_ -> throwChatError CEInvalidChatItemUpdate
|
startUpdatedTimedItemThread user (ChatRef CTGroup groupId) ci ci'
|
||||||
CChatItem SMDRcv _ -> throwChatError CEInvalidChatItemUpdate
|
pure $ CRChatItemUpdated user (AChatItem SCTGroup SMDSnd (GroupChat gInfo) ci')
|
||||||
|
else pure $ CRChatItemNotChanged user (AChatItem SCTGroup SMDSnd (GroupChat gInfo) ci)
|
||||||
|
_ -> throwChatError CEInvalidChatItemUpdate
|
||||||
|
CChatItem SMDRcv _ -> throwChatError CEInvalidChatItemUpdate
|
||||||
CTLocal -> do
|
CTLocal -> do
|
||||||
(nf@NoteFolder {noteFolderId}, cci) <- withStore $ \db -> (,) <$> getNoteFolder db user chatId <*> getLocalChatItem db user chatId itemId
|
(nf@NoteFolder {noteFolderId}, cci) <- withStore $ \db -> (,) <$> getNoteFolder db user chatId <*> getLocalChatItem db user chatId itemId
|
||||||
case cci of
|
case cci of
|
||||||
|
@ -1357,6 +1361,9 @@ processChatCommand' vr = \case
|
||||||
pure $ CRNetworkConfig cfg
|
pure $ CRNetworkConfig cfg
|
||||||
APISetNetworkInfo info -> lift (withAgent' (`setUserNetworkInfo` info)) >> ok_
|
APISetNetworkInfo info -> lift (withAgent' (`setUserNetworkInfo` info)) >> ok_
|
||||||
ReconnectAllServers -> withUser' $ \_ -> lift (withAgent' reconnectAllServers) >> ok_
|
ReconnectAllServers -> withUser' $ \_ -> lift (withAgent' reconnectAllServers) >> ok_
|
||||||
|
ReconnectServer userId srv -> withUserId userId $ \user -> do
|
||||||
|
lift (withAgent' $ \a -> reconnectSMPServer a (aUserId user) srv)
|
||||||
|
ok_
|
||||||
APISetChatSettings (ChatRef cType chatId) chatSettings -> withUser $ \user -> case cType of
|
APISetChatSettings (ChatRef cType chatId) chatSettings -> withUser $ \user -> case cType of
|
||||||
CTDirect -> do
|
CTDirect -> do
|
||||||
ct <- withStore $ \db -> do
|
ct <- withStore $ \db -> do
|
||||||
|
@ -2918,9 +2925,17 @@ prohibitedGroupContent :: GroupInfo -> GroupMember -> MsgContent -> Maybe f -> M
|
||||||
prohibitedGroupContent gInfo m mc file_
|
prohibitedGroupContent gInfo m mc file_
|
||||||
| isVoice mc && not (groupFeatureMemberAllowed SGFVoice m gInfo) = Just GFVoice
|
| isVoice mc && not (groupFeatureMemberAllowed SGFVoice m gInfo) = Just GFVoice
|
||||||
| not (isVoice mc) && isJust file_ && not (groupFeatureMemberAllowed SGFFiles m gInfo) = Just GFFiles
|
| not (isVoice mc) && isJust file_ && not (groupFeatureMemberAllowed SGFFiles m gInfo) = Just GFFiles
|
||||||
| not (groupFeatureMemberAllowed SGFSimplexLinks m gInfo) && containsFormat isSimplexLink (parseMarkdown $ msgContentText mc) = Just GFSimplexLinks
|
| prohibitedSimplexLinks gInfo m mc = Just GFSimplexLinks
|
||||||
| otherwise = Nothing
|
| otherwise = Nothing
|
||||||
|
|
||||||
|
prohibitedSimplexLinks :: GroupInfo -> GroupMember -> MsgContent -> Bool
|
||||||
|
prohibitedSimplexLinks gInfo m mc =
|
||||||
|
not (groupFeatureMemberAllowed SGFSimplexLinks m gInfo)
|
||||||
|
&& maybe False (any ftIsSimplexLink) (parseMaybeMarkdownList $ msgContentText mc)
|
||||||
|
where
|
||||||
|
ftIsSimplexLink :: FormattedText -> Bool
|
||||||
|
ftIsSimplexLink FormattedText {format} = maybe False isSimplexLink format
|
||||||
|
|
||||||
roundedFDCount :: Int -> Int
|
roundedFDCount :: Int -> Int
|
||||||
roundedFDCount n
|
roundedFDCount n
|
||||||
| n <= 0 = 4
|
| n <= 0 = 4
|
||||||
|
@ -3214,7 +3229,7 @@ receiveViaCompleteFD user fileId RcvFileDescr {fileDescrText, fileDescrComplete}
|
||||||
forM_ aci_ $ \aci -> toView $ CRChatItemUpdated user aci
|
forM_ aci_ $ \aci -> toView $ CRChatItemUpdated user aci
|
||||||
throwChatError $ CEFileNotApproved fileId unknownSrvs
|
throwChatError $ CEFileNotApproved fileId unknownSrvs
|
||||||
|
|
||||||
getNetworkConfig :: CM' NetworkConfig
|
getNetworkConfig :: CM' NetworkConfig
|
||||||
getNetworkConfig = withAgent' $ liftIO . getNetworkConfig'
|
getNetworkConfig = withAgent' $ liftIO . getNetworkConfig'
|
||||||
|
|
||||||
resetRcvCIFileStatus :: User -> FileTransferId -> CIFileStatus 'MDRcv -> CM (Maybe AChatItem)
|
resetRcvCIFileStatus :: User -> FileTransferId -> CIFileStatus 'MDRcv -> CM (Maybe AChatItem)
|
||||||
|
@ -5250,18 +5265,21 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||||
groupMsgToView gInfo ci' {reactions}
|
groupMsgToView gInfo ci' {reactions}
|
||||||
|
|
||||||
groupMessageUpdate :: GroupInfo -> GroupMember -> SharedMsgId -> MsgContent -> RcvMessage -> UTCTime -> Maybe Int -> Maybe Bool -> CM ()
|
groupMessageUpdate :: GroupInfo -> GroupMember -> SharedMsgId -> MsgContent -> RcvMessage -> UTCTime -> Maybe Int -> Maybe Bool -> CM ()
|
||||||
groupMessageUpdate gInfo@GroupInfo {groupId} m@GroupMember {groupMemberId, memberId} sharedMsgId mc msg@RcvMessage {msgId} brokerTs ttl_ live_ =
|
groupMessageUpdate gInfo@GroupInfo {groupId} m@GroupMember {groupMemberId, memberId} sharedMsgId mc msg@RcvMessage {msgId} brokerTs ttl_ live_
|
||||||
updateRcvChatItem `catchCINotFound` \_ -> do
|
| prohibitedSimplexLinks gInfo m mc =
|
||||||
-- This patches initial sharedMsgId into chat item when locally deleted chat item
|
messageWarning $ "x.msg.update ignored: feature not allowed " <> groupFeatureNameText GFSimplexLinks
|
||||||
-- received an update from the sender, so that it can be referenced later (e.g. by broadcast delete).
|
| otherwise = do
|
||||||
-- Chat item and update message which created it will have different sharedMsgId in this case...
|
updateRcvChatItem `catchCINotFound` \_ -> do
|
||||||
let timed_ = rcvGroupCITimed gInfo ttl_
|
-- This patches initial sharedMsgId into chat item when locally deleted chat item
|
||||||
ci <- saveRcvChatItem' user (CDGroupRcv gInfo m) msg (Just sharedMsgId) brokerTs content Nothing timed_ live
|
-- received an update from the sender, so that it can be referenced later (e.g. by broadcast delete).
|
||||||
ci' <- withStore' $ \db -> do
|
-- Chat item and update message which created it will have different sharedMsgId in this case...
|
||||||
createChatItemVersion db (chatItemId' ci) brokerTs mc
|
let timed_ = rcvGroupCITimed gInfo ttl_
|
||||||
ci' <- updateGroupChatItem db user groupId ci content True live Nothing
|
ci <- saveRcvChatItem' user (CDGroupRcv gInfo m) msg (Just sharedMsgId) brokerTs content Nothing timed_ live
|
||||||
blockedMember m ci' $ markGroupChatItemBlocked db user gInfo ci'
|
ci' <- withStore' $ \db -> do
|
||||||
toView $ CRChatItemUpdated user (AChatItem SCTGroup SMDRcv (GroupChat gInfo) ci')
|
createChatItemVersion db (chatItemId' ci) brokerTs mc
|
||||||
|
ci' <- updateGroupChatItem db user groupId ci content True live Nothing
|
||||||
|
blockedMember m ci' $ markGroupChatItemBlocked db user gInfo ci'
|
||||||
|
toView $ CRChatItemUpdated user (AChatItem SCTGroup SMDRcv (GroupChat gInfo) ci')
|
||||||
where
|
where
|
||||||
content = CIRcvMsgContent mc
|
content = CIRcvMsgContent mc
|
||||||
live = fromMaybe False live_
|
live = fromMaybe False live_
|
||||||
|
@ -7410,6 +7428,7 @@ chatCommandP =
|
||||||
"/_network " *> (APISetNetworkConfig <$> jsonP),
|
"/_network " *> (APISetNetworkConfig <$> jsonP),
|
||||||
("/network " <|> "/net ") *> (SetNetworkConfig <$> netCfgP),
|
("/network " <|> "/net ") *> (SetNetworkConfig <$> netCfgP),
|
||||||
("/network" <|> "/net") $> APIGetNetworkConfig,
|
("/network" <|> "/net") $> APIGetNetworkConfig,
|
||||||
|
"/reconnect " *> (ReconnectServer <$> A.decimal <* A.space <*> strP),
|
||||||
"/reconnect" $> ReconnectAllServers,
|
"/reconnect" $> ReconnectAllServers,
|
||||||
"/_settings " *> (APISetChatSettings <$> chatRefP <* A.space <*> jsonP),
|
"/_settings " *> (APISetChatSettings <$> chatRefP <* A.space <*> jsonP),
|
||||||
"/_member settings #" *> (APISetMemberSettings <$> A.decimal <* A.space <*> A.decimal <* A.space <*> jsonP),
|
"/_member settings #" *> (APISetMemberSettings <$> A.decimal <* A.space <*> A.decimal <* A.space <*> jsonP),
|
||||||
|
|
|
@ -355,6 +355,7 @@ data ChatCommand
|
||||||
| SetNetworkConfig SimpleNetCfg
|
| SetNetworkConfig SimpleNetCfg
|
||||||
| APISetNetworkInfo UserNetworkInfo
|
| APISetNetworkInfo UserNetworkInfo
|
||||||
| ReconnectAllServers
|
| ReconnectAllServers
|
||||||
|
| ReconnectServer UserId SMPServer
|
||||||
| APISetChatSettings ChatRef ChatSettings
|
| APISetChatSettings ChatRef ChatSettings
|
||||||
| APISetMemberSettings GroupId GroupMemberId GroupMemberSettings
|
| APISetMemberSettings GroupId GroupMemberId GroupMemberSettings
|
||||||
| APIContactInfo ContactId
|
| APIContactInfo ContactId
|
||||||
|
|
|
@ -144,10 +144,6 @@ markdownToList (m1 :|: m2) = markdownToList m1 <> markdownToList m2
|
||||||
parseMarkdown :: Text -> Markdown
|
parseMarkdown :: Text -> Markdown
|
||||||
parseMarkdown s = fromRight (unmarked s) $ A.parseOnly (markdownP <* A.endOfInput) s
|
parseMarkdown s = fromRight (unmarked s) $ A.parseOnly (markdownP <* A.endOfInput) s
|
||||||
|
|
||||||
containsFormat :: (Format -> Bool) -> Markdown -> Bool
|
|
||||||
containsFormat p (Markdown f _) = maybe False p f
|
|
||||||
containsFormat p (m1 :|: m2) = containsFormat p m1 || containsFormat p m2
|
|
||||||
|
|
||||||
isSimplexLink :: Format -> Bool
|
isSimplexLink :: Format -> Bool
|
||||||
isSimplexLink = \case
|
isSimplexLink = \case
|
||||||
SimplexLink {} -> True;
|
SimplexLink {} -> True;
|
||||||
|
|
|
@ -198,7 +198,8 @@ mobileChatOpts dbFilePrefix =
|
||||||
logAgent = Nothing,
|
logAgent = Nothing,
|
||||||
logFile = Nothing,
|
logFile = Nothing,
|
||||||
tbqSize = 1024,
|
tbqSize = 1024,
|
||||||
highlyAvailable = False
|
highlyAvailable = False,
|
||||||
|
yesToUpMigrations = False
|
||||||
},
|
},
|
||||||
deviceName = Nothing,
|
deviceName = Nothing,
|
||||||
chatCmd = "",
|
chatCmd = "",
|
||||||
|
|
|
@ -62,7 +62,8 @@ data CoreChatOpts = CoreChatOpts
|
||||||
logAgent :: Maybe LogLevel,
|
logAgent :: Maybe LogLevel,
|
||||||
logFile :: Maybe FilePath,
|
logFile :: Maybe FilePath,
|
||||||
tbqSize :: Natural,
|
tbqSize :: Natural,
|
||||||
highlyAvailable :: Bool
|
highlyAvailable :: Bool,
|
||||||
|
yesToUpMigrations :: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
data ChatCmdLog = CCLAll | CCLMessages | CCLNone
|
data ChatCmdLog = CCLAll | CCLMessages | CCLNone
|
||||||
|
@ -204,6 +205,12 @@ coreChatOptsP appDir defaultDbFileName = do
|
||||||
( long "ha"
|
( long "ha"
|
||||||
<> help "Run as a highly available client (this may increase traffic in groups)"
|
<> help "Run as a highly available client (this may increase traffic in groups)"
|
||||||
)
|
)
|
||||||
|
yesToUpMigrations <-
|
||||||
|
switch
|
||||||
|
( long "--yes-migrate"
|
||||||
|
<> short 'y'
|
||||||
|
<> help "Automatically confirm \"up\" database migrations"
|
||||||
|
)
|
||||||
pure
|
pure
|
||||||
CoreChatOpts
|
CoreChatOpts
|
||||||
{ dbFilePrefix,
|
{ dbFilePrefix,
|
||||||
|
@ -217,7 +224,8 @@ coreChatOptsP appDir defaultDbFileName = do
|
||||||
logAgent = if logAgent || logLevel == CLLDebug then Just $ agentLogLevel logLevel else Nothing,
|
logAgent = if logAgent || logLevel == CLLDebug then Just $ agentLogLevel logLevel else Nothing,
|
||||||
logFile,
|
logFile,
|
||||||
tbqSize,
|
tbqSize,
|
||||||
highlyAvailable
|
highlyAvailable,
|
||||||
|
yesToUpMigrations
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
useTcpTimeout p t = 1000000 * if t > 0 then t else maybe 7 (const 15) p
|
useTcpTimeout p t = 1000000 * if t > 0 then t else maybe 7 (const 15) p
|
||||||
|
|
|
@ -54,8 +54,9 @@ import qualified Simplex.FileTransfer.Transport as XFTP
|
||||||
import Simplex.Messaging.Agent.Client (ProtocolTestFailure (..), ProtocolTestStep (..), SubscriptionsInfo (..))
|
import Simplex.Messaging.Agent.Client (ProtocolTestFailure (..), ProtocolTestStep (..), SubscriptionsInfo (..))
|
||||||
import Simplex.Messaging.Agent.Env.SQLite (NetworkConfig (..))
|
import Simplex.Messaging.Agent.Env.SQLite (NetworkConfig (..))
|
||||||
import Simplex.Messaging.Agent.Protocol
|
import Simplex.Messaging.Agent.Protocol
|
||||||
|
import Simplex.Messaging.Agent.Protocol (AgentErrorType (RCP))
|
||||||
import Simplex.Messaging.Agent.Store.SQLite.DB (SlowQueryStats (..))
|
import Simplex.Messaging.Agent.Store.SQLite.DB (SlowQueryStats (..))
|
||||||
import Simplex.Messaging.Client (SMPProxyMode (..), SMPProxyFallback)
|
import Simplex.Messaging.Client (SMPProxyFallback, SMPProxyMode (..))
|
||||||
import qualified Simplex.Messaging.Crypto as C
|
import qualified Simplex.Messaging.Crypto as C
|
||||||
import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
|
import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
|
||||||
import qualified Simplex.Messaging.Crypto.Ratchet as CR
|
import qualified Simplex.Messaging.Crypto.Ratchet as CR
|
||||||
|
@ -67,7 +68,7 @@ import qualified Simplex.Messaging.Protocol as SMP
|
||||||
import Simplex.Messaging.Transport.Client (TransportHost (..))
|
import Simplex.Messaging.Transport.Client (TransportHost (..))
|
||||||
import Simplex.Messaging.Util (safeDecodeUtf8, tshow)
|
import Simplex.Messaging.Util (safeDecodeUtf8, tshow)
|
||||||
import Simplex.Messaging.Version hiding (version)
|
import Simplex.Messaging.Version hiding (version)
|
||||||
import Simplex.RemoteControl.Types (RCCtrlAddress (..))
|
import Simplex.RemoteControl.Types (RCCtrlAddress (..), RCErrorType (..))
|
||||||
import System.Console.ANSI.Types
|
import System.Console.ANSI.Types
|
||||||
|
|
||||||
type CurrentTime = UTCTime
|
type CurrentTime = UTCTime
|
||||||
|
@ -350,7 +351,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||||
]
|
]
|
||||||
CRRemoteCtrlConnected RemoteCtrlInfo {remoteCtrlId = rcId, ctrlDeviceName} ->
|
CRRemoteCtrlConnected RemoteCtrlInfo {remoteCtrlId = rcId, ctrlDeviceName} ->
|
||||||
["remote controller " <> sShow rcId <> " session started with " <> plain ctrlDeviceName]
|
["remote controller " <> sShow rcId <> " session started with " <> plain ctrlDeviceName]
|
||||||
CRRemoteCtrlStopped {} -> ["remote controller stopped"]
|
CRRemoteCtrlStopped {rcStopReason} -> viewRemoteCtrlStopped rcStopReason
|
||||||
CRContactPQEnabled u c (CR.PQEncryption pqOn) -> ttyUser u [ttyContact' c <> ": " <> (if pqOn then "quantum resistant" else "standard") <> " end-to-end encryption enabled"]
|
CRContactPQEnabled u c (CR.PQEncryption pqOn) -> ttyUser u [ttyContact' c <> ": " <> (if pqOn then "quantum resistant" else "standard") <> " end-to-end encryption enabled"]
|
||||||
CRSQLResult rows -> map plain rows
|
CRSQLResult rows -> map plain rows
|
||||||
CRSlowSQLQueries {chatQueries, agentQueries} ->
|
CRSlowSQLQueries {chatQueries, agentQueries} ->
|
||||||
|
@ -1843,7 +1844,7 @@ viewCallAnswer ct WebRTCSession {rtcSession = answer, rtcIceCandidates = iceCand
|
||||||
[ ttyContact' ct <> " continued the WebRTC call",
|
[ ttyContact' ct <> " continued the WebRTC call",
|
||||||
"To connect, please paste the data below in your browser window you opened earlier and click Connect button",
|
"To connect, please paste the data below in your browser window you opened earlier and click Connect button",
|
||||||
"",
|
"",
|
||||||
viewJSON WCCallAnswer {answer, iceCandidates}
|
viewJSON WCCallAnswer {answer, iceCandidates}
|
||||||
]
|
]
|
||||||
|
|
||||||
callMediaStr :: CallType -> StyledString
|
callMediaStr :: CallType -> StyledString
|
||||||
|
@ -1914,6 +1915,12 @@ viewRemoteCtrl CtrlAppInfo {deviceName, appVersionRange = AppVersionRange _ (App
|
||||||
| otherwise = ""
|
| otherwise = ""
|
||||||
showCompatible = if compatible then "" else ", " <> bold' "not compatible"
|
showCompatible = if compatible then "" else ", " <> bold' "not compatible"
|
||||||
|
|
||||||
|
viewRemoteCtrlStopped :: RemoteCtrlStopReason -> [StyledString]
|
||||||
|
viewRemoteCtrlStopped = \case
|
||||||
|
RCSRConnectionFailed (ChatErrorAgent (RCP RCEIdentity) _) ->
|
||||||
|
["remote controller stopped: this link was used with another controller, please create a new link on the host"]
|
||||||
|
_ -> ["remote controller stopped"]
|
||||||
|
|
||||||
viewChatError :: Bool -> ChatLogLevel -> Bool -> ChatError -> [StyledString]
|
viewChatError :: Bool -> ChatLogLevel -> Bool -> ChatError -> [StyledString]
|
||||||
viewChatError isCmd logLevel testView = \case
|
viewChatError isCmd logLevel testView = \case
|
||||||
ChatError err -> case err of
|
ChatError err -> case err of
|
||||||
|
|
|
@ -101,7 +101,8 @@ testCoreOpts =
|
||||||
logAgent = Nothing,
|
logAgent = Nothing,
|
||||||
logFile = Nothing,
|
logFile = Nothing,
|
||||||
tbqSize = 16,
|
tbqSize = 16,
|
||||||
highlyAvailable = False
|
highlyAvailable = False,
|
||||||
|
yesToUpMigrations = False
|
||||||
}
|
}
|
||||||
|
|
||||||
getTestOpts :: Bool -> ScrubbedBytes -> ChatOpts
|
getTestOpts :: Bool -> ScrubbedBytes -> ChatOpts
|
||||||
|
|
|
@ -2032,10 +2032,19 @@ testGroupPrefsSimplexLinksForRole = testChat3 aliceProfile bobProfile cathProfil
|
||||||
threadDelay 1000000
|
threadDelay 1000000
|
||||||
bob ##> "/c"
|
bob ##> "/c"
|
||||||
inv <- getInvitation bob
|
inv <- getInvitation bob
|
||||||
bob ##> ("#team " <> inv)
|
bob ##> ("#team \"" <> inv <> "\\ntest\"")
|
||||||
|
bob <## "bad chat command: feature not allowed SimpleX links"
|
||||||
|
bob ##> ("/_send #1 json {\"msgContent\": {\"type\": \"text\", \"text\": \"" <> inv <> "\\ntest\"}}")
|
||||||
bob <## "bad chat command: feature not allowed SimpleX links"
|
bob <## "bad chat command: feature not allowed SimpleX links"
|
||||||
(alice </)
|
(alice </)
|
||||||
(cath </)
|
(cath </)
|
||||||
|
bob `send` ("@alice \"" <> inv <> "\\ntest\"")
|
||||||
|
bob <# ("@alice " <> inv)
|
||||||
|
bob <## "test"
|
||||||
|
alice <# ("bob> " <> inv)
|
||||||
|
alice <## "test"
|
||||||
|
bob ##> "#team <- @alice https://simplex.chat"
|
||||||
|
bob <## "bad chat command: feature not allowed SimpleX links"
|
||||||
alice #> ("#team " <> inv)
|
alice #> ("#team " <> inv)
|
||||||
bob <# ("#team alice> " <> inv)
|
bob <# ("#team alice> " <> inv)
|
||||||
cath <# ("#team alice> " <> inv)
|
cath <# ("#team alice> " <> inv)
|
||||||
|
|
|
@ -210,3 +210,10 @@ multilineMarkdownList = describe "multiline markdown" do
|
||||||
parseMaybeMarkdownList "http://simplex.chat\ntext 1\ntext 2\nhttp://app.simplex.chat" `shouldBe` Just [uri' "http://simplex.chat", "\ntext 1\ntext 2\n", uri' "http://app.simplex.chat"]
|
parseMaybeMarkdownList "http://simplex.chat\ntext 1\ntext 2\nhttp://app.simplex.chat" `shouldBe` Just [uri' "http://simplex.chat", "\ntext 1\ntext 2\n", uri' "http://app.simplex.chat"]
|
||||||
it "no markdown" do
|
it "no markdown" do
|
||||||
parseMaybeMarkdownList "not a\nmarkdown" `shouldBe` Nothing
|
parseMaybeMarkdownList "not a\nmarkdown" `shouldBe` Nothing
|
||||||
|
let inv = "/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D"
|
||||||
|
it "multiline with simplex link" do
|
||||||
|
parseMaybeMarkdownList ("https://simplex.chat" <> inv <> "\ntext")
|
||||||
|
`shouldBe` Just
|
||||||
|
[ FormattedText (Just $ SimplexLink XLInvitation ("simplex:" <> inv) ["smp.simplex.im"]) ("https://simplex.chat" <> inv),
|
||||||
|
"\ntext"
|
||||||
|
]
|
||||||
|
|
|
@ -119,7 +119,7 @@ remoteHandshakeRejectTest = testChat3 aliceProfile aliceDesktopProfile bobProfil
|
||||||
inv <- getTermLine desktop
|
inv <- getTermLine desktop
|
||||||
mobileBob ##> ("/connect remote ctrl " <> inv)
|
mobileBob ##> ("/connect remote ctrl " <> inv)
|
||||||
mobileBob <## ("connecting new remote controller: My desktop, v" <> versionNumber)
|
mobileBob <## ("connecting new remote controller: My desktop, v" <> versionNumber)
|
||||||
mobileBob <## "remote controller stopped"
|
mobileBob <## "remote controller stopped: this link was used with another controller, please create a new link on the host"
|
||||||
|
|
||||||
-- the server remains active after rejecting invalid client
|
-- the server remains active after rejecting invalid client
|
||||||
mobile ##> ("/connect remote ctrl " <> inv)
|
mobile ##> ("/connect remote ctrl " <> inv)
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
"simplex-explained-tab-2-p-1": "لكل اتصال، تستخدم قائمتي انتظار منفصلتين للمُراسلة لإرسال واستلام الرسائل عبر خوادم مختلفة.",
|
"simplex-explained-tab-2-p-1": "لكل اتصال، تستخدم قائمتي انتظار منفصلتين للمُراسلة لإرسال واستلام الرسائل عبر خوادم مختلفة.",
|
||||||
"simplex-explained-tab-2-p-2": "تقوم الخوادم بتمرير الرسائل في اتجاه واحد فقط، دون الحصول على الصورة الكاملة لمُحادثات المستخدم أو اتصالاته.",
|
"simplex-explained-tab-2-p-2": "تقوم الخوادم بتمرير الرسائل في اتجاه واحد فقط، دون الحصول على الصورة الكاملة لمُحادثات المستخدم أو اتصالاته.",
|
||||||
"simplex-explained-tab-3-p-1": "تحتوي الخوادم على بيانات اعتماد مجهولة منفصلة لكل قائمة انتظار، ولا تعرف المستخدمين الذين ينتمون إليهم.",
|
"simplex-explained-tab-3-p-1": "تحتوي الخوادم على بيانات اعتماد مجهولة منفصلة لكل قائمة انتظار، ولا تعرف المستخدمين الذين ينتمون إليهم.",
|
||||||
"copyright-label": "مشروع مفتوح المصدر © SimpleX 2020-2023",
|
"copyright-label": "مشروع مفتوح المصدر © SimpleX 2020-2024",
|
||||||
"simplex-chat-protocol": "بروتوكول دردشة SimpleX",
|
"simplex-chat-protocol": "بروتوكول دردشة SimpleX",
|
||||||
"developers": "المطورين",
|
"developers": "المطورين",
|
||||||
"hero-subheader": "أول نظام مُراسلة<br> دون معرّفات مُستخدم",
|
"hero-subheader": "أول نظام مُراسلة<br> دون معرّفات مُستخدم",
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
"smp-protocol": "СМП Протокол",
|
"smp-protocol": "СМП Протокол",
|
||||||
"chat-protocol": "Чат протокол",
|
"chat-protocol": "Чат протокол",
|
||||||
"donate": "Дарете",
|
"donate": "Дарете",
|
||||||
"copyright-label": "© 2020-2023 SimpleX | Проект с отворен код",
|
"copyright-label": "© 2020-2024 SimpleX | Проект с отворен код",
|
||||||
"simplex-chat-protocol": "SimpleX Чат протокол",
|
"simplex-chat-protocol": "SimpleX Чат протокол",
|
||||||
"terminal-cli": "Системна конзола",
|
"terminal-cli": "Системна конзола",
|
||||||
"terms-and-privacy-policy": "Условия и политика за поверителност",
|
"terms-and-privacy-policy": "Условия и политика за поверителност",
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
"smp-protocol": "SMP protokol",
|
"smp-protocol": "SMP protokol",
|
||||||
"chat-protocol": "Chat protokol",
|
"chat-protocol": "Chat protokol",
|
||||||
"donate": "Darovat",
|
"donate": "Darovat",
|
||||||
"copyright-label": "© 2020-2023 SimpleX | Projekt s otevřeným zdrojovým kódem",
|
"copyright-label": "© 2020-2024 SimpleX | Projekt s otevřeným zdrojovým kódem",
|
||||||
"simplex-chat-protocol": "SimpleX Chat protokol",
|
"simplex-chat-protocol": "SimpleX Chat protokol",
|
||||||
"terminal-cli": "Terminálové rozhraní příkazového řádku",
|
"terminal-cli": "Terminálové rozhraní příkazového řádku",
|
||||||
"terms-and-privacy-policy": "Podmínky a zásady ochrany osobních údajů",
|
"terms-and-privacy-policy": "Podmínky a zásady ochrany osobních údajů",
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
"smp-protocol": "SMP Protokoll",
|
"smp-protocol": "SMP Protokoll",
|
||||||
"chat-bot-example": "Beispiel für einen Chatbot",
|
"chat-bot-example": "Beispiel für einen Chatbot",
|
||||||
"donate": "Spenden",
|
"donate": "Spenden",
|
||||||
"copyright-label": "© 2020-2023 SimpleX | Open-Source Projekt",
|
"copyright-label": "© 2020-2024 SimpleX | Open-Source Projekt",
|
||||||
"chat-protocol": "Chat Protokoll",
|
"chat-protocol": "Chat Protokoll",
|
||||||
"simplex-chat-protocol": "SimpleX Chat Protokoll",
|
"simplex-chat-protocol": "SimpleX Chat Protokoll",
|
||||||
"terminal-cli": "Terminal Kommandozeilen-Schnittstelle",
|
"terminal-cli": "Terminal Kommandozeilen-Schnittstelle",
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
"smp-protocol": "SMP protocol",
|
"smp-protocol": "SMP protocol",
|
||||||
"chat-protocol": "Chat protocol",
|
"chat-protocol": "Chat protocol",
|
||||||
"donate": "Donate",
|
"donate": "Donate",
|
||||||
"copyright-label": "© 2020-2023 SimpleX | Open-Source Project",
|
"copyright-label": "© 2020-2024 SimpleX | Open-Source Project",
|
||||||
"simplex-chat-protocol": "SimpleX Chat protocol",
|
"simplex-chat-protocol": "SimpleX Chat protocol",
|
||||||
"terminal-cli": "Terminal CLI",
|
"terminal-cli": "Terminal CLI",
|
||||||
"terms-and-privacy-policy": "Privacy Policy",
|
"terms-and-privacy-policy": "Privacy Policy",
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"simplex-explained-tab-3-p-2": "El usuario puede mejorar aún más la privacidad de sus metadatos haciendo uso de la red Tor para acceder a los servidores, evitando así la correlación por dirección IP.",
|
"simplex-explained-tab-3-p-2": "El usuario puede mejorar aún más la privacidad de sus metadatos haciendo uso de la red Tor para acceder a los servidores, evitando así la correlación por dirección IP.",
|
||||||
"smp-protocol": "Protocolo SMP",
|
"smp-protocol": "Protocolo SMP",
|
||||||
"donate": "Donación",
|
"donate": "Donación",
|
||||||
"copyright-label": "© 2020-2023 SimpleX | Proyecto de Código Abierto",
|
"copyright-label": "© 2020-2024 SimpleX | Proyecto de Código Abierto",
|
||||||
"simplex-chat-protocol": "Protocolo de SimpleX Chat",
|
"simplex-chat-protocol": "Protocolo de SimpleX Chat",
|
||||||
"terms-and-privacy-policy": "Términos y Política de Privacidad",
|
"terms-and-privacy-policy": "Términos y Política de Privacidad",
|
||||||
"hero-header": "Privacidad redefinida",
|
"hero-header": "Privacidad redefinida",
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
"smp-protocol": "Protocole SMP",
|
"smp-protocol": "Protocole SMP",
|
||||||
"chat-protocol": "Protocole de chat",
|
"chat-protocol": "Protocole de chat",
|
||||||
"donate": "Faire un don",
|
"donate": "Faire un don",
|
||||||
"copyright-label": "© 2020-2023 SimpleX | Projet Open-Source",
|
"copyright-label": "© 2020-2024 SimpleX | Projet Open-Source",
|
||||||
"simplex-chat-protocol": "Protocole SimpleX Chat",
|
"simplex-chat-protocol": "Protocole SimpleX Chat",
|
||||||
"terminal-cli": "Terminal CLI",
|
"terminal-cli": "Terminal CLI",
|
||||||
"terms-and-privacy-policy": "Politique de confidentialité",
|
"terms-and-privacy-policy": "Politique de confidentialité",
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
"smp-protocol": "SMP protokoll",
|
"smp-protocol": "SMP protokoll",
|
||||||
"chat-protocol": "Csevegés protokoll",
|
"chat-protocol": "Csevegés protokoll",
|
||||||
"donate": "Támogatás",
|
"donate": "Támogatás",
|
||||||
"copyright-label": "© 2020-2023 SimpleX | Nyílt forráskódú projekt",
|
"copyright-label": "© 2020-2024 SimpleX | Nyílt forráskódú projekt",
|
||||||
"simplex-chat-protocol": "SimpleX Chat protokoll",
|
"simplex-chat-protocol": "SimpleX Chat protokoll",
|
||||||
"terminal-cli": "Terminál CLI",
|
"terminal-cli": "Terminál CLI",
|
||||||
"terms-and-privacy-policy": "Adatvédelmi irányelvek",
|
"terms-and-privacy-policy": "Adatvédelmi irányelvek",
|
||||||
|
@ -256,4 +256,4 @@
|
||||||
"simplex-chat-via-f-droid": "SimpleX Chat az F-Droidon keresztül",
|
"simplex-chat-via-f-droid": "SimpleX Chat az F-Droidon keresztül",
|
||||||
"simplex-chat-repo": "SimpleX Chat tároló",
|
"simplex-chat-repo": "SimpleX Chat tároló",
|
||||||
"stable-and-beta-versions-built-by-developers": "A fejlesztők által készített stabil és béta verziók"
|
"stable-and-beta-versions-built-by-developers": "A fejlesztők által készített stabil és béta verziók"
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"simplex-explained-tab-3-p-1": "I server hanno credenziali anonime separate per ogni coda e non sanno a quali utenti appartengano.",
|
"simplex-explained-tab-3-p-1": "I server hanno credenziali anonime separate per ogni coda e non sanno a quali utenti appartengano.",
|
||||||
"chat-protocol": "Protocollo di chat",
|
"chat-protocol": "Protocollo di chat",
|
||||||
"donate": "Dona",
|
"donate": "Dona",
|
||||||
"copyright-label": "© 2020-2023 SimpleX | Progetto Open-Source",
|
"copyright-label": "© 2020-2024 SimpleX | Progetto Open-Source",
|
||||||
"simplex-chat-protocol": "Protocollo di SimpleX Chat",
|
"simplex-chat-protocol": "Protocollo di SimpleX Chat",
|
||||||
"terminal-cli": "Terminale CLI",
|
"terminal-cli": "Terminale CLI",
|
||||||
"terms-and-privacy-policy": "Informativa sulla privacy",
|
"terms-and-privacy-policy": "Informativa sulla privacy",
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
"chat-protocol": "チャットプロトコル",
|
"chat-protocol": "チャットプロトコル",
|
||||||
"chat-bot-example": "チャットボットの例",
|
"chat-bot-example": "チャットボットの例",
|
||||||
"donate": "寄付",
|
"donate": "寄付",
|
||||||
"copyright-label": "© 2020-2023 SimpleX | Open-Source Project",
|
"copyright-label": "© 2020-2024 SimpleX | Open-Source Project",
|
||||||
"hero-p-1": "他のアプリにはユーザー ID があります: Signal、Matrix、Session、Briar、Jami、Cwtch など。<br> SimpleX にはありません。<strong>乱数さえもありません</strong>。<br> これにより、プライバシーが大幅に向上します。",
|
"hero-p-1": "他のアプリにはユーザー ID があります: Signal、Matrix、Session、Briar、Jami、Cwtch など。<br> SimpleX にはありません。<strong>乱数さえもありません</strong>。<br> これにより、プライバシーが大幅に向上します。",
|
||||||
"copy-the-command-below-text": "以下のコマンドをコピーしてチャットで使用します:",
|
"copy-the-command-below-text": "以下のコマンドをコピーしてチャットで使用します:",
|
||||||
"simplex-private-card-9-point-1": "各メッセージ キューは、異なる送信アドレスと受信アドレスを使用してメッセージを一方向に渡します。",
|
"simplex-private-card-9-point-1": "各メッセージ キューは、異なる送信アドレスと受信アドレスを使用してメッセージを一方向に渡します。",
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
"chat-bot-example": "Chatbot voorbeeld",
|
"chat-bot-example": "Chatbot voorbeeld",
|
||||||
"smp-protocol": "SMP protocol",
|
"smp-protocol": "SMP protocol",
|
||||||
"donate": "Doneer",
|
"donate": "Doneer",
|
||||||
"copyright-label": "© 2020-2023 SimpleX | Open-sourceproject",
|
"copyright-label": "© 2020-2024 SimpleX | Open-sourceproject",
|
||||||
"simplex-chat-protocol": "SimpleX Chat protocol",
|
"simplex-chat-protocol": "SimpleX Chat protocol",
|
||||||
"terminal-cli": "Terminal CLI",
|
"terminal-cli": "Terminal CLI",
|
||||||
"terms-and-privacy-policy": "Privacybeleid",
|
"terms-and-privacy-policy": "Privacybeleid",
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"smp-protocol": "Protokół SMP",
|
"smp-protocol": "Protokół SMP",
|
||||||
"chat-protocol": "Protokół czatu",
|
"chat-protocol": "Protokół czatu",
|
||||||
"donate": "Darowizna",
|
"donate": "Darowizna",
|
||||||
"copyright-label": "© 2020-2023 SimpleX | Projekt Open-Source",
|
"copyright-label": "© 2020-2024 SimpleX | Projekt Open-Source",
|
||||||
"simplex-chat-protocol": "Protokół SimpleX Chat",
|
"simplex-chat-protocol": "Protokół SimpleX Chat",
|
||||||
"terminal-cli": "Terminal CLI",
|
"terminal-cli": "Terminal CLI",
|
||||||
"terms-and-privacy-policy": "Polityka prywatności",
|
"terms-and-privacy-policy": "Polityka prywatności",
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
"smp-protocol": "Protocolo SMP",
|
"smp-protocol": "Protocolo SMP",
|
||||||
"chat-protocol": "Protocolo de bate-papo",
|
"chat-protocol": "Protocolo de bate-papo",
|
||||||
"donate": "Doar",
|
"donate": "Doar",
|
||||||
"copyright-label": "© 2020-2023 SimpleX | Projeto de Código Livre",
|
"copyright-label": "© 2020-2024 SimpleX | Projeto de Código Livre",
|
||||||
"simplex-chat-protocol": "Protocolo Chat SimpleX",
|
"simplex-chat-protocol": "Protocolo Chat SimpleX",
|
||||||
"terminal-cli": "CLI Terminal",
|
"terminal-cli": "CLI Terminal",
|
||||||
"hero-header": "Privacidade redefinida",
|
"hero-header": "Privacidade redefinida",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"copy-the-command-below-text": "скопируйте приведенную ниже команду и используйте ее в чате:",
|
"copy-the-command-below-text": "скопируйте приведенную ниже команду и используйте ее в чате:",
|
||||||
"copyright-label": "© 2020-2023 SimpleX | Проект с открытым исходным кодом",
|
"copyright-label": "© 2020-2024 SimpleX | Проект с открытым исходным кодом",
|
||||||
"chat-bot-example": "Пример Чат бота",
|
"chat-bot-example": "Пример Чат бота",
|
||||||
"simplex-private-card-9-point-1": "Каждая очередь сообщений передает сообщения в одном направлении с разными адресами отправки и получения.",
|
"simplex-private-card-9-point-1": "Каждая очередь сообщений передает сообщения в одном направлении с разными адресами отправки и получения.",
|
||||||
"simplex-private-card-1-point-2": "Криптобокс NaCL в каждой очереди для предотвращения корреляции трафика между очередями сообщений, в случае компрометации TLS.",
|
"simplex-private-card-1-point-2": "Криптобокс NaCL в каждой очереди для предотвращения корреляции трафика между очередями сообщений, в случае компрометации TLS.",
|
||||||
|
|
|
@ -58,7 +58,7 @@ active_blog: true
|
||||||
</div>
|
</div>
|
||||||
<div class="p-6 md:py-8 flex-[2.5] flex flex-col">
|
<div class="p-6 md:py-8 flex-[2.5] flex flex-col">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-grey-black dark:text-white text-lg md:text-xl font-bold ">
|
<h1 class="text-grey-black dark:text-white !text-lg md:!text-xl font-bold ">
|
||||||
<a href="{{ blog.url }}">{{ blog.data.title | safe }}</a>
|
<a href="{{ blog.url }}">{{ blog.data.title | safe }}</a>
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-sm text-[#A8B0B4] font-medium mt-2 mb-4 tracking-[0.03em]">
|
<p class="text-sm text-[#A8B0B4] font-medium mt-2 mb-4 tracking-[0.03em]">
|
||||||
|
|
|
@ -11,28 +11,31 @@ metadata:
|
||||||
email: chat@simplex.chat
|
email: chat@simplex.chat
|
||||||
---
|
---
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="{{ metadata.url }}">
|
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="{{ metadata.language }}">
|
||||||
|
<id>{{ metadata.url }}</id>
|
||||||
|
<link type="text/html" rel="alternate" href="{{ metadata.url }}"/>
|
||||||
|
<link type="application/atom+xml" rel="self" href="{{ permalink | absoluteUrl(metadata.url) }}"/>
|
||||||
<title>{{ metadata.title }}</title>
|
<title>{{ metadata.title }}</title>
|
||||||
<subtitle>{{ metadata.subtitle }}</subtitle>
|
<subtitle>{{ metadata.subtitle }}</subtitle>
|
||||||
<link href="{{ permalink | absoluteUrl(metadata.url) }}" rel="self"/>
|
|
||||||
<link href="{{ metadata.url }}"/>
|
|
||||||
<updated>{{ collections.blogs | getNewestCollectionItemDate | dateToRfc3339 }}</updated>
|
<updated>{{ collections.blogs | getNewestCollectionItemDate | dateToRfc3339 }}</updated>
|
||||||
<id>{{ metadata.url }}</id>
|
|
||||||
<author>
|
<author>
|
||||||
<name>{{ metadata.author.name }}</name>
|
<name>{{ metadata.author.name }}</name>
|
||||||
<email>{{ metadata.author.email }}</email>
|
<email>{{ metadata.author.email }}</email>
|
||||||
</author>
|
</author>
|
||||||
{%- for blog in collections.blogs | reverse %}
|
{%- for blog in collections.blogs | reverse %}
|
||||||
{%- if not blog.data.draft %}
|
{%- if not blog.data.draft %}
|
||||||
{%- set absolutePostUrl = blog.url | absoluteUrl(metadata.url) %}
|
{%- set absolutePostUrl = blog.data.permalink | absoluteUrl(metadata.url) %}
|
||||||
<entry>
|
<entry>
|
||||||
|
<id>{{ blog.data.permalink | absoluteUrl(metadata.url) }}</id>
|
||||||
|
<!-- <updated>{{ blog.data.date.toUTCString().split(' ').slice(1, 4).join(' ') }}</updated> -->
|
||||||
|
<updated>{{ blog.data.date | dateToRfc3339 }}</updated>
|
||||||
|
<link rel="alternate" type="text/html" href="{{ absolutePostUrl }}"/>
|
||||||
<title>{{ blog.data.title }}</title>
|
<title>{{ blog.data.title }}</title>
|
||||||
<link href="{{ absolutePostUrl }}"/>
|
<content type="html">{{ blog.templateContent | htmlToAbsoluteUrls(absolutePostUrl) }}</content>
|
||||||
{# <updated>{{ blog.date | dateToRfc3339 }}</updated> #}
|
<author>
|
||||||
<updated>{{ blog.data.date.toUTCString().split(' ').slice(1, 4).join(' ') }}</updated>
|
<name>{{ metadata.author.name }}</name>
|
||||||
<id>{{ absolutePostUrl }}</id>
|
<email>{{ metadata.author.email }}</email>
|
||||||
<content xml:lang="{{ metadata.language }}" type="html">{{ blog.templateContent | htmlToAbsoluteUrls(absolutePostUrl) }}</content>
|
</author>
|
||||||
{# <content xml:lang="{{ metadata.language }}" type="html">{{ blog.templateContent | striptags | truncate(200) }}</content> #}
|
|
||||||
</entry>
|
</entry>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
|
|
|
@ -26,8 +26,8 @@ metadata:
|
||||||
<link>{{ absolutePostUrl }}</link>
|
<link>{{ absolutePostUrl }}</link>
|
||||||
<description>{{ blog.templateContent | htmlToAbsoluteUrls(absolutePostUrl) }}</description>
|
<description>{{ blog.templateContent | htmlToAbsoluteUrls(absolutePostUrl) }}</description>
|
||||||
{# <description>{{ blog.templateContent | striptags | truncate(200) }}</description> #}
|
{# <description>{{ blog.templateContent | striptags | truncate(200) }}</description> #}
|
||||||
{# <pubDate>{{ blog.data.date | dateToRfc822 }}</pubDate> #}
|
<pubDate>{{ blog.data.date | dateToRfc822 }}</pubDate>
|
||||||
<pubDate>{{ blog.data.date.toUTCString().split(' ').slice(1, 4).join(' ') }}</pubDate>
|
{# <pubDate>{{ blog.data.date.toUTCString().split(' ').slice(1, 4).join(' ') }}</pubDate> #}
|
||||||
<dc:creator>{{ metadata.author.name }}</dc:creator>
|
<dc:creator>{{ metadata.author.name }}</dc:creator>
|
||||||
<guid>{{ absolutePostUrl }}</guid>
|
<guid>{{ absolutePostUrl }}</guid>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -46,6 +46,10 @@ img{
|
||||||
-ms-user-select: none; /* For Internet Explorer and Edge */
|
-ms-user-select: none; /* For Internet Explorer and Edge */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a{
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
/* #comparison::before {
|
/* #comparison::before {
|
||||||
display: block;
|
display: block;
|
||||||
content: " ";
|
content: " ";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue