Merge pull request #646 from simplex-chat/master (version 2.0.1)
This commit is contained in:
@@ -11,8 +11,8 @@ android {
|
||||
applicationId "chat.simplex.app"
|
||||
minSdk 29
|
||||
targetSdk 32
|
||||
versionCode 30
|
||||
versionName "2.0"
|
||||
versionCode 31
|
||||
versionName "2.0.1"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
ndk {
|
||||
|
||||
@@ -35,6 +35,7 @@ fun TerminalView(chatModel: ChatModel, close: () -> Unit) {
|
||||
withApi {
|
||||
// show "in progress"
|
||||
chatModel.controller.sendCmd(CC.Console(composeState.value.message))
|
||||
composeState.value = ComposeState()
|
||||
// hide "in progress"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -185,6 +185,7 @@ fun ComposeView(
|
||||
|
||||
fun clearState() {
|
||||
composeState.value = ComposeState()
|
||||
textStyle.value = smallFont
|
||||
chosenImage.value = null
|
||||
chosenFile.value = null
|
||||
linkUrl.value = null
|
||||
|
||||
@@ -10,8 +10,7 @@ import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Close
|
||||
import androidx.compose.material.icons.outlined.PhotoCamera
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -25,8 +24,10 @@ import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.model.Profile
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import chat.simplex.app.views.isValidDisplayName
|
||||
import com.google.accompanist.insets.ProvideWindowInsets
|
||||
import com.google.accompanist.insets.navigationBarsWithImePadding
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -128,7 +129,12 @@ fun UserProfileLayout(
|
||||
}
|
||||
}
|
||||
}
|
||||
ProfileNameTextField(displayName)
|
||||
Box {
|
||||
if (!isValidDisplayName(displayName.value)) {
|
||||
Icon(Icons.Outlined.Info, tint = Color.Red, contentDescription = stringResource(R.string.display_name_cannot_contain_whitespace))
|
||||
}
|
||||
ProfileNameTextField(displayName)
|
||||
}
|
||||
ProfileNameTextField(fullName)
|
||||
Row {
|
||||
TextButton(stringResource(R.string.cancel_verb)) {
|
||||
@@ -138,9 +144,22 @@ fun UserProfileLayout(
|
||||
editProfile.value = false
|
||||
}
|
||||
Spacer(Modifier.padding(horizontal = 8.dp))
|
||||
TextButton(stringResource(R.string.save_and_notify_contacts)) {
|
||||
saveProfile(displayName.value, fullName.value, profileImage.value)
|
||||
val enabled = displayName.value.isNotEmpty() && isValidDisplayName(displayName.value)
|
||||
val saveModifier: Modifier
|
||||
val saveColor: Color
|
||||
if (enabled) {
|
||||
saveModifier = Modifier
|
||||
.clickable { saveProfile(displayName.value, fullName.value, profileImage.value) }
|
||||
saveColor = MaterialTheme.colors.primary
|
||||
} else {
|
||||
saveModifier = Modifier
|
||||
saveColor = HighOrLowlight
|
||||
}
|
||||
Text(
|
||||
stringResource(R.string.save_and_notify_contacts),
|
||||
modifier = saveModifier,
|
||||
color = saveColor
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -187,6 +206,7 @@ private fun ProfileNameTextField(name: MutableState<String>) {
|
||||
onValueChange = { name.value = it },
|
||||
modifier = Modifier
|
||||
.padding(bottom = 24.dp)
|
||||
.padding(start = 28.dp)
|
||||
.fillMaxWidth(),
|
||||
textStyle = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
|
||||
@@ -106,15 +106,15 @@ struct CreateProfile: View {
|
||||
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||
}
|
||||
|
||||
func validDisplayName(_ name: String) -> Bool {
|
||||
name.firstIndex(of: " ") == nil
|
||||
}
|
||||
|
||||
func canCreateProfile() -> Bool {
|
||||
displayName != "" && validDisplayName(displayName)
|
||||
}
|
||||
}
|
||||
|
||||
func validDisplayName(_ name: String) -> Bool {
|
||||
name.firstIndex(of: " ") == nil
|
||||
}
|
||||
|
||||
struct CreateProfile_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
CreateProfile()
|
||||
|
||||
@@ -45,11 +45,19 @@ struct UserProfile: View {
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
profileNameTextEdit("Display name", $profile.displayName)
|
||||
ZStack(alignment: .leading) {
|
||||
if !validDisplayName(profile.displayName) {
|
||||
Image(systemName: "exclamationmark.circle")
|
||||
.foregroundColor(.red)
|
||||
.padding(.bottom, 10)
|
||||
}
|
||||
profileNameTextEdit("Display name", $profile.displayName)
|
||||
}
|
||||
profileNameTextEdit("Full name (optional)", $profile.fullName)
|
||||
HStack(spacing: 20) {
|
||||
Button("Cancel") { editProfile = false }
|
||||
Button("Save (and notify contacts)") { saveProfile() }
|
||||
.disabled(profile.displayName == "" || !validDisplayName(profile.displayName))
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, minHeight: 120, alignment: .leading)
|
||||
@@ -111,6 +119,7 @@ struct UserProfile: View {
|
||||
.textInputAutocapitalization(.never)
|
||||
.disableAutocorrection(true)
|
||||
.padding(.bottom)
|
||||
.padding(.leading, 28)
|
||||
}
|
||||
|
||||
func profileNameView(_ label: String, _ name: String) -> some View {
|
||||
|
||||
@@ -16,16 +16,6 @@
|
||||
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
|
||||
5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C13730A28156D2700F43030 /* ContactConnectionView.swift */; };
|
||||
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; };
|
||||
5C293B582829490C00CB16C0 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C293B532829490C00CB16C0 /* libffi.a */; };
|
||||
5C293B592829490C00CB16C0 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C293B532829490C00CB16C0 /* libffi.a */; };
|
||||
5C293B5A2829490C00CB16C0 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C293B542829490C00CB16C0 /* libgmpxx.a */; };
|
||||
5C293B5B2829490C00CB16C0 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C293B542829490C00CB16C0 /* libgmpxx.a */; };
|
||||
5C293B5C2829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C293B552829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M-ghc8.10.7.a */; };
|
||||
5C293B5D2829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C293B552829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M-ghc8.10.7.a */; };
|
||||
5C293B5E2829490C00CB16C0 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C293B562829490C00CB16C0 /* libgmp.a */; };
|
||||
5C293B5F2829490C00CB16C0 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C293B562829490C00CB16C0 /* libgmp.a */; };
|
||||
5C293B602829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C293B572829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M.a */; };
|
||||
5C293B612829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C293B572829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M.a */; };
|
||||
5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; };
|
||||
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; };
|
||||
5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; };
|
||||
@@ -100,6 +90,11 @@
|
||||
5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE4407827ADB701007B033A /* EmojiItemView.swift */; };
|
||||
5CEACCE327DE9246000BD591 /* ComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCE227DE9246000BD591 /* ComposeView.swift */; };
|
||||
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; };
|
||||
640DA1A5282D50D700AB8E8D /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 640DA1A0282D50D700AB8E8D /* libffi.a */; };
|
||||
640DA1A6282D50D700AB8E8D /* libHSsimplex-chat-2.0.0-JAEoKkoXJYeE6TLyZconIN-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 640DA1A1282D50D700AB8E8D /* libHSsimplex-chat-2.0.0-JAEoKkoXJYeE6TLyZconIN-ghc8.10.7.a */; };
|
||||
640DA1A7282D50D700AB8E8D /* libHSsimplex-chat-2.0.0-JAEoKkoXJYeE6TLyZconIN.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 640DA1A2282D50D700AB8E8D /* libHSsimplex-chat-2.0.0-JAEoKkoXJYeE6TLyZconIN.a */; };
|
||||
640DA1A8282D50D700AB8E8D /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 640DA1A3282D50D700AB8E8D /* libgmpxx.a */; };
|
||||
640DA1A9282D50D700AB8E8D /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 640DA1A4282D50D700AB8E8D /* libgmp.a */; };
|
||||
640F50E327CF991C001E05C2 /* SMPServers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640F50E227CF991C001E05C2 /* SMPServers.swift */; };
|
||||
6454036F2822A9750090DDFF /* ComposeFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6454036E2822A9750090DDFF /* ComposeFileView.swift */; };
|
||||
648010AB281ADD15009009B9 /* CIFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648010AA281ADD15009009B9 /* CIFileView.swift */; };
|
||||
@@ -131,11 +126,6 @@
|
||||
5C13730A28156D2700F43030 /* ContactConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionView.swift; sourceTree = "<group>"; };
|
||||
5C13730C2815740A00F43030 /* DebugJSON.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = DebugJSON.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemView.swift; sourceTree = "<group>"; };
|
||||
5C293B532829490C00CB16C0 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C293B542829490C00CB16C0 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C293B552829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
5C293B562829490C00CB16C0 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C293B572829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M.a"; sourceTree = "<group>"; };
|
||||
5C2E260627A2941F00F70299 /* SimpleXAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleXAPI.swift; sourceTree = "<group>"; };
|
||||
5C2E260A27A30CFA00F70299 /* ChatListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListView.swift; sourceTree = "<group>"; };
|
||||
5C2E260E27A30FDC00F70299 /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; };
|
||||
@@ -209,6 +199,11 @@
|
||||
5CE4407827ADB701007B033A /* EmojiItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItemView.swift; sourceTree = "<group>"; };
|
||||
5CEACCE227DE9246000BD591 /* ComposeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeView.swift; sourceTree = "<group>"; };
|
||||
5CEACCEC27DEA495000BD591 /* MsgContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgContentView.swift; sourceTree = "<group>"; };
|
||||
640DA1A0282D50D700AB8E8D /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
640DA1A1282D50D700AB8E8D /* libHSsimplex-chat-2.0.0-JAEoKkoXJYeE6TLyZconIN-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-2.0.0-JAEoKkoXJYeE6TLyZconIN-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
640DA1A2282D50D700AB8E8D /* libHSsimplex-chat-2.0.0-JAEoKkoXJYeE6TLyZconIN.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-2.0.0-JAEoKkoXJYeE6TLyZconIN.a"; sourceTree = "<group>"; };
|
||||
640DA1A3282D50D700AB8E8D /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
640DA1A4282D50D700AB8E8D /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
640F50E227CF991C001E05C2 /* SMPServers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMPServers.swift; sourceTree = "<group>"; };
|
||||
6454036E2822A9750090DDFF /* ComposeFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeFileView.swift; sourceTree = "<group>"; };
|
||||
648010AA281ADD15009009B9 /* CIFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIFileView.swift; sourceTree = "<group>"; };
|
||||
@@ -225,14 +220,14 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
640DA1A6282D50D700AB8E8D /* libHSsimplex-chat-2.0.0-JAEoKkoXJYeE6TLyZconIN-ghc8.10.7.a in Frameworks */,
|
||||
5C8F01CD27A6F0D8007D2C8D /* CodeScanner in Frameworks */,
|
||||
5C293B5E2829490C00CB16C0 /* libgmp.a in Frameworks */,
|
||||
5C293B5C2829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M-ghc8.10.7.a in Frameworks */,
|
||||
5C293B5A2829490C00CB16C0 /* libgmpxx.a in Frameworks */,
|
||||
5C293B602829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M.a in Frameworks */,
|
||||
640DA1A9282D50D700AB8E8D /* libgmp.a in Frameworks */,
|
||||
5C764E83279C748B000C6508 /* libz.tbd in Frameworks */,
|
||||
5C764E82279C748B000C6508 /* libiconv.tbd in Frameworks */,
|
||||
5C293B582829490C00CB16C0 /* libffi.a in Frameworks */,
|
||||
640DA1A5282D50D700AB8E8D /* libffi.a in Frameworks */,
|
||||
640DA1A7282D50D700AB8E8D /* libHSsimplex-chat-2.0.0-JAEoKkoXJYeE6TLyZconIN.a in Frameworks */,
|
||||
640DA1A8282D50D700AB8E8D /* libgmpxx.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -247,12 +242,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5C293B5B2829490C00CB16C0 /* libgmpxx.a in Frameworks */,
|
||||
5CDCAD5F28187D6900503DA2 /* libiconv.tbd in Frameworks */,
|
||||
5C293B592829490C00CB16C0 /* libffi.a in Frameworks */,
|
||||
5C293B5D2829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M-ghc8.10.7.a in Frameworks */,
|
||||
5C293B5F2829490C00CB16C0 /* libgmp.a in Frameworks */,
|
||||
5C293B612829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M.a in Frameworks */,
|
||||
5CDCAD6128187D8000503DA2 /* libz.tbd in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -302,11 +292,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5C293B532829490C00CB16C0 /* libffi.a */,
|
||||
5C293B562829490C00CB16C0 /* libgmp.a */,
|
||||
5C293B542829490C00CB16C0 /* libgmpxx.a */,
|
||||
5C293B552829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M-ghc8.10.7.a */,
|
||||
5C293B572829490C00CB16C0 /* libHSsimplex-chat-1.6.0-H7MppEuDeVu3ntZTT7iY0M.a */,
|
||||
640DA1A0282D50D700AB8E8D /* libffi.a */,
|
||||
640DA1A4282D50D700AB8E8D /* libgmp.a */,
|
||||
640DA1A3282D50D700AB8E8D /* libgmpxx.a */,
|
||||
640DA1A1282D50D700AB8E8D /* libHSsimplex-chat-2.0.0-JAEoKkoXJYeE6TLyZconIN-ghc8.10.7.a */,
|
||||
640DA1A2282D50D700AB8E8D /* libHSsimplex-chat-2.0.0-JAEoKkoXJYeE6TLyZconIN.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
@@ -913,7 +903,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 45;
|
||||
CURRENT_PROJECT_VERSION = 46;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -935,7 +925,7 @@
|
||||
);
|
||||
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*]" = "$(PROJECT_DIR)/Libraries/ios";
|
||||
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]" = "$(PROJECT_DIR)/Libraries/sim";
|
||||
MARKETING_VERSION = 2.0;
|
||||
MARKETING_VERSION = 2.0.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||
PRODUCT_NAME = SimpleX;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -955,7 +945,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 45;
|
||||
CURRENT_PROJECT_VERSION = 46;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -977,7 +967,7 @@
|
||||
);
|
||||
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*]" = "$(PROJECT_DIR)/Libraries/ios";
|
||||
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]" = "$(PROJECT_DIR)/Libraries/sim";
|
||||
MARKETING_VERSION = 2.0;
|
||||
MARKETING_VERSION = 2.0.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||
PRODUCT_NAME = SimpleX;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1036,7 +1026,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 45;
|
||||
CURRENT_PROJECT_VERSION = 46;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1057,7 +1047,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Libraries/sim",
|
||||
);
|
||||
MARKETING_VERSION = 2.0;
|
||||
MARKETING_VERSION = 2.0.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1076,7 +1066,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 45;
|
||||
CURRENT_PROJECT_VERSION = 46;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1097,7 +1087,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Libraries/sim",
|
||||
);
|
||||
MARKETING_VERSION = 2.0;
|
||||
MARKETING_VERSION = 2.0.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
|
||||
129
docs/rfcs/files.mmd
Normal file
129
docs/rfcs/files.mmd
Normal file
@@ -0,0 +1,129 @@
|
||||
sequenceDiagram
|
||||
participant AU as Alice's<br>chat UI
|
||||
participant AC as Alice's<br>chat core
|
||||
participant AA as Alice's<br>agent
|
||||
participant BA as Bob's<br>agent
|
||||
participant BC as Bob's<br>chat
|
||||
participant BU as Bob's<br>chat UI
|
||||
|
||||
note over AU, AA: Alice's app
|
||||
note over BA, BU: Bob's app
|
||||
|
||||
note over AU, BU: Direct file transfer handshake
|
||||
|
||||
note over AU: Send message with file
|
||||
AU ->> AC: APISendMessage with filePath
|
||||
AC ->> AA: createConnection
|
||||
AA ->> AC: connId, fileConnReq
|
||||
note left of AC: createSndFileTransfer<br>files.ci_file_status = CIFSSndStored<br>snd_files.file_status = FSNew<br>connections.conn_type = ConnSndFile<br>connections.conn_status = ConnNew
|
||||
AC ->> BC: XMsgNew with FileInvitation, fileConnReq = fileConnReq
|
||||
note right of BC: createRcvFileTransfer<br>files.ci_file_status = CIFSRcvInvitation<br>rcv_files.file_status = FSNew<br>rcv_files.file_queue_info = fileConnReq
|
||||
BC ->> BU: CRNewChatItem
|
||||
note over BU: Accept file
|
||||
BU ->> BC: ReceiveFile
|
||||
BC ->> BA: joinConnection with ConnInfo = XFileAcpt
|
||||
BA ->> BC: connId
|
||||
note right of BC: acceptRcvFileTransfer<br>files.ci_file_status = CIFSRcvAccepted<br>rcv_files.file_status = FSAccepted<br>connections.conn_type = ConnRcvFile<br>connections.conn_status = ConnJoined
|
||||
BA ->> AC: CONF with XFileAcpt
|
||||
note left of AC: snd_files.file_status = FSAccepted
|
||||
AC ->> AA: allowConnection
|
||||
note left of AC: connections.conn_status = ConnAccepted
|
||||
note over AA, BA: ...<br>Connection handshake<br>...
|
||||
par Alice connected
|
||||
AA ->> AC: CON
|
||||
note left of AC: connections.conn_status = ConnReady<br>snd_files.file_status = FSConnected<br>files.ci_file_status = CIFSSndTransfer
|
||||
AC ->> AU: CRSndFileStart
|
||||
note over AC: sendFileChunk
|
||||
and Bob Connected
|
||||
BA ->> BC: CON
|
||||
note right of BC: connections.conn_status = ConnReady<br>rcv_files.file_status = FSConnected<br>files.ci_file_status = CIFSRcvTransfer
|
||||
BC ->> BU: CRRcvFileStart
|
||||
end
|
||||
|
||||
note over AU, BU: Group file transfer handshake
|
||||
|
||||
note over AU: Send message with file
|
||||
AU ->> AC: APISendMessage with filePath
|
||||
note left of AC: createSndGroupFileTransfer<br>files.ci_file_status = CIFSSndStored
|
||||
AC ->> BC: XMsgNew with FileInvitation, fileConnReq = Nothing
|
||||
note right of BC: createRcvGroupFileTransfer<br>files.ci_file_status = CIFSRcvInvitation<br>rcv_files.file_status = FSNew<br>rcv_files.file_queue_info = NULL
|
||||
BC ->> BU: CRNewChatItem
|
||||
note over BU: Accept file
|
||||
BU ->> BC: ReceiveFile
|
||||
BC ->> BA: createConnection
|
||||
BA ->> BC: connId, fileInvConnReq
|
||||
note right of BC: acceptRcvFileTransfer<br>files.ci_file_status = CIFSRcvAccepted<br>rcv_files.file_status = FSAccepted<br>connections.conn_type = ConnRcvFile<br>connections.conn_status = ConnNew
|
||||
BC ->> AC: XFileAcptInv sharedMsgId fileInvConnReq
|
||||
AC ->> AA: joinConnection with ConnInfo = XOk
|
||||
AA ->> AC: connId
|
||||
note left of AC: createSndGroupFileTransferConnection<br>connections.conn_type = ConnSndFile<br>connections.conn_status = ConnNew<br>snd_files.file_status = FSAccepted
|
||||
AA ->> BC: CONF with XOk
|
||||
BC ->> BA: allowConnection
|
||||
note right of BC: connections.conn_status = ConnAccepted
|
||||
note over AA, BA: ...<br>Connection handshake<br>...
|
||||
par Alice connected
|
||||
AA ->> AC: CON
|
||||
note left of AC: connections.conn_status = ConnReady<br>snd_files.file_status = FSConnected<br>files.ci_file_status = CIFSSndTransfer
|
||||
AC ->> AU: CRSndFileStart
|
||||
note over AC: sendFileChunk
|
||||
and Bob Connected
|
||||
BA ->> BC: CON
|
||||
note right of BC: connections.conn_status = ConnReady<br>rcv_files.file_status = FSConnected<br>files.ci_file_status = CIFSRcvTransfer
|
||||
BC ->> BU: CRRcvFileStart
|
||||
end
|
||||
|
||||
note over AU, BU: File transfer
|
||||
|
||||
loop while createSndFileChunk returns chunkNo
|
||||
AC ->> BC: FileChunk
|
||||
AA ->> AC: SENT
|
||||
note over BC: appendFileChunk
|
||||
end
|
||||
opt receiver cancelled file transfer
|
||||
note over BU: Cancel file
|
||||
BU ->> BC: CancelFile
|
||||
note right of BC: files.cancelled = true<br>files.ci_file_status = CIFSRcvCancelled<br>rcv_files.file_status = FSCancelled<br>deleteRcvFileChunks
|
||||
BC ->> BA: deleteConnection
|
||||
BC ->> BU: CRRcvFileSndCancelled
|
||||
note over AA, BA: connection is deleted
|
||||
AC ->> BA: FileChunk
|
||||
BA ->> AA: MERR AUTH
|
||||
AA ->> AC: MERR
|
||||
note over AC: cancelSndFileTransfer
|
||||
alt AUTH
|
||||
AC ->> AU: CRSndFileRcvCancelled
|
||||
else other error, possibly not due to cancel
|
||||
AC ->> AU: CEFileSend
|
||||
end
|
||||
end
|
||||
AC ->> BC: Final FileChunk
|
||||
note left of AC: snd_files.file_status = FSComplete<br>deleteSndFileChunks<br>files.ci_file_status = CIFSSndComplete
|
||||
AC ->> AU: CRSndFileComplete
|
||||
AC ->> AA: deleteConnection
|
||||
note over BC: appendFileChunk
|
||||
note right of BC: rcv_files.file_status = FSComplete<br>files.ci_file_status = CIFSRcvComplete<br>deleteRcvFileChunks
|
||||
BC ->> BU: CRRcvFileComplete
|
||||
BC ->> BA: deleteConnection
|
||||
|
||||
note over AU, BU: Sender cancels file transfer
|
||||
|
||||
note over AU: Cancel file
|
||||
AU ->> AC: CancelFile
|
||||
note left of AC: files.cancelled = true<br>files.ci_file_status = CIFSSndCancelled<br>snd_files.file_status = FSCancelled<br>deleteSndFileChunks
|
||||
AC ->> BA: FileChunkCancel (over file connection)
|
||||
note left of AC: deleteConnection
|
||||
AC ->> BA: XFileCancel (over direct/group connection)
|
||||
AC ->> AU: CRSndGroupFileCancelled
|
||||
par FileChunkCancel
|
||||
BA ->> BC: FileChunkCancel
|
||||
note over BC: Cancel file (if it wasn't already cancelled)
|
||||
note right of BC: files.cancelled = true<br>files.ci_file_status = CIFSRcvCancelled<br>rcv_files.file_status = FSCancelled<br>deleteRcvFileChunks
|
||||
BC ->> BA: deleteConnection
|
||||
BC ->> BU: CRRcvFileSndCancelled
|
||||
and XFileCancel
|
||||
BA ->> BC: XFileCancel
|
||||
note over BC: Cancel file (if it wasn't already cancelled)
|
||||
note right of BC: files.cancelled = true<br>files.ci_file_status = CIFSRcvCancelled<br>rcv_files.file_status = FSCancelled<br>deleteRcvFileChunks
|
||||
BC ->> BA: deleteConnection
|
||||
BC ->> BU: CRRcvFileSndCancelled
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
name: simplex-chat
|
||||
version: 2.0.0
|
||||
version: 2.0.1
|
||||
#synopsis:
|
||||
#description:
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
|
||||
@@ -5,7 +5,7 @@ cabal-version: 1.12
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: simplex-chat
|
||||
version: 2.0.0
|
||||
version: 2.0.1
|
||||
category: Web, System, Services, Cryptography
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
author: simplex.chat
|
||||
|
||||
@@ -842,7 +842,7 @@ acceptFileReceive user@User {userId} RcvFileTransfer {fileId, fileInvitation = F
|
||||
tryError (withAgent $ \a -> joinConnection a connReq . directMessage $ XFileAcpt fName) >>= \case
|
||||
Right agentConnId -> do
|
||||
filePath <- getRcvFilePath filePath_ fName
|
||||
withStore $ \st -> acceptRcvFileTransfer st user fileId agentConnId filePath
|
||||
withStore $ \st -> acceptRcvFileTransfer st user fileId agentConnId ConnJoined filePath
|
||||
Left e -> throwError e
|
||||
-- group file protocol
|
||||
Nothing ->
|
||||
@@ -855,7 +855,7 @@ acceptFileReceive user@User {userId} RcvFileTransfer {fileId, fileInvitation = F
|
||||
sharedMsgId <- withStore $ \st -> getSharedMsgIdByFileId st userId fileId
|
||||
(agentConnId, fileInvConnReq) <- withAgent (`createConnection` SCMInvitation)
|
||||
filePath <- getRcvFilePath filePath_ fName
|
||||
ci <- withStore $ \st -> acceptRcvFileTransfer st user fileId agentConnId filePath
|
||||
ci <- withStore $ \st -> acceptRcvFileTransfer st user fileId agentConnId ConnNew filePath
|
||||
void $ sendDirectMessage conn (XFileAcptInv sharedMsgId fileInvConnReq fName) (GroupId groupId)
|
||||
pure ci
|
||||
_ -> throwChatError $ CEFileInternal "member connection not active"
|
||||
@@ -1836,16 +1836,18 @@ parseFileChunk msg =
|
||||
appendFileChunk :: ChatMonad m => RcvFileTransfer -> Integer -> ByteString -> m ()
|
||||
appendFileChunk ft@RcvFileTransfer {fileId, fileStatus} chunkNo chunk =
|
||||
case fileStatus of
|
||||
RFSConnected RcvFileInfo {filePath} -> do
|
||||
fsFilePath <- toFSFilePath filePath
|
||||
append_ filePath fsFilePath
|
||||
RFSConnected RcvFileInfo {filePath} -> append_ filePath
|
||||
-- sometimes update of file transfer status to FSConnected
|
||||
-- doesn't complete in time before MSG with first file chunk
|
||||
RFSAccepted RcvFileInfo {filePath} -> append_ filePath
|
||||
RFSCancelled _ -> pure ()
|
||||
_ -> throwChatError $ CEFileInternal "receiving file transfer not in progress"
|
||||
where
|
||||
append_ fPath fPathUsed = do
|
||||
h <- getFileHandle fileId fPathUsed rcvFiles AppendMode
|
||||
append_ filePath = do
|
||||
fsFilePath <- toFSFilePath filePath
|
||||
h <- getFileHandle fileId fsFilePath rcvFiles AppendMode
|
||||
E.try (liftIO $ B.hPut h chunk >> hFlush h) >>= \case
|
||||
Left (e :: E.SomeException) -> throwChatError . CEFileWrite fPath $ show e
|
||||
Left (e :: E.SomeException) -> throwChatError . CEFileWrite fsFilePath $ show e
|
||||
Right () -> withStore $ \st -> updatedRcvFileChunkStored st ft chunkNo
|
||||
|
||||
getFileHandle :: ChatMonad m => Int64 -> FilePath -> (ChatController -> TVar (Map Int64 Handle)) -> IOMode -> m Handle
|
||||
|
||||
@@ -2025,8 +2025,8 @@ createRcvGroupFileTransfer st userId GroupMember {groupId, groupMemberId, localD
|
||||
currentTs <- getCurrentTime
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO files (user_id, group_id, file_name, file_size, chunk_size, created_at, updated_at) VALUES (?,?,?,?,?,?,?)"
|
||||
(userId, groupId, fileName, fileSize, chunkSize, currentTs, currentTs)
|
||||
"INSERT INTO files (user_id, group_id, file_name, file_size, chunk_size, ci_file_status, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)"
|
||||
(userId, groupId, fileName, fileSize, chunkSize, CIFSRcvInvitation, currentTs, currentTs)
|
||||
fileId <- insertedRowId db
|
||||
DB.execute
|
||||
db
|
||||
@@ -2082,8 +2082,8 @@ getRcvFileTransfer_ db userId fileId =
|
||||
cancelled = fromMaybe False cancelled_
|
||||
rcvFileTransfer _ = Left $ SERcvFileNotFound fileId
|
||||
|
||||
acceptRcvFileTransfer :: StoreMonad m => SQLiteStore -> User -> Int64 -> ConnId -> FilePath -> m AChatItem
|
||||
acceptRcvFileTransfer st user@User {userId} fileId agentConnId filePath =
|
||||
acceptRcvFileTransfer :: StoreMonad m => SQLiteStore -> User -> Int64 -> ConnId -> ConnStatus -> FilePath -> m AChatItem
|
||||
acceptRcvFileTransfer st user@User {userId} fileId agentConnId connStatus filePath =
|
||||
liftIOEither . withTransaction st $ \db -> do
|
||||
currentTs <- getCurrentTime
|
||||
DB.execute
|
||||
@@ -2097,7 +2097,7 @@ acceptRcvFileTransfer st user@User {userId} fileId agentConnId filePath =
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO connections (agent_conn_id, conn_status, conn_type, rcv_file_id, user_id, created_at, updated_at) VALUES (?,?,?,?,?,?,?)"
|
||||
(agentConnId, ConnJoined, ConnRcvFile, fileId, userId, currentTs, currentTs)
|
||||
(agentConnId, connStatus, ConnRcvFile, fileId, userId, currentTs, currentTs)
|
||||
getChatItemByFileId_ db user fileId
|
||||
|
||||
updateRcvFileStatus :: MonadUnliftIO m => SQLiteStore -> RcvFileTransfer -> FileStatus -> m ()
|
||||
|
||||
Reference in New Issue
Block a user