-Invest in SimpleX Chat. [Learn more on Wefunder](https://wefunder.com/simplexchat).
+Invest in SimpleX Chat. [Learn more on Wefunder](https://wefunder.com/simplex.chat?utm_source=github).
# SimpleX - the first messaging platform that has no user identifiers of any kind - 100% private by design!
diff --git a/apps/ios/Shared/Model/AppAPITypes.swift b/apps/ios/Shared/Model/AppAPITypes.swift
index 40b88ec338..db70511a5a 100644
--- a/apps/ios/Shared/Model/AppAPITypes.swift
+++ b/apps/ios/Shared/Model/AppAPITypes.swift
@@ -21,6 +21,7 @@ enum ChatCommand: ChatCmdProtocol {
case apiSetUserContactReceipts(userId: Int64, userMsgReceiptSettings: UserMsgReceiptSettings)
case apiSetUserGroupReceipts(userId: Int64, userMsgReceiptSettings: UserMsgReceiptSettings)
case apiSetUserAutoAcceptMemberContacts(userId: Int64, enable: Bool)
+ case apiSetUserAutoAcceptGroupInvitations(userId: Int64, enable: Bool)
case apiHideUser(userId: Int64, viewPwd: String)
case apiUnhideUser(userId: Int64, viewPwd: String)
case apiMuteUser(userId: Int64)
@@ -215,6 +216,8 @@ enum ChatCommand: ChatCmdProtocol {
return "/_set receipts groups \(userId) \(onOff(umrs.enable)) clear_overrides=\(onOff(umrs.clearOverrides))"
case let .apiSetUserAutoAcceptMemberContacts(userId, enable):
return "/_set accept member contacts \(userId) \(onOff(enable))"
+ case let .apiSetUserAutoAcceptGroupInvitations(userId, enable):
+ return "/_set accept group invitations \(userId) \(onOff(enable))"
case let .apiHideUser(userId, viewPwd): return "/_hide user \(userId) \(encodeJSON(viewPwd))"
case let .apiUnhideUser(userId, viewPwd): return "/_unhide user \(userId) \(encodeJSON(viewPwd))"
case let .apiMuteUser(userId): return "/_mute user \(userId)"
@@ -430,6 +433,7 @@ enum ChatCommand: ChatCmdProtocol {
case .apiSetUserContactReceipts: return "apiSetUserContactReceipts"
case .apiSetUserGroupReceipts: return "apiSetUserGroupReceipts"
case .apiSetUserAutoAcceptMemberContacts: return "apiSetUserAutoAcceptMemberContacts"
+ case .apiSetUserAutoAcceptGroupInvitations: return "apiSetUserAutoAcceptGroupInvitations"
case .apiHideUser: return "apiHideUser"
case .apiUnhideUser: return "apiUnhideUser"
case .apiMuteUser: return "apiMuteUser"
diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift
index 7a934fc746..74f97134d7 100644
--- a/apps/ios/Shared/Model/SimpleXAPI.swift
+++ b/apps/ios/Shared/Model/SimpleXAPI.swift
@@ -302,6 +302,10 @@ func apiSetUserAutoAcceptMemberContacts(_ userId: Int64, enable: Bool) async thr
try await sendCommandOkResp(.apiSetUserAutoAcceptMemberContacts(userId: userId, enable: enable))
}
+func apiSetUserAutoAcceptGroupInvitations(_ userId: Int64, enable: Bool) async throws {
+ try await sendCommandOkResp(.apiSetUserAutoAcceptGroupInvitations(userId: userId, enable: enable))
+}
+
func apiHideUser(_ userId: Int64, viewPwd: String) async throws -> User {
try await setUserPrivacy_(.apiHideUser(userId: userId, viewPwd: viewPwd))
}
diff --git a/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift b/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift
index 6ea24ec91c..4495fd8cc2 100644
--- a/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift
+++ b/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift
@@ -42,7 +42,10 @@ private struct FeatureView {
let view: () -> any View
}
-private let isInUS = SKStorefront().countryCode == "USA"
+let isInUS = {
+ let code = SKStorefront().countryCode
+ return code == "USA" || code == ""
+}()
private let versionDescriptions: [VersionDescription] = [
VersionDescription(
@@ -668,12 +671,12 @@ private let versionDescriptions: [VersionDescription] = [
]
),
VersionDescription(
- version: "v7.0",
+ version: isInUS ? "v7.0.1" : "v7.0",
post: nil,
features: (isInUS ? [
.view(FeatureView(
icon: nil,
- title: "Invest in SimpleX Chat",
+ title: "You can now invest in SimpleX Chat",
view: { InvestInSimpleXChat() }
))
] : []) + [
@@ -772,38 +775,130 @@ fileprivate struct CreateUpdateAddressShortLink: View {
}
fileprivate struct InvestInSimpleXChat: View {
- @Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
+ @State private var showGetStakeSheet = false
var body: some View {
- HStack(alignment: .top, spacing: 8) {
- VStack(alignment: .leading, spacing: 4) {
- HStack(alignment: .center, spacing: 4) {
- Image(systemName: "dollarsign.circle")
- .symbolRenderingMode(.monochrome)
- .foregroundColor(theme.colors.secondary)
- .frame(minWidth: 30, alignment: .center)
- Text(verbatim: "Invest in SimpleX Chat").font(.title3).bold()
- }
- Text(verbatim: "Equity crowdfunding launched!")
- .multilineTextAlignment(.leading)
- .lineLimit(2)
- if let url = URL("https://wefunder.com/simplexchat") {
- ExternalLink(destination: url) {
- HStack {
- Text(verbatim: "Learn more on Wefunder")
- Image(systemName: "arrow.up.right.circle")
- }
- }
- }
- }
- .frame(maxWidth: .infinity, alignment: .leading)
- Image(colorScheme == .light ? "own-stake" : "own-stake-light")
+ VStack(alignment: .leading, spacing: 4) {
+ Text("You can now invest in SimpleX Chat! 🚀").font(.title3).bold()
+ (Text("Crowdfunding on Wefunder.") + Text(verbatim: " ") + Text("Learn more").foregroundColor(theme.colors.primary))
+ .multilineTextAlignment(.leading)
+ .onTapGesture { showGetStakeSheet = true }
+ #if SIMPLEX_ASSETS
+ Image("crowdfunding_1")
.resizable()
- .scaledToFill()
- .frame(width: UIScreen.main.bounds.width / 5)
+ .scaledToFit()
+ .cornerRadius(12)
+ .padding(.vertical, 4)
+ .onTapGesture { showGetStakeSheet = true }
+ #endif
}
.frame(maxWidth: .infinity, alignment: .leading)
+ .sheet(isPresented: $showGetStakeSheet) {
+ GetStakeView(fromSettings: false)
+ }
+ }
+}
+
+fileprivate let getStakeSlides: [(image: String, heading: String, info: String?, text: String)] = [
+ (
+ "crowdfunding_1",
+ "The first and the only messaging network without any user IDs",
+ nil,
+ "By investing, you can benefit from the company growth, and help us build the future of private and secure communications."
+ ),
+ (
+ "crowdfunding_2",
+ "480,000+ users joined on their own",
+ nil,
+ "SimpleX users have been more than doubling every year without any paid marketing, and donated over $650,000."
+ ),
+ (
+ "crowdfunding_3",
+ "Developers already bet on SimpleX success",
+ "Independent developers created moderation and AI bots, Telegram bridges, and a public server registry.",
+ "Every service developers build on SimpleX Network may increase its value, and bring new users to SimpleX Chat."
+ ),
+ (
+ "crowdfunding_4",
+ "Revenue plan: free for users, channels & businesses pay",
+ "SimpleX Chat plans to earn from the infrastructure and services that creators, businesses and large communities need as they grow.",
+ "Read about how we plan to make SimpleX Chat and network profitable, and about all the investment terms on Wefunder."
+ ),
+]
+
+private let wefunderURL = URL(string: "https://wefunder.com/simplex.chat?utm_source=app")!
+
+private let simplexCrowdfundingURL = URL(string: "simplex:/a#JxGcOA1_QhlmVFzYYabloMbvMZk5Y9d9iS3ITDnhzYo?h=smp11.simplex.im")!
+
+struct GetStakeView: View {
+ @Environment(\.dismiss) var dismiss: DismissAction
+ @EnvironmentObject var chatModel: ChatModel
+ var fromSettings: Bool
+
+ var body: some View {
+ ZoomablePageView {
+ VStack(alignment: .leading, spacing: 18) {
+ Text(verbatim: "Get a stake in\nSimpleX Chat")
+ .font(.largeTitle)
+ .bold()
+ .fixedSize(horizontal: false, vertical: true)
+ .if(!fromSettings) { $0.padding(.top) }
+ if fromSettings {
+ slideImage(getStakeSlides[0])
+ }
+ (Text(verbatim: getStakeSlides[0].text) + Text(verbatim: " Learn more and invest on Wefunder.").bold().foregroundColor(.accentColor))
+ .multilineTextAlignment(.leading)
+ .onTapGesture {
+ UIApplication.shared.open(wefunderURL)
+ }
+ .padding(.bottom)
+ ForEach(getStakeSlides[1...3], id: \.image) { slide in
+ VStack(alignment: .leading) {
+ slideImage(slide)
+ Text(slide.text)
+ }
+ .padding(.bottom)
+ }
+
+ Button {
+ UIApplication.shared.open(wefunderURL)
+ } label: {
+ Text(verbatim: "Learn more on Wefunder")
+ }
+ .buttonStyle(OnboardingButtonStyle())
+
+ Button {
+ dismiss()
+ DispatchQueue.main.async {
+ ChatModel.shared.appOpenUrl = simplexCrowdfundingURL
+ }
+ } label: {
+ Text(verbatim: "or ask SimpleX team")
+ .font(.callout)
+ }
+ .disabled(chatModel.chatRunning != true)
+ .frame(maxWidth: .infinity)
+ }
+ .padding()
+ }
+ .ignoresSafeArea(edges: .bottom)
+ .modifier(ThemedBackground(grouped: true))
+ }
+
+ @ViewBuilder
+ func slideImage(_ slide: (image: String, heading: String, info: String?, text: String?)) -> some View {
+ #if SIMPLEX_ASSETS
+ Image(slide.image)
+ .resizable()
+ .scaledToFit()
+ .cornerRadius(12)
+ #else
+ Text(slide.heading).font(.title3).bold()
+ if let info = slide.info {
+ Text(info)
+ }
+ #endif
}
}
diff --git a/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift b/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift
index d891efcd90..bf3dbac440 100644
--- a/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift
+++ b/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift
@@ -37,6 +37,8 @@ struct PrivacySettings: View {
@State private var groupReceiptsDialogue = false
@State private var autoAcceptMemberContacts = false
@State private var autoAcceptMemberContactsReset = false
+ @State private var autoAcceptGroupInvitations = false
+ @State private var autoAcceptGroupInvitationsReset = false
@State private var alert: PrivacySettingsViewAlert?
enum PrivacySettingsViewAlert: Identifiable {
@@ -117,14 +119,17 @@ struct PrivacySettings: View {
}
Section {
- settingsRow("checkmark", color: theme.colors.secondary) {
- Toggle("Auto-accept", isOn: $autoAcceptMemberContacts)
+ settingsRow("person", color: theme.colors.secondary) {
+ Toggle("Contact requests in groups", isOn: $autoAcceptMemberContacts)
+ }
+ settingsRow("person.2", color: theme.colors.secondary) {
+ Toggle("Group invitations", isOn: $autoAcceptGroupInvitations)
}
} header: {
- Text("Contact requests from groups")
+ Text("Auto-accept")
.foregroundColor(theme.colors.secondary)
} footer: {
- Text("This setting is for your current profile **\(m.currentUser?.displayName ?? "")**.")
+ Text("These settings are for your current profile **\(m.currentUser?.displayName ?? "")**.")
.foregroundColor(theme.colors.secondary)
}
@@ -139,7 +144,14 @@ struct PrivacySettings: View {
if autoAcceptMemberContactsReset {
autoAcceptMemberContactsReset = false
} else {
- setAutoAcceptGrpDirectInvs(autoAcceptMemberContacts)
+ setAutoAcceptMemberContacts(autoAcceptMemberContacts)
+ }
+ }
+ .onChange(of: autoAcceptGroupInvitations) { _ in
+ if autoAcceptGroupInvitationsReset {
+ autoAcceptGroupInvitationsReset = false
+ } else {
+ setAutoAcceptGroupInvitations(autoAcceptGroupInvitations)
}
}
.onAppear {
@@ -148,6 +160,10 @@ struct PrivacySettings: View {
autoAcceptMemberContactsReset = true
autoAcceptMemberContacts = u.autoAcceptMemberContacts
}
+ if autoAcceptGroupInvitations != u.autoAcceptGroupInvitations {
+ autoAcceptGroupInvitationsReset = true
+ autoAcceptGroupInvitations = u.autoAcceptGroupInvitations
+ }
}
}
.alert(item: $alert) { alert in
@@ -426,7 +442,7 @@ struct PrivacySettings: View {
}
}
- private func setAutoAcceptGrpDirectInvs(_ enable: Bool) {
+ private func setAutoAcceptMemberContacts(_ enable: Bool) {
Task {
do {
if let currentUser = m.currentUser {
@@ -443,6 +459,23 @@ struct PrivacySettings: View {
}
}
+ private func setAutoAcceptGroupInvitations(_ enable: Bool) {
+ Task {
+ do {
+ if let currentUser = m.currentUser {
+ try await apiSetUserAutoAcceptGroupInvitations(currentUser.userId, enable: enable)
+ await MainActor.run {
+ var updatedUser = currentUser
+ updatedUser.autoAcceptGroupInvitations = enable
+ m.updateUser(updatedUser)
+ }
+ }
+ } catch let error {
+ alert = .error(title: "Error setting auto-accept", error: "Error: \(responseError(error))")
+ }
+ }
+ }
+
private func simplexLockRow(_ value: LocalizedStringKey) -> some View {
HStack {
Text("SimpleX Lock")
diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift
index 4bd5f7db1b..ae317cd864 100644
--- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift
+++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift
@@ -380,6 +380,17 @@ struct SettingsView: View {
Text(verbatim: "v\(appVersion ?? "?")")
}
}
+
+ if isInUS {
+ Section(header: Text("You can now invest in SimpleX Chat").foregroundColor(theme.colors.secondary)) {
+ NavigationLink {
+ GetStakeView(fromSettings: true)
+ .navigationBarTitle("", displayMode: .inline)
+ } label: {
+ settingsRow("dollarsign.circle", color: theme.colors.secondary) { Text("Crowdfunding on Wefunder") }
+ }
+ }
+ }
}
.navigationTitle("Your settings")
.modifier(ThemedBackground(grouped: true))
diff --git a/apps/ios/Shared/Views/ZoomableScrollView.swift b/apps/ios/Shared/Views/ZoomableScrollView.swift
index 83528b593a..87eb645822 100644
--- a/apps/ios/Shared/Views/ZoomableScrollView.swift
+++ b/apps/ios/Shared/Views/ZoomableScrollView.swift
@@ -58,3 +58,54 @@ struct ZoomableScrollViewNew in v7.0-v7.0.1.
+SimpleX public names for channels and businesses (BETA).
+Better channels:
+Add a longer description to your profile.
+Simplified app settings.
+{{ "index-hero-p1" | i18n({}, lang) | safe }}
-{{ "index-hero-invest" | i18n({}, lang) | safe }} {{ "index-hero-invest-cta" | i18n({}, lang) | safe }}
+{{ "index-hero-invest" | i18n({}, lang) | safe }} {{ "index-hero-invest-cta" | i18n({}, lang) | safe }}