mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
ios: optionally lock share extension when SimpleX Lock is enabled (default is to lock), allow link previews if enabled in the app, use the same shape of avatars (#4547)
This commit is contained in:
committed by
GitHub
parent
676b533393
commit
93e88c3953
@@ -9,8 +9,6 @@
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
let defaultProfileImageCorner = 22.5
|
||||
|
||||
struct ProfileImage: View {
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var imageStr: String? = nil
|
||||
@@ -34,26 +32,6 @@ struct ProfileImage: View {
|
||||
}
|
||||
}
|
||||
|
||||
private let squareToCircleRatio = 0.935
|
||||
|
||||
private let radiusFactor = (1 - squareToCircleRatio) / 50
|
||||
|
||||
@ViewBuilder func clipProfileImage(_ img: Image, size: CGFloat, radius: Double) -> some View {
|
||||
let v = img.resizable()
|
||||
if radius >= 50 {
|
||||
v.frame(width: size, height: size).clipShape(Circle())
|
||||
} else if radius <= 0 {
|
||||
let sz = size * squareToCircleRatio
|
||||
v.frame(width: sz, height: sz).padding((size - sz) / 2)
|
||||
} else {
|
||||
let sz = size * (squareToCircleRatio + radius * radiusFactor)
|
||||
v.frame(width: sz, height: sz)
|
||||
.clipShape(RoundedRectangle(cornerRadius: sz * radius / 100, style: .continuous))
|
||||
.padding((size - sz) / 2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Color {
|
||||
func asAnotherColorFromSecondary(_ theme: AppTheme) -> Color {
|
||||
return self
|
||||
|
||||
@@ -76,7 +76,7 @@ extension AppSettings {
|
||||
c.androidCallOnLockScreen = AppSettingsLockScreenCalls(rawValue: def.string(forKey: ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN)!)
|
||||
c.iosCallKitEnabled = callKitEnabledGroupDefault.get()
|
||||
c.iosCallKitCallsInRecents = def.bool(forKey: DEFAULT_CALL_KIT_CALLS_IN_RECENTS)
|
||||
c.uiProfileImageCornerRadius = def.float(forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS)
|
||||
c.uiProfileImageCornerRadius = def.double(forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS)
|
||||
c.uiColorScheme = currentThemeDefault.get()
|
||||
c.uiDarkColorScheme = systemDarkThemeDefault.get()
|
||||
c.uiCurrentThemeIds = currentThemeIdsDefault.get()
|
||||
|
||||
@@ -143,7 +143,8 @@ struct AppearanceSettings: View {
|
||||
Text("Themes")
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
}
|
||||
.onChange(of: profileImageCornerRadius) { _ in
|
||||
.onChange(of: profileImageCornerRadius) { cornerRadius in
|
||||
profileImageCornerRadiusGroupDefault.set(cornerRadius)
|
||||
saveThemeToDatabase(nil)
|
||||
}
|
||||
.onChange(of: colorMode) { mode in
|
||||
|
||||
@@ -70,6 +70,9 @@ struct PrivacySettings: View {
|
||||
Section {
|
||||
settingsRow("network", color: theme.colors.secondary) {
|
||||
Toggle("Send link previews", isOn: $useLinkPreviews)
|
||||
.onChange(of: useLinkPreviews) { linkPreviews in
|
||||
privacyLinkPreviewsGroupDefault.set(linkPreviews)
|
||||
}
|
||||
}
|
||||
settingsRow("message", color: theme.colors.secondary) {
|
||||
Toggle("Show last messages", isOn: $showChatPreviews)
|
||||
@@ -365,6 +368,7 @@ struct SimplexLockView: View {
|
||||
@State private var selfDestruct: Bool = UserDefaults.standard.bool(forKey: DEFAULT_LA_SELF_DESTRUCT)
|
||||
@State private var currentSelfDestruct: Bool = UserDefaults.standard.bool(forKey: DEFAULT_LA_SELF_DESTRUCT)
|
||||
@AppStorage(DEFAULT_LA_SELF_DESTRUCT_DISPLAY_NAME) private var selfDestructDisplayName = ""
|
||||
@AppStorage(GROUP_DEFAULT_ALLOW_SHARE_EXTENSION, store: groupDefaults) private var allowShareExtension = false
|
||||
@State private var performLAToggleReset = false
|
||||
@State private var performLAModeReset = false
|
||||
@State private var performLASelfDestructReset = false
|
||||
@@ -436,6 +440,12 @@ struct SimplexLockView: View {
|
||||
}
|
||||
}
|
||||
|
||||
if performLA {
|
||||
Section("Share to SimpleX") {
|
||||
Toggle("Allow sharing", isOn: $allowShareExtension)
|
||||
}
|
||||
}
|
||||
|
||||
if performLA && laMode == .passcode {
|
||||
Section(header: Text("Self-destruct passcode").foregroundColor(theme.colors.secondary)) {
|
||||
Toggle(isOn: $selfDestruct) {
|
||||
@@ -460,6 +470,7 @@ struct SimplexLockView: View {
|
||||
}
|
||||
}
|
||||
.onChange(of: performLA) { performLAToggle in
|
||||
performLAGroupDefault.set(performLAToggle)
|
||||
prefLANoticeShown = true
|
||||
if performLAToggleReset {
|
||||
performLAToggleReset = false
|
||||
|
||||
@@ -18,7 +18,7 @@ let appBuild = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as?
|
||||
|
||||
let DEFAULT_SHOW_LA_NOTICE = "showLocalAuthenticationNotice"
|
||||
let DEFAULT_LA_NOTICE_SHOWN = "localAuthenticationNoticeShown"
|
||||
let DEFAULT_PERFORM_LA = "performLocalAuthentication"
|
||||
let DEFAULT_PERFORM_LA = "performLocalAuthentication" // deprecated, moved to app group
|
||||
let DEFAULT_LA_MODE = "localAuthenticationMode"
|
||||
let DEFAULT_LA_LOCK_DELAY = "localAuthenticationLockDelay"
|
||||
let DEFAULT_LA_SELF_DESTRUCT = "localAuthenticationSelfDestruct"
|
||||
@@ -28,7 +28,7 @@ let DEFAULT_WEBRTC_POLICY_RELAY = "webrtcPolicyRelay"
|
||||
let DEFAULT_WEBRTC_ICE_SERVERS = "webrtcICEServers"
|
||||
let DEFAULT_CALL_KIT_CALLS_IN_RECENTS = "callKitCallsInRecents"
|
||||
let DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages" // unused. Use GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES instead
|
||||
let DEFAULT_PRIVACY_LINK_PREVIEWS = "privacyLinkPreviews"
|
||||
let DEFAULT_PRIVACY_LINK_PREVIEWS = "privacyLinkPreviews" // deprecated, moved to app group
|
||||
let DEFAULT_PRIVACY_SIMPLEX_LINK_MODE = "privacySimplexLinkMode"
|
||||
let DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS = "privacyShowChatPreviews"
|
||||
let DEFAULT_PRIVACY_SAVE_LAST_DRAFT = "privacySaveLastDraft"
|
||||
@@ -165,6 +165,9 @@ let themeOverridesDefault: CodableDefault<[ThemeOverrides]> = CodableDefault(def
|
||||
|
||||
func setGroupDefaults() {
|
||||
privacyAcceptImagesGroupDefault.set(UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_ACCEPT_IMAGES))
|
||||
performLAGroupDefault.set(UserDefaults.standard.bool(forKey: DEFAULT_PERFORM_LA))
|
||||
privacyLinkPreviewsGroupDefault.set(UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_LINK_PREVIEWS))
|
||||
profileImageCornerRadiusGroupDefault.set(UserDefaults.standard.double(forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS))
|
||||
}
|
||||
|
||||
public class StringDefault {
|
||||
|
||||
@@ -58,6 +58,10 @@ class ShareModel: ObservableObject {
|
||||
}
|
||||
|
||||
func setup(context: NSExtensionContext) {
|
||||
if performLAGroupDefault.get() && !allowShareExtensionGroupDefault.get() {
|
||||
errorAlert = ErrorAlert(title: "App is locked!", message: "You can allow sharing in Privacy & Security / SimpleX Lock settings.")
|
||||
return
|
||||
}
|
||||
if let item = context.inputItems.first as? NSExtensionItem,
|
||||
let itemProvider = item.attachments?.first {
|
||||
self.itemProvider = itemProvider
|
||||
@@ -433,12 +437,11 @@ extension NSItemProvider {
|
||||
case .url:
|
||||
if let url = try? await loadItem(forTypeIdentifier: type.identifier) as? URL {
|
||||
let content: SharedContent =
|
||||
// Option to disable previews needs to be taken into account
|
||||
// if let linkPreview = await getLinkPreview(for: url) {
|
||||
// .url(preview: linkPreview)
|
||||
// } else {
|
||||
if privacyLinkPreviewsGroupDefault.get(), let linkPreview = await getLinkPreview(for: url) {
|
||||
.url(preview: linkPreview)
|
||||
} else {
|
||||
.text(string: url.absoluteString)
|
||||
// }
|
||||
}
|
||||
return .success(content)
|
||||
} else { return .failure(ErrorAlert("Error preparing message")) }
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import SimpleXChat
|
||||
struct ShareView: View {
|
||||
@ObservedObject var model: ShareModel
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@AppStorage(GROUP_DEFAULT_PROFILE_IMAGE_CORNER_RADIUS, store: groupDefaults) private var radius = defaultProfileImageCorner
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
@@ -21,7 +22,7 @@ struct ShareView: View {
|
||||
HStack {
|
||||
profileImage(
|
||||
chatInfoId: chat.chatInfo.id,
|
||||
systemFallback: chatIconName(chat.chatInfo),
|
||||
iconName: chatIconName(chat.chatInfo),
|
||||
size: 30
|
||||
)
|
||||
Text(chat.chatInfo.displayName)
|
||||
@@ -159,17 +160,17 @@ struct ShareView: View {
|
||||
.background(Material.ultraThin)
|
||||
}
|
||||
|
||||
private func profileImage(chatInfoId: ChatInfo.ID, systemFallback: String, size: Double) -> some View {
|
||||
Group {
|
||||
if let uiImage = model.profileImages[chatInfoId] {
|
||||
Image(uiImage: uiImage).resizable()
|
||||
} else {
|
||||
Image(systemName: systemFallback).resizable()
|
||||
}
|
||||
@ViewBuilder private func profileImage(chatInfoId: ChatInfo.ID, iconName: String, size: Double) -> some View {
|
||||
if let uiImage = model.profileImages[chatInfoId] {
|
||||
clipProfileImage(Image(uiImage: uiImage), size: size, radius: radius)
|
||||
} else {
|
||||
Image(systemName: iconName)
|
||||
.resizable()
|
||||
.foregroundColor(Color(uiColor: .tertiaryLabel))
|
||||
.frame(width: size, height: size)
|
||||
// add background when adding themes to SE
|
||||
// .background(Circle().fill(backgroundColor != nil ? backgroundColor! : .clear))
|
||||
}
|
||||
.foregroundStyle(Color(.tertiaryLabel))
|
||||
.frame(width: size, height: size)
|
||||
.clipShape(RoundedRectangle(cornerRadius: size * 0.225, style: .continuous))
|
||||
}
|
||||
|
||||
private func radioButton(selected: Bool) -> some View {
|
||||
|
||||
@@ -2115,7 +2115,7 @@ public struct AppSettings: Codable, Equatable, Hashable {
|
||||
public var androidCallOnLockScreen: AppSettingsLockScreenCalls? = nil
|
||||
public var iosCallKitEnabled: Bool? = nil
|
||||
public var iosCallKitCallsInRecents: Bool? = nil
|
||||
public var uiProfileImageCornerRadius: Float? = nil
|
||||
public var uiProfileImageCornerRadius: Double? = nil
|
||||
public var uiColorScheme: String? = nil
|
||||
public var uiDarkColorScheme: String? = nil
|
||||
public var uiCurrentThemeIds: [String: String]? = nil
|
||||
|
||||
@@ -11,6 +11,8 @@ import SwiftUI
|
||||
|
||||
public let appSuspendTimeout: Int = 15 // seconds
|
||||
|
||||
public let defaultProfileImageCorner: Double = 22.5
|
||||
|
||||
let GROUP_DEFAULT_APP_STATE = "appState"
|
||||
let GROUP_DEFAULT_NSE_STATE = "nseState"
|
||||
let GROUP_DEFAULT_SE_STATE = "seState"
|
||||
@@ -20,11 +22,18 @@ public let GROUP_DEFAULT_CHAT_LAST_BACKGROUND_RUN = "chatLastBackgroundRun"
|
||||
let GROUP_DEFAULT_NTF_PREVIEW_MODE = "ntfPreviewMode"
|
||||
public let GROUP_DEFAULT_NTF_ENABLE_LOCAL = "ntfEnableLocal" // no longer used
|
||||
public let GROUP_DEFAULT_NTF_ENABLE_PERIODIC = "ntfEnablePeriodic" // no longer used
|
||||
// replaces DEFAULT_PERFORM_LA
|
||||
let GROUP_DEFAULT_PERFORM_LA = "performLocalAuthentication"
|
||||
public let GROUP_DEFAULT_ALLOW_SHARE_EXTENSION = "allowShareExtension"
|
||||
// replaces DEFAULT_PRIVACY_LINK_PREVIEWS
|
||||
let GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS = "privacyLinkPreviews"
|
||||
// This setting is a main one, while having an unused duplicate from the past: DEFAULT_PRIVACY_ACCEPT_IMAGES
|
||||
let GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages"
|
||||
public let GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE = "privacyTransferImagesInline" // no longer used
|
||||
public let GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES = "privacyEncryptLocalFiles"
|
||||
public let GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS = "privacyAskToApproveRelays"
|
||||
// replaces DEFAULT_PROFILE_IMAGE_CORNER_RADIUS
|
||||
public let GROUP_DEFAULT_PROFILE_IMAGE_CORNER_RADIUS = "profileImageCornerRadius"
|
||||
let GROUP_DEFAULT_NTF_BADGE_COUNT = "ntgBadgeCount"
|
||||
let GROUP_DEFAULT_NETWORK_USE_ONION_HOSTS = "networkUseOnionHosts"
|
||||
let GROUP_DEFAULT_NETWORK_SESSION_MODE = "networkSessionMode"
|
||||
@@ -72,10 +81,14 @@ public func registerGroupDefaults() {
|
||||
GROUP_DEFAULT_INCOGNITO: false,
|
||||
GROUP_DEFAULT_STORE_DB_PASSPHRASE: true,
|
||||
GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE: false,
|
||||
GROUP_DEFAULT_PERFORM_LA: true,
|
||||
GROUP_DEFAULT_ALLOW_SHARE_EXTENSION: false,
|
||||
GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS: false,
|
||||
GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES: true,
|
||||
GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE: false,
|
||||
GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES: true,
|
||||
GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS: true,
|
||||
GROUP_DEFAULT_PROFILE_IMAGE_CORNER_RADIUS: defaultProfileImageCorner,
|
||||
GROUP_DEFAULT_CONFIRM_DB_UPGRADES: false,
|
||||
GROUP_DEFAULT_CALL_KIT_ENABLED: true,
|
||||
GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED: false,
|
||||
@@ -190,6 +203,12 @@ public let ntfPreviewModeGroupDefault = EnumDefault<NotificationPreviewMode>(
|
||||
|
||||
public let incognitoGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_INCOGNITO)
|
||||
|
||||
public let performLAGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PERFORM_LA)
|
||||
|
||||
public let allowShareExtensionGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_ALLOW_SHARE_EXTENSION)
|
||||
|
||||
public let privacyLinkPreviewsGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS)
|
||||
|
||||
// This setting is a main one, while having an unused duplicate from the past: DEFAULT_PRIVACY_ACCEPT_IMAGES
|
||||
public let privacyAcceptImagesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES)
|
||||
|
||||
@@ -197,6 +216,8 @@ public let privacyEncryptLocalFilesGroupDefault = BoolDefault(defaults: groupDef
|
||||
|
||||
public let privacyAskToApproveRelaysGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS)
|
||||
|
||||
public let profileImageCornerRadiusGroupDefault = Default<Double>(defaults: groupDefaults, forKey: GROUP_DEFAULT_PROFILE_IMAGE_CORNER_RADIUS)
|
||||
|
||||
public let ntfBadgeCountGroupDefault = IntDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_NTF_BADGE_COUNT)
|
||||
|
||||
public let networkUseOnionHostsGroupDefault = EnumDefault<OnionHosts>(
|
||||
|
||||
@@ -428,3 +428,22 @@ public func getLinkPreview(for url: URL) async -> LinkPreview? {
|
||||
getLinkPreview(url: url) { cont.resume(returning: $0) }
|
||||
}
|
||||
}
|
||||
|
||||
private let squareToCircleRatio = 0.935
|
||||
|
||||
private let radiusFactor = (1 - squareToCircleRatio) / 50
|
||||
|
||||
@ViewBuilder public func clipProfileImage(_ img: Image, size: CGFloat, radius: Double) -> some View {
|
||||
let v = img.resizable()
|
||||
if radius >= 50 {
|
||||
v.frame(width: size, height: size).clipShape(Circle())
|
||||
} else if radius <= 0 {
|
||||
let sz = size * squareToCircleRatio
|
||||
v.frame(width: sz, height: sz).padding((size - sz) / 2)
|
||||
} else {
|
||||
let sz = size * (squareToCircleRatio + radius * radiusFactor)
|
||||
v.frame(width: sz, height: sz)
|
||||
.clipShape(RoundedRectangle(cornerRadius: sz * radius / 100, style: .continuous))
|
||||
.padding((size - sz) / 2)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user