mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-03 11:58:00 +00:00
ui: add asset image on create channel view; allow to choose image on create profile (#6891)
* ui: create channel picture * more centered * better symmetry * less diff * choose image on create profile * fix padding * fix padding, fit into screen * fix button layout * placeholders * fix padding * channel pictures * adjust asset_dir in scripts --------- Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com> Co-authored-by: shum <github.shum@liber.li>
This commit is contained in:
@@ -302,6 +302,7 @@ jobs:
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
|
||||
shell: docker exec -t builder sh -eu {0}
|
||||
run: |
|
||||
export ASSETS_DIR='../../assets'
|
||||
scripts/desktop/make-deb-linux.sh
|
||||
|
||||
- name: Prepare Desktop
|
||||
@@ -327,6 +328,7 @@ jobs:
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == '22.04' && matrix.should_run == true
|
||||
shell: docker exec -t builder sh -eu {0}
|
||||
run: |
|
||||
export ASSETS_DIR='../../assets'
|
||||
scripts/desktop/make-appimage-linux.sh
|
||||
|
||||
- name: Prepare AppImage
|
||||
@@ -549,6 +551,7 @@ jobs:
|
||||
APPLE_SIMPLEX_NOTARIZATION_APPLE_ID: ${{ secrets.APPLE_SIMPLEX_NOTARIZATION_APPLE_ID }}
|
||||
APPLE_SIMPLEX_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_SIMPLEX_NOTARIZATION_PASSWORD }}
|
||||
run: |
|
||||
export ASSETS_DIR='../../assets'
|
||||
scripts/ci/build-desktop-mac.sh
|
||||
path=$(echo $PWD/apps/multiplatform/release/main/dmg/SimpleX-*.dmg)
|
||||
echo "package_path=$path" >> $GITHUB_OUTPUT
|
||||
|
||||
@@ -10,6 +10,7 @@ import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct AddChannelView: View {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@StateObject private var channelRelaysModel = ChannelRelaysModel.shared
|
||||
@@ -45,28 +46,39 @@ struct AddChannelView: View {
|
||||
private func profileStepView() -> some View {
|
||||
List {
|
||||
Group {
|
||||
ZStack(alignment: .center) {
|
||||
ZStack(alignment: .topTrailing) {
|
||||
ProfileImage(imageStr: profile.image, iconName: "antenna.radiowaves.left.and.right.circle.fill", size: 128)
|
||||
if profile.image != nil {
|
||||
Button {
|
||||
profile.image = nil
|
||||
} label: {
|
||||
Image(systemName: "multiply")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 12)
|
||||
HStack(spacing: 0) {
|
||||
Spacer(minLength: 0)
|
||||
ZStack(alignment: .center) {
|
||||
ZStack(alignment: .topTrailing) {
|
||||
ProfileImage(imageStr: profile.image, iconName: "antenna.radiowaves.left.and.right.circle.fill", size: 128)
|
||||
if profile.image != nil {
|
||||
Button {
|
||||
profile.image = nil
|
||||
} label: {
|
||||
Image(systemName: "multiply")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 12)
|
||||
}
|
||||
}
|
||||
}
|
||||
editImageButton { showChooseSource = true }
|
||||
.buttonStyle(BorderlessButtonStyle())
|
||||
}
|
||||
editImageButton { showChooseSource = true }
|
||||
.buttonStyle(BorderlessButtonStyle())
|
||||
.padding(.horizontal, 10) // Offsets transparent space built into 3D asset
|
||||
#if SIMPLEX_ASSETS
|
||||
Spacer(minLength: 0)
|
||||
Image(colorScheme == .light ? "create-channel" : "create-channel-light")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(height: 140)
|
||||
#endif
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
}
|
||||
.listRowBackground(Color.clear)
|
||||
.listRowSeparator(.hidden)
|
||||
.listRowInsets(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0))
|
||||
.listRowInsets(EdgeInsets(top: 8, leading: 0, bottom: 0, trailing: 0))
|
||||
|
||||
Section {
|
||||
channelNameTextField()
|
||||
|
||||
@@ -68,6 +68,7 @@ struct AddGroupView: View {
|
||||
List {
|
||||
Group {
|
||||
HStack(spacing: 0) {
|
||||
Spacer(minLength: 0)
|
||||
ZStack(alignment: .center) {
|
||||
ZStack(alignment: .topTrailing) {
|
||||
ProfileImage(imageStr: profile.image, iconName: "person.2.circle.fill", size: 128)
|
||||
@@ -86,16 +87,16 @@ struct AddGroupView: View {
|
||||
editImageButton { showChooseSource = true }
|
||||
.buttonStyle(BorderlessButtonStyle()) // otherwise whole "list row" is clickable
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.horizontal, 10) // Offsets transparent space built into 3D asset
|
||||
#if SIMPLEX_ASSETS
|
||||
Spacer(minLength: 0)
|
||||
Image(colorScheme == .light ? "create-group" : "create-group-light")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(height: 140)
|
||||
.frame(maxWidth: .infinity)
|
||||
#endif
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.listRowBackground(Color.clear)
|
||||
.listRowSeparator(.hidden)
|
||||
|
||||
@@ -29,45 +29,78 @@ enum UserProfileAlert: Identifiable {
|
||||
let MAX_BIO_LENGTH_BYTES = 160
|
||||
|
||||
struct CreateProfile: View {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@State private var displayName: String = ""
|
||||
@State private var profileBio: String = ""
|
||||
@FocusState private var focusDisplayName
|
||||
@State private var alert: UserProfileAlert?
|
||||
@State private var showChooseSource = false
|
||||
@State private var showImagePicker = false
|
||||
@State private var showTakePhoto = false
|
||||
@State private var chosenImage: UIImage? = nil
|
||||
@State private var profileImage: String? = nil
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section {
|
||||
TextField("Enter your name…", text: $displayName)
|
||||
.focused($focusDisplayName)
|
||||
TextField("Bio", text: $profileBio)
|
||||
Button {
|
||||
createProfile()
|
||||
} label: {
|
||||
Label("Create profile", systemImage: "checkmark")
|
||||
}
|
||||
.disabled(!canCreateProfile(displayName) || !bioFitsLimit())
|
||||
} header: {
|
||||
HStack {
|
||||
Text("Your profile")
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
|
||||
let name = displayName.trimmingCharacters(in: .whitespaces)
|
||||
let validName = mkValidName(name)
|
||||
if name != validName {
|
||||
Spacer()
|
||||
validationErrorIndicator {
|
||||
alert = .invalidNameError(validName: validName)
|
||||
}
|
||||
} else if !bioFitsLimit() {
|
||||
Spacer()
|
||||
validationErrorIndicator {
|
||||
showAlert(NSLocalizedString("Bio too large", comment: "alert title"))
|
||||
Group {
|
||||
ZStack(alignment: .center) {
|
||||
ZStack(alignment: .topTrailing) {
|
||||
ProfileImage(imageStr: profileImage, size: 128)
|
||||
if profileImage != nil {
|
||||
Button {
|
||||
profileImage = nil
|
||||
} label: {
|
||||
Image(systemName: "multiply")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 12)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editImageButton { showChooseSource = true }
|
||||
.buttonStyle(BorderlessButtonStyle())
|
||||
}
|
||||
.frame(height: 20)
|
||||
// TODO: add 3D asset image next to profile image (fix asset first - trim transparent space)
|
||||
// #if SIMPLEX_ASSETS
|
||||
// Image(colorScheme == .light ? "your-profile" : "your-profile-light")
|
||||
// .resizable()
|
||||
// .scaledToFit()
|
||||
// .frame(height: 140)
|
||||
// #endif
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.listRowBackground(Color.clear)
|
||||
.listRowSeparator(.hidden)
|
||||
.listRowInsets(EdgeInsets(top: 8, leading: 0, bottom: 0, trailing: 0))
|
||||
|
||||
Section {
|
||||
ZStack(alignment: .leading) {
|
||||
let name = displayName.trimmingCharacters(in: .whitespaces)
|
||||
if name != mkValidName(name) {
|
||||
Button {
|
||||
alert = .invalidNameError(validName: mkValidName(name))
|
||||
} label: {
|
||||
Image(systemName: "exclamationmark.circle").foregroundColor(.red)
|
||||
}
|
||||
} else {
|
||||
Image(systemName: "pencil").foregroundColor(theme.colors.secondary)
|
||||
}
|
||||
TextField("Enter your name…", text: $displayName)
|
||||
.padding(.leading, 36)
|
||||
.focused($focusDisplayName)
|
||||
}
|
||||
ZStack(alignment: .leading) {
|
||||
Image(systemName: "pencil").foregroundColor(theme.colors.secondary)
|
||||
TextField("Bio", text: $profileBio)
|
||||
.padding(.leading, 36)
|
||||
}
|
||||
Button(action: createProfile) {
|
||||
settingsRow("checkmark", color: theme.colors.primary) { Text("Create profile") }
|
||||
}
|
||||
.disabled(!canCreateProfile(displayName) || !bioFitsLimit())
|
||||
} footer: {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("Your profile is stored on your device and only shared with your contacts.")
|
||||
@@ -75,10 +108,42 @@ struct CreateProfile: View {
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.compactSectionSpacing()
|
||||
}
|
||||
.navigationTitle("Create your profile")
|
||||
.modifier(ThemedBackground(grouped: true))
|
||||
.alert(item: $alert) { a in userProfileAlert(a, $displayName) }
|
||||
.confirmationDialog("Profile image", isPresented: $showChooseSource, titleVisibility: .visible) {
|
||||
Button("Take picture") {
|
||||
showTakePhoto = true
|
||||
}
|
||||
Button("Choose from library") {
|
||||
showImagePicker = true
|
||||
}
|
||||
}
|
||||
.fullScreenCover(isPresented: $showTakePhoto) {
|
||||
ZStack {
|
||||
Color.black.edgesIgnoringSafeArea(.all)
|
||||
CameraImagePicker(image: $chosenImage)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showImagePicker) {
|
||||
LibraryImagePicker(image: $chosenImage) { _ in
|
||||
await MainActor.run {
|
||||
showImagePicker = false
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: chosenImage) { image in
|
||||
Task {
|
||||
let resized: String? = if let image {
|
||||
await resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500)
|
||||
} else {
|
||||
nil
|
||||
}
|
||||
await MainActor.run { profileImage = resized }
|
||||
}
|
||||
}
|
||||
.onAppear() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
focusDisplayName = true
|
||||
@@ -86,14 +151,6 @@ struct CreateProfile: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func validationErrorIndicator(_ onTap: @escaping () -> Void) -> some View {
|
||||
Image(systemName: "exclamationmark.circle")
|
||||
.foregroundColor(.red)
|
||||
.onTapGesture {
|
||||
onTap()
|
||||
}
|
||||
}
|
||||
|
||||
private func bioFitsLimit() -> Bool {
|
||||
chatJsonLength(profileBio) <= MAX_BIO_LENGTH_BYTES
|
||||
}
|
||||
@@ -104,7 +161,8 @@ struct CreateProfile: View {
|
||||
let profile = Profile(
|
||||
displayName: displayName.trimmingCharacters(in: .whitespaces),
|
||||
fullName: "",
|
||||
shortDescr: shortDescr
|
||||
shortDescr: shortDescr,
|
||||
image: profileImage
|
||||
)
|
||||
let m = ChatModel.shared
|
||||
do {
|
||||
|
||||
@@ -182,8 +182,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.5.0.16-45m1zumjYj2Eu6IsS525uz-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.16-45m1zumjYj2Eu6IsS525uz-ghc9.6.3.a */; };
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.16-45m1zumjYj2Eu6IsS525uz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.16-45m1zumjYj2Eu6IsS525uz.a */; };
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.17-3blIIOCVZmm6YZ1Ew74Hcu-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.17-3blIIOCVZmm6YZ1Ew74Hcu-ghc9.6.3.a */; };
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.17-3blIIOCVZmm6YZ1Ew74Hcu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.17-3blIIOCVZmm6YZ1Ew74Hcu.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 */; };
|
||||
@@ -559,8 +559,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.5.0.16-45m1zumjYj2Eu6IsS525uz-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.0.16-45m1zumjYj2Eu6IsS525uz-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.16-45m1zumjYj2Eu6IsS525uz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.0.16-45m1zumjYj2Eu6IsS525uz.a"; sourceTree = "<group>"; };
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.17-3blIIOCVZmm6YZ1Ew74Hcu-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.0.17-3blIIOCVZmm6YZ1Ew74Hcu-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.17-3blIIOCVZmm6YZ1Ew74Hcu.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.0.17-3blIIOCVZmm6YZ1Ew74Hcu.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>"; };
|
||||
@@ -729,8 +729,8 @@
|
||||
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */,
|
||||
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */,
|
||||
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */,
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.16-45m1zumjYj2Eu6IsS525uz-ghc9.6.3.a in Frameworks */,
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.16-45m1zumjYj2Eu6IsS525uz.a in Frameworks */,
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.17-3blIIOCVZmm6YZ1Ew74Hcu-ghc9.6.3.a in Frameworks */,
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.17-3blIIOCVZmm6YZ1Ew74Hcu.a in Frameworks */,
|
||||
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -816,8 +816,8 @@
|
||||
64C829992D54AEEE006B9E89 /* libffi.a */,
|
||||
64C829982D54AEED006B9E89 /* libgmp.a */,
|
||||
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */,
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.16-45m1zumjYj2Eu6IsS525uz-ghc9.6.3.a */,
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.16-45m1zumjYj2Eu6IsS525uz.a */,
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.17-3blIIOCVZmm6YZ1Ew74Hcu-ghc9.6.3.a */,
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.17-3blIIOCVZmm6YZ1Ew74Hcu.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
|
||||
+64
-15
@@ -42,11 +42,14 @@ import chat.simplex.common.views.newchat.darkStops
|
||||
import chat.simplex.common.views.newchat.gradientPoints
|
||||
import chat.simplex.common.views.newchat.lightStops
|
||||
import chat.simplex.common.views.onboarding.*
|
||||
import chat.simplex.common.views.usersettings.DeleteImageButton
|
||||
import chat.simplex.common.views.usersettings.EditImageButton
|
||||
import chat.simplex.common.views.usersettings.SettingsActionItem
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.launch
|
||||
import java.net.URI
|
||||
|
||||
const val MAX_BIO_LENGTH_BYTES = 160
|
||||
|
||||
@@ -60,18 +63,63 @@ fun CreateProfile(chatModel: ChatModel, close: () -> Unit) {
|
||||
val scrollState = rememberScrollState()
|
||||
val keyboardState by getKeyboardState()
|
||||
var savedKeyboardState by remember { mutableStateOf(keyboardState) }
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(top = 20.dp)
|
||||
) {
|
||||
val displayName = rememberSaveable { mutableStateOf("") }
|
||||
val shortDescr = rememberSaveable { mutableStateOf("") }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val bottomSheetModalState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
|
||||
val displayName = rememberSaveable { mutableStateOf("") }
|
||||
val shortDescr = rememberSaveable { mutableStateOf("") }
|
||||
val chosenImage = rememberSaveable { mutableStateOf<URI?>(null) }
|
||||
val profileImage = rememberSaveable { mutableStateOf<String?>(null) }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
ModalBottomSheetLayout(
|
||||
scrimColor = Color.Black.copy(alpha = 0.12F),
|
||||
modifier = Modifier.imePadding(),
|
||||
sheetContent = {
|
||||
GetImageBottomSheet(
|
||||
chosenImage,
|
||||
onImageChange = { bitmap -> profileImage.value = resizeImageToStrSize(cropToSquare(bitmap), maxDataSize = 12500) },
|
||||
hideBottomSheet = {
|
||||
scope.launch { bottomSheetModalState.hide() }
|
||||
})
|
||||
},
|
||||
sheetState = bottomSheetModalState,
|
||||
sheetShape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
ColumnWithScrollBar {
|
||||
AppBarTitle(stringResource(MR.strings.create_profile), bottomPadding = DEFAULT_PADDING_HALF)
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = DEFAULT_PADDING_HALF),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Box(contentAlignment = Alignment.TopEnd) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
ProfileImage(128.dp, image = profileImage.value)
|
||||
EditImageButton { scope.launch { bottomSheetModalState.show() } }
|
||||
}
|
||||
if (profileImage.value != null) {
|
||||
DeleteImageButton { profileImage.value = null }
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: add 3D asset image next to profile image (fix asset first - trim transparent space)
|
||||
// if (BuildConfigCommon.SIMPLEX_ASSETS) {
|
||||
// Image(
|
||||
// painterResource(if (isInDarkTheme()) MR.images.your_profile_light else MR.images.your_profile),
|
||||
// contentDescription = null,
|
||||
// contentScale = ContentScale.Fit,
|
||||
// modifier = Modifier.height(140.dp)
|
||||
// )
|
||||
// }
|
||||
}
|
||||
Column(Modifier.padding(horizontal = DEFAULT_PADDING)) {
|
||||
AppBarTitle(stringResource(MR.strings.create_profile), withPadding = false, bottomPadding = DEFAULT_PADDING)
|
||||
Row(Modifier.padding(bottom = DEFAULT_PADDING_HALF).fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Text(
|
||||
stringResource(MR.strings.display_name),
|
||||
@@ -114,9 +162,9 @@ fun CreateProfile(chatModel: ChatModel, close: () -> Unit) {
|
||||
iconColor = MaterialTheme.colors.primary,
|
||||
click = {
|
||||
if (chatModel.localUserCreated.value == true) {
|
||||
createProfileInProfiles(chatModel, displayName.value, shortDescr.value, close)
|
||||
createProfileInProfiles(chatModel, displayName.value, shortDescr.value, profileImage.value, close)
|
||||
} else {
|
||||
createProfileInNoProfileSetup(displayName.value, close)
|
||||
createProfileInNoProfileSetup(displayName.value, profileImage.value, close)
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -137,6 +185,7 @@ fun CreateProfile(chatModel: ChatModel, close: () -> Unit) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -304,9 +353,9 @@ private fun CreateFirstProfileDesktop(chatModel: ChatModel, close: () -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
fun createProfileInNoProfileSetup(displayName: String, close: () -> Unit) {
|
||||
fun createProfileInNoProfileSetup(displayName: String, image: String? = null, close: () -> Unit) {
|
||||
withBGApi {
|
||||
val user = controller.apiCreateActiveUser(null, Profile(displayName.trim(), "", null, null)) ?: return@withBGApi
|
||||
val user = controller.apiCreateActiveUser(null, Profile(displayName.trim(), "", null, image)) ?: return@withBGApi
|
||||
if (!chatModel.connectedToRemote()) {
|
||||
chatModel.localUserCreated.value = true
|
||||
}
|
||||
@@ -317,11 +366,11 @@ fun createProfileInNoProfileSetup(displayName: String, close: () -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
fun createProfileInProfiles(chatModel: ChatModel, displayName: String, shortDescr: String, close: () -> Unit) {
|
||||
fun createProfileInProfiles(chatModel: ChatModel, displayName: String, shortDescr: String, image: String? = null, close: () -> Unit) {
|
||||
withBGApi {
|
||||
val rhId = chatModel.remoteHostId()
|
||||
val user = chatModel.controller.apiCreateActiveUser(
|
||||
rhId, Profile(displayName.trim(), "", shortDescr.trim().ifEmpty { null }, null)
|
||||
rhId, Profile(displayName.trim(), "", shortDescr.trim().ifEmpty { null }, image)
|
||||
) ?: return@withBGApi
|
||||
chatModel.currentUser.value = user
|
||||
if (chatModel.users.isEmpty()) {
|
||||
|
||||
+28
-11
@@ -23,6 +23,8 @@ import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.getUserServers
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import chat.simplex.common.BuildConfigCommon
|
||||
import chat.simplex.common.views.*
|
||||
import chat.simplex.common.views.chat.group.GroupLinkView
|
||||
import chat.simplex.common.views.chatlist.openGroupChat
|
||||
@@ -257,22 +259,37 @@ private fun ProfileStepView(
|
||||
) {
|
||||
ModalView(close = close) {
|
||||
ColumnWithScrollBar {
|
||||
AppBarTitle(generalGetString(MR.strings.create_channel_title))
|
||||
Box(
|
||||
AppBarTitle(generalGetString(MR.strings.create_channel_title), bottomPadding = DEFAULT_PADDING_HALF)
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 24.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
.padding(vertical = DEFAULT_PADDING_HALF),
|
||||
horizontalArrangement = if (BuildConfigCommon.SIMPLEX_ASSETS) Arrangement.SpaceEvenly else Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(contentAlignment = Alignment.TopEnd) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
ProfileImage(108.dp, image = profileImage.value, icon = MR.images.ic_bigtop_updates_circle_filled)
|
||||
EditImageButton { scope.launch { bottomSheetModalState.show() } }
|
||||
}
|
||||
if (profileImage.value != null) {
|
||||
DeleteImageButton { profileImage.value = null }
|
||||
// Padding offsets transparent space built into 3D asset
|
||||
Box(
|
||||
modifier = if (BuildConfigCommon.SIMPLEX_ASSETS) Modifier.padding(horizontal = 3.dp) else Modifier,
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Box(contentAlignment = Alignment.TopEnd) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
ProfileImage(128.dp, image = profileImage.value, icon = MR.images.ic_bigtop_updates_circle_filled)
|
||||
EditImageButton { scope.launch { bottomSheetModalState.show() } }
|
||||
}
|
||||
if (profileImage.value != null) {
|
||||
DeleteImageButton { profileImage.value = null }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (BuildConfigCommon.SIMPLEX_ASSETS) {
|
||||
Image(
|
||||
painterResource(if (isInDarkTheme()) MR.images.create_channel_light else MR.images.create_channel),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Fit,
|
||||
modifier = Modifier.height(140.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING_HALF).fillMaxWidth(),
|
||||
|
||||
+5
-1
@@ -109,7 +109,11 @@ fun AddGroupLayout(
|
||||
horizontalArrangement = if (BuildConfigCommon.SIMPLEX_ASSETS) Arrangement.SpaceEvenly else Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
// Padding offsets transparent space built into 3D asset
|
||||
Box(
|
||||
modifier = if (BuildConfigCommon.SIMPLEX_ASSETS) Modifier.padding(horizontal = 3.dp) else Modifier,
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Box(contentAlignment = Alignment.TopEnd) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
ProfileImage(128.dp, image = profileImage.value, icon = MR.images.ic_supervised_user_circle_filled)
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="M0,0h24v24H0z" fill="#00000000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 175 B |
+4
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="M0,0h24v24H0z" fill="#00000000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 175 B |
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
@@ -16,5 +16,5 @@ security unlock-keychain -p "" /tmp/simplex.keychain
|
||||
security list-keychains -s `security list-keychains | xargs` /tmp/simplex.keychain
|
||||
scripts/desktop/build-lib-mac.sh
|
||||
cd apps/multiplatform
|
||||
./gradlew -Psimplex.assets.dir=../../assets packageDmg
|
||||
./gradlew -Psimplex.assets.dir=../../assets notarizeDmg
|
||||
./gradlew -Psimplex.assets.dir="$ASSETS_DIR" packageDmg
|
||||
./gradlew -Psimplex.assets.dir="$ASSETS_DIR" notarizeDmg
|
||||
|
||||
@@ -18,7 +18,7 @@ libcrypto_path=$(ldd common/src/commonMain/cpp/desktop/libs/*/libHSdirect-sqlcip
|
||||
trap "rm common/src/commonMain/cpp/desktop/libs/*/`basename $libcrypto_path` 2> /dev/null || true" EXIT
|
||||
cp $libcrypto_path common/src/commonMain/cpp/desktop/libs/*
|
||||
|
||||
./gradlew -Psimplex.assets.dir=../../assets createDistributable
|
||||
./gradlew -Psimplex.assets.dir="$ASSETS_DIR" createDistributable
|
||||
rm common/src/commonMain/cpp/desktop/libs/*/`basename $libcrypto_path`
|
||||
|
||||
rm -rf $release_app_dir/AppDir 2>/dev/null
|
||||
|
||||
@@ -4,7 +4,7 @@ ARCH="$(uname -m)"
|
||||
|
||||
scripts/desktop/build-lib-linux.sh
|
||||
cd apps/multiplatform
|
||||
./gradlew -Psimplex.assets.dir=../../assets packageDeb
|
||||
./gradlew -Psimplex.assets.dir="$ASSETS_DIR" packageDeb
|
||||
|
||||
# Workaround for skiko library
|
||||
#
|
||||
|
||||
@@ -110,7 +110,7 @@ for os_pair in ${oses}; do
|
||||
# Desktop: deb
|
||||
docker exec \
|
||||
-t "${container_name}" \
|
||||
sh -c './scripts/desktop/make-deb-linux.sh'
|
||||
sh -c "export ASSETS_DIR='../../assets'; ./scripts/desktop/make-deb-linux.sh"
|
||||
|
||||
# Copy deb
|
||||
docker cp \
|
||||
@@ -128,7 +128,7 @@ for os_pair in ${oses}; do
|
||||
# Appimage
|
||||
docker exec \
|
||||
-t "${container_name}" \
|
||||
sh -c './scripts/desktop/make-appimage-linux.sh && mv ./apps/multiplatform/release/main/*imple*.AppImage ./apps/multiplatform/release/main/simplex.appimage'
|
||||
sh -c "export ASSETS_DIR='../../assets'; ./scripts/desktop/make-appimage-linux.sh && mv ./apps/multiplatform/release/main/*imple*.AppImage ./apps/multiplatform/release/main/simplex.appimage"
|
||||
|
||||
# Copy appimage
|
||||
docker cp \
|
||||
|
||||
Reference in New Issue
Block a user