mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a121b4442 | |||
| 1bb7a954f5 | |||
| ddd8bd9061 | |||
| 179b9e093f | |||
| 352a4f3d2a | |||
| e06d4e5c85 | |||
| 385ebd2298 |
@@ -108,7 +108,8 @@ jobs:
|
||||
echo "::set-output name=bin_path::$(cabal list-bin simplex-chat)"
|
||||
|
||||
- name: Unix test
|
||||
if: matrix.os != 'windows-latest'
|
||||
if: matrix.os != 'windows-latest' && matrix.os != 'ubuntu-20.04'
|
||||
timeout-minutes: 10
|
||||
shell: bash
|
||||
run: cabal test --test-show-details=direct
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ struct ChatListNavLink: View {
|
||||
ChatPreviewView(chat: chat)
|
||||
.frame(height: rowHeights[dynamicTypeSize])
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
|
||||
joinGroupButton(groupInfo.hostConnCustomUserProfileId)
|
||||
joinGroupButton()
|
||||
if groupInfo.canDelete {
|
||||
deleteGroupChatButton(groupInfo)
|
||||
}
|
||||
@@ -137,7 +137,7 @@ struct ChatListNavLink: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func joinGroupButton(_ hostConnCustomUserProfileId: Int64?) -> some View {
|
||||
private func joinGroupButton() -> some View {
|
||||
Button {
|
||||
joinGroup(chat.chatInfo.apiId)
|
||||
} label: {
|
||||
|
||||
@@ -253,6 +253,11 @@
|
||||
<target>Inkognito akzeptieren</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept requests" xml:space="preserve">
|
||||
<source>Accept requests</source>
|
||||
<target>***Accept requests</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Advanced network settings" xml:space="preserve">
|
||||
<source>Advanced network settings</source>
|
||||
<target>Erweiterte Netzwerkeinstellungen</target>
|
||||
@@ -318,6 +323,11 @@
|
||||
<target>Bilder automatisch akzeptieren</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Automatically" xml:space="preserve">
|
||||
<source>Automatically</source>
|
||||
<target>***Automatically</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Call already ended!" xml:space="preserve">
|
||||
<source>Call already ended!</source>
|
||||
<target>Anruf ist bereits beendet!</target>
|
||||
@@ -568,6 +578,11 @@
|
||||
<target>Kontaktname</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Contact requests" xml:space="preserve">
|
||||
<source>Contact requests</source>
|
||||
<target>***Contact requests</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy" xml:space="preserve">
|
||||
<source>Copy</source>
|
||||
<target>Kopieren</target>
|
||||
@@ -2532,6 +2547,11 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
|
||||
<target>Willkommen %@!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Welcome message" xml:space="preserve">
|
||||
<source>Welcome message</source>
|
||||
<target>***Welcome message</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="When available" xml:space="preserve">
|
||||
<source>When available</source>
|
||||
<target>Wenn verfügbar</target>
|
||||
|
||||
@@ -253,6 +253,11 @@
|
||||
<target>Accept incognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept requests" xml:space="preserve">
|
||||
<source>Accept requests</source>
|
||||
<target>Accept requests</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Advanced network settings" xml:space="preserve">
|
||||
<source>Advanced network settings</source>
|
||||
<target>Advanced network settings</target>
|
||||
@@ -318,6 +323,11 @@
|
||||
<target>Auto-accept images</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Automatically" xml:space="preserve">
|
||||
<source>Automatically</source>
|
||||
<target>Automatically</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Call already ended!" xml:space="preserve">
|
||||
<source>Call already ended!</source>
|
||||
<target>Call already ended!</target>
|
||||
@@ -568,6 +578,11 @@
|
||||
<target>Contact name</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Contact requests" xml:space="preserve">
|
||||
<source>Contact requests</source>
|
||||
<target>Contact requests</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy" xml:space="preserve">
|
||||
<source>Copy</source>
|
||||
<target>Copy</target>
|
||||
@@ -2532,6 +2547,11 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Welcome %@!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Welcome message" xml:space="preserve">
|
||||
<source>Welcome message</source>
|
||||
<target>Welcome message</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="When available" xml:space="preserve">
|
||||
<source>When available</source>
|
||||
<target>When available</target>
|
||||
|
||||
@@ -253,6 +253,11 @@
|
||||
<target>Принять инкогнито</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept requests" xml:space="preserve">
|
||||
<source>Accept requests</source>
|
||||
<target>Принимать запросы</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Advanced network settings" xml:space="preserve">
|
||||
<source>Advanced network settings</source>
|
||||
<target>Настройки сети</target>
|
||||
@@ -318,6 +323,11 @@
|
||||
<target>Автоприем изображений</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Automatically" xml:space="preserve">
|
||||
<source>Automatically</source>
|
||||
<target>Автоматически</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Call already ended!" xml:space="preserve">
|
||||
<source>Call already ended!</source>
|
||||
<target>Звонок уже завершен!</target>
|
||||
@@ -568,6 +578,11 @@
|
||||
<target>Имена контактов</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Contact requests" xml:space="preserve">
|
||||
<source>Contact requests</source>
|
||||
<target>Запросы контактов</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy" xml:space="preserve">
|
||||
<source>Copy</source>
|
||||
<target>Скопировать</target>
|
||||
@@ -2532,6 +2547,11 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Здравствуйте %@!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Welcome message" xml:space="preserve">
|
||||
<source>Welcome message</source>
|
||||
<target>Приветственное сообщение</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="When available" xml:space="preserve">
|
||||
<source>When available</source>
|
||||
<target>Когда возможно</target>
|
||||
|
||||
@@ -89,11 +89,6 @@
|
||||
5CC1C99527A6CF7F000D9FF6 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */; };
|
||||
5CC2C0FC2809BF11000C35E3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FA2809BF11000C35E3 /* Localizable.strings */; };
|
||||
5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */; };
|
||||
5CCA7DED2901E32900C8FEBA /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCA7DE82901E32800C8FEBA /* libgmp.a */; };
|
||||
5CCA7DEE2901E32900C8FEBA /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCA7DE92901E32800C8FEBA /* libffi.a */; };
|
||||
5CCA7DEF2901E32900C8FEBA /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCA7DEA2901E32800C8FEBA /* libgmpxx.a */; };
|
||||
5CCA7DF02901E32900C8FEBA /* libHSsimplex-chat-4.1.1-YMBxN0i6bGIdLfYB8ndX-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCA7DEB2901E32800C8FEBA /* libHSsimplex-chat-4.1.1-YMBxN0i6bGIdLfYB8ndX-ghc8.10.7.a */; };
|
||||
5CCA7DF12901E32900C8FEBA /* libHSsimplex-chat-4.1.1-YMBxN0i6bGIdLfYB8ndX.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCA7DEC2901E32800C8FEBA /* libHSsimplex-chat-4.1.1-YMBxN0i6bGIdLfYB8ndX.a */; };
|
||||
5CCA7DF32905735700C8FEBA /* AcceptRequestsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCA7DF22905735700C8FEBA /* AcceptRequestsView.swift */; };
|
||||
5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; };
|
||||
5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */; };
|
||||
@@ -125,6 +120,11 @@
|
||||
5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; };
|
||||
5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; };
|
||||
640F50E327CF991C001E05C2 /* SMPServers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640F50E227CF991C001E05C2 /* SMPServers.swift */; };
|
||||
6432855F290BEE2B00FBE5C8 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6432855A290BEE2B00FBE5C8 /* libffi.a */; };
|
||||
64328560290BEE2B00FBE5C8 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6432855B290BEE2B00FBE5C8 /* libgmp.a */; };
|
||||
64328561290BEE2B00FBE5C8 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6432855C290BEE2B00FBE5C8 /* libgmpxx.a */; };
|
||||
64328562290BEE2B00FBE5C8 /* libHSsimplex-chat-4.2.0-LeYwSLlznBGLTzzJTly7Iu-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6432855D290BEE2B00FBE5C8 /* libHSsimplex-chat-4.2.0-LeYwSLlznBGLTzzJTly7Iu-ghc8.10.7.a */; };
|
||||
64328563290BEE2B00FBE5C8 /* libHSsimplex-chat-4.2.0-LeYwSLlznBGLTzzJTly7Iu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6432855E290BEE2B00FBE5C8 /* libHSsimplex-chat-4.2.0-LeYwSLlznBGLTzzJTly7Iu.a */; };
|
||||
6440CA00288857A10062C672 /* CIGroupEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440C9FF288857A10062C672 /* CIGroupEventView.swift */; };
|
||||
6440CA03288AECA70062C672 /* AddGroupMembersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */; };
|
||||
6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6442E0B9287F169300CEC0F9 /* AddGroupView.swift */; };
|
||||
@@ -290,11 +290,6 @@
|
||||
5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheet.swift; sourceTree = "<group>"; };
|
||||
5CC2C0FB2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5CC2C0FE2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = "ru.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5CCA7DE82901E32800C8FEBA /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5CCA7DE92901E32800C8FEBA /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5CCA7DEA2901E32800C8FEBA /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5CCA7DEB2901E32800C8FEBA /* libHSsimplex-chat-4.1.1-YMBxN0i6bGIdLfYB8ndX-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.1.1-YMBxN0i6bGIdLfYB8ndX-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
5CCA7DEC2901E32800C8FEBA /* libHSsimplex-chat-4.1.1-YMBxN0i6bGIdLfYB8ndX.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.1.1-YMBxN0i6bGIdLfYB8ndX.a"; sourceTree = "<group>"; };
|
||||
5CCA7DF22905735700C8FEBA /* AcceptRequestsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AcceptRequestsView.swift; sourceTree = "<group>"; };
|
||||
5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = "<group>"; };
|
||||
5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanToConnectView.swift; sourceTree = "<group>"; };
|
||||
@@ -324,6 +319,11 @@
|
||||
5CFA59CF286477B400863A68 /* ChatArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatArchiveView.swift; sourceTree = "<group>"; };
|
||||
5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ZoomableScrollView.swift; path = Shared/Views/ZoomableScrollView.swift; sourceTree = SOURCE_ROOT; };
|
||||
640F50E227CF991C001E05C2 /* SMPServers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMPServers.swift; sourceTree = "<group>"; };
|
||||
6432855A290BEE2B00FBE5C8 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
6432855B290BEE2B00FBE5C8 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
6432855C290BEE2B00FBE5C8 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
6432855D290BEE2B00FBE5C8 /* libHSsimplex-chat-4.2.0-LeYwSLlznBGLTzzJTly7Iu-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.2.0-LeYwSLlznBGLTzzJTly7Iu-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
6432855E290BEE2B00FBE5C8 /* libHSsimplex-chat-4.2.0-LeYwSLlznBGLTzzJTly7Iu.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.2.0-LeYwSLlznBGLTzzJTly7Iu.a"; sourceTree = "<group>"; };
|
||||
6440C9FF288857A10062C672 /* CIGroupEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIGroupEventView.swift; sourceTree = "<group>"; };
|
||||
6440CA02288AECA70062C672 /* AddGroupMembersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupMembersView.swift; sourceTree = "<group>"; };
|
||||
6442E0B9287F169300CEC0F9 /* AddGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupView.swift; sourceTree = "<group>"; };
|
||||
@@ -375,12 +375,12 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||
5CCA7DF02901E32900C8FEBA /* libHSsimplex-chat-4.1.1-YMBxN0i6bGIdLfYB8ndX-ghc8.10.7.a in Frameworks */,
|
||||
5CCA7DEF2901E32900C8FEBA /* libgmpxx.a in Frameworks */,
|
||||
5CCA7DED2901E32900C8FEBA /* libgmp.a in Frameworks */,
|
||||
64328561290BEE2B00FBE5C8 /* libgmpxx.a in Frameworks */,
|
||||
64328562290BEE2B00FBE5C8 /* libHSsimplex-chat-4.2.0-LeYwSLlznBGLTzzJTly7Iu-ghc8.10.7.a in Frameworks */,
|
||||
6432855F290BEE2B00FBE5C8 /* libffi.a in Frameworks */,
|
||||
64328560290BEE2B00FBE5C8 /* libgmp.a in Frameworks */,
|
||||
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
||||
5CCA7DF12901E32900C8FEBA /* libHSsimplex-chat-4.1.1-YMBxN0i6bGIdLfYB8ndX.a in Frameworks */,
|
||||
5CCA7DEE2901E32900C8FEBA /* libffi.a in Frameworks */,
|
||||
64328563290BEE2B00FBE5C8 /* libHSsimplex-chat-4.2.0-LeYwSLlznBGLTzzJTly7Iu.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -435,11 +435,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CCA7DE92901E32800C8FEBA /* libffi.a */,
|
||||
5CCA7DE82901E32800C8FEBA /* libgmp.a */,
|
||||
5CCA7DEA2901E32800C8FEBA /* libgmpxx.a */,
|
||||
5CCA7DEB2901E32800C8FEBA /* libHSsimplex-chat-4.1.1-YMBxN0i6bGIdLfYB8ndX-ghc8.10.7.a */,
|
||||
5CCA7DEC2901E32800C8FEBA /* libHSsimplex-chat-4.1.1-YMBxN0i6bGIdLfYB8ndX.a */,
|
||||
6432855A290BEE2B00FBE5C8 /* libffi.a */,
|
||||
6432855B290BEE2B00FBE5C8 /* libgmp.a */,
|
||||
6432855C290BEE2B00FBE5C8 /* libgmpxx.a */,
|
||||
6432855D290BEE2B00FBE5C8 /* libHSsimplex-chat-4.2.0-LeYwSLlznBGLTzzJTly7Iu-ghc8.10.7.a */,
|
||||
6432855E290BEE2B00FBE5C8 /* libHSsimplex-chat-4.2.0-LeYwSLlznBGLTzzJTly7Iu.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
|
||||
@@ -161,6 +161,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Accept incognito" = "Inkognito akzeptieren";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Accept requests" = "***Accept requests";
|
||||
|
||||
/* call status */
|
||||
"accepted call" = "Anruf angenommen";
|
||||
|
||||
@@ -209,6 +212,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Auto-accept images" = "Bilder automatisch akzeptieren";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Automatically" = "***Automatically";
|
||||
|
||||
/* integrity error chat item */
|
||||
"bad message hash" = "Ungültiger Nachrichten-Hash";
|
||||
|
||||
@@ -428,6 +434,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Contact name" = "Kontaktname";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Contact requests" = "***Contact requests";
|
||||
|
||||
/* chat item action */
|
||||
"Copy" = "Kopieren";
|
||||
|
||||
@@ -1757,6 +1766,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Welcome %@!" = "Willkommen %@!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Welcome message" = "***Welcome message";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"When available" = "Wenn verfügbar";
|
||||
|
||||
|
||||
@@ -161,6 +161,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Accept incognito" = "Принять инкогнито";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Accept requests" = "Принимать запросы";
|
||||
|
||||
/* call status */
|
||||
"accepted call" = " принятый звонок";
|
||||
|
||||
@@ -209,6 +212,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Auto-accept images" = "Автоприем изображений";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Automatically" = "Автоматически";
|
||||
|
||||
/* integrity error chat item */
|
||||
"bad message hash" = "ошибка хэш сообщения";
|
||||
|
||||
@@ -428,6 +434,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Contact name" = "Имена контактов";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Contact requests" = "Запросы контактов";
|
||||
|
||||
/* chat item action */
|
||||
"Copy" = "Скопировать";
|
||||
|
||||
@@ -1757,6 +1766,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Welcome %@!" = "Здравствуйте %@!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Welcome message" = "Приветственное сообщение";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"When available" = "Когда возможно";
|
||||
|
||||
|
||||
+10
-2
@@ -493,7 +493,7 @@ processChatCommand = \case
|
||||
-- functions below are called in separate transactions to prevent crashes on android
|
||||
-- (possibly, race condition on integrity check?)
|
||||
withStore' $ \db -> deleteContactConnectionsAndFiles db userId ct
|
||||
withStore' $ \db -> deleteContact db userId ct
|
||||
withStore' $ \db -> deleteContact db user ct
|
||||
unsetActive $ ActiveC localDisplayName
|
||||
pure $ CRContactDeleted ct
|
||||
CTContactConnection -> withChatLock "deleteChat contactConnection" . procCmd $ do
|
||||
@@ -537,6 +537,8 @@ processChatCommand = \case
|
||||
maxItemTs_ <- withStore' $ \db -> getGroupMaxItemTs db user gInfo
|
||||
forM_ filesInfo $ \fileInfo -> deleteFile user fileInfo
|
||||
withStore' $ \db -> deleteGroupCIs db user gInfo
|
||||
membersToDelete <- withStore' $ \db -> getGroupMembersForExpiration db user gInfo
|
||||
forM_ membersToDelete $ \m -> withStore' $ \db -> deleteGroupMember db user m
|
||||
gInfo' <- case maxItemTs_ of
|
||||
Just ts -> do
|
||||
withStore' $ \db -> updateGroupTs db user gInfo ts
|
||||
@@ -905,7 +907,10 @@ processChatCommand = \case
|
||||
ci <- saveSndChatItem user (CDGroupSnd gInfo) msg (CISndGroupEvent $ SGEMemberDeleted memberId (fromLocalProfile memberProfile)) Nothing Nothing
|
||||
toView . CRNewChatItem $ AChatItem SCTGroup SMDSnd (GroupChat gInfo) ci
|
||||
deleteMemberConnection user m
|
||||
withStore' $ \db -> updateGroupMemberStatus db userId m GSMemRemoved
|
||||
withStore' $ \db ->
|
||||
checkGroupMemberHasItems db user m >>= \case
|
||||
Just _ -> updateGroupMemberStatus db userId m GSMemRemoved
|
||||
Nothing -> deleteGroupMember db user m
|
||||
pure $ CRUserDeletedMember gInfo m {memberStatus = GSMemRemoved}
|
||||
APILeaveGroup groupId -> withUser $ \user@User {userId} -> do
|
||||
Group gInfo@GroupInfo {membership} members <- withStore $ \db -> getGroup db user groupId
|
||||
@@ -1536,6 +1541,8 @@ expireChatItems user ttl sync = do
|
||||
maxItemTs_ <- withStore' $ \db -> getGroupMaxItemTs db user gInfo
|
||||
forM_ filesInfo $ \fileInfo -> deleteFile user fileInfo
|
||||
withStore' $ \db -> deleteGroupExpiredCIs db user gInfo expirationDate createdAtCutoff
|
||||
membersToDelete <- withStore' $ \db -> getGroupMembersForExpiration db user gInfo
|
||||
forM_ membersToDelete $ \m -> withStore' $ \db -> deleteGroupMember db user m
|
||||
withStore' $ \db -> do
|
||||
ciCount_ <- getGroupCICount db user gInfo
|
||||
case (maxItemTs_, ciCount_) of
|
||||
@@ -1703,6 +1710,7 @@ processAgentMessage (Just user@User {userId, profile}) corrId agentConnId agentM
|
||||
gVar <- asks idsDrg
|
||||
groupConnIds <- createAgentConnectionAsync user CFCreateConnGrpInv True SCMInvitation
|
||||
withStore $ \db -> createNewContactMemberAsync db gVar user groupId ct GRMember groupConnIds
|
||||
probeMatchingContacts ct $ contactConnIncognito ct
|
||||
_ -> pure ()
|
||||
Just (gInfo@GroupInfo {membership}, m@GroupMember {activeConn}) -> do
|
||||
when (maybe False ((== ConnReady) . connStatus) activeConn) $ do
|
||||
|
||||
@@ -41,7 +41,7 @@ CREATE TABLE display_names (
|
||||
|
||||
CREATE TABLE contacts (
|
||||
contact_id INTEGER PRIMARY KEY,
|
||||
contact_profile_id INTEGER REFERENCES contact_profiles ON DELETE SET NULL, -- NULL if it's an incognito profile
|
||||
contact_profile_id INTEGER REFERENCES contact_profiles ON DELETE SET NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
local_display_name TEXT NOT NULL,
|
||||
is_user INTEGER NOT NULL DEFAULT 0, -- 1 if this contact is a user
|
||||
@@ -223,7 +223,7 @@ CREATE TABLE contact_requests (
|
||||
ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
agent_invitation_id BLOB NOT NULL,
|
||||
contact_profile_id INTEGER REFERENCES contact_profiles
|
||||
ON DELETE SET NULL -- NULL if it's an incognito profile
|
||||
ON DELETE SET NULL
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
local_display_name TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
|
||||
@@ -47,10 +47,10 @@ CREATE TABLE display_names(
|
||||
) WITHOUT ROWID;
|
||||
CREATE TABLE contacts(
|
||||
contact_id INTEGER PRIMARY KEY,
|
||||
contact_profile_id INTEGER REFERENCES contact_profiles ON DELETE SET NULL, -- NULL if it's an incognito profile
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
local_display_name TEXT NOT NULL,
|
||||
is_user INTEGER NOT NULL DEFAULT 0, -- 1 if this contact is a user
|
||||
contact_profile_id INTEGER REFERENCES contact_profiles ON DELETE SET NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
local_display_name TEXT NOT NULL,
|
||||
is_user INTEGER NOT NULL DEFAULT 0, -- 1 if this contact is a user
|
||||
via_group INTEGER REFERENCES groups(group_id) ON DELETE SET NULL,
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
@@ -278,18 +278,20 @@ CREATE TABLE contact_requests(
|
||||
ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
agent_invitation_id BLOB NOT NULL,
|
||||
contact_profile_id INTEGER REFERENCES contact_profiles
|
||||
ON DELETE SET NULL -- NULL if it's an incognito profile
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
local_display_name TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, updated_at TEXT CHECK(updated_at NOT NULL), xcontact_id BLOB,
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
UNIQUE(user_id, local_display_name),
|
||||
UNIQUE(user_id, contact_profile_id)
|
||||
ON DELETE SET NULL
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
local_display_name TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
xcontact_id BLOB,
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
UNIQUE(user_id, local_display_name),
|
||||
UNIQUE(user_id, contact_profile_id)
|
||||
);
|
||||
CREATE TABLE messages(
|
||||
message_id INTEGER PRIMARY KEY,
|
||||
|
||||
+76
-14
@@ -83,6 +83,7 @@ module Simplex.Chat.Store
|
||||
getGroupInfoByName,
|
||||
getGroupMember,
|
||||
getGroupMembers,
|
||||
getGroupMembersForExpiration,
|
||||
deleteGroupConnectionsAndFiles,
|
||||
deleteGroupItemsAndMembers,
|
||||
deleteGroup,
|
||||
@@ -98,6 +99,7 @@ module Simplex.Chat.Store
|
||||
updateGroupMemberStatus,
|
||||
updateGroupMemberStatusById,
|
||||
createNewGroupMember,
|
||||
checkGroupMemberHasItems,
|
||||
deleteGroupMember,
|
||||
deleteGroupMemberConnection,
|
||||
updateGroupMemberRole,
|
||||
@@ -558,8 +560,8 @@ deleteContactConnectionsAndFiles db userId Contact {contactId} = do
|
||||
(userId, contactId)
|
||||
DB.execute db "DELETE FROM files WHERE user_id = ? AND contact_id = ?" (userId, contactId)
|
||||
|
||||
deleteContact :: DB.Connection -> UserId -> Contact -> IO ()
|
||||
deleteContact db userId Contact {contactId, localDisplayName} = do
|
||||
deleteContact :: DB.Connection -> User -> Contact -> IO ()
|
||||
deleteContact db user@User {userId} Contact {contactId, localDisplayName, activeConn = Connection {customUserProfileId}} = do
|
||||
DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND contact_id = ?" (userId, contactId)
|
||||
ctMember :: (Maybe ContactId) <- maybeFirstRow fromOnly $ DB.query db "SELECT contact_id FROM group_members WHERE user_id = ? AND contact_id = ? LIMIT 1" (userId, contactId)
|
||||
if isNothing ctMember
|
||||
@@ -570,6 +572,25 @@ deleteContact db userId Contact {contactId, localDisplayName} = do
|
||||
currentTs <- getCurrentTime
|
||||
DB.execute db "UPDATE group_members SET contact_id = NULL, updated_at = ? WHERE user_id = ? AND contact_id = ?" (currentTs, userId, contactId)
|
||||
DB.execute db "DELETE FROM contacts WHERE user_id = ? AND contact_id = ?" (userId, contactId)
|
||||
forM_ customUserProfileId $ \profileId -> deleteUnusedIncognitoProfileById_ db user profileId
|
||||
|
||||
deleteUnusedIncognitoProfileById_ :: DB.Connection -> User -> ProfileId -> IO ()
|
||||
deleteUnusedIncognitoProfileById_ db User {userId} profile_id =
|
||||
DB.executeNamed
|
||||
db
|
||||
[sql|
|
||||
DELETE FROM contact_profiles
|
||||
WHERE user_id = :user_id AND contact_profile_id = :profile_id AND incognito = 1
|
||||
AND 1 NOT IN (
|
||||
SELECT 1 FROM connections
|
||||
WHERE user_id = :user_id AND custom_user_profile_id = :profile_id LIMIT 1
|
||||
)
|
||||
AND 1 NOT IN (
|
||||
SELECT 1 FROM group_members
|
||||
WHERE user_id = :user_id AND member_profile_id = :profile_id LIMIT 1
|
||||
)
|
||||
|]
|
||||
[":user_id" := userId, ":profile_id" := profile_id]
|
||||
|
||||
deleteContactProfile_ :: DB.Connection -> UserId -> ContactId -> IO ()
|
||||
deleteContactProfile_ db userId contactId =
|
||||
@@ -1324,6 +1345,10 @@ mergeContactRecords db userId Contact {contactId = toContactId} Contact {contact
|
||||
db
|
||||
"UPDATE group_members SET invited_by = ?, updated_at = ? WHERE invited_by = ? AND user_id = ?"
|
||||
(toContactId, currentTs, fromContactId, userId)
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE chat_items SET contact_id = ?, updated_at = ? WHERE contact_id = ? AND user_id = ?"
|
||||
(toContactId, currentTs, fromContactId, userId)
|
||||
DB.executeNamed
|
||||
db
|
||||
[sql|
|
||||
@@ -1676,9 +1701,7 @@ deleteGroupItemsAndMembers :: DB.Connection -> User -> GroupInfo -> [GroupMember
|
||||
deleteGroupItemsAndMembers db user@User {userId} GroupInfo {groupId} members = do
|
||||
DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND group_id = ?" (userId, groupId)
|
||||
DB.execute db "DELETE FROM group_members WHERE user_id = ? AND group_id = ?" (userId, groupId)
|
||||
forM_ members $ \m@GroupMember {groupMemberId, memberContactId, memberContactProfileId} -> unless (isJust memberContactId) $ do
|
||||
sameProfileMember :: (Maybe GroupMemberId) <- maybeFirstRow fromOnly $ DB.query db "SELECT group_member_id FROM group_members WHERE user_id = ? AND contact_profile_id = ? AND group_member_id != ? LIMIT 1" (userId, memberContactProfileId, groupMemberId)
|
||||
unless (isJust sameProfileMember) $ deleteMemberProfileAndName_ db user m
|
||||
forM_ members $ \m -> cleanupMemberContactAndProfile_ db user m
|
||||
|
||||
deleteGroup :: DB.Connection -> User -> GroupInfo -> IO ()
|
||||
deleteGroup db User {userId} GroupInfo {groupId, localDisplayName} = do
|
||||
@@ -1779,6 +1802,32 @@ getGroupMembers db user@User {userId, userContactId} GroupInfo {groupId} = do
|
||||
|]
|
||||
(groupId, userId, userContactId)
|
||||
|
||||
getGroupMembersForExpiration :: DB.Connection -> User -> GroupInfo -> IO [GroupMember]
|
||||
getGroupMembersForExpiration db user@User {userId, userContactId} GroupInfo {groupId} = do
|
||||
map (toContactMember user)
|
||||
<$> DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
m.group_member_id, m.group_id, m.member_id, m.member_role, m.member_category, m.member_status,
|
||||
m.invited_by, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.local_alias,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.custom_user_profile_id,
|
||||
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
||||
FROM group_members m
|
||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
LEFT JOIN connections c ON c.connection_id = (
|
||||
SELECT max(cc.connection_id)
|
||||
FROM connections cc
|
||||
where cc.group_member_id = m.group_member_id
|
||||
)
|
||||
WHERE m.group_id = ? AND m.user_id = ? AND (m.contact_id IS NULL OR m.contact_id != ?)
|
||||
AND m.member_status IN (?, ?, ?)
|
||||
AND m.group_member_id NOT IN (
|
||||
SELECT DISTINCT group_member_id FROM chat_items
|
||||
)
|
||||
|]
|
||||
(groupId, userId, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted)
|
||||
|
||||
toContactMember :: User -> (GroupMemberRow :. MaybeConnectionRow) -> GroupMember
|
||||
toContactMember User {userContactId} (memberRow :. connRow) =
|
||||
(toGroupMember userContactId memberRow) {activeConn = toMaybeConnection connRow}
|
||||
@@ -1986,19 +2035,32 @@ createNewMember_
|
||||
groupMemberId <- insertedRowId db
|
||||
pure GroupMember {groupMemberId, groupId, memberId, memberRole, memberCategory, memberStatus, invitedBy, localDisplayName, memberProfile = toLocalProfile memberContactProfileId memberProfile "", memberContactId, memberContactProfileId, activeConn}
|
||||
|
||||
checkGroupMemberHasItems :: DB.Connection -> User -> GroupMember -> IO (Maybe ChatItemId)
|
||||
checkGroupMemberHasItems db User {userId} GroupMember {groupMemberId, groupId} =
|
||||
maybeFirstRow fromOnly $ DB.query db "SELECT chat_item_id FROM chat_items WHERE user_id = ? AND group_id = ? AND group_member_id = ? LIMIT 1" (userId, groupId, groupMemberId)
|
||||
|
||||
deleteGroupMember :: DB.Connection -> User -> GroupMember -> IO ()
|
||||
deleteGroupMember db user@User {userId} m@GroupMember {groupMemberId, groupId, memberContactId, memberContactProfileId} = do
|
||||
deleteGroupMember db user@User {userId} m@GroupMember {groupMemberId, groupId} = do
|
||||
deleteGroupMemberConnection db user m
|
||||
DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND group_id = ? AND group_member_id = ?" (userId, groupId, groupMemberId)
|
||||
DB.execute db "DELETE FROM group_members WHERE user_id = ? AND group_member_id = ?" (userId, groupMemberId)
|
||||
unless (isJust memberContactId) $ do
|
||||
sameProfileMember :: (Maybe GroupMemberId) <- maybeFirstRow fromOnly $ DB.query db "SELECT group_member_id FROM group_members WHERE user_id = ? AND contact_profile_id = ? AND group_member_id != ? LIMIT 1" (userId, memberContactProfileId, groupMemberId)
|
||||
unless (isJust sameProfileMember) $ deleteMemberProfileAndName_ db user m
|
||||
cleanupMemberContactAndProfile_ db user m
|
||||
|
||||
deleteMemberProfileAndName_ :: DB.Connection -> User -> GroupMember -> IO ()
|
||||
deleteMemberProfileAndName_ db User {userId} GroupMember {memberContactProfileId, localDisplayName} = do
|
||||
DB.execute db "DELETE FROM contact_profiles WHERE user_id = ? AND contact_profile_id = ?" (userId, memberContactProfileId)
|
||||
DB.execute db "DELETE FROM display_names WHERE user_id = ? AND local_display_name = ?" (userId, localDisplayName)
|
||||
-- it's important this function is used in transaction after the actual group_members record is deleted, see checkIncognitoProfileInUse_
|
||||
cleanupMemberContactAndProfile_ :: DB.Connection -> User -> GroupMember -> IO ()
|
||||
cleanupMemberContactAndProfile_ db user@User {userId} m@GroupMember {groupMemberId, localDisplayName, memberContactId, memberContactProfileId, memberProfile = LocalProfile {profileId}} =
|
||||
case memberContactId of
|
||||
Just contactId ->
|
||||
runExceptT (getContact db userId contactId) >>= \case
|
||||
Right ct@Contact {activeConn = Connection {connLevel, viaGroupLink}, contactUsed} ->
|
||||
unless ((connLevel == 0 && not viaGroupLink) || contactUsed) $ deleteContact db user ct
|
||||
_ -> pure ()
|
||||
Nothing -> do
|
||||
sameProfileMember :: (Maybe GroupMemberId) <- maybeFirstRow fromOnly $ DB.query db "SELECT group_member_id FROM group_members WHERE user_id = ? AND contact_profile_id = ? AND group_member_id != ? LIMIT 1" (userId, memberContactProfileId, groupMemberId)
|
||||
unless (isJust sameProfileMember) $ do
|
||||
DB.execute db "DELETE FROM contact_profiles WHERE user_id = ? AND contact_profile_id = ?" (userId, memberContactProfileId)
|
||||
DB.execute db "DELETE FROM display_names WHERE user_id = ? AND local_display_name = ?" (userId, localDisplayName)
|
||||
when (memberIncognito m) $ deleteUnusedIncognitoProfileById_ db user profileId
|
||||
|
||||
deleteGroupMemberConnection :: DB.Connection -> User -> GroupMember -> IO ()
|
||||
deleteGroupMemberConnection db User {userId} GroupMember {groupMemberId} =
|
||||
@@ -3122,7 +3184,7 @@ getDirectChatPreviews_ db User {userId} = do
|
||||
) ChatStats ON ChatStats.contact_id = ct.contact_id
|
||||
LEFT JOIN chat_items ri ON i.quoted_shared_msg_id = ri.shared_msg_id
|
||||
WHERE ct.user_id = ?
|
||||
AND ((c.conn_level = 0 AND c.via_group_link = 0) OR i.chat_item_id IS NOT NULL OR ct.contact_used = 1)
|
||||
AND ((c.conn_level = 0 AND c.via_group_link = 0) OR ct.contact_used = 1)
|
||||
AND c.connection_id = (
|
||||
SELECT cc_connection_id FROM (
|
||||
SELECT
|
||||
|
||||
+62
-20
@@ -138,6 +138,7 @@ chatTests = do
|
||||
it "set chat item TTL" testSetChatItemTTL
|
||||
describe "group links" $ do
|
||||
it "create group link, join via group link" testGroupLink
|
||||
it "sending message to contact created via group link marks it used" testGroupLinkContactUsed
|
||||
it "create group link, join via group link - incognito membership" testGroupLinkIncognitoMembership
|
||||
it "deleting invited member does not leave broken chat item" testGroupLinkDeleteInvitedMemberNoBrokenItem
|
||||
|
||||
@@ -768,7 +769,13 @@ testGroupDelete =
|
||||
cath <## "#team: you deleted the group"
|
||||
alice <##> bob
|
||||
alice <##> cath
|
||||
bob <##> cath
|
||||
-- unused group contacts are deleted
|
||||
bob ##> "@cath hi"
|
||||
bob <## "no contact cath"
|
||||
(cath </)
|
||||
cath ##> "@bob hi"
|
||||
cath <## "no contact bob"
|
||||
(bob </)
|
||||
|
||||
testGroupSameName :: IO ()
|
||||
testGroupSameName =
|
||||
@@ -3349,24 +3356,23 @@ testGroupLink =
|
||||
cath ##> ("/c " <> gLink)
|
||||
cath <## "connection request sent!"
|
||||
alice <## "cath_1 (Catherine): accepting request to join group #team..."
|
||||
-- if contact existed it is merged
|
||||
concurrentlyN_
|
||||
[ do
|
||||
alice <## "cath_1 (Catherine): contact is connected"
|
||||
alice <## "cath_1 invited to group #team via your group link",
|
||||
alice <## "cath_1 invited to group #team via your group link"
|
||||
alice <## "contact cath_1 is merged into cath"
|
||||
alice <## "use @cath <message> to send messages",
|
||||
do
|
||||
cath <## "alice_1 (Alice): contact is connected"
|
||||
cath <## "#team: alice_1 invites you to join the group as member"
|
||||
cath <## "contact alice_1 is merged into alice"
|
||||
cath <## "use @alice <message> to send messages"
|
||||
cath <## "#team: alice invites you to join the group as member"
|
||||
cath <## "use /j team to accept"
|
||||
]
|
||||
-- sending message to contact marks it as used
|
||||
alice @@@ [("@cath", "hey"), ("@bob", "hey"), ("#team", "invited via your group link")]
|
||||
alice #> "@cath_1 hello"
|
||||
cath <# "alice_1> hello"
|
||||
alice #$> ("/clear cath_1", id, "cath_1: all messages are removed locally ONLY")
|
||||
alice @@@ [("@cath_1", ""), ("@cath", "hey"), ("@bob", "hey"), ("#team", "invited via your group link")]
|
||||
cath ##> "/j team"
|
||||
concurrentlyN_
|
||||
[ alice <## "#team: cath_1 joined the group",
|
||||
[ alice <## "#team: cath joined the group",
|
||||
do
|
||||
cath <## "#team: you joined the group"
|
||||
cath <## "#team: member bob (Bob) is connected",
|
||||
@@ -3377,14 +3383,14 @@ testGroupLink =
|
||||
alice #> "#team hello"
|
||||
concurrently_
|
||||
(bob <# "#team alice> hello")
|
||||
(cath <# "#team alice_1> hello")
|
||||
(cath <# "#team alice> hello")
|
||||
bob #> "#team hi there"
|
||||
concurrently_
|
||||
(alice <# "#team bob> hi there")
|
||||
(cath <# "#team bob> hi there")
|
||||
cath #> "#team hey team"
|
||||
concurrently_
|
||||
(alice <# "#team cath_1> hey team")
|
||||
(alice <# "#team cath> hey team")
|
||||
(bob <# "#team cath> hey team")
|
||||
|
||||
-- leaving team removes link
|
||||
@@ -3394,11 +3400,45 @@ testGroupLink =
|
||||
alice <## "#team: you left the group"
|
||||
alice <## "use /d #team to delete the group",
|
||||
bob <## "#team: alice left the group",
|
||||
cath <## "#team: alice_1 left the group"
|
||||
cath <## "#team: alice left the group"
|
||||
]
|
||||
alice ##> "/show link #team"
|
||||
alice <## "no group link, to create: /create link #team"
|
||||
|
||||
testGroupLinkContactUsed :: IO ()
|
||||
testGroupLinkContactUsed =
|
||||
testChat2 aliceProfile bobProfile $
|
||||
\alice bob -> do
|
||||
alice ##> "/g team"
|
||||
alice <## "group #team is created"
|
||||
alice <## "use /a team <name> to add members"
|
||||
alice ##> "/show link #team"
|
||||
alice <## "no group link, to create: /create link #team"
|
||||
alice ##> "/create link #team"
|
||||
gLink <- getGroupLink alice "team" True
|
||||
alice ##> "/show link #team"
|
||||
_ <- getGroupLink alice "team" False
|
||||
alice ##> "/create link #team"
|
||||
alice <## "you already have link for this group, to show: /show link #team"
|
||||
bob ##> ("/c " <> gLink)
|
||||
bob <## "connection request sent!"
|
||||
alice <## "bob (Bob): accepting request to join group #team..."
|
||||
concurrentlyN_
|
||||
[ do
|
||||
alice <## "bob (Bob): contact is connected"
|
||||
alice <## "bob invited to group #team via your group link",
|
||||
do
|
||||
bob <## "alice (Alice): contact is connected"
|
||||
bob <## "#team: alice invites you to join the group as member"
|
||||
bob <## "use /j team to accept"
|
||||
]
|
||||
-- sending message to contact marks it as used
|
||||
alice @@@ [("#team", "invited via your group link")]
|
||||
alice #> "@bob hello"
|
||||
bob <# "alice> hello"
|
||||
alice #$> ("/clear bob", id, "bob: all messages are removed locally ONLY")
|
||||
alice @@@ [("@bob", ""), ("#team", "invited via your group link")]
|
||||
|
||||
testGroupLinkIncognitoMembership :: IO ()
|
||||
testGroupLinkIncognitoMembership =
|
||||
testChat4 aliceProfile bobProfile cathProfile danProfile $
|
||||
@@ -3557,8 +3597,10 @@ testGroupLinkDeleteInvitedMemberNoBrokenItem =
|
||||
alice <## "#team: you removed bob from the group"
|
||||
alice #$> ("/_get chat #1 count=100", chat, [])
|
||||
alice @@@ [("#team", "")]
|
||||
alice <##> bob
|
||||
alice @@@ [("@bob", "hey"), ("#team", "")]
|
||||
-- removing member deletes unused group contact
|
||||
alice ##> "@bob hi"
|
||||
alice <## "no contact bob"
|
||||
(bob </)
|
||||
bob ##> "/j team"
|
||||
bob <## "error: connection authorization failed - this could happen if connection was deleted, secured with different credentials, or due to a bug - please re-create the connection"
|
||||
-- repeat request is prohibited because of the re-used XContactId, until contact is deleted
|
||||
@@ -3568,11 +3610,11 @@ testGroupLinkDeleteInvitedMemberNoBrokenItem =
|
||||
bob <## "alice: contact is deleted"
|
||||
bob ##> ("/c " <> gLink)
|
||||
bob <## "connection request sent!"
|
||||
alice <## "bob_1 (Bob): accepting request to join group #team..."
|
||||
alice <## "bob (Bob): accepting request to join group #team..."
|
||||
concurrentlyN_
|
||||
[ do
|
||||
alice <## "bob_1 (Bob): contact is connected"
|
||||
alice <## "bob_1 invited to group #team via your group link",
|
||||
alice <## "bob (Bob): contact is connected"
|
||||
alice <## "bob invited to group #team via your group link",
|
||||
do
|
||||
bob <## "alice_1 (Alice): contact is connected"
|
||||
bob <## "#team_1 (team): alice_1 invites you to join the group as member"
|
||||
@@ -3580,12 +3622,12 @@ testGroupLinkDeleteInvitedMemberNoBrokenItem =
|
||||
]
|
||||
bob ##> "/j team_1"
|
||||
concurrently_
|
||||
(alice <## "#team: bob_1 joined the group")
|
||||
(alice <## "#team: bob joined the group")
|
||||
(bob <## "#team_1: you joined the group")
|
||||
alice #> "#team hello"
|
||||
bob <# "#team_1 alice_1> hello"
|
||||
bob #> "#team_1 hi there"
|
||||
alice <# "#team bob_1> hi there"
|
||||
alice <# "#team bob> hi there"
|
||||
|
||||
withTestChatContactConnected :: String -> (TestCC -> IO a) -> IO a
|
||||
withTestChatContactConnected dbPrefix action =
|
||||
|
||||
Reference in New Issue
Block a user