Merge branch 'master' into remote-desktop

This commit is contained in:
Evgeny Poberezkin 2023-10-05 14:15:25 +01:00
commit 27e8a81c9f
21 changed files with 419 additions and 338 deletions

View File

@ -9,6 +9,18 @@
import SwiftUI import SwiftUI
import SimpleXChat import SimpleXChat
enum GroupProfileAlert: Identifiable {
case saveError(err: String)
case invalidName(validName: String)
var id: String {
switch self {
case let .saveError(err): return "saveError \(err)"
case let .invalidName(validName): return "invalidName \(validName)"
}
}
}
struct GroupProfileView: View { struct GroupProfileView: View {
@EnvironmentObject var chatModel: ChatModel @EnvironmentObject var chatModel: ChatModel
@Environment(\.dismiss) var dismiss: DismissAction @Environment(\.dismiss) var dismiss: DismissAction
@ -18,8 +30,7 @@ struct GroupProfileView: View {
@State private var showImagePicker = false @State private var showImagePicker = false
@State private var showTakePhoto = false @State private var showTakePhoto = false
@State private var chosenImage: UIImage? = nil @State private var chosenImage: UIImage? = nil
@State private var showSaveErrorAlert = false @State private var alert: GroupProfileAlert?
@State private var saveGroupError: String? = nil
@FocusState private var focusDisplayName @FocusState private var focusDisplayName
var body: some View { var body: some View {
@ -47,20 +58,29 @@ struct GroupProfileView: View {
.frame(maxWidth: .infinity, alignment: .center) .frame(maxWidth: .infinity, alignment: .center)
VStack(alignment: .leading) { VStack(alignment: .leading) {
ZStack(alignment: .leading) { ZStack(alignment: .topLeading) {
if !validDisplayName(groupProfile.displayName) { if !validNewProfileName() {
Image(systemName: "exclamationmark.circle") Button {
.foregroundColor(.red) alert = .invalidName(validName: mkValidName(groupProfile.displayName))
.padding(.bottom, 10) } label: {
Image(systemName: "exclamationmark.circle").foregroundColor(.red)
}
} else {
Image(systemName: "exclamationmark.circle").foregroundColor(.clear)
} }
profileNameTextEdit("Group display name", $groupProfile.displayName) profileNameTextEdit("Group display name", $groupProfile.displayName)
.focused($focusDisplayName) .focused($focusDisplayName)
} }
profileNameTextEdit("Group full name (optional)", $groupProfile.fullName) .padding(.bottom)
let fullName = groupInfo.groupProfile.fullName
if fullName != "" && fullName != groupProfile.displayName {
profileNameTextEdit("Group full name (optional)", $groupProfile.fullName)
.padding(.bottom)
}
HStack(spacing: 20) { HStack(spacing: 20) {
Button("Cancel") { dismiss() } Button("Cancel") { dismiss() }
Button("Save group profile") { saveProfile() } Button("Save group profile") { saveProfile() }
.disabled(groupProfile.displayName == "" || !validDisplayName(groupProfile.displayName)) .disabled(groupProfile.displayName == "" || !validNewProfileName())
} }
} }
.frame(maxWidth: .infinity, minHeight: 120, alignment: .leading) .frame(maxWidth: .infinity, minHeight: 120, alignment: .leading)
@ -99,27 +119,35 @@ struct GroupProfileView: View {
focusDisplayName = true focusDisplayName = true
} }
} }
.alert(isPresented: $showSaveErrorAlert) { .alert(item: $alert) { a in
Alert( switch a {
title: Text("Error saving group profile"), case let .saveError(err):
message: Text("\(saveGroupError ?? "Unexpected error")") return Alert(
) title: Text("Error saving group profile"),
message: Text(err)
)
case let .invalidName(name):
return createInvalidNameAlert(name, $groupProfile.displayName)
}
} }
.contentShape(Rectangle()) .contentShape(Rectangle())
.onTapGesture { hideKeyboard() } .onTapGesture { hideKeyboard() }
} }
private func validNewProfileName() -> Bool {
groupProfile.displayName == groupInfo.groupProfile.displayName
|| validDisplayName(groupProfile.displayName.trimmingCharacters(in: .whitespaces))
}
func profileNameTextEdit(_ label: LocalizedStringKey, _ name: Binding<String>) -> some View { func profileNameTextEdit(_ label: LocalizedStringKey, _ name: Binding<String>) -> some View {
TextField(label, text: name) TextField(label, text: name)
.textInputAutocapitalization(.never) .padding(.leading, 32)
.disableAutocorrection(true)
.padding(.bottom)
.padding(.leading, 28)
} }
func saveProfile() { func saveProfile() {
Task { Task {
do { do {
groupProfile.displayName = groupProfile.displayName.trimmingCharacters(in: .whitespaces)
let gInfo = try await apiUpdateGroup(groupInfo.groupId, groupProfile) let gInfo = try await apiUpdateGroup(groupInfo.groupId, groupProfile)
await MainActor.run { await MainActor.run {
groupInfo = gInfo groupInfo = gInfo
@ -128,8 +156,7 @@ struct GroupProfileView: View {
} }
} catch let error { } catch let error {
let err = responseError(error) let err = responseError(error)
saveGroupError = err alert = .saveError(err: err)
showSaveErrorAlert = true
logger.error("GroupProfile apiUpdateGroup error: \(err)") logger.error("GroupProfile apiUpdateGroup error: \(err)")
} }
} }

View File

@ -16,11 +16,11 @@ struct AddGroupView: View {
@State private var groupInfo: GroupInfo? @State private var groupInfo: GroupInfo?
@State private var profile = GroupProfile(displayName: "", fullName: "") @State private var profile = GroupProfile(displayName: "", fullName: "")
@FocusState private var focusDisplayName @FocusState private var focusDisplayName
@FocusState private var focusFullName
@State private var showChooseSource = false @State private var showChooseSource = false
@State private var showImagePicker = false @State private var showImagePicker = false
@State private var showTakePhoto = false @State private var showTakePhoto = false
@State private var chosenImage: UIImage? = nil @State private var chosenImage: UIImage? = nil
@State private var showInvalidNameAlert = false
var body: some View { var body: some View {
if let chat = chat, let groupInfo = groupInfo { if let chat = chat, let groupInfo = groupInfo {
@ -76,26 +76,24 @@ struct AddGroupView: View {
.padding(.bottom, 4) .padding(.bottom, 4)
ZStack(alignment: .topLeading) { ZStack(alignment: .topLeading) {
if !validDisplayName(profile.displayName) { let name = profile.displayName.trimmingCharacters(in: .whitespaces)
Image(systemName: "exclamationmark.circle") if name != mkValidName(name) {
.foregroundColor(.red) Button {
.padding(.top, 4) showInvalidNameAlert = true
} label: {
Image(systemName: "exclamationmark.circle").foregroundColor(.red)
}
} else {
Image(systemName: "exclamationmark.circle").foregroundColor(.clear)
} }
textField("Group display name", text: $profile.displayName) textField("Enter group name…", text: $profile.displayName)
.focused($focusDisplayName) .focused($focusDisplayName)
.submitLabel(.next) .submitLabel(.go)
.onSubmit { .onSubmit {
if canCreateProfile() { focusFullName = true } if canCreateProfile() { createGroup() }
else { focusDisplayName = true }
} }
} }
textField("Group full name (optional)", text: $profile.fullName) .padding(.bottom)
.focused($focusFullName)
.submitLabel(.go)
.onSubmit {
if canCreateProfile() { createGroup() }
else { focusFullName = true }
}
Spacer() Spacer()
@ -133,6 +131,9 @@ struct AddGroupView: View {
didSelectItem in showImagePicker = false didSelectItem in showImagePicker = false
} }
} }
.alert(isPresented: $showInvalidNameAlert) {
createInvalidNameAlert(mkValidName(profile.displayName), $profile.displayName)
}
.onChange(of: chosenImage) { image in .onChange(of: chosenImage) { image in
if let image = image { if let image = image {
profile.image = resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500) profile.image = resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500)
@ -146,15 +147,13 @@ struct AddGroupView: View {
func textField(_ placeholder: LocalizedStringKey, text: Binding<String>) -> some View { func textField(_ placeholder: LocalizedStringKey, text: Binding<String>) -> some View {
TextField(placeholder, text: text) TextField(placeholder, text: text)
.textInputAutocapitalization(.never) .padding(.leading, 32)
.disableAutocorrection(true)
.padding(.leading, 28)
.padding(.bottom)
} }
func createGroup() { func createGroup() {
hideKeyboard() hideKeyboard()
do { do {
profile.displayName = profile.displayName.trimmingCharacters(in: .whitespaces)
let gInfo = try apiNewGroup(profile) let gInfo = try apiNewGroup(profile)
Task { Task {
let groupMembers = await apiListMembers(gInfo.groupId) let groupMembers = await apiListMembers(gInfo.groupId)
@ -180,7 +179,7 @@ struct AddGroupView: View {
} }
func canCreateProfile() -> Bool { func canCreateProfile() -> Bool {
profile.displayName != "" && validDisplayName(profile.displayName) profile.displayName != "" && validDisplayName(profile.displayName.trimmingCharacters(in: .whitespaces))
} }
} }

View File

@ -9,175 +9,244 @@
import SwiftUI import SwiftUI
import SimpleXChat import SimpleXChat
enum UserProfileAlert: Identifiable {
case duplicateUserError
case createUserError(error: LocalizedStringKey)
case invalidNameError(validName: String)
var id: String {
switch self {
case .duplicateUserError: return "duplicateUserError"
case .createUserError: return "createUserError"
case let .invalidNameError(validName): return "invalidNameError \(validName)"
}
}
}
struct CreateProfile: View { struct CreateProfile: View {
@Environment(\.dismiss) var dismiss
@State private var displayName: String = ""
@FocusState private var focusDisplayName
@State private var alert: UserProfileAlert?
var body: some View {
List {
Section {
TextField("Enter your name…", text: $displayName)
.focused($focusDisplayName)
Button {
createProfile(displayName, showAlert: { alert = $0 }, dismiss: dismiss)
} label: {
Label("Create profile", systemImage: "checkmark")
}
.disabled(!canCreateProfile(displayName))
} header: {
HStack {
Text("Your profile")
let name = displayName.trimmingCharacters(in: .whitespaces)
let validName = mkValidName(name)
if name != validName {
Spacer()
Image(systemName: "exclamationmark.circle")
.foregroundColor(.red)
.onTapGesture {
alert = .invalidNameError(validName: validName)
}
}
}
.frame(height: 20)
} footer: {
VStack(alignment: .leading, spacing: 8) {
Text("Your profile, contacts and delivered messages are stored on your device.")
Text("The profile is only shared with your contacts.")
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
.navigationTitle("Create your profile")
.alert(item: $alert) { a in userProfileAlert(a, $displayName) }
.onAppear() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
focusDisplayName = true
}
}
.keyboardPadding()
}
}
struct CreateFirstProfile: View {
@EnvironmentObject var m: ChatModel @EnvironmentObject var m: ChatModel
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
@State private var displayName: String = "" @State private var displayName: String = ""
@State private var fullName: String = ""
@FocusState private var focusDisplayName @FocusState private var focusDisplayName
@FocusState private var focusFullName
@State private var alert: CreateProfileAlert?
private enum CreateProfileAlert: Identifiable {
case duplicateUserError
case createUserError(error: LocalizedStringKey)
var id: String {
switch self {
case .duplicateUserError: return "duplicateUserError"
case .createUserError: return "createUserError"
}
}
}
var body: some View { var body: some View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
Text("Create your profile") Group {
.font(.largeTitle) Text("Create your profile")
.bold() .font(.largeTitle)
.padding(.bottom, 4) .bold()
.frame(maxWidth: .infinity) Text("Your profile, contacts and delivered messages are stored on your device.")
Text("Your profile, contacts and delivered messages are stored on your device.") .foregroundColor(.secondary)
.padding(.bottom, 4) Text("The profile is only shared with your contacts.")
Text("The profile is only shared with your contacts.") .foregroundColor(.secondary)
.padding(.bottom) .padding(.bottom)
}
.padding(.bottom)
ZStack(alignment: .topLeading) { ZStack(alignment: .topLeading) {
if !validDisplayName(displayName) { let name = displayName.trimmingCharacters(in: .whitespaces)
Image(systemName: "exclamationmark.circle") let validName = mkValidName(name)
.foregroundColor(.red) if name != validName {
.padding(.top, 4) Button {
showAlert(.invalidNameError(validName: validName))
} label: {
Image(systemName: "exclamationmark.circle").foregroundColor(.red)
}
} else {
Image(systemName: "exclamationmark.circle").foregroundColor(.clear)
} }
textField("Display name", text: $displayName) TextField("Enter your name…", text: $displayName)
.focused($focusDisplayName) .focused($focusDisplayName)
.submitLabel(.next) .padding(.leading, 32)
.onSubmit {
if canCreateProfile() { focusFullName = true }
else { focusDisplayName = true }
}
} }
textField("Full name (optional)", text: $fullName) .padding(.bottom)
.focused($focusFullName)
.submitLabel(.go)
.onSubmit {
if canCreateProfile() { createProfile() }
else { focusFullName = true }
}
Spacer() Spacer()
onboardingButtons()
HStack {
if m.users.isEmpty {
Button {
hideKeyboard()
withAnimation {
m.onboardingStage = .step1_SimpleXInfo
}
} label: {
HStack {
Image(systemName: "lessthan")
Text("About SimpleX")
}
}
}
Spacer()
HStack {
Button {
createProfile()
} label: {
Text("Create")
Image(systemName: "greaterthan")
}
.disabled(!canCreateProfile())
}
}
} }
.onAppear() { .onAppear() {
focusDisplayName = true focusDisplayName = true
setLastVersionDefault() setLastVersionDefault()
} }
.alert(item: $alert) { a in
switch a {
case .duplicateUserError: return duplicateUserAlert
case let .createUserError(err): return creatUserErrorAlert(err)
}
}
.padding() .padding()
.frame(maxWidth: .infinity, alignment: .leading)
.keyboardPadding() .keyboardPadding()
} }
func textField(_ placeholder: LocalizedStringKey, text: Binding<String>) -> some View { func onboardingButtons() -> some View {
TextField(placeholder, text: text) HStack {
.textInputAutocapitalization(.never) Button {
.disableAutocorrection(true) hideKeyboard()
.padding(.leading, 28)
.padding(.bottom)
}
func createProfile() {
hideKeyboard()
let profile = Profile(
displayName: displayName,
fullName: fullName
)
do {
m.currentUser = try apiCreateActiveUser(profile)
if m.users.isEmpty {
try startChat()
withAnimation { withAnimation {
onboardingStageDefault.set(.step3_CreateSimpleXAddress) m.onboardingStage = .step1_SimpleXInfo
m.onboardingStage = .step3_CreateSimpleXAddress
} }
} else { } label: {
onboardingStageDefault.set(.onboardingComplete) HStack {
m.onboardingStage = .onboardingComplete Image(systemName: "lessthan")
dismiss() Text("About SimpleX")
m.users = try listUsers()
try getUserChatData()
}
} catch let error {
switch error as? ChatResponse {
case .chatCmdError(_, .errorStore(.duplicateName)),
.chatCmdError(_, .error(.userExists)):
if m.currentUser == nil {
AlertManager.shared.showAlert(duplicateUserAlert)
} else {
alert = .duplicateUserError
}
default:
let err: LocalizedStringKey = "Error: \(responseError(error))"
if m.currentUser == nil {
AlertManager.shared.showAlert(creatUserErrorAlert(err))
} else {
alert = .createUserError(error: err)
} }
} }
logger.error("Failed to create user or start chat: \(responseError(error))")
Spacer()
Button {
createProfile(displayName, showAlert: showAlert, dismiss: dismiss)
} label: {
HStack {
Text("Create")
Image(systemName: "greaterthan")
}
}
.disabled(!canCreateProfile(displayName))
} }
} }
func canCreateProfile() -> Bool { private func showAlert(_ alert: UserProfileAlert) {
displayName != "" && validDisplayName(displayName) AlertManager.shared.showAlert(userProfileAlert(alert, $displayName))
}
private var duplicateUserAlert: Alert {
Alert(
title: Text("Duplicate display name!"),
message: Text("You already have a chat profile with the same display name. Please choose another name.")
)
}
private func creatUserErrorAlert(_ err: LocalizedStringKey) -> Alert {
Alert(
title: Text("Error creating profile!"),
message: Text(err)
)
} }
} }
private func createProfile(_ displayName: String, showAlert: (UserProfileAlert) -> Void, dismiss: DismissAction) {
hideKeyboard()
let profile = Profile(
displayName: displayName.trimmingCharacters(in: .whitespaces),
fullName: ""
)
let m = ChatModel.shared
do {
m.currentUser = try apiCreateActiveUser(profile)
if m.users.isEmpty {
try startChat()
withAnimation {
onboardingStageDefault.set(.step3_CreateSimpleXAddress)
m.onboardingStage = .step3_CreateSimpleXAddress
}
} else {
onboardingStageDefault.set(.onboardingComplete)
m.onboardingStage = .onboardingComplete
dismiss()
m.users = try listUsers()
try getUserChatData()
}
} catch let error {
switch error as? ChatResponse {
case .chatCmdError(_, .errorStore(.duplicateName)),
.chatCmdError(_, .error(.userExists)):
if m.currentUser == nil {
AlertManager.shared.showAlert(duplicateUserAlert)
} else {
showAlert(.duplicateUserError)
}
default:
let err: LocalizedStringKey = "Error: \(responseError(error))"
if m.currentUser == nil {
AlertManager.shared.showAlert(creatUserErrorAlert(err))
} else {
showAlert(.createUserError(error: err))
}
}
logger.error("Failed to create user or start chat: \(responseError(error))")
}
}
private func canCreateProfile(_ displayName: String) -> Bool {
let name = displayName.trimmingCharacters(in: .whitespaces)
return name != "" && mkValidName(name) == name
}
func userProfileAlert(_ alert: UserProfileAlert, _ displayName: Binding<String>) -> Alert {
switch alert {
case .duplicateUserError: return duplicateUserAlert
case let .createUserError(err): return creatUserErrorAlert(err)
case let .invalidNameError(name): return createInvalidNameAlert(name, displayName)
}
}
private var duplicateUserAlert: Alert {
Alert(
title: Text("Duplicate display name!"),
message: Text("You already have a chat profile with the same display name. Please choose another name.")
)
}
private func creatUserErrorAlert(_ err: LocalizedStringKey) -> Alert {
Alert(
title: Text("Error creating profile!"),
message: Text(err)
)
}
func createInvalidNameAlert(_ name: String, _ displayName: Binding<String>) -> Alert {
name == ""
? Alert(title: Text("Invalid name!"))
: Alert(
title: Text("Invalid name!"),
message: Text("Correct name to \(name)?"),
primaryButton: .default(
Text("Ok"),
action: { displayName.wrappedValue = name }
),
secondaryButton: .cancel()
)
}
func validDisplayName(_ name: String) -> Bool { func validDisplayName(_ name: String) -> Bool {
name.firstIndex(of: " ") == nil && name.first != "@" && name.first != "#" mkValidName(name.trimmingCharacters(in: .whitespaces)) == name
}
func mkValidName(_ s: String) -> String {
var c = s.cString(using: .utf8)!
return fromCString(chat_valid_name(&c)!)
} }
struct CreateProfile_Previews: PreviewProvider { struct CreateProfile_Previews: PreviewProvider {

View File

@ -14,7 +14,7 @@ struct OnboardingView: View {
var body: some View { var body: some View {
switch onboarding { switch onboarding {
case .step1_SimpleXInfo: SimpleXInfo(onboarding: true) case .step1_SimpleXInfo: SimpleXInfo(onboarding: true)
case .step2_CreateProfile: CreateProfile() case .step2_CreateProfile: CreateFirstProfile()
case .step3_CreateSimpleXAddress: CreateSimpleXAddress() case .step3_CreateSimpleXAddress: CreateSimpleXAddress()
case .step4_SetNotificationsMode: SetNotificationsMode() case .step4_SetNotificationsMode: SetNotificationsMode()
case .onboardingComplete: EmptyView() case .onboardingComplete: EmptyView()

View File

@ -381,7 +381,9 @@ struct ProfilePreview: View {
Text(profileOf.displayName) Text(profileOf.displayName)
.fontWeight(.bold) .fontWeight(.bold)
.font(.title2) .font(.title2)
Text(profileOf.fullName) if profileOf.fullName != "" && profileOf.fullName != profileOf.displayName {
Text(profileOf.fullName)
}
} }
} }
} }

View File

@ -17,6 +17,8 @@ struct UserProfile: View {
@State private var showImagePicker = false @State private var showImagePicker = false
@State private var showTakePhoto = false @State private var showTakePhoto = false
@State private var chosenImage: UIImage? = nil @State private var chosenImage: UIImage? = nil
@State private var alert: UserProfileAlert?
@FocusState private var focusDisplayName
var body: some View { var body: some View {
let user: User = chatModel.currentUser! let user: User = chatModel.currentUser!
@ -47,18 +49,27 @@ struct UserProfile: View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
ZStack(alignment: .leading) { ZStack(alignment: .leading) {
if !validDisplayName(profile.displayName) { if !validNewProfileName(user) {
Image(systemName: "exclamationmark.circle") Button {
.foregroundColor(.red) alert = .invalidNameError(validName: mkValidName(profile.displayName))
.padding(.bottom, 10) } label: {
Image(systemName: "exclamationmark.circle").foregroundColor(.red)
}
} else {
Image(systemName: "exclamationmark.circle").foregroundColor(.clear)
} }
profileNameTextEdit("Display name", $profile.displayName) profileNameTextEdit("Profile name", $profile.displayName)
.focused($focusDisplayName)
}
.padding(.bottom)
if showFullName(user) {
profileNameTextEdit("Full name (optional)", $profile.fullName)
.padding(.bottom)
} }
profileNameTextEdit("Full name (optional)", $profile.fullName)
HStack(spacing: 20) { HStack(spacing: 20) {
Button("Cancel") { editProfile = false } Button("Cancel") { editProfile = false }
Button("Save (and notify contacts)") { saveProfile() } Button("Save (and notify contacts)") { saveProfile() }
.disabled(profile.displayName == "" || !validDisplayName(profile.displayName)) .disabled(!canSaveProfile(user))
} }
} }
.frame(maxWidth: .infinity, minHeight: 120, alignment: .leading) .frame(maxWidth: .infinity, minHeight: 120, alignment: .leading)
@ -74,11 +85,14 @@ struct UserProfile: View {
.frame(maxWidth: .infinity, alignment: .center) .frame(maxWidth: .infinity, alignment: .center)
VStack(alignment: .leading) { VStack(alignment: .leading) {
profileNameView("Display name:", user.profile.displayName) profileNameView("Profile name:", user.profile.displayName)
profileNameView("Full name:", user.profile.fullName) if showFullName(user) {
profileNameView("Full name:", user.profile.fullName)
}
Button("Edit") { Button("Edit") {
profile = fromLocalProfile(user.profile) profile = fromLocalProfile(user.profile)
editProfile = true editProfile = true
focusDisplayName = true
} }
} }
.frame(maxWidth: .infinity, minHeight: 120, alignment: .leading) .frame(maxWidth: .infinity, minHeight: 120, alignment: .leading)
@ -117,14 +131,12 @@ struct UserProfile: View {
profile.image = nil profile.image = nil
} }
} }
.alert(item: $alert) { a in userProfileAlert(a, $profile.displayName) }
} }
func profileNameTextEdit(_ label: LocalizedStringKey, _ name: Binding<String>) -> some View { func profileNameTextEdit(_ label: LocalizedStringKey, _ name: Binding<String>) -> some View {
TextField(label, text: name) TextField(label, text: name)
.textInputAutocapitalization(.never) .padding(.leading, 32)
.disableAutocorrection(true)
.padding(.bottom)
.padding(.leading, 28)
} }
func profileNameView(_ label: LocalizedStringKey, _ name: String) -> some View { func profileNameView(_ label: LocalizedStringKey, _ name: String) -> some View {
@ -141,9 +153,22 @@ struct UserProfile: View {
showChooseSource = true showChooseSource = true
} }
private func validNewProfileName(_ user: User) -> Bool {
profile.displayName == user.profile.displayName || validDisplayName(profile.displayName.trimmingCharacters(in: .whitespaces))
}
private func showFullName(_ user: User) -> Bool {
user.profile.fullName != "" && user.profile.fullName != user.profile.displayName
}
private func canSaveProfile(_ user: User) -> Bool {
profile.displayName != "" && validNewProfileName(user)
}
func saveProfile() { func saveProfile() {
Task { Task {
do { do {
profile.displayName = profile.displayName.trimmingCharacters(in: .whitespaces)
if let (newProfile, _) = try await apiUpdateProfile(profile: profile) { if let (newProfile, _) = try await apiUpdateProfile(profile: profile) {
DispatchQueue.main.async { DispatchQueue.main.async {
chatModel.updateCurrentUser(newProfile) chatModel.updateCurrentUser(newProfile)

View File

@ -23,6 +23,7 @@ extern char *chat_recv_msg_wait(chat_ctrl ctl, int wait);
extern char *chat_parse_markdown(char *str); extern char *chat_parse_markdown(char *str);
extern char *chat_parse_server(char *str); extern char *chat_parse_server(char *str);
extern char *chat_password_hash(char *pwd, char *salt); extern char *chat_password_hash(char *pwd, char *salt);
extern char *chat_valid_name(char *name);
extern char *chat_encrypt_media(char *key, char *frame, int len); extern char *chat_encrypt_media(char *key, char *frame, int len);
extern char *chat_decrypt_media(char *key, char *frame, int len); extern char *chat_decrypt_media(char *key, char *frame, int len);

View File

@ -11,7 +11,6 @@
local.properties local.properties
common/src/commonMain/cpp/android/libs/ common/src/commonMain/cpp/android/libs/
common/src/commonMain/cpp/desktop/libs/ common/src/commonMain/cpp/desktop/libs/
desktop/src/jvmMain/resources/libs/
android/build android/build
android/release android/release
common/build common/build

View File

@ -54,12 +54,11 @@ add_library( # Sets the name of the library.
simplex-api.c) simplex-api.c)
add_library( simplex SHARED IMPORTED ) add_library( simplex SHARED IMPORTED )
# Lib has different name because of version, find it
FILE(GLOB SIMPLEXLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/lib*simplex*.${OS_LIB_EXT})
if(WIN32) if(WIN32)
FILE(GLOB SIMPLEXLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/lib*simplex*.${OS_LIB_EXT})
set_target_properties( simplex PROPERTIES IMPORTED_IMPLIB ${SIMPLEXLIB}) set_target_properties( simplex PROPERTIES IMPORTED_IMPLIB ${SIMPLEXLIB})
else() else()
FILE(GLOB SIMPLEXLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/lib*simplex-chat*.${OS_LIB_EXT})
set_target_properties( simplex PROPERTIES IMPORTED_LOCATION ${SIMPLEXLIB}) set_target_properties( simplex PROPERTIES IMPORTED_LOCATION ${SIMPLEXLIB})
endif() endif()
@ -72,7 +71,7 @@ if(NOT APPLE)
else() else()
# Without direct linking it can't find hs_init in linking step # Without direct linking it can't find hs_init in linking step
add_library( rts SHARED IMPORTED ) add_library( rts SHARED IMPORTED )
FILE(GLOB RTSLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/deps/libHSrts*_thr-*.${OS_LIB_EXT}) FILE(GLOB RTSLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/libHSrts*_thr-*.${OS_LIB_EXT})
set_target_properties( rts PROPERTIES IMPORTED_LOCATION ${RTSLIB}) set_target_properties( rts PROPERTIES IMPORTED_LOCATION ${RTSLIB})
target_link_libraries(app-lib rts simplex) target_link_libraries(app-lib rts simplex)

View File

@ -21,8 +21,6 @@ actual val agentDatabaseFileName: String = "simplex_v1_agent.db"
actual val databaseExportDir: File = tmpDir actual val databaseExportDir: File = tmpDir
val vlcDir: File = File(System.getProperty("java.io.tmpdir") + File.separator + "simplex-vlc").also { it.deleteOnExit() }
actual fun desktopOpenDatabaseDir() { actual fun desktopOpenDatabaseDir() {
if (Desktop.isDesktopSupported()) { if (Desktop.isDesktopSupported()) {
try { try {

View File

@ -8,12 +8,12 @@ private val unixConfigPath = (System.getenv("XDG_CONFIG_HOME") ?: "$home/.config
private val unixDataPath = (System.getenv("XDG_DATA_HOME") ?: "$home/.local/share") + "/simplex" private val unixDataPath = (System.getenv("XDG_DATA_HOME") ?: "$home/.local/share") + "/simplex"
val desktopPlatform = detectDesktopPlatform() val desktopPlatform = detectDesktopPlatform()
enum class DesktopPlatform(val libPath: String, val libExtension: String, val configPath: String, val dataPath: String) { enum class DesktopPlatform(val libExtension: String, val configPath: String, val dataPath: String) {
LINUX_X86_64("/libs/linux-x86_64", "so", unixConfigPath, unixDataPath), LINUX_X86_64("so", unixConfigPath, unixDataPath),
LINUX_AARCH64("/libs/aarch64", "so", unixConfigPath, unixDataPath), LINUX_AARCH64("so", unixConfigPath, unixDataPath),
WINDOWS_X86_64("/libs/windows-x86_64", "dll", System.getenv("AppData") + File.separator + "SimpleX", System.getenv("AppData") + File.separator + "SimpleX"), WINDOWS_X86_64("dll", System.getenv("AppData") + File.separator + "SimpleX", System.getenv("AppData") + File.separator + "SimpleX"),
MAC_X86_64("/libs/mac-x86_64", "dylib", unixConfigPath, unixDataPath), MAC_X86_64("dylib", unixConfigPath, unixDataPath),
MAC_AARCH64("/libs/mac-aarch64", "dylib", unixConfigPath, unixDataPath); MAC_AARCH64("dylib", unixConfigPath, unixDataPath);
fun isLinux() = this == LINUX_X86_64 || this == LINUX_AARCH64 fun isLinux() = this == LINUX_X86_64 || this == LINUX_AARCH64
fun isWindows() = this == WINDOWS_X86_64 fun isWindows() = this == WINDOWS_X86_64

View File

@ -52,6 +52,7 @@ compose {
} }
//includeAllModules = true //includeAllModules = true
outputBaseDir.set(project.file("../release")) outputBaseDir.set(project.file("../release"))
appResourcesRootDir.set(project.file("../build/links"))
targetFormats( targetFormats(
TargetFormat.Deb, TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Exe TargetFormat.Deb, TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Exe
//, TargetFormat.AppImage // Gradle doesn't sync on Mac with it //, TargetFormat.AppImage // Gradle doesn't sync on Mac with it
@ -156,11 +157,10 @@ tasks.named("compileJava") {
afterEvaluate { afterEvaluate {
tasks.create("cmakeBuildAndCopy") { tasks.create("cmakeBuildAndCopy") {
dependsOn("cmakeBuild") dependsOn("cmakeBuild")
val copyDetails = mutableMapOf<String, ArrayList<FileCopyDetails>>()
doLast { doLast {
copy { copy {
from("${project(":desktop").buildDir}/cmake/main/linux-amd64", "$cppPath/desktop/libs/linux-x86_64", "$cppPath/desktop/libs/linux-x86_64/deps") from("${project(":desktop").buildDir}/cmake/main/linux-amd64")
into("src/jvmMain/resources/libs/linux-x86_64") into("$cppPath/desktop/libs/linux-x86_64")
include("*.so*") include("*.so*")
eachFile { eachFile {
path = name path = name
@ -169,16 +169,8 @@ afterEvaluate {
duplicatesStrategy = DuplicatesStrategy.INCLUDE duplicatesStrategy = DuplicatesStrategy.INCLUDE
} }
copy { copy {
val destinationDir = "src/jvmMain/resources/libs/linux-x86_64/vlc" from("${project(":desktop").buildDir}/cmake/main/linux-aarch64")
from("$cppPath/desktop/libs/linux-x86_64/deps/vlc") into("$cppPath/desktop/libs/linux-aarch64")
into(destinationDir)
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
}
copy {
from("${project(":desktop").buildDir}/cmake/main/linux-aarch64", "$cppPath/desktop/libs/linux-aarch64", "$cppPath/desktop/libs/linux-aarch64/deps")
into("src/jvmMain/resources/libs/linux-aarch64")
include("*.so*") include("*.so*")
eachFile { eachFile {
path = name path = name
@ -187,16 +179,18 @@ afterEvaluate {
duplicatesStrategy = DuplicatesStrategy.INCLUDE duplicatesStrategy = DuplicatesStrategy.INCLUDE
} }
copy { copy {
val destinationDir = "src/jvmMain/resources/libs/linux-aarch64/vlc" from("${project(":desktop").buildDir}/cmake/main/windows-amd64")
from("$cppPath/desktop/libs/linux-aarch64/deps/vlc") into("$cppPath/desktop/libs/windows-x86_64")
into(destinationDir) include("*.dll")
eachFile {
path = name
}
includeEmptyDirs = false includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
} }
copy { copy {
from("${project(":desktop").buildDir}/cmake/main/windows-amd64", "$cppPath/desktop/libs/windows-x86_64", "$cppPath/desktop/libs/windows-x86_64/deps") from("${project(":desktop").buildDir}/cmake/main/windows-amd64")
into("src/jvmMain/resources/libs/windows-x86_64") into("../build/links/windows-x64")
include("*.dll") include("*.dll")
eachFile { eachFile {
path = name path = name
@ -205,16 +199,8 @@ afterEvaluate {
duplicatesStrategy = DuplicatesStrategy.INCLUDE duplicatesStrategy = DuplicatesStrategy.INCLUDE
} }
copy { copy {
val destinationDir = "src/jvmMain/resources/libs/windows-x86_64/vlc" from("${project(":desktop").buildDir}/cmake/main/mac-x86_64")
from("$cppPath/desktop/libs/windows-x86_64/deps/vlc") into("$cppPath/desktop/libs/mac-x86_64")
into(destinationDir)
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
}
copy {
from("${project(":desktop").buildDir}/cmake/main/mac-x86_64", "$cppPath/desktop/libs/mac-x86_64", "$cppPath/desktop/libs/mac-x86_64/deps")
into("src/jvmMain/resources/libs/mac-x86_64")
include("*.dylib") include("*.dylib")
eachFile { eachFile {
path = name path = name
@ -223,16 +209,8 @@ afterEvaluate {
duplicatesStrategy = DuplicatesStrategy.INCLUDE duplicatesStrategy = DuplicatesStrategy.INCLUDE
} }
copy { copy {
val destinationDir = "src/jvmMain/resources/libs/mac-x86_64/vlc" from("${project(":desktop").buildDir}/cmake/main/mac-aarch64")
from("$cppPath/desktop/libs/mac-x86_64/deps/vlc") into("$cppPath/desktop/libs/mac-aarch64")
into(destinationDir)
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
}
copy {
from("${project(":desktop").buildDir}/cmake/main/mac-aarch64", "$cppPath/desktop/libs/mac-aarch64", "$cppPath/desktop/libs/mac-aarch64/deps")
into("src/jvmMain/resources/libs/mac-aarch64")
include("*.dylib") include("*.dylib")
eachFile { eachFile {
path = name path = name
@ -240,39 +218,6 @@ afterEvaluate {
includeEmptyDirs = false includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE duplicatesStrategy = DuplicatesStrategy.INCLUDE
} }
copy {
val destinationDir = "src/jvmMain/resources/libs/mac-aarch64/vlc"
from("$cppPath/desktop/libs/mac-aarch64/deps/vlc")
into(destinationDir)
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
}
}
afterEvaluate {
doLast {
copyDetails.forEach { (destinationDir, details) ->
details.forEach { detail ->
val target = File(projectDir.absolutePath + File.separator + destinationDir + File.separator + detail.path)
if (target.exists()) {
target.setLastModified(detail.lastModified)
}
}
}
}
} }
} }
} }
fun CopySpec.copyIfNeeded(destinationDir: String, into: MutableMap<String, ArrayList<FileCopyDetails>>) {
val details = arrayListOf<FileCopyDetails>()
eachFile {
val targetFile = File(destinationDir, path)
if (file.lastModified() == targetFile.lastModified() && file.length() == targetFile.length()) {
exclude()
} else {
details.add(this)
}
}
into[destinationDir] = details
}

View File

@ -18,51 +18,29 @@ fun main() {
@Suppress("UnsafeDynamicallyLoadedCode") @Suppress("UnsafeDynamicallyLoadedCode")
private fun initHaskell() { private fun initHaskell() {
val libsTmpDir = File(tmpDir.absolutePath + File.separator + "libs") val resourcesDir = File(System.getProperty("compose.application.resources.dir"))
copyResources(desktopPlatform.libPath, libsTmpDir.toPath()) val vlcDir = File(resourcesDir.absolutePath + File.separator + "vlc")
vlcDir.deleteRecursively()
Files.move(File(libsTmpDir, "vlc").toPath(), vlcDir.toPath(), StandardCopyOption.REPLACE_EXISTING)
if (desktopPlatform == DesktopPlatform.WINDOWS_X86_64) { if (desktopPlatform == DesktopPlatform.WINDOWS_X86_64) {
windowsLoadRequiredLibs(libsTmpDir) windowsLoadRequiredLibs(resourcesDir, vlcDir)
} else { } else {
System.load(File(libsTmpDir, "libapp-lib.${desktopPlatform.libExtension}").absolutePath) System.load(File(resourcesDir, "libapp-lib.${desktopPlatform.libExtension}").absolutePath)
} }
// No picture without preloading it, only sound. However, with libs from AppImage it works without preloading // No picture without preloading it, only sound. However, with libs from AppImage it works without preloading
//val libXcb = "libvlc_xcb_events.so.0.0.0" //val libXcb = "libvlc_xcb_events.so.0.0.0"
//System.load(File(File(vlcDir, "vlc"), libXcb).absolutePath) //System.load(File(File(vlcDir, "vlc"), libXcb).absolutePath)
System.setProperty("jna.library.path", vlcDir.absolutePath) System.setProperty("jna.library.path", vlcDir.absolutePath)
//discoverVlcLibs(File(File(vlcDir, "vlc"), "plugins").absolutePath) //discoverVlcLibs(File(File(vlcDir, "vlc"), "plugins").absolutePath)
libsTmpDir.deleteRecursively()
initHS() initHS()
} }
private fun copyResources(from: String, to: Path) { private fun windowsLoadRequiredLibs(libsTmpDir: File, vlcDir: File) {
val resource = Class.forName("chat.simplex.desktop.MainKt").getResource("")!!.toURI()
val fileSystem = FileSystems.newFileSystem(resource, emptyMap<String, String>())
val resPath = fileSystem.getPath(from)
Files.walkFileTree(resPath, object: SimpleFileVisitor<Path>() {
override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult {
Files.createDirectories(to.resolve(resPath.relativize(dir).toString()))
return FileVisitResult.CONTINUE
}
override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
val dest = to.resolve(resPath.relativize(file).toString())
Files.copy(file, dest, StandardCopyOption.REPLACE_EXISTING)
// Setting the same time on file as the time set in script that generates VLC libs
if (dest.toString().contains("." + desktopPlatform.libExtension)) {
dest.setLastModifiedTime(FileTime.fromMillis(0))
}
return FileVisitResult.CONTINUE
}
})
}
private fun windowsLoadRequiredLibs(libsTmpDir: File) {
val mainLibs = arrayOf( val mainLibs = arrayOf(
"libcrypto-3-x64.dll", "libcrypto-3-x64.dll",
"mcfgthread-12.dll",
"libgcc_s_seh-1.dll",
"libstdc++-6.dll",
"libffi-8.dll", "libffi-8.dll",
"libgmp-10.dll", "libgmp-10.dll",
"libsimplex.dll", "libsimplex.dll",
"libapp-lib.dll" "libapp-lib.dll"
) )
@ -72,7 +50,7 @@ private fun windowsLoadRequiredLibs(libsTmpDir: File) {
val vlcLibs = arrayOf( val vlcLibs = arrayOf(
"libvlccore.dll", "libvlccore.dll",
"libvlc.dll", "libvlc.dll",
"axvlc.dll", "axvlc.dll",
"npvlc.dll" "npvlc.dll"
) )
vlcLibs.forEach { vlcLibs.forEach {

View File

@ -1,9 +1,23 @@
#!/bin/bash #!/bin/bash
set -e
function readlink() {
echo "$(cd "$(dirname "$1")"; pwd -P)"
}
OS=linux OS=linux
ARCH=${1:-`uname -a | rev | cut -d' ' -f2 | rev`} ARCH=${1:-`uname -a | rev | cut -d' ' -f2 | rev`}
GHC_VERSION=9.6.2 GHC_VERSION=9.6.2
if [ "$ARCH" == "aarch64" ]; then
COMPOSE_ARCH=arm64
else
COMPOSE_ARCH=x64
fi
root_dir="$(dirname "$(dirname "$(readlink "$0")")")"
cd $root_dir
BUILD_DIR=dist-newstyle/build/$ARCH-$OS/ghc-${GHC_VERSION}/simplex-chat-* BUILD_DIR=dist-newstyle/build/$ARCH-$OS/ghc-${GHC_VERSION}/simplex-chat-*
rm -rf $BUILD_DIR rm -rf $BUILD_DIR
@ -11,16 +25,20 @@ cabal build lib:simplex-chat --ghc-options='-optl-Wl,-rpath,$ORIGIN -flink-rts -
cd $BUILD_DIR/build cd $BUILD_DIR/build
#patchelf --add-needed libHSrts_thr-ghc${GHC_VERSION}.so libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so #patchelf --add-needed libHSrts_thr-ghc${GHC_VERSION}.so libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so
#patchelf --add-rpath '$ORIGIN' libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so #patchelf --add-rpath '$ORIGIN' libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so
mkdir deps mkdir deps 2> /dev/null || true
ldd libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so | grep "ghc" | cut -d' ' -f 3 | xargs -I {} cp {} ./deps/ ldd libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so | grep "ghc" | cut -d' ' -f 3 | xargs -I {} cp {} ./deps/
cd - cd -
rm -rf apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/ rm -rf apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
rm -rf apps/multiplatform/desktop/src/jvmMain/resources/libs/$OS-$ARCH/
rm -rf apps/multiplatform/desktop/build/cmake rm -rf apps/multiplatform/desktop/build/cmake
mkdir -p apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/ mkdir -p apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
cp -r $BUILD_DIR/build/deps apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/ cp -r $BUILD_DIR/build/deps/* apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
cp $BUILD_DIR/build/libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/ cp $BUILD_DIR/build/libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
scripts/desktop/prepare-vlc-linux.sh scripts/desktop/prepare-vlc-linux.sh
links_dir=apps/multiplatform/build/links
mkdir -p $links_dir
cd $links_dir
ln -sfT ../../common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/ $OS-$COMPOSE_ARCH

View File

@ -4,10 +4,13 @@ set -e
OS=mac OS=mac
ARCH="${1:-`uname -a | rev | cut -d' ' -f1 | rev`}" ARCH="${1:-`uname -a | rev | cut -d' ' -f1 | rev`}"
COMPOSE_ARCH=$ARCH
GHC_VERSION=9.6.2 GHC_VERSION=9.6.2
if [ "$ARCH" == "arm64" ]; then if [ "$ARCH" == "arm64" ]; then
ARCH=aarch64 ARCH=aarch64
else
COMPOSE_ARCH=x64
fi fi
LIB_EXT=dylib LIB_EXT=dylib
@ -84,30 +87,29 @@ rm deps/`basename $LIB`
cd - cd -
rm -rf apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/ rm -rf apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
rm -rf apps/multiplatform/desktop/src/jvmMain/resources/libs/$OS-$ARCH/
rm -rf apps/multiplatform/desktop/build/cmake rm -rf apps/multiplatform/desktop/build/cmake
mkdir -p apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/ mkdir -p apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
cp -r $BUILD_DIR/build/deps apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/ cp -r $BUILD_DIR/build/deps/* apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
cp $BUILD_DIR/build/libHSsimplex-chat-*-inplace-ghc*.$LIB_EXT apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/ cp $BUILD_DIR/build/libHSsimplex-chat-*-inplace-ghc*.$LIB_EXT apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
cd apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/ cd apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
LIBCRYPTO_PATH=$(otool -l deps/libHSdrct-*.$LIB_EXT | grep libcrypto | cut -d' ' -f11) LIBCRYPTO_PATH=$(otool -l libHSdrct-*.$LIB_EXT | grep libcrypto | cut -d' ' -f11)
install_name_tool -change $LIBCRYPTO_PATH @rpath/libcrypto.1.1.$LIB_EXT deps/libHSdrct-*.$LIB_EXT install_name_tool -change $LIBCRYPTO_PATH @rpath/libcrypto.1.1.$LIB_EXT libHSdrct-*.$LIB_EXT
cp $LIBCRYPTO_PATH deps/libcrypto.1.1.$LIB_EXT cp $LIBCRYPTO_PATH libcrypto.1.1.$LIB_EXT
chmod 755 deps/libcrypto.1.1.$LIB_EXT chmod 755 libcrypto.1.1.$LIB_EXT
install_name_tool -id "libcrypto.1.1.$LIB_EXT" deps/libcrypto.1.1.$LIB_EXT install_name_tool -id "libcrypto.1.1.$LIB_EXT" libcrypto.1.1.$LIB_EXT
install_name_tool -id "libffi.8.$LIB_EXT" deps/libffi.$LIB_EXT install_name_tool -id "libffi.8.$LIB_EXT" libffi.$LIB_EXT
LIBCRYPTO_PATH=$(otool -l $LIB | grep libcrypto | cut -d' ' -f11) LIBCRYPTO_PATH=$(otool -l $LIB | grep libcrypto | cut -d' ' -f11)
if [ -n "$LIBCRYPTO_PATH" ]; then if [ -n "$LIBCRYPTO_PATH" ]; then
install_name_tool -change $LIBCRYPTO_PATH @rpath/libcrypto.1.1.$LIB_EXT $LIB install_name_tool -change $LIBCRYPTO_PATH @rpath/libcrypto.1.1.$LIB_EXT $LIB
fi fi
LIBCRYPTO_PATH=$(otool -l deps/libHSsmplxmq*.$LIB_EXT | grep libcrypto | cut -d' ' -f11) LIBCRYPTO_PATH=$(otool -l libHSsmplxmq*.$LIB_EXT | grep libcrypto | cut -d' ' -f11)
if [ -n "$LIBCRYPTO_PATH" ]; then if [ -n "$LIBCRYPTO_PATH" ]; then
install_name_tool -change $LIBCRYPTO_PATH @rpath/libcrypto.1.1.$LIB_EXT deps/libHSsmplxmq*.$LIB_EXT install_name_tool -change $LIBCRYPTO_PATH @rpath/libcrypto.1.1.$LIB_EXT libHSsmplxmq*.$LIB_EXT
fi fi
for lib in $(find . -type f -name "*.$LIB_EXT"); do for lib in $(find . -type f -name "*.$LIB_EXT"); do
@ -126,3 +128,9 @@ fi
cd - cd -
scripts/desktop/prepare-vlc-mac.sh scripts/desktop/prepare-vlc-mac.sh
links_dir=apps/multiplatform/build/links
mkdir -p $links_dir
cd $links_dir
rm macos-$COMPOSE_ARCH 2>/dev/null | true
ln -sf ../../common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/ macos-$COMPOSE_ARCH

View File

@ -8,15 +8,26 @@ function readlink() {
root_dir="$(dirname "$(dirname "$(readlink "$0")")")" root_dir="$(dirname "$(dirname "$(readlink "$0")")")"
OS=windows OS=windows
ARCH=`uname -a | rev | cut -d' ' -f2 | rev` ARCH="x86_64"
JOB_REPO=${1:-$SIMPLEX_CI_REPO_URL} JOB_REPO=${1:-$SIMPLEX_CI_REPO_URL}
if [ "$ARCH" == "aarch64" ]; then
COMPOSE_ARCH=arm64
else
COMPOSE_ARCH=x64
fi
cd $root_dir cd $root_dir
rm -rf apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/ rm -rf apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
rm -rf apps/multiplatform/desktop/src/jvmMain/resources/libs/$OS-$ARCH/
rm -rf apps/multiplatform/desktop/build/cmake rm -rf apps/multiplatform/desktop/build/cmake
mkdir -p apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/ mkdir -p apps/multiplatform/common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/
scripts/desktop/download-lib-windows.sh $JOB_REPO scripts/desktop/download-lib-windows.sh $JOB_REPO
scripts/desktop/prepare-vlc-windows.sh scripts/desktop/prepare-vlc-windows.sh
links_dir=apps/multiplatform/build/links
mkdir -p $links_dir
cd $links_dir
rm -rf $OS-$COMPOSE_ARCH
ln -sfT ../../common/src/commonMain/cpp/desktop/libs/$OS-$ARCH/ $OS-$COMPOSE_ARCH

View File

@ -7,7 +7,7 @@ function readlink() {
} }
if [ -z "${1}" ]; then if [ -z "${1}" ]; then
echo "Job repo is unset. Provide it via first argument like: $(readlink "$0")/download-lib-windows.sh https://something.com/job/something/{windows,windows-8107}" echo "Job repo is unset. Provide it via first argument like: $(readlink "$0")/download-lib-windows.sh https://something.com/job/something/{master,stable}"
exit 1 exit 1
fi fi
@ -16,12 +16,15 @@ arch=x86_64
root_dir="$(dirname "$(dirname "$(readlink "$0")")")" root_dir="$(dirname "$(dirname "$(readlink "$0")")")"
output_dir="$root_dir/apps/multiplatform/common/src/commonMain/cpp/desktop/libs/windows-$arch/" output_dir="$root_dir/apps/multiplatform/common/src/commonMain/cpp/desktop/libs/windows-$arch/"
mkdir -p "$output_dir"/deps 2> /dev/null mkdir -p "$output_dir" 2> /dev/null
curl --location -o libsimplex.zip $job_repo/$arch-linux.$arch-windows:lib:simplex-chat/latest/download/1 && \ curl --location -o libsimplex.zip $job_repo/$arch-linux.$arch-windows:lib:simplex-chat/latest/download/1 && \
$WINDIR\\System32\\tar.exe -xf libsimplex.zip && \ $WINDIR\\System32\\tar.exe -xf libsimplex.zip && \
mv libsimplex.dll "$output_dir" && \ mv libsimplex.dll "$output_dir" && \
mv libcrypto*.dll "$output_dir/deps" && \ mv libcrypto*.dll "$output_dir" && \
mv libffi*.dll "$output_dir/deps" && \ mv libffi*.dll "$output_dir" && \
mv libgmp*.dll "$output_dir/deps" && \ mv libgmp*.dll "$output_dir" && \
mv mcfgthread*.dll "$output_dir" && \
mv libgcc_s_seh*.dll "$output_dir" && \
mv libstdc++*.dll "$output_dir" && \
rm libsimplex.zip rm libsimplex.zip

View File

@ -11,13 +11,12 @@ multiplatform_dir=$root_dir/apps/multiplatform
release_app_dir=$root_dir/apps/multiplatform/release/main/app release_app_dir=$root_dir/apps/multiplatform/release/main/app
cd $multiplatform_dir cd $multiplatform_dir
libcrypto_path=$(ldd common/src/commonMain/cpp/desktop/libs/*/deps/libHSdirect-sqlcipher-*.so | grep libcrypto | cut -d'=' -f 2 | cut -d ' ' -f 2) libcrypto_path=$(ldd common/src/commonMain/cpp/desktop/libs/*/libHSdirect-sqlcipher-*.so | grep libcrypto | cut -d'=' -f 2 | cut -d ' ' -f 2)
trap "rm common/src/commonMain/cpp/desktop/libs/*/deps/`basename $libcrypto_path` 2> /dev/null || true" EXIT 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/*/deps cp $libcrypto_path common/src/commonMain/cpp/desktop/libs/*
./gradlew createDistributable ./gradlew createDistributable
rm common/src/commonMain/cpp/desktop/libs/*/deps/`basename $libcrypto_path` rm common/src/commonMain/cpp/desktop/libs/*/`basename $libcrypto_path`
rm desktop/src/jvmMain/resources/libs/*/`basename $libcrypto_path`
rm -rf $release_app_dir/AppDir 2>/dev/null rm -rf $release_app_dir/AppDir 2>/dev/null
mkdir -p $release_app_dir/AppDir/usr mkdir -p $release_app_dir/AppDir/usr

View File

@ -6,7 +6,7 @@ function readlink() {
echo "$(cd "$(dirname "$1")"; pwd -P)" echo "$(cd "$(dirname "$1")"; pwd -P)"
} }
root_dir="$(dirname "$(dirname "$(readlink "$0")")")" root_dir="$(dirname "$(dirname "$(readlink "$0")")")"
vlc_dir=$root_dir/apps/multiplatform/common/src/commonMain/cpp/desktop/libs/linux-x86_64/deps/vlc vlc_dir=$root_dir/apps/multiplatform/common/src/commonMain/cpp/desktop/libs/linux-x86_64/vlc
mkdir $vlc_dir || exit 0 mkdir $vlc_dir || exit 0

View File

@ -16,7 +16,7 @@ function readlink() {
} }
root_dir="$(dirname "$(dirname "$(readlink "$0")")")" root_dir="$(dirname "$(dirname "$(readlink "$0")")")"
vlc_dir=$root_dir/apps/multiplatform/common/src/commonMain/cpp/desktop/libs/mac-$ARCH/deps/vlc vlc_dir=$root_dir/apps/multiplatform/common/src/commonMain/cpp/desktop/libs/mac-$ARCH/vlc
#rm -rf $vlc_dir #rm -rf $vlc_dir
mkdir -p $vlc_dir/vlc || exit 0 mkdir -p $vlc_dir/vlc || exit 0

View File

@ -6,7 +6,7 @@ function readlink() {
echo "$(cd "$(dirname "$1")"; pwd -P)" echo "$(cd "$(dirname "$1")"; pwd -P)"
} }
root_dir="$(dirname "$(dirname "$(readlink "$0")")")" root_dir="$(dirname "$(dirname "$(readlink "$0")")")"
vlc_dir=$root_dir/apps/multiplatform/common/src/commonMain/cpp/desktop/libs/windows-x86_64/deps/vlc vlc_dir=$root_dir/apps/multiplatform/common/src/commonMain/cpp/desktop/libs/windows-x86_64/vlc
rm -rf $vlc_dir rm -rf $vlc_dir
mkdir -p $vlc_dir/vlc || exit 0 mkdir -p $vlc_dir/vlc || exit 0