mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-29 09:49:04 +00:00
Merge branch 'master' into master-android
This commit is contained in:
@@ -234,16 +234,14 @@ jobs:
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
|
||||
shell: docker exec -t builder sh -eu {0}
|
||||
run: |
|
||||
scripts/desktop/build-lib-linux.sh
|
||||
cd apps/multiplatform
|
||||
./gradlew packageDeb
|
||||
scripts/desktop/make-deb-linux.sh
|
||||
|
||||
- name: Prepare Desktop
|
||||
id: linux_desktop_build
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
|
||||
shell: bash
|
||||
run: |
|
||||
path=$(echo ${{ github.workspace }}/apps/multiplatform/release/main/deb/simplex_*_amd64.deb )
|
||||
path=$(echo ${{ github.workspace }}/apps/multiplatform/release/main/deb/simplex_amd64.deb )
|
||||
echo "package_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "package_hash=$(echo SHA2-256\(${{ matrix.desktop_asset_name }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ RUN apt-get update && \
|
||||
apt-get install -y curl \
|
||||
libpq-dev \
|
||||
git \
|
||||
strip-nondeterminism \
|
||||
sqlite3 \
|
||||
libsqlite3-dev \
|
||||
build-essential \
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
# SimpleX - the first messaging platform that has no user identifiers of any kind - 100% private by design!
|
||||
|
||||
[<img src="./images/trail-of-bits.jpg" height="100">](http://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html) [<img src="./images/privacy-guides.jpg" height="80">](https://www.privacyguides.org/en/real-time-communication/#simplex-chat) [<img src="./images/kuketz-blog.jpg" height="80">](https://www.kuketz-blog.de/simplex-eindruecke-vom-messenger-ohne-identifier/)
|
||||
[<img src="./images/trail-of-bits.jpg" height="80">](http://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html) [<img src="./images/privacy-guides.jpg" height="64">](https://www.privacyguides.org/en/real-time-communication/#simplex-chat) [<img src="./images/whonix-logo.jpg" height="64">](https://www.whonix.org/wiki/Chat#Recommendation) [<img src="./images/kuketz-blog.jpg" height="64">](https://www.kuketz-blog.de/simplex-eindruecke-vom-messenger-ohne-identifier/)
|
||||
|
||||
## Welcome to SimpleX Chat!
|
||||
|
||||
@@ -110,6 +110,15 @@ After you connect, you can [verify connection security code](./blog/20230103-sim
|
||||
|
||||
Read about the app features and settings in the new [User guide](./docs/guide/README.md).
|
||||
|
||||
## Contribute
|
||||
|
||||
We would love to have you join the development! You can help us with:
|
||||
|
||||
- [share the color theme](./docs/THEMES.md) you use in Android app!
|
||||
- writing a tutorial or recipes about hosting servers, chat bot automations, etc.
|
||||
- contributing to SimpleX Chat knowledge-base.
|
||||
- developing features - please connect to us via chat so we can help you get started.
|
||||
|
||||
## Help translating SimpleX Chat
|
||||
|
||||
Thanks to our users and [Weblate](https://hosted.weblate.org/engage/simplex-chat/), SimpleX Chat apps, website and documents are translated to many other languages.
|
||||
@@ -141,15 +150,6 @@ Join our translators to help SimpleX grow!
|
||||
|
||||
Languages in progress: Arabic, Japanese, Korean, Portuguese and [others](https://hosted.weblate.org/projects/simplex-chat/#languages). We will be adding more languages as some of the already added are completed – please suggest new languages, review the [translation guide](./docs/TRANSLATIONS.md) and get in touch with us!
|
||||
|
||||
## Contribute
|
||||
|
||||
We would love to have you join the development! You can help us with:
|
||||
|
||||
- [share the color theme](./docs/THEMES.md) you use in Android app!
|
||||
- writing a tutorial or recipes about hosting servers, chat bot automations, etc.
|
||||
- contributing to SimpleX Chat knowledge-base.
|
||||
- developing features - please connect to us via chat so we can help you get started.
|
||||
|
||||
## Please support us with your donations
|
||||
|
||||
Huge thank you to everybody who donated to SimpleX Chat!
|
||||
|
||||
@@ -45,21 +45,10 @@ struct ContentView: View {
|
||||
@State private var showChooseLAMode = false
|
||||
@State private var showSetPasscode = false
|
||||
@State private var waitingForOrPassedAuth = true
|
||||
@State private var chatListActionSheet: ChatListActionSheet? = nil
|
||||
@State private var chatListUserPickerSheet: UserPickerSheet? = nil
|
||||
|
||||
private let callTopPadding: CGFloat = 40
|
||||
|
||||
private enum ChatListActionSheet: Identifiable {
|
||||
case planAndConnectSheet(sheet: PlanAndConnectActionSheet)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case let .planAndConnectSheet(sheet): return sheet.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var accessAuthenticated: Bool {
|
||||
chatModel.contentViewAccessAuthenticated || contentAccessAuthenticationExtended
|
||||
}
|
||||
@@ -181,11 +170,6 @@ struct ContentView: View {
|
||||
if case .onboardingComplete = step,
|
||||
chatModel.currentUser != nil {
|
||||
mainView()
|
||||
.actionSheet(item: $chatListActionSheet) { sheet in
|
||||
switch sheet {
|
||||
case let .planAndConnectSheet(sheet): return planAndConnectActionSheet(sheet, dismiss: false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
OnboardingView(onboarding: step)
|
||||
}
|
||||
@@ -453,10 +437,7 @@ struct ContentView: View {
|
||||
let link = url.absoluteString.replacingOccurrences(of: "///\(path)", with: "/\(path)")
|
||||
planAndConnect(
|
||||
link,
|
||||
showAlert: showPlanAndConnectAlert,
|
||||
showActionSheet: { chatListActionSheet = .planAndConnectSheet(sheet: $0) },
|
||||
dismiss: false,
|
||||
incognito: nil
|
||||
dismiss: false
|
||||
)
|
||||
} else {
|
||||
AlertManager.shared.showAlert(Alert(title: Text("Error: URL is invalid")))
|
||||
@@ -479,10 +460,6 @@ struct ContentView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func showPlanAndConnectAlert(_ alert: PlanAndConnectAlert) {
|
||||
AlertManager.shared.showAlert(planAndConnectAlert(alert, dismiss: false))
|
||||
}
|
||||
}
|
||||
|
||||
final class AlertManager: ObservableObject {
|
||||
|
||||
@@ -26,7 +26,6 @@ struct GroupMemberInfoView: View {
|
||||
@State private var knownContactConnectionStats: ConnectionStats? = nil
|
||||
@State private var newRole: GroupMemberRole = .member
|
||||
@State private var alert: GroupMemberInfoViewAlert?
|
||||
@State private var sheet: PlanAndConnectActionSheet?
|
||||
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
|
||||
@State private var justOpened = true
|
||||
@State private var progressIndicator = false
|
||||
@@ -41,7 +40,6 @@ struct GroupMemberInfoView: View {
|
||||
case switchAddressAlert
|
||||
case abortSwitchAddressAlert
|
||||
case syncConnectionForceAlert
|
||||
case planAndConnectAlert(alert: PlanAndConnectAlert)
|
||||
case queueInfo(info: String)
|
||||
case someAlert(alert: SomeAlert)
|
||||
case error(title: LocalizedStringKey, error: LocalizedStringKey?)
|
||||
@@ -57,7 +55,6 @@ struct GroupMemberInfoView: View {
|
||||
case .switchAddressAlert: return "switchAddressAlert"
|
||||
case .abortSwitchAddressAlert: return "abortSwitchAddressAlert"
|
||||
case .syncConnectionForceAlert: return "syncConnectionForceAlert"
|
||||
case let .planAndConnectAlert(alert): return "planAndConnectAlert \(alert.id)"
|
||||
case let .queueInfo(info): return "queueInfo \(info)"
|
||||
case let .someAlert(alert): return "someAlert \(alert.id)"
|
||||
case let .error(title, _): return "error \(title)"
|
||||
@@ -270,13 +267,11 @@ struct GroupMemberInfoView: View {
|
||||
case .switchAddressAlert: return switchAddressAlert(switchMemberAddress)
|
||||
case .abortSwitchAddressAlert: return abortSwitchAddressAlert(abortSwitchMemberAddress)
|
||||
case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncMemberConnection(force: true) })
|
||||
case let .planAndConnectAlert(alert): return planAndConnectAlert(alert, dismiss: true)
|
||||
case let .queueInfo(info): return queueInfoAlert(info)
|
||||
case let .someAlert(a): return a.alert
|
||||
case let .error(title, error): return mkAlert(title: title, message: error)
|
||||
}
|
||||
}
|
||||
.actionSheet(item: $sheet) { s in planAndConnectActionSheet(s, dismiss: true) }
|
||||
|
||||
if progressIndicator {
|
||||
ProgressView().scaleEffect(2)
|
||||
@@ -350,10 +345,7 @@ struct GroupMemberInfoView: View {
|
||||
Button {
|
||||
planAndConnect(
|
||||
contactLink,
|
||||
showAlert: { alert = .planAndConnectAlert(alert: $0) },
|
||||
showActionSheet: { sheet = $0 },
|
||||
dismiss: true,
|
||||
incognito: nil
|
||||
dismiss: true
|
||||
)
|
||||
} label: {
|
||||
Label("Connect", systemImage: "link")
|
||||
|
||||
@@ -579,8 +579,6 @@ struct ChatListSearchBar: View {
|
||||
@Binding var searchChatFilteredBySimplexLink: String?
|
||||
@Binding var parentSheet: SomeSheet<AnyView>?
|
||||
@State private var ignoreSearchTextChange = false
|
||||
@State private var alert: PlanAndConnectAlert?
|
||||
@State private var sheet: PlanAndConnectActionSheet?
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 12) {
|
||||
@@ -645,12 +643,6 @@ struct ChatListSearchBar: View {
|
||||
.onChange(of: chatTagsModel.activeFilter) { _ in
|
||||
searchText = ""
|
||||
}
|
||||
.alert(item: $alert) { a in
|
||||
planAndConnectAlert(a, dismiss: true, cleanup: { searchText = "" })
|
||||
}
|
||||
.actionSheet(item: $sheet) { s in
|
||||
planAndConnectActionSheet(s, dismiss: true, cleanup: { searchText = "" })
|
||||
}
|
||||
}
|
||||
|
||||
private func toggleFilterButton() -> some View {
|
||||
@@ -676,10 +668,11 @@ struct ChatListSearchBar: View {
|
||||
private func connect(_ link: String) {
|
||||
planAndConnect(
|
||||
link,
|
||||
showAlert: { alert = $0 },
|
||||
showActionSheet: { sheet = $0 },
|
||||
dismiss: false,
|
||||
incognito: nil,
|
||||
cleanup: {
|
||||
searchText = ""
|
||||
searchFocussed = false
|
||||
},
|
||||
filterKnownContact: { searchChatFilteredBySimplexLink = $0.id },
|
||||
filterKnownGroup: { searchChatFilteredBySimplexLink = $0.id }
|
||||
)
|
||||
|
||||
@@ -65,6 +65,27 @@ func showAlert(
|
||||
}
|
||||
}
|
||||
|
||||
func showSheet(
|
||||
_ title: String?,
|
||||
message: String? = nil,
|
||||
actions: () -> [UIAlertAction] = { [okAlertAction] },
|
||||
sourceView: UIView? = nil // For iPad support
|
||||
) {
|
||||
if let topController = getTopViewController() {
|
||||
let sheet = UIAlertController(title: title, message: message, preferredStyle: .actionSheet)
|
||||
for action in actions() { sheet.addAction(action) }
|
||||
|
||||
// Required for iPad: Configure popover presentation
|
||||
if let popover = sheet.popoverPresentationController {
|
||||
popover.sourceView = sourceView ?? topController.view
|
||||
popover.sourceRect = sourceView?.bounds ?? CGRect(x: topController.view.bounds.midX, y: topController.view.bounds.midY, width: 0, height: 0)
|
||||
popover.permittedArrowDirections = []
|
||||
}
|
||||
|
||||
topController.present(sheet, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
let okAlertAction = UIAlertAction(title: NSLocalizedString("Ok", comment: "alert button"), style: .default)
|
||||
|
||||
let cancelAlertAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: "alert button"), style: .cancel)
|
||||
|
||||
@@ -346,8 +346,6 @@ struct ContactsListSearchBar: View {
|
||||
@Binding var searchShowingSimplexLink: Bool
|
||||
@Binding var searchChatFilteredBySimplexLink: String?
|
||||
@State private var ignoreSearchTextChange = false
|
||||
@State private var alert: PlanAndConnectAlert?
|
||||
@State private var sheet: PlanAndConnectActionSheet?
|
||||
@AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false
|
||||
|
||||
var body: some View {
|
||||
@@ -416,12 +414,6 @@ struct ContactsListSearchBar: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert(item: $alert) { a in
|
||||
planAndConnectAlert(a, dismiss: true, cleanup: { searchText = "" })
|
||||
}
|
||||
.actionSheet(item: $sheet) { s in
|
||||
planAndConnectActionSheet(s, dismiss: true, cleanup: { searchText = "" })
|
||||
}
|
||||
}
|
||||
|
||||
private func toggleFilterButton() -> some View {
|
||||
@@ -442,10 +434,11 @@ struct ContactsListSearchBar: View {
|
||||
private func connect(_ link: String) {
|
||||
planAndConnect(
|
||||
link,
|
||||
showAlert: { alert = $0 },
|
||||
showActionSheet: { sheet = $0 },
|
||||
dismiss: true,
|
||||
incognito: nil,
|
||||
cleanup: {
|
||||
searchText = ""
|
||||
searchFocussed = false
|
||||
},
|
||||
filterKnownContact: { searchChatFilteredBySimplexLink = $0.id }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,11 +29,9 @@ struct SomeSheet<Content: View>: Identifiable {
|
||||
}
|
||||
|
||||
private enum NewChatViewAlert: Identifiable {
|
||||
case planAndConnectAlert(alert: PlanAndConnectAlert)
|
||||
case newChatSomeAlert(alert: SomeAlert)
|
||||
var id: String {
|
||||
switch self {
|
||||
case let .planAndConnectAlert(alert): return "planAndConnectAlert \(alert.id)"
|
||||
case let .newChatSomeAlert(alert): return "newChatSomeAlert \(alert.id)"
|
||||
}
|
||||
}
|
||||
@@ -165,8 +163,6 @@ struct NewChatView: View {
|
||||
}
|
||||
.alert(item: $alert) { a in
|
||||
switch(a) {
|
||||
case let .planAndConnectAlert(alert):
|
||||
return planAndConnectAlert(alert, dismiss: true, cleanup: { pastedLink = "" })
|
||||
case let .newChatSomeAlert(a):
|
||||
return a.alert
|
||||
}
|
||||
@@ -593,7 +589,6 @@ private struct ConnectView: View {
|
||||
@Binding var showQRCodeScanner: Bool
|
||||
@Binding var pastedLink: String
|
||||
@Binding var alert: NewChatViewAlert?
|
||||
@State private var sheet: PlanAndConnectActionSheet?
|
||||
@State private var pasteboardHasStrings = UIPasteboard.general.hasStrings
|
||||
|
||||
var body: some View {
|
||||
@@ -605,9 +600,6 @@ private struct ConnectView: View {
|
||||
ScannerInView(showQRCodeScanner: $showQRCodeScanner, processQRCode: processQRCode)
|
||||
}
|
||||
}
|
||||
.actionSheet(item: $sheet) { s in
|
||||
planAndConnectActionSheet(s, dismiss: true, cleanup: { pastedLink = "" })
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func pasteLinkView() -> some View {
|
||||
@@ -662,10 +654,7 @@ private struct ConnectView: View {
|
||||
private func connect(_ link: String) {
|
||||
planAndConnect(
|
||||
link,
|
||||
showAlert: { alert = .planAndConnectAlert(alert: $0) },
|
||||
showActionSheet: { sheet = $0 },
|
||||
dismiss: true,
|
||||
incognito: nil
|
||||
dismiss: true
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -839,187 +828,184 @@ func sharedProfileInfo(_ incognito: Bool) -> Text {
|
||||
)
|
||||
}
|
||||
|
||||
enum PlanAndConnectAlert: Identifiable {
|
||||
case ownInvitationLinkConfirmConnect(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case invitationLinkConnecting(connectionLink: CreatedConnLink)
|
||||
case ownContactAddressConfirmConnect(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case contactAddressConnectingConfirmReconnect(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case groupLinkConfirmConnect(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case groupLinkConnectingConfirmReconnect(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case groupLinkConnecting(connectionLink: CreatedConnLink, groupInfo: GroupInfo?)
|
||||
case error(shortOrFullLink: String, alert: Alert)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case let .ownInvitationLinkConfirmConnect(connectionLink, _, _): return "ownInvitationLinkConfirmConnect \(connectionLink.connFullLink)"
|
||||
case let .invitationLinkConnecting(connectionLink): return "invitationLinkConnecting \(connectionLink.connFullLink)"
|
||||
case let .ownContactAddressConfirmConnect(connectionLink, _, _): return "ownContactAddressConfirmConnect \(connectionLink.connFullLink)"
|
||||
case let .contactAddressConnectingConfirmReconnect(connectionLink, _, _): return "contactAddressConnectingConfirmReconnect \(connectionLink.connFullLink)"
|
||||
case let .groupLinkConfirmConnect(connectionLink, _, _): return "groupLinkConfirmConnect \(connectionLink.connFullLink)"
|
||||
case let .groupLinkConnectingConfirmReconnect(connectionLink, _, _): return "groupLinkConnectingConfirmReconnect \(connectionLink.connFullLink)"
|
||||
case let .groupLinkConnecting(connectionLink, _): return "groupLinkConnecting \(connectionLink.connFullLink)"
|
||||
case let .error(shortOrFullLink, alert): return "error \(shortOrFullLink)"
|
||||
}
|
||||
}
|
||||
private func showInvitationLinkConnectingAlert(cleanup: (() -> Void)?) {
|
||||
showAlert(
|
||||
NSLocalizedString("Already connecting!", comment: "new chat sheet title"),
|
||||
message: NSLocalizedString("You are already connecting via this one-time link!", comment: "new chat sheet message"),
|
||||
actions: {[
|
||||
okCleanupAlertAction(cleanup: cleanup)
|
||||
]}
|
||||
)
|
||||
}
|
||||
|
||||
func planAndConnectAlert(_ alert: PlanAndConnectAlert, dismiss: Bool, cleanup: (() -> Void)? = nil) -> Alert {
|
||||
switch alert {
|
||||
case let .ownInvitationLinkConfirmConnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Connect to yourself?"),
|
||||
message: Text("This is your own one-time link!"),
|
||||
primaryButton: .destructive(
|
||||
Text(incognito ? "Connect incognito" : "Connect"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup) }
|
||||
),
|
||||
secondaryButton: .cancel() { cleanup?() }
|
||||
)
|
||||
case .invitationLinkConnecting:
|
||||
return Alert(
|
||||
title: Text("Already connecting!"),
|
||||
message: Text("You are already connecting via this one-time link!"),
|
||||
dismissButton: .default(Text("OK")) { cleanup?() }
|
||||
)
|
||||
case let .ownContactAddressConfirmConnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Connect to yourself?"),
|
||||
message: Text("This is your own SimpleX address!"),
|
||||
primaryButton: .destructive(
|
||||
Text(incognito ? "Connect incognito" : "Connect"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup) }
|
||||
),
|
||||
secondaryButton: .cancel() { cleanup?() }
|
||||
)
|
||||
case let .contactAddressConnectingConfirmReconnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Repeat connection request?"),
|
||||
message: Text("You have already requested connection via this address!"),
|
||||
primaryButton: .destructive(
|
||||
Text(incognito ? "Connect incognito" : "Connect"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup) }
|
||||
),
|
||||
secondaryButton: .cancel() { cleanup?() }
|
||||
)
|
||||
case let .groupLinkConfirmConnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Join group?"),
|
||||
message: Text("You will connect to all group members."),
|
||||
primaryButton: .default(
|
||||
Text(incognito ? "Join incognito" : "Join"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup) }
|
||||
),
|
||||
secondaryButton: .cancel() { cleanup?() }
|
||||
)
|
||||
case let .groupLinkConnectingConfirmReconnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Repeat join request?"),
|
||||
message: Text("You are already joining the group via this link!"),
|
||||
primaryButton: .destructive(
|
||||
Text(incognito ? "Join incognito" : "Join"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup) }
|
||||
),
|
||||
secondaryButton: .cancel() { cleanup?() }
|
||||
)
|
||||
case let .groupLinkConnecting(_, groupInfo):
|
||||
if let groupInfo = groupInfo {
|
||||
return groupInfo.businessChat == nil
|
||||
? Alert(
|
||||
title: Text("Group already exists!"),
|
||||
message: Text("You are already joining the group \(groupInfo.displayName)."),
|
||||
dismissButton: .default(Text("OK")) { cleanup?() }
|
||||
)
|
||||
: Alert(
|
||||
title: Text("Chat already exists!"),
|
||||
message: Text("You are already connecting to \(groupInfo.displayName)."),
|
||||
dismissButton: .default(Text("OK")) { cleanup?() }
|
||||
private func showGroupLinkConnectingAlert(groupInfo: GroupInfo?, cleanup: (() -> Void)?) {
|
||||
if let groupInfo = groupInfo {
|
||||
if groupInfo.businessChat == nil {
|
||||
showAlert(
|
||||
NSLocalizedString("Group already exists!", comment: "new chat sheet title"),
|
||||
message:
|
||||
String.localizedStringWithFormat(
|
||||
NSLocalizedString("You are already joining the group %@.", comment: "new chat sheet message"),
|
||||
groupInfo.displayName
|
||||
),
|
||||
actions: {[
|
||||
okCleanupAlertAction(cleanup: cleanup)
|
||||
]}
|
||||
)
|
||||
} else {
|
||||
return Alert(
|
||||
title: Text("Already joining the group!"),
|
||||
message: Text("You are already joining the group via this link."),
|
||||
dismissButton: .default(Text("OK")) { cleanup?() }
|
||||
showAlert(
|
||||
NSLocalizedString("Chat already exists!", comment: "new chat sheet title"),
|
||||
message:
|
||||
String.localizedStringWithFormat(
|
||||
NSLocalizedString("You are already connecting to %@.", comment: "new chat sheet message"),
|
||||
groupInfo.displayName
|
||||
),
|
||||
actions: {[
|
||||
okCleanupAlertAction(cleanup: cleanup)
|
||||
]}
|
||||
)
|
||||
}
|
||||
case let .error(_, alert): return alert
|
||||
} else {
|
||||
showAlert(
|
||||
NSLocalizedString("Already joining the group!", comment: "new chat sheet title"),
|
||||
message: NSLocalizedString("You are already joining the group via this link.", comment: "new chat sheet message"),
|
||||
actions: {[
|
||||
okCleanupAlertAction(cleanup: cleanup)
|
||||
]}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum PlanAndConnectActionSheet: Identifiable {
|
||||
case askCurrentOrIncognitoProfile(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan?, title: LocalizedStringKey)
|
||||
case askCurrentOrIncognitoProfileDestructive(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan, title: LocalizedStringKey)
|
||||
case askCurrentOrIncognitoProfileConnectContactViaAddress(contact: Contact)
|
||||
case ownGroupLinkConfirmConnect(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan, incognito: Bool?, groupInfo: GroupInfo)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case let .askCurrentOrIncognitoProfile(connectionLink, _, _): return "askCurrentOrIncognitoProfile \(connectionLink.connFullLink)"
|
||||
case let .askCurrentOrIncognitoProfileDestructive(connectionLink, _, _): return "askCurrentOrIncognitoProfileDestructive \(connectionLink.connFullLink)"
|
||||
case let .askCurrentOrIncognitoProfileConnectContactViaAddress(contact): return "askCurrentOrIncognitoProfileConnectContactViaAddress \(contact.contactId)"
|
||||
case let .ownGroupLinkConfirmConnect(connectionLink, _, _, _): return "ownGroupLinkConfirmConnect \(connectionLink.connFullLink)"
|
||||
private func okCleanupAlertAction(cleanup: (() -> Void)?) -> UIAlertAction {
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Ok", comment: "new chat action"),
|
||||
style: .default,
|
||||
handler: { _ in
|
||||
cleanup?()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func planAndConnectActionSheet(_ sheet: PlanAndConnectActionSheet, dismiss: Bool, cleanup: (() -> Void)? = nil) -> ActionSheet {
|
||||
switch sheet {
|
||||
case let .askCurrentOrIncognitoProfile(connectionLink, connectionPlan, title):
|
||||
return ActionSheet(
|
||||
title: Text(title),
|
||||
buttons: [
|
||||
.default(Text("Use current profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: false, cleanup: cleanup) },
|
||||
.default(Text("Use new incognito profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: true, cleanup: cleanup) },
|
||||
.cancel() { cleanup?() }
|
||||
]
|
||||
)
|
||||
case let .askCurrentOrIncognitoProfileDestructive(connectionLink, connectionPlan, title):
|
||||
return ActionSheet(
|
||||
title: Text(title),
|
||||
buttons: [
|
||||
.destructive(Text("Use current profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: false, cleanup: cleanup) },
|
||||
.destructive(Text("Use new incognito profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: true, cleanup: cleanup) },
|
||||
.cancel() { cleanup?() }
|
||||
]
|
||||
)
|
||||
case let .askCurrentOrIncognitoProfileConnectContactViaAddress(contact):
|
||||
return ActionSheet(
|
||||
title: Text("Connect with \(contact.chatViewName)"),
|
||||
buttons: [
|
||||
.default(Text("Use current profile")) { connectContactViaAddress_(contact, dismiss: dismiss, incognito: false, cleanup: cleanup) },
|
||||
.default(Text("Use new incognito profile")) { connectContactViaAddress_(contact, dismiss: dismiss, incognito: true, cleanup: cleanup) },
|
||||
.cancel() { cleanup?() }
|
||||
]
|
||||
)
|
||||
case let .ownGroupLinkConfirmConnect(connectionLink, connectionPlan, incognito, groupInfo):
|
||||
if let incognito = incognito {
|
||||
return ActionSheet(
|
||||
title: Text("Join your group?\nThis is your link for group \(groupInfo.displayName)!"),
|
||||
buttons: [
|
||||
.default(Text("Open group")) { openKnownGroup(groupInfo, dismiss: dismiss, showAlreadyExistsAlert: nil) },
|
||||
.destructive(Text(incognito ? "Join incognito" : "Join with current profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup) },
|
||||
.cancel() { cleanup?() }
|
||||
]
|
||||
private func showAskCurrentOrIncognitoProfileSheet(
|
||||
title: String,
|
||||
actionStyle: UIAlertAction.Style = .default,
|
||||
connectionLink: CreatedConnLink,
|
||||
connectionPlan: ConnectionPlan?,
|
||||
dismiss: Bool,
|
||||
cleanup: (() -> Void)?
|
||||
) {
|
||||
showSheet(
|
||||
title,
|
||||
actions: {[
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Use current profile", comment: "new chat action"),
|
||||
style: actionStyle,
|
||||
handler: { _ in
|
||||
connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: false, cleanup: cleanup)
|
||||
}
|
||||
),
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Use new incognito profile", comment: "new chat action"),
|
||||
style: actionStyle,
|
||||
handler: { _ in
|
||||
connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: true, cleanup: cleanup)
|
||||
}
|
||||
),
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Cancel", comment: "new chat action"),
|
||||
style: .default,
|
||||
handler: { _ in
|
||||
cleanup?()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
return ActionSheet(
|
||||
title: Text("Join your group?\nThis is your link for group \(groupInfo.displayName)!"),
|
||||
buttons: [
|
||||
.default(Text("Open group")) { openKnownGroup(groupInfo, dismiss: dismiss, showAlreadyExistsAlert: nil) },
|
||||
.destructive(Text("Use current profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: false, cleanup: cleanup) },
|
||||
.destructive(Text("Use new incognito profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: true, cleanup: cleanup) },
|
||||
.cancel() { cleanup?() }
|
||||
]
|
||||
]}
|
||||
)
|
||||
}
|
||||
|
||||
private func showAskCurrentOrIncognitoProfileConnectContactViaAddressSheet(
|
||||
contact: Contact,
|
||||
dismiss: Bool,
|
||||
cleanup: (() -> Void)?
|
||||
) {
|
||||
showSheet(
|
||||
String.localizedStringWithFormat(
|
||||
NSLocalizedString("Connect with %@", comment: "new chat action"),
|
||||
contact.chatViewName
|
||||
),
|
||||
actions: {[
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Use current profile", comment: "new chat action"),
|
||||
style: .default,
|
||||
handler: { _ in
|
||||
connectContactViaAddress_(contact, dismiss: dismiss, incognito: false, cleanup: cleanup)
|
||||
}
|
||||
),
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Use new incognito profile", comment: "new chat action"),
|
||||
style: .default,
|
||||
handler: { _ in
|
||||
connectContactViaAddress_(contact, dismiss: dismiss, incognito: true, cleanup: cleanup)
|
||||
}
|
||||
),
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Cancel", comment: "new chat action"),
|
||||
style: .default,
|
||||
handler: { _ in
|
||||
cleanup?()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
]}
|
||||
)
|
||||
}
|
||||
|
||||
private func showOwnGroupLinkConfirmConnectSheet(
|
||||
groupInfo: GroupInfo,
|
||||
connectionLink: CreatedConnLink,
|
||||
connectionPlan: ConnectionPlan?,
|
||||
dismiss: Bool,
|
||||
cleanup: (() -> Void)?
|
||||
) {
|
||||
showSheet(
|
||||
String.localizedStringWithFormat(
|
||||
NSLocalizedString("Join your group?\nThis is your link for group %@!", comment: "new chat action"),
|
||||
groupInfo.displayName
|
||||
),
|
||||
actions: {[
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Open group", comment: "new chat action"),
|
||||
style: .default,
|
||||
handler: { _ in
|
||||
openKnownGroup(groupInfo, dismiss: dismiss, showAlreadyExistsAlert: nil)
|
||||
}
|
||||
),
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Use current profile", comment: "new chat action"),
|
||||
style: .destructive,
|
||||
handler: { _ in
|
||||
connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: false, cleanup: cleanup)
|
||||
}
|
||||
),
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Use new incognito profile", comment: "new chat action"),
|
||||
style: .destructive,
|
||||
handler: { _ in
|
||||
connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: true, cleanup: cleanup)
|
||||
}
|
||||
),
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Cancel", comment: "new chat action"),
|
||||
style: .default,
|
||||
handler: { _ in
|
||||
cleanup?()
|
||||
}
|
||||
)
|
||||
]}
|
||||
)
|
||||
}
|
||||
|
||||
func planAndConnect(
|
||||
_ shortOrFullLink: String,
|
||||
showAlert: @escaping (PlanAndConnectAlert) -> Void,
|
||||
showActionSheet: @escaping (PlanAndConnectActionSheet) -> Void,
|
||||
dismiss: Bool,
|
||||
incognito: Bool?,
|
||||
cleanup: (() -> Void)? = nil,
|
||||
filterKnownContact: ((Contact) -> Void)? = nil,
|
||||
filterKnownGroup: ((GroupInfo) -> Void)? = nil
|
||||
@@ -1031,25 +1017,30 @@ func planAndConnect(
|
||||
case let .invitationLink(ilp):
|
||||
switch ilp {
|
||||
case .ok:
|
||||
logger.debug("planAndConnect, .invitationLink, .ok, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup)
|
||||
} else {
|
||||
await MainActor.run {
|
||||
showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect via one-time link"))
|
||||
}
|
||||
logger.debug("planAndConnect, .invitationLink, .ok")
|
||||
await MainActor.run {
|
||||
showAskCurrentOrIncognitoProfileSheet(
|
||||
title: NSLocalizedString("Connect via one-time link", comment: "new chat sheet title"),
|
||||
connectionLink: connectionLink,
|
||||
connectionPlan: connectionPlan,
|
||||
dismiss: dismiss,
|
||||
cleanup: cleanup
|
||||
)
|
||||
}
|
||||
case .ownLink:
|
||||
logger.debug("planAndConnect, .invitationLink, .ownLink, incognito=\(incognito?.description ?? "nil")")
|
||||
logger.debug("planAndConnect, .invitationLink, .ownLink")
|
||||
await MainActor.run {
|
||||
if let incognito = incognito {
|
||||
showAlert(.ownInvitationLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect to yourself?\nThis is your own one-time link!"))
|
||||
}
|
||||
showAskCurrentOrIncognitoProfileSheet(
|
||||
title: NSLocalizedString("Connect to yourself?\nThis is your own one-time link!", comment: "new chat sheet title"),
|
||||
actionStyle: .destructive,
|
||||
connectionLink: connectionLink,
|
||||
connectionPlan: connectionPlan,
|
||||
dismiss: dismiss,
|
||||
cleanup: cleanup
|
||||
)
|
||||
}
|
||||
case let .connecting(contact_):
|
||||
logger.debug("planAndConnect, .invitationLink, .connecting, incognito=\(incognito?.description ?? "nil")")
|
||||
logger.debug("planAndConnect, .invitationLink, .connecting")
|
||||
await MainActor.run {
|
||||
if let contact = contact_ {
|
||||
if let f = filterKnownContact {
|
||||
@@ -1058,11 +1049,11 @@ func planAndConnect(
|
||||
openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyConnectingAlert(contact)) }
|
||||
}
|
||||
} else {
|
||||
showAlert(.invitationLinkConnecting(connectionLink: connectionLink))
|
||||
showInvitationLinkConnectingAlert(cleanup: cleanup)
|
||||
}
|
||||
}
|
||||
case let .known(contact):
|
||||
logger.debug("planAndConnect, .invitationLink, .known, incognito=\(incognito?.description ?? "nil")")
|
||||
logger.debug("planAndConnect, .invitationLink, .known")
|
||||
await MainActor.run {
|
||||
if let f = filterKnownContact {
|
||||
f(contact)
|
||||
@@ -1074,34 +1065,42 @@ func planAndConnect(
|
||||
case let .contactAddress(cap):
|
||||
switch cap {
|
||||
case .ok:
|
||||
logger.debug("planAndConnect, .contactAddress, .ok, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup)
|
||||
} else {
|
||||
await MainActor.run {
|
||||
showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect via contact address"))
|
||||
}
|
||||
logger.debug("planAndConnect, .contactAddress, .ok")
|
||||
await MainActor.run {
|
||||
showAskCurrentOrIncognitoProfileSheet(
|
||||
title: NSLocalizedString("Connect via contact address", comment: "new chat sheet title"),
|
||||
connectionLink: connectionLink,
|
||||
connectionPlan: connectionPlan,
|
||||
dismiss: dismiss,
|
||||
cleanup: cleanup
|
||||
)
|
||||
}
|
||||
case .ownLink:
|
||||
logger.debug("planAndConnect, .contactAddress, .ownLink, incognito=\(incognito?.description ?? "nil")")
|
||||
logger.debug("planAndConnect, .contactAddress, .ownLink")
|
||||
await MainActor.run {
|
||||
if let incognito = incognito {
|
||||
showAlert(.ownContactAddressConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect to yourself?\nThis is your own SimpleX address!"))
|
||||
}
|
||||
showAskCurrentOrIncognitoProfileSheet(
|
||||
title: NSLocalizedString("Connect to yourself?\nThis is your own SimpleX address!", comment: "new chat sheet title"),
|
||||
actionStyle: .destructive,
|
||||
connectionLink: connectionLink,
|
||||
connectionPlan: connectionPlan,
|
||||
dismiss: dismiss,
|
||||
cleanup: cleanup
|
||||
)
|
||||
}
|
||||
case .connectingConfirmReconnect:
|
||||
logger.debug("planAndConnect, .contactAddress, .connectingConfirmReconnect, incognito=\(incognito?.description ?? "nil")")
|
||||
logger.debug("planAndConnect, .contactAddress, .connectingConfirmReconnect")
|
||||
await MainActor.run {
|
||||
if let incognito = incognito {
|
||||
showAlert(.contactAddressConnectingConfirmReconnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "You have already requested connection!\nRepeat connection request?"))
|
||||
}
|
||||
showAskCurrentOrIncognitoProfileSheet(
|
||||
title: NSLocalizedString("You have already requested connection!\nRepeat connection request?", comment: "new chat sheet title"),
|
||||
actionStyle: .destructive,
|
||||
connectionLink: connectionLink,
|
||||
connectionPlan: connectionPlan,
|
||||
dismiss: dismiss,
|
||||
cleanup: cleanup
|
||||
)
|
||||
}
|
||||
case let .connectingProhibit(contact):
|
||||
logger.debug("planAndConnect, .contactAddress, .connectingProhibit, incognito=\(incognito?.description ?? "nil")")
|
||||
logger.debug("planAndConnect, .contactAddress, .connectingProhibit")
|
||||
await MainActor.run {
|
||||
if let f = filterKnownContact {
|
||||
f(contact)
|
||||
@@ -1110,7 +1109,7 @@ func planAndConnect(
|
||||
}
|
||||
}
|
||||
case let .known(contact):
|
||||
logger.debug("planAndConnect, .contactAddress, .known, incognito=\(incognito?.description ?? "nil")")
|
||||
logger.debug("planAndConnect, .contactAddress, .known")
|
||||
await MainActor.run {
|
||||
if let f = filterKnownContact {
|
||||
f(contact)
|
||||
@@ -1119,49 +1118,60 @@ func planAndConnect(
|
||||
}
|
||||
}
|
||||
case let .contactViaAddress(contact):
|
||||
logger.debug("planAndConnect, .contactAddress, .contactViaAddress, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
connectContactViaAddress_(contact, dismiss: dismiss, incognito: incognito, cleanup: cleanup)
|
||||
} else {
|
||||
await MainActor.run {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileConnectContactViaAddress(contact: contact))
|
||||
}
|
||||
logger.debug("planAndConnect, .contactAddress, .contactViaAddress")
|
||||
await MainActor.run {
|
||||
showAskCurrentOrIncognitoProfileConnectContactViaAddressSheet(
|
||||
contact: contact,
|
||||
dismiss: dismiss,
|
||||
cleanup: cleanup
|
||||
)
|
||||
}
|
||||
}
|
||||
case let .groupLink(glp):
|
||||
switch glp {
|
||||
case .ok:
|
||||
await MainActor.run {
|
||||
if let incognito = incognito {
|
||||
showAlert(.groupLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Join group"))
|
||||
}
|
||||
showAskCurrentOrIncognitoProfileSheet(
|
||||
title: NSLocalizedString("Join group", comment: "new chat sheet title"),
|
||||
connectionLink: connectionLink,
|
||||
connectionPlan: connectionPlan,
|
||||
dismiss: dismiss,
|
||||
cleanup: cleanup
|
||||
)
|
||||
}
|
||||
case let .ownLink(groupInfo):
|
||||
logger.debug("planAndConnect, .groupLink, .ownLink, incognito=\(incognito?.description ?? "nil")")
|
||||
logger.debug("planAndConnect, .groupLink, .ownLink")
|
||||
await MainActor.run {
|
||||
if let f = filterKnownGroup {
|
||||
f(groupInfo)
|
||||
}
|
||||
showActionSheet(.ownGroupLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito, groupInfo: groupInfo))
|
||||
showOwnGroupLinkConfirmConnectSheet(
|
||||
groupInfo: groupInfo,
|
||||
connectionLink: connectionLink,
|
||||
connectionPlan: connectionPlan,
|
||||
dismiss: dismiss,
|
||||
cleanup: cleanup
|
||||
)
|
||||
}
|
||||
case .connectingConfirmReconnect:
|
||||
logger.debug("planAndConnect, .groupLink, .connectingConfirmReconnect, incognito=\(incognito?.description ?? "nil")")
|
||||
logger.debug("planAndConnect, .groupLink, .connectingConfirmReconnect")
|
||||
await MainActor.run {
|
||||
if let incognito = incognito {
|
||||
showAlert(.groupLinkConnectingConfirmReconnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "You are already joining the group!\nRepeat join request?"))
|
||||
}
|
||||
showAskCurrentOrIncognitoProfileSheet(
|
||||
title: NSLocalizedString("You are already joining the group!\nRepeat join request?", comment: "new chat sheet title"),
|
||||
actionStyle: .destructive,
|
||||
connectionLink: connectionLink,
|
||||
connectionPlan: connectionPlan,
|
||||
dismiss: dismiss,
|
||||
cleanup: cleanup
|
||||
)
|
||||
}
|
||||
case let .connectingProhibit(groupInfo_):
|
||||
logger.debug("planAndConnect, .groupLink, .connectingProhibit, incognito=\(incognito?.description ?? "nil")")
|
||||
logger.debug("planAndConnect, .groupLink, .connectingProhibit")
|
||||
await MainActor.run {
|
||||
showAlert(.groupLinkConnecting(connectionLink: connectionLink, groupInfo: groupInfo_))
|
||||
showGroupLinkConnectingAlert(groupInfo: groupInfo_, cleanup: cleanup)
|
||||
}
|
||||
case let .known(groupInfo):
|
||||
logger.debug("planAndConnect, .groupLink, .known, incognito=\(incognito?.description ?? "nil")")
|
||||
logger.debug("planAndConnect, .groupLink, .known")
|
||||
await MainActor.run {
|
||||
if let f = filterKnownGroup {
|
||||
f(groupInfo)
|
||||
@@ -1172,15 +1182,20 @@ func planAndConnect(
|
||||
}
|
||||
case let .error(chatError):
|
||||
logger.debug("planAndConnect, .error \(chatErrorString(chatError))")
|
||||
if let incognito = incognito {
|
||||
connectViaLink(connectionLink, connectionPlan: nil, dismiss: dismiss, incognito: incognito, cleanup: cleanup)
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: nil, title: "Connect via link"))
|
||||
}
|
||||
showAskCurrentOrIncognitoProfileSheet(
|
||||
title: NSLocalizedString("Connect via link", comment: "new chat sheet title"),
|
||||
connectionLink: connectionLink,
|
||||
connectionPlan: nil,
|
||||
dismiss: dismiss,
|
||||
cleanup: cleanup
|
||||
)
|
||||
}
|
||||
} else if let alert {
|
||||
await MainActor.run {
|
||||
showAlert(.error(shortOrFullLink: shortOrFullLink, alert: alert))
|
||||
dismissAllSheets(animated: true) {
|
||||
AlertManager.shared.showAlert(alert)
|
||||
cleanup?()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,8 +184,8 @@
|
||||
64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; };
|
||||
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; };
|
||||
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; };
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a */; };
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a */; };
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-BLKUepIn6JF2NJxeTwxEay-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-BLKUepIn6JF2NJxeTwxEay-ghc9.6.3.a */; };
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-BLKUepIn6JF2NJxeTwxEay.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-BLKUepIn6JF2NJxeTwxEay.a */; };
|
||||
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */; };
|
||||
64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; };
|
||||
64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; };
|
||||
@@ -553,8 +553,8 @@
|
||||
64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = "<group>"; };
|
||||
64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a"; sourceTree = "<group>"; };
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-BLKUepIn6JF2NJxeTwxEay-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.2-BLKUepIn6JF2NJxeTwxEay-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-BLKUepIn6JF2NJxeTwxEay.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.2-BLKUepIn6JF2NJxeTwxEay.a"; sourceTree = "<group>"; };
|
||||
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = "<group>"; };
|
||||
64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = "<group>"; };
|
||||
@@ -712,8 +712,8 @@
|
||||
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */,
|
||||
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */,
|
||||
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */,
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a in Frameworks */,
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a in Frameworks */,
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-BLKUepIn6JF2NJxeTwxEay-ghc9.6.3.a in Frameworks */,
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-BLKUepIn6JF2NJxeTwxEay.a in Frameworks */,
|
||||
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -798,8 +798,8 @@
|
||||
64C829992D54AEEE006B9E89 /* libffi.a */,
|
||||
64C829982D54AEED006B9E89 /* libgmp.a */,
|
||||
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */,
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a */,
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a */,
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-BLKUepIn6JF2NJxeTwxEay-ghc9.6.3.a */,
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-BLKUepIn6JF2NJxeTwxEay.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/simplex-chat/simplexmq.git
|
||||
tag: 4c33d8ac43874660c342c00cb078bd3814d5d9eb
|
||||
tag: 1e82104224e2e63c18a9d50d8e0b253f4842512c
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.0 KiB |
@@ -37,13 +37,27 @@ cp *imple*.desktop usr/share/applications/
|
||||
cp $multiplatform_dir/desktop/src/jvmMain/resources/distribute/*.appdata.xml usr/share/metainfo
|
||||
|
||||
if [ ! -f ../appimagetool-x86_64.AppImage ]; then
|
||||
wget --secure-protocol=TLSv1_3 https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage -O ../appimagetool-x86_64.AppImage
|
||||
wget --secure-protocol=TLSv1_3 https://github.com/simplex-chat/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage -O ../appimagetool-x86_64.AppImage
|
||||
chmod +x ../appimagetool-x86_64.AppImage
|
||||
fi
|
||||
if [ ! -f ../runtime-x86_64 ]; then
|
||||
wget --secure-protocol=TLSv1_3 https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-x86_64 -O ../runtime-x86_64
|
||||
wget --secure-protocol=TLSv1_3 https://github.com/simplex-chat/type2-runtime/releases/download/continuous/runtime-x86_64 -O ../runtime-x86_64
|
||||
chmod +x ../runtime-x86_64
|
||||
fi
|
||||
../appimagetool-x86_64.AppImage --runtime-file ../runtime-x86_64 .
|
||||
|
||||
# Determenistic build
|
||||
|
||||
export SOURCE_DATE_EPOCH=1704067200
|
||||
|
||||
# Delete redundant jar file and modify cfg
|
||||
rm -f ./usr/lib/app/*skiko-awt-runtime-linux*
|
||||
sed -i -e '/skiko-awt-runtime-linux/d' ./usr/lib/app/simplex.cfg
|
||||
|
||||
# Set all files to fixed time
|
||||
find . -exec touch -d "@$SOURCE_DATE_EPOCH" {} +
|
||||
|
||||
../appimagetool-x86_64.AppImage --verbose --no-appstream --runtime-file ../runtime-x86_64 .
|
||||
mv *imple*.AppImage ../../
|
||||
|
||||
# Just a safeguard
|
||||
strip-nondeterminism ../../*imple*.AppImage
|
||||
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
scripts/desktop/build-lib-linux.sh
|
||||
cd apps/multiplatform
|
||||
./gradlew packageDeb
|
||||
|
||||
# Workaround for skiko library
|
||||
#
|
||||
# Compose Multiplatform depends on skiko library, that
|
||||
# handles all of the window managment and graphics drawing.
|
||||
#
|
||||
# This skiko library comes with two jar's:
|
||||
# - platform-agnostic "skiko-awt"
|
||||
# - and platform-specific "skiko-awt-runtime"
|
||||
#
|
||||
# In case of Linux, second jar is called "skiko-awt-runtime-linux-x64".
|
||||
# Essentially, this jar has the Linux .so library called "libskiko-linux-x64.so"
|
||||
# that is being unpacked to runtime libs.
|
||||
#
|
||||
# Since the jar is nothing more than a zip archive, extracting library
|
||||
# from "skiko-awt-runtime-linux-x64" modifies it's timestamps
|
||||
# with current time, which in changes it's hash, which in turn
|
||||
# makes the whole build unreproducible.
|
||||
#
|
||||
# It seems to be there is no way to handle this extraction in our code and
|
||||
# https://docs.gradle.org/current/userguide/working_with_files.html#sec:reproducible_archives
|
||||
# unfortunately doesn't solve the issue.
|
||||
#
|
||||
# Instead, just modify the deb, removing the redundant skiko library.
|
||||
#
|
||||
# Also, it seems this is related to:
|
||||
# https://youtrack.jetbrains.com/issue/CMP-1971/createDistributable-produces-duplicated-skiko-awt.jar-and-skiko-awt-runtime-windows-x64.jar
|
||||
|
||||
export SOURCE_DATE_EPOCH=1704067200
|
||||
|
||||
dpkg-deb -R ./release/main/deb/simplex*.deb ./extracted
|
||||
|
||||
rm -f ./extracted/opt/*imple*/lib/app/*skiko-awt-runtime-linux*
|
||||
sed -i -e '/skiko-awt-runtime-linux/d' ./extracted/opt/*imple*/lib/app/simplex.cfg
|
||||
find ./extracted/ -exec touch -d "@$SOURCE_DATE_EPOCH" {} +
|
||||
|
||||
dpkg-deb --build --root-owner-group --uniform-compression ./extracted ./release/main/deb/simplex_amd64.deb
|
||||
|
||||
strip-nondeterminism ./release/main/deb/simplex_amd64.deb
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"https://github.com/simplex-chat/simplexmq.git"."4c33d8ac43874660c342c00cb078bd3814d5d9eb" = "0223qr6vz3vigcx9279kmfxjwb7vwfsqgd7qz271wz86sfxg9hip";
|
||||
"https://github.com/simplex-chat/simplexmq.git"."1e82104224e2e63c18a9d50d8e0b253f4842512c" = "0qi8zxj94zch07h500980fyqqh5vjhvyy8i2swda2w5ky5jxljhy";
|
||||
"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/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ cabal-version: 1.12
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: simplex-chat
|
||||
version: 6.4.0.2
|
||||
version: 6.4.0.3
|
||||
category: Web, System, Services, Cryptography
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
author: simplex.chat
|
||||
|
||||
@@ -737,7 +737,7 @@ data ChatResponse
|
||||
| CRArchiveImported {archiveErrors :: [ArchiveError]}
|
||||
| CRSlowSQLQueries {chatQueries :: [SlowSQLQuery], agentQueries :: [SlowSQLQuery]}
|
||||
#endif
|
||||
| CRDebugLocks {chatLockName :: Maybe String, chatEntityLocks :: Map String String, agentLocks :: AgentLocks}
|
||||
| CRDebugLocks {chatLockName :: Maybe Text, chatEntityLocks :: Map Text Text, agentLocks :: AgentLocks}
|
||||
| CRAgentSubsTotal {user :: User, subsTotal :: SMPServerSubs, hasSession :: Bool}
|
||||
| CRAgentServersSummary {user :: User, serversSummary :: PresentedServersSummary}
|
||||
| CRAgentWorkersDetails {agentWorkersDetails :: AgentWorkersDetails}
|
||||
|
||||
@@ -1667,7 +1667,8 @@ processChatCommand' vr = \case
|
||||
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
let userData = shortLinkUserData short
|
||||
(connId, ccLink) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation userData Nothing IKPQOn subMode
|
||||
-- TODO [certs rcv]
|
||||
(connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation userData Nothing IKPQOn subMode
|
||||
ccLink' <- shortenCreatedLink ccLink
|
||||
-- TODO PQ pass minVersion from the current range
|
||||
conn <- withFastStore' $ \db -> createDirectConnection db user connId ccLink' ConnNew incognitoProfile subMode initialChatVersion PQSupportOn
|
||||
@@ -1717,7 +1718,8 @@ processChatCommand' vr = \case
|
||||
recreateConn user conn@PendingContactConnection {customUserProfileId, connLinkInv} newUser = do
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
let userData = shortLinkUserData $ isJust $ connShortLink =<< connLinkInv
|
||||
(agConnId, ccLink) <- withAgent $ \a -> createConnection a (aUserId newUser) True SCMInvitation userData Nothing IKPQOn subMode
|
||||
-- TODO [certs rcv]
|
||||
(agConnId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a (aUserId newUser) True SCMInvitation userData Nothing IKPQOn subMode
|
||||
ccLink' <- shortenCreatedLink ccLink
|
||||
conn' <- withFastStore' $ \db -> do
|
||||
deleteConnectionRecord db user connId
|
||||
@@ -1790,7 +1792,8 @@ processChatCommand' vr = \case
|
||||
APICreateMyAddress userId short -> withUserId userId $ \user -> procCmd $ do
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
let userData = shortLinkUserData short
|
||||
(connId, ccLink) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact userData Nothing IKPQOn subMode
|
||||
-- TODO [certs rcv]
|
||||
(connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact userData Nothing IKPQOn subMode
|
||||
ccLink' <- shortenCreatedLink ccLink
|
||||
withFastStore $ \db -> createUserContactLink db user connId ccLink' subMode
|
||||
pure $ CRUserContactLinkCreated user ccLink'
|
||||
@@ -2007,7 +2010,8 @@ processChatCommand' vr = \case
|
||||
Nothing -> do
|
||||
gVar <- asks random
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
(agentConnId, CCLink cReq _) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing Nothing IKPQOff subMode
|
||||
-- TODO [certs rcv]
|
||||
(agentConnId, (CCLink cReq _, _serviceId)) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing Nothing IKPQOff subMode
|
||||
member <- withFastStore $ \db -> createNewContactMember db gVar user gInfo contact memRole agentConnId cReq subMode
|
||||
sendInvitation member cReq
|
||||
pure $ CRSentGroupInvitation user gInfo contact member
|
||||
@@ -2396,7 +2400,8 @@ processChatCommand' vr = \case
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
let crClientData = encodeJSON $ CRDataGroup groupLinkId
|
||||
userData = shortLinkUserData short
|
||||
(connId, ccLink) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact userData (Just crClientData) IKPQOff subMode
|
||||
-- TODO [certs rcv]
|
||||
(connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact userData (Just crClientData) IKPQOff subMode
|
||||
ccLink' <- createdGroupLink <$> shortenCreatedLink ccLink
|
||||
withFastStore $ \db -> createGroupLink db user gInfo connId ccLink' groupLinkId mRole subMode
|
||||
pure $ CRGroupLinkCreated user gInfo ccLink' mRole
|
||||
@@ -2437,7 +2442,8 @@ processChatCommand' vr = \case
|
||||
when (isJust $ memberContactId m) $ throwCmdError "member contact already exists"
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
-- TODO PQ should negotitate contact connection with PQSupportOn?
|
||||
(connId, CCLink cReq _) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing Nothing IKPQOff subMode
|
||||
-- TODO [certs rcv]
|
||||
(connId, (CCLink cReq _, _serviceId)) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing Nothing IKPQOff subMode
|
||||
-- [incognito] reuse membership incognito profile
|
||||
ct <- withFastStore' $ \db -> createMemberContact db user connId cReq g m mConn subMode
|
||||
-- TODO not sure it is correct to set connections status here?
|
||||
@@ -2689,12 +2695,12 @@ processChatCommand' vr = \case
|
||||
where
|
||||
getLocks ls = atomically $ M.mapKeys enityLockString . M.mapMaybe id <$> (mapM tryReadTMVar =<< readTVar ls)
|
||||
enityLockString cle = case cle of
|
||||
CLInvitation bs -> "Invitation " <> B.unpack bs
|
||||
CLConnection connId -> "Connection " <> show connId
|
||||
CLContact ctId -> "Contact " <> show ctId
|
||||
CLGroup gId -> "Group " <> show gId
|
||||
CLUserContact ucId -> "UserContact " <> show ucId
|
||||
CLFile fId -> "File " <> show fId
|
||||
CLInvitation bs -> "Invitation " <> safeDecodeUtf8 bs
|
||||
CLConnection connId -> "Connection " <> tshow connId
|
||||
CLContact ctId -> "Contact " <> tshow ctId
|
||||
CLGroup gId -> "Group " <> tshow gId
|
||||
CLUserContact ucId -> "UserContact " <> tshow ucId
|
||||
CLFile fId -> "File " <> tshow fId
|
||||
DebugEvent event -> toView event >> ok_
|
||||
GetAgentSubsTotal userId -> withUserId userId $ \user -> do
|
||||
users <- withStore' $ \db -> getUsers db
|
||||
@@ -3719,8 +3725,9 @@ agentSubscriber = do
|
||||
where
|
||||
run action = action `catchChatError'` (eToView')
|
||||
|
||||
type AgentBatchSubscribe = AgentClient -> [ConnId] -> ExceptT AgentErrorType IO (Map ConnId (Either AgentErrorType ()))
|
||||
type AgentBatchSubscribe = AgentClient -> [ConnId] -> ExceptT AgentErrorType IO (Map ConnId (Either AgentErrorType (Maybe ClientServiceId)))
|
||||
|
||||
-- TODO [certs rcv]
|
||||
subscribeUserConnections :: VersionRangeChat -> Bool -> AgentBatchSubscribe -> User -> CM ()
|
||||
subscribeUserConnections vr onlyNeeded agentBatchSubscribe user = do
|
||||
-- get user connections
|
||||
@@ -3814,7 +3821,7 @@ subscribeUserConnections vr onlyNeeded agentBatchSubscribe user = do
|
||||
pcs <- withStore_ getPendingContactConnections
|
||||
let connIds = map aConnId' pcs
|
||||
pure (connIds, M.fromList $ zip connIds pcs)
|
||||
contactSubsToView :: Map ConnId (Either AgentErrorType ()) -> Map ConnId Contact -> Bool -> CM ()
|
||||
contactSubsToView :: Map ConnId (Either AgentErrorType (Maybe ClientServiceId)) -> Map ConnId Contact -> Bool -> CM ()
|
||||
contactSubsToView rs cts ce = do
|
||||
chatModifyVar connNetworkStatuses $ M.union (M.fromList statuses)
|
||||
ifM (asks $ coreApi . config) (notifyAPI statuses) notifyCLI
|
||||
@@ -3840,9 +3847,9 @@ subscribeUserConnections vr onlyNeeded agentBatchSubscribe user = do
|
||||
ChatErrorAgent (SMP _ SMP.AUTH) _ -> "contact deleted"
|
||||
e -> show e
|
||||
-- TODO possibly below could be replaced with less noisy events for API
|
||||
contactLinkSubsToView :: Map ConnId (Either AgentErrorType ()) -> Map ConnId UserContact -> CM ()
|
||||
contactLinkSubsToView :: Map ConnId (Either AgentErrorType (Maybe ClientServiceId)) -> Map ConnId UserContact -> CM ()
|
||||
contactLinkSubsToView rs = toView . CEvtUserContactSubSummary user . map (uncurry UserContactSubStatus) . resultsFor rs
|
||||
groupSubsToView :: Map ConnId (Either AgentErrorType ()) -> [ShortGroup] -> Map ConnId ShortGroupMember -> Bool -> CM ()
|
||||
groupSubsToView :: Map ConnId (Either AgentErrorType (Maybe ClientServiceId)) -> [ShortGroup] -> Map ConnId ShortGroupMember -> Bool -> CM ()
|
||||
groupSubsToView rs gs ms ce = do
|
||||
mapM_ groupSub $
|
||||
sortOn (\(ShortGroup ShortGroupInfo {groupName = g} _) -> g) gs
|
||||
@@ -3864,7 +3871,7 @@ subscribeUserConnections vr onlyNeeded agentBatchSubscribe user = do
|
||||
| membershipStatus == GSMemInvited = TEGroupInvitation user g
|
||||
| null members = TEGroupEmpty user g
|
||||
| otherwise = TEGroupSubscribed user g
|
||||
sndFileSubsToView :: Map ConnId (Either AgentErrorType ()) -> Map ConnId SndFileTransfer -> CM ()
|
||||
sndFileSubsToView :: Map ConnId (Either AgentErrorType (Maybe ClientServiceId)) -> Map ConnId SndFileTransfer -> CM ()
|
||||
sndFileSubsToView rs sfts = do
|
||||
let sftRs = resultsFor rs sfts
|
||||
forM_ sftRs $ \(ft@SndFileTransfer {fileId, fileStatus}, err_) -> do
|
||||
@@ -3873,20 +3880,20 @@ subscribeUserConnections vr onlyNeeded agentBatchSubscribe user = do
|
||||
threadDelay 1000000
|
||||
when (fileStatus == FSConnected) . unlessM (isFileActive fileId sndFiles) . withChatLock "subscribe sendFileChunk" $
|
||||
sendFileChunk user ft
|
||||
rcvFileSubsToView :: Map ConnId (Either AgentErrorType ()) -> Map ConnId RcvFileTransfer -> CM ()
|
||||
rcvFileSubsToView :: Map ConnId (Either AgentErrorType (Maybe ClientServiceId)) -> Map ConnId RcvFileTransfer -> CM ()
|
||||
rcvFileSubsToView rs = mapM_ (toViewTE . uncurry (TERcvFileSubError user)) . filterErrors . resultsFor rs
|
||||
pendingConnSubsToView :: Map ConnId (Either AgentErrorType ()) -> Map ConnId PendingContactConnection -> CM ()
|
||||
pendingConnSubsToView :: Map ConnId (Either AgentErrorType (Maybe ClientServiceId)) -> Map ConnId PendingContactConnection -> CM ()
|
||||
pendingConnSubsToView rs = toViewTE . TEPendingSubSummary user . map (uncurry PendingSubStatus) . resultsFor rs
|
||||
withStore_ :: (DB.Connection -> User -> IO [a]) -> CM [a]
|
||||
withStore_ a = withStore' (`a` user) `catchChatError` \e -> eToView e $> []
|
||||
filterErrors :: [(a, Maybe ChatError)] -> [(a, ChatError)]
|
||||
filterErrors = mapMaybe (\(a, e_) -> (a,) <$> e_)
|
||||
resultsFor :: Map ConnId (Either AgentErrorType ()) -> Map ConnId a -> [(a, Maybe ChatError)]
|
||||
resultsFor :: Map ConnId (Either AgentErrorType (Maybe ClientServiceId)) -> Map ConnId a -> [(a, Maybe ChatError)]
|
||||
resultsFor rs = M.foldrWithKey' addResult []
|
||||
where
|
||||
addResult :: ConnId -> a -> [(a, Maybe ChatError)] -> [(a, Maybe ChatError)]
|
||||
addResult connId = (:) . (,resultErr connId rs)
|
||||
resultErr :: ConnId -> Map ConnId (Either AgentErrorType ()) -> Maybe ChatError
|
||||
resultErr :: ConnId -> Map ConnId (Either AgentErrorType (Maybe ClientServiceId)) -> Maybe ChatError
|
||||
resultErr connId rs = case M.lookup connId rs of
|
||||
Just (Left e) -> Just $ ChatErrorAgent e Nothing
|
||||
Just _ -> Nothing
|
||||
|
||||
@@ -111,37 +111,37 @@ maxRcvMentions = 5
|
||||
maxSndMentions :: Int
|
||||
maxSndMentions = 3
|
||||
|
||||
withChatLock :: String -> CM a -> CM a
|
||||
withChatLock :: Text -> CM a -> CM a
|
||||
withChatLock name action = asks chatLock >>= \l -> withLock l name action
|
||||
|
||||
withEntityLock :: String -> ChatLockEntity -> CM a -> CM a
|
||||
withEntityLock :: Text -> ChatLockEntity -> CM a -> CM a
|
||||
withEntityLock name entity action = do
|
||||
chatLock <- asks chatLock
|
||||
ls <- asks entityLocks
|
||||
atomically $ unlessM (isEmptyTMVar chatLock) retry
|
||||
withLockMap ls entity name action
|
||||
|
||||
withInvitationLock :: String -> ByteString -> CM a -> CM a
|
||||
withInvitationLock :: Text -> ByteString -> CM a -> CM a
|
||||
withInvitationLock name = withEntityLock name . CLInvitation
|
||||
{-# INLINE withInvitationLock #-}
|
||||
|
||||
withConnectionLock :: String -> Int64 -> CM a -> CM a
|
||||
withConnectionLock :: Text -> Int64 -> CM a -> CM a
|
||||
withConnectionLock name = withEntityLock name . CLConnection
|
||||
{-# INLINE withConnectionLock #-}
|
||||
|
||||
withContactLock :: String -> ContactId -> CM a -> CM a
|
||||
withContactLock :: Text -> ContactId -> CM a -> CM a
|
||||
withContactLock name = withEntityLock name . CLContact
|
||||
{-# INLINE withContactLock #-}
|
||||
|
||||
withGroupLock :: String -> GroupId -> CM a -> CM a
|
||||
withGroupLock :: Text -> GroupId -> CM a -> CM a
|
||||
withGroupLock name = withEntityLock name . CLGroup
|
||||
{-# INLINE withGroupLock #-}
|
||||
|
||||
withUserContactLock :: String -> Int64 -> CM a -> CM a
|
||||
withUserContactLock :: Text -> Int64 -> CM a -> CM a
|
||||
withUserContactLock name = withEntityLock name . CLUserContact
|
||||
{-# INLINE withUserContactLock #-}
|
||||
|
||||
withFileLock :: String -> Int64 -> CM a -> CM a
|
||||
withFileLock :: Text -> Int64 -> CM a -> CM a
|
||||
withFileLock name = withEntityLock name . CLFile
|
||||
{-# INLINE withFileLock #-}
|
||||
|
||||
@@ -889,7 +889,8 @@ acceptContactRequest user@User {userId} UserContactRequest {agentInvitationId =
|
||||
pure (ct, conn, ExistingIncognito <$> incognitoProfile)
|
||||
let profileToSend = profileToSendOnAccept user incognitoProfile False
|
||||
dm <- encodeConnInfoPQ pqSup' chatV $ XInfo profileToSend
|
||||
(ct,conn,) <$> withAgent (\a -> acceptContact a (aConnId conn) True invId dm pqSup' subMode)
|
||||
-- TODO [certs rcv]
|
||||
(ct,conn,) . fst <$> withAgent (\a -> acceptContact a (aConnId conn) True invId dm pqSup' subMode)
|
||||
|
||||
acceptContactRequestAsync :: User -> UserContactRequest -> Maybe IncognitoProfile -> PQSupport -> CM Contact
|
||||
acceptContactRequestAsync user cReq@UserContactRequest {agentInvitationId = AgentInvId invId, cReqChatVRange, localDisplayName = cName, profileId, profile = p, userContactLinkId, xContactId} incognitoProfile pqSup = do
|
||||
|
||||
@@ -385,7 +385,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
|
||||
agentMsgConnStatus :: AEvent e -> Maybe ConnStatus
|
||||
agentMsgConnStatus = \case
|
||||
JOINED True -> Just ConnSndReady
|
||||
JOINED True _ -> Just ConnSndReady
|
||||
CONF {} -> Just ConnRequested
|
||||
INFO {} -> Just ConnSndReady
|
||||
CON _ -> Just ConnReady
|
||||
@@ -430,7 +430,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
OK ->
|
||||
-- [async agent commands] continuation on receiving OK
|
||||
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure ()
|
||||
JOINED _ ->
|
||||
-- TODO [certs rcv]
|
||||
JOINED _ _serviceId ->
|
||||
-- [async agent commands] continuation on receiving JOINED
|
||||
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure ()
|
||||
QCONT ->
|
||||
@@ -449,7 +450,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
-- TODO add debugging output
|
||||
_ -> pure ()
|
||||
Just ct@Contact {contactId} -> case agentMsg of
|
||||
INV (ACR _ cReq) ->
|
||||
-- TODO [certs rcv]
|
||||
INV (ACR _ cReq) _serviceId ->
|
||||
-- [async agent commands] XGrpMemIntro continuation on receiving INV
|
||||
withCompletedCommand conn agentMsg $ \_ ->
|
||||
case cReq of
|
||||
@@ -638,7 +640,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
OK ->
|
||||
-- [async agent commands] continuation on receiving OK
|
||||
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure ()
|
||||
JOINED sqSecured ->
|
||||
-- TODO [certs rcv]
|
||||
JOINED sqSecured _serviceId ->
|
||||
-- [async agent commands] continuation on receiving JOINED
|
||||
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData ->
|
||||
when (directOrUsed ct && sqSecured) $ do
|
||||
@@ -676,7 +679,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
|
||||
processGroupMessage :: AEvent e -> ConnectionEntity -> Connection -> GroupInfo -> GroupMember -> CM ()
|
||||
processGroupMessage agentMsg connEntity conn@Connection {connId, connChatVersion, connectionCode} gInfo@GroupInfo {groupId, groupProfile, membership, chatSettings} m = case agentMsg of
|
||||
INV (ACR _ cReq) ->
|
||||
-- TODO [certs rcv]
|
||||
INV (ACR _ cReq) _serviceId ->
|
||||
withCompletedCommand conn agentMsg $ \CommandData {cmdFunction} ->
|
||||
case cReq of
|
||||
groupConnReq@(CRInvitationUri _ _) -> case cmdFunction of
|
||||
@@ -981,7 +985,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
OK ->
|
||||
-- [async agent commands] continuation on receiving OK
|
||||
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure ()
|
||||
JOINED sqSecured ->
|
||||
-- TODO [certs rcv]
|
||||
JOINED sqSecured _serviceId ->
|
||||
-- [async agent commands] continuation on receiving JOINED
|
||||
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData ->
|
||||
when (sqSecured && connChatVersion >= batchSend2Version) sendGroupAutoReply
|
||||
@@ -1095,7 +1100,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
OK ->
|
||||
-- [async agent commands] continuation on receiving OK
|
||||
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure ()
|
||||
JOINED _ ->
|
||||
-- TODO [certs rcv]
|
||||
JOINED _ _serviceId->
|
||||
-- [async agent commands] continuation on receiving JOINED
|
||||
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure ()
|
||||
ERR err -> do
|
||||
@@ -1107,7 +1113,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
processRcvFileConn :: AEvent e -> ConnectionEntity -> Connection -> RcvFileTransfer -> CM ()
|
||||
processRcvFileConn agentMsg connEntity conn ft@RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileName}, grpMemberId} =
|
||||
case agentMsg of
|
||||
INV (ACR _ cReq) ->
|
||||
-- TODO [certs rcv]
|
||||
INV (ACR _ cReq) _serviceId ->
|
||||
withCompletedCommand conn agentMsg $ \CommandData {cmdFunction} ->
|
||||
case cReq of
|
||||
fileInvConnReq@(CRInvitationUri _ _) -> case cmdFunction of
|
||||
@@ -1144,7 +1151,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
OK ->
|
||||
-- [async agent commands] continuation on receiving OK
|
||||
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure ()
|
||||
JOINED _ ->
|
||||
-- TODO [certs rcv]
|
||||
JOINED _ _serviceId ->
|
||||
-- [async agent commands] continuation on receiving JOINED
|
||||
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure ()
|
||||
MERR _ err -> do
|
||||
|
||||
@@ -75,11 +75,11 @@ remoteFilesFolder = "simplex_v1_files"
|
||||
|
||||
-- when acting as host
|
||||
minRemoteCtrlVersion :: AppVersion
|
||||
minRemoteCtrlVersion = AppVersion [6, 4, 0, 2]
|
||||
minRemoteCtrlVersion = AppVersion [6, 4, 0, 3]
|
||||
|
||||
-- when acting as controller
|
||||
minRemoteHostVersion :: AppVersion
|
||||
minRemoteHostVersion = AppVersion [6, 4, 0, 2]
|
||||
minRemoteHostVersion = AppVersion [6, 4, 0, 3]
|
||||
|
||||
currentAppVersion :: AppVersion
|
||||
currentAppVersion = AppVersion SC.version
|
||||
|
||||
@@ -177,7 +177,7 @@ import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
|
||||
import Simplex.Messaging.Util (eitherToMaybe)
|
||||
import UnliftIO.STM
|
||||
#if defined(dbPostgres)
|
||||
import Database.PostgreSQL.Simple (FromRow, Only (..), Query, ToRow, (:.) (..))
|
||||
import Database.PostgreSQL.Simple (FromRow, In (..), Only (..), Query, ToRow, (:.) (..))
|
||||
import Database.PostgreSQL.Simple.SqlQQ (sql)
|
||||
#else
|
||||
import Database.SQLite.Simple (FromRow, Only (..), Query, ToRow, (:.) (..))
|
||||
@@ -2675,8 +2675,14 @@ updateGroupChatItemModerated db User {userId} GroupInfo {groupId} ci m@GroupMemb
|
||||
updateMemberCIsModerated :: MsgDirectionI d => DB.Connection -> User -> GroupInfo -> GroupMember -> GroupMember -> SMsgDirection d -> UTCTime -> IO ()
|
||||
updateMemberCIsModerated db User {userId} GroupInfo {groupId, membership} member byGroupMember md deletedTs = do
|
||||
itemIds <- updateCIs =<< getCurrentTime
|
||||
#if defined(dbPostgres)
|
||||
let inItemIds = Only $ In (map fromOnly itemIds)
|
||||
DB.execute db "DELETE FROM messages WHERE message_id IN (SELECT message_id FROM chat_item_messages WHERE chat_item_id IN ?)" inItemIds
|
||||
DB.execute db "DELETE FROM chat_item_versions WHERE chat_item_id IN ?" inItemIds
|
||||
#else
|
||||
DB.executeMany db deleteChatItemMessagesQuery itemIds
|
||||
DB.executeMany db "DELETE FROM chat_item_versions WHERE chat_item_id = ?" itemIds
|
||||
#endif
|
||||
where
|
||||
memId = groupMemberId' member
|
||||
updateQuery =
|
||||
@@ -3199,7 +3205,7 @@ getGroupCIMentions db ciId =
|
||||
SELECT r.display_name, r.member_id, m.group_member_id, m.member_role, p.display_name, p.local_alias
|
||||
FROM chat_item_mentions r
|
||||
LEFT JOIN group_members m ON r.group_id = m.group_id AND r.member_id = m.member_id
|
||||
LEFT JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
LEFT JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
WHERE r.chat_item_id = ?
|
||||
|]
|
||||
(Only ciId)
|
||||
|
||||
@@ -3254,7 +3254,7 @@ Query:
|
||||
SELECT r.display_name, r.member_id, m.group_member_id, m.member_role, p.display_name, p.local_alias
|
||||
FROM chat_item_mentions r
|
||||
LEFT JOIN group_members m ON r.group_id = m.group_id AND r.member_id = m.member_id
|
||||
LEFT JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
LEFT JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
WHERE r.chat_item_id = ?
|
||||
|
||||
Plan:
|
||||
|
||||
+12
-12
@@ -41,7 +41,7 @@ import Simplex.Chat.Types
|
||||
import Simplex.FileTransfer.Description (kb, mb)
|
||||
import Simplex.FileTransfer.Server (runXFTPServerBlocking)
|
||||
import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..), defaultFileExpiration)
|
||||
import Simplex.FileTransfer.Transport (supportedFileServerVRange)
|
||||
import Simplex.FileTransfer.Transport (alpnSupportedXFTPhandshakes, supportedFileServerVRange)
|
||||
import Simplex.Messaging.Agent (disposeAgentClient)
|
||||
import Simplex.Messaging.Agent.Env.SQLite
|
||||
import Simplex.Messaging.Agent.Protocol (currentSMPAgentVersion, duplexHandshakeSMPAgentVersion, pqdrSMPAgentVersion, supportedSMPAgentVRange)
|
||||
@@ -55,10 +55,10 @@ import Simplex.Messaging.Crypto.Ratchet (supportedE2EEncryptVRange)
|
||||
import qualified Simplex.Messaging.Crypto.Ratchet as CR
|
||||
import Simplex.Messaging.Protocol (srvHostnamesSMPClientVersion)
|
||||
import Simplex.Messaging.Server (runSMPServerBlocking)
|
||||
import Simplex.Messaging.Server.Env.STM (AServerStoreCfg (..), ServerConfig (..), ServerStoreCfg (..), StartOptions (..), StorePaths (..), defaultMessageExpiration, defaultIdleQueueInterval, defaultNtfExpiration, defaultInactiveClientExpiration)
|
||||
import Simplex.Messaging.Server.MsgStore.Types (SQSType (..), SMSType (..))
|
||||
import Simplex.Messaging.Server.Env.STM (ServerConfig (..), ServerStoreCfg (..), StartOptions (..), StorePaths (..), defaultMessageExpiration, defaultIdleQueueInterval, defaultNtfExpiration, defaultInactiveClientExpiration)
|
||||
import Simplex.Messaging.Server.MsgStore.STM (STMMsgStore)
|
||||
import Simplex.Messaging.Transport
|
||||
import Simplex.Messaging.Transport.Server (ServerCredentials (..), defaultTransportServerConfig)
|
||||
import Simplex.Messaging.Transport.Server (ServerCredentials (..), mkTransportServerConfig)
|
||||
import Simplex.Messaging.Version
|
||||
import Simplex.Messaging.Version.Internal
|
||||
import System.Directory (createDirectoryIfMissing, removeDirectoryRecursive)
|
||||
@@ -491,7 +491,7 @@ testChatCfg5 cfg p1 p2 p3 p4 p5 test = testChatN cfg testOpts [p1, p2, p3, p4, p
|
||||
concurrentlyN_ :: [IO a] -> IO ()
|
||||
concurrentlyN_ = mapConcurrently_ id
|
||||
|
||||
smpServerCfg :: ServerConfig
|
||||
smpServerCfg :: ServerConfig STMMsgStore
|
||||
smpServerCfg =
|
||||
ServerConfig
|
||||
{ transports = [(serverPort, transport @TLS, False)],
|
||||
@@ -501,7 +501,7 @@ smpServerCfg =
|
||||
maxJournalStateLines = 4,
|
||||
queueIdBytes = 24,
|
||||
msgIdBytes = 6,
|
||||
serverStoreCfg = ASSCfg SQSMemory SMSMemory $ SSCMemory Nothing,
|
||||
serverStoreCfg = SSCMemory Nothing,
|
||||
storeNtfsFile = Nothing,
|
||||
allowNewQueues = True,
|
||||
-- server password is disabled as otherwise v1 tests fail
|
||||
@@ -529,7 +529,7 @@ smpServerCfg =
|
||||
pendingENDInterval = 500000,
|
||||
ntfDeliveryInterval = 200000,
|
||||
smpServerVRange = supportedServerSMPRelayVRange,
|
||||
transportConfig = defaultTransportServerConfig,
|
||||
transportConfig = mkTransportServerConfig True (Just alpnSupportedSMPHandshakes) True,
|
||||
smpHandshakeTimeout = 1000000,
|
||||
controlPort = Nothing,
|
||||
smpAgentCfg = defaultSMPClientAgentConfig,
|
||||
@@ -539,13 +539,13 @@ smpServerCfg =
|
||||
startOptions = StartOptions {maintenance = False, compactLog = False, logLevel = LogError, skipWarnings = False, confirmMigrations = MCYesUp}
|
||||
}
|
||||
|
||||
persistentServerStoreCfg :: FilePath -> AServerStoreCfg
|
||||
persistentServerStoreCfg tmp = ASSCfg SQSMemory SMSMemory $ SSCMemory $ Just StorePaths {storeLogFile = tmp <> "/smp-server-store.log", storeMsgsFile = Just $ tmp <> "/smp-server-messages.log"}
|
||||
persistentServerStoreCfg :: FilePath -> ServerStoreCfg STMMsgStore
|
||||
persistentServerStoreCfg tmp = SSCMemory $ Just StorePaths {storeLogFile = tmp <> "/smp-server-store.log", storeMsgsFile = Just $ tmp <> "/smp-server-messages.log"}
|
||||
|
||||
withSmpServer :: IO () -> IO ()
|
||||
withSmpServer = withSmpServer' smpServerCfg
|
||||
|
||||
withSmpServer' :: ServerConfig -> IO a -> IO a
|
||||
withSmpServer' :: ServerConfig STMMsgStore -> IO a -> IO a
|
||||
withSmpServer' cfg = serverBracket (\started -> runSMPServerBlocking started cfg Nothing)
|
||||
|
||||
xftpTestPort :: ServiceName
|
||||
@@ -582,7 +582,7 @@ xftpServerConfig =
|
||||
serverStatsLogFile = "tests/tmp/xftp-server-stats.daily.log",
|
||||
serverStatsBackupFile = Nothing,
|
||||
controlPort = Nothing,
|
||||
transportConfig = defaultTransportServerConfig,
|
||||
transportConfig = mkTransportServerConfig True (Just alpnSupportedXFTPhandshakes) False,
|
||||
responseDelay = 0
|
||||
}
|
||||
|
||||
@@ -594,7 +594,7 @@ withXFTPServer' cfg =
|
||||
serverBracket
|
||||
( \started -> do
|
||||
createDirectoryIfMissing False xftpServerFiles
|
||||
runXFTPServerBlocking started cfg Nothing
|
||||
runXFTPServerBlocking started cfg
|
||||
)
|
||||
|
||||
serverBracket :: (TMVar Bool -> IO ()) -> IO a -> IO a
|
||||
|
||||
@@ -84,7 +84,10 @@ chatGroupTests = do
|
||||
describe "batch send messages" $ do
|
||||
it "send multiple messages api" testSendMulti
|
||||
it "send multiple timed messages" testSendMultiTimed
|
||||
#if !defined(dbPostgres)
|
||||
-- TODO [postgres] this test hangs with PostgreSQL
|
||||
it "send multiple messages (many chat batches)" testSendMultiManyBatches
|
||||
#endif
|
||||
xit'' "shared message body is reused" testSharedMessageBody
|
||||
xit'' "shared batch body is reused" testSharedBatchBody
|
||||
describe "async group connections" $ do
|
||||
@@ -1826,7 +1829,7 @@ testDeleteMemberWithMessages =
|
||||
do
|
||||
cath <## "alice updated group #team:"
|
||||
cath <## "updated group preferences:"
|
||||
cath <## "Full deletion: on"
|
||||
cath <## "Full deletion: on"
|
||||
]
|
||||
threadDelay 750000
|
||||
bob #> "#team hello"
|
||||
@@ -6792,7 +6795,7 @@ testForwardQuoteMention =
|
||||
bob <## " hello @alice @cath",
|
||||
do
|
||||
cath <# "#team alice!> -> forwarded"
|
||||
cath <## " hello @alice @cath"
|
||||
cath <## " hello @alice @cath"
|
||||
]
|
||||
-- forward mentions
|
||||
alice `send` "@bob <- #team hello"
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
<img class="h-[40px] w-auto block dark:hidden" src="/img/privacy-guides-light.png" alt="Privacy Guides">
|
||||
<img class="h-[40px] w-auto hidden dark:block" src="/img/privacy-guides-dark.png" alt="Privacy Guides">
|
||||
</a>
|
||||
<a class="block rounded overflow-hidden" title="Whonix messenger recommendations" href="https://www.whonix.org/wiki/Chat#Recommendation" target="_blank">
|
||||
<img class="h-[24px] mb-[8px] w-auto block dark:hidden" src="/img/whonix-light.png" alt="Whonix">
|
||||
<img class="h-[24px] mb-[8px] w-auto hidden dark:block" src="/img/whonix-dark.png" alt="Whonix">
|
||||
</a>
|
||||
<a class="block rounded overflow-hidden" title="publication" href="https://www.heise.de/suche/?q=simplex+chat&sort_by=date&rm=search" target="_blank">
|
||||
<img class="h-[40px] w-auto block dark:hidden" src="/img/heise-light.png" alt="Heise Online">
|
||||
<img class="h-[40px] w-auto hidden dark:block" src="/img/heise-dark.png" alt="Heise Online">
|
||||
@@ -43,7 +47,7 @@
|
||||
<img class="h-[40px] w-auto" src="/img/optout.jpg" alt="Opt Out Podcast">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<p class="text-black dark:text-white hidden md:block text-center xl:text-left xl:rtl:text-right text-[16px] leading-[26px] mb-[11px] md:mt-6">{{ "get-simplex" | i18n({}, lang ) | safe }}</p>
|
||||
<div class="socials flex items-center justify-center xl:justify-start gap-4 flex-wrap mt-[30px]">
|
||||
@@ -66,7 +70,7 @@
|
||||
<video class="absolute rounded-lg top-10 w-[235px] ml-[-6px] mt-1" controls>
|
||||
<source src="/video/connect.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
|
||||
{# <div class="absolute flex flex-col items-center gap-3">
|
||||
<img src="/img/new/play-btn.svg" alt="" />
|
||||
<p class="text-center text-[16px] leading-[24px] tracking-[0.04em] text-white">PLAY</p>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
Reference in New Issue
Block a user