From 8ddcc4cbcf40242efa7a3846454581afc3a9ebb1 Mon Sep 17 00:00:00 2001 From: bjoern Date: Wed, 18 Dec 2024 11:24:33 +0100 Subject: [PATCH 01/15] add option to copy links instead opening (#2445) * factor our gesture detection * get link text to copy * make linter happy * Update deltachat-ios/Chat/Views/MessageLabel.swift Co-authored-by: zeitschlag * Update deltachat-ios/Chat/Views/MessageLabel.swift Co-authored-by: zeitschlag * Update deltachat-ios/Chat/Views/MessageLabel.swift Co-authored-by: zeitschlag * Update deltachat-ios/Chat/Views/MessageLabel.swift Co-authored-by: zeitschlag * Update deltachat-ios/Chat/Views/MessageLabel.swift Co-authored-by: zeitschlag * Update deltachat-ios/Chat/Views/MessageLabel.swift Co-authored-by: zeitschlag * Update deltachat-ios/Chat/Views/MessageLabel.swift Co-authored-by: zeitschlag * add CHANGELOG entry --------- Co-authored-by: zeitschlag --- CHANGELOG.md | 2 + deltachat-ios/Chat/ChatViewController.swift | 17 +++++++- deltachat-ios/Chat/Views/MessageLabel.swift | 46 ++++++++++++++------- 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 496269f84..97f17e967 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## Unreleased - Don't show message-input when forwarding (#2435) +- Long-tap links for copying to clipboard (#2445) + ## v1.50.3 diff --git a/deltachat-ios/Chat/ChatViewController.swift b/deltachat-ios/Chat/ChatViewController.swift index bd412610e..dc160c170 100644 --- a/deltachat-ios/Chat/ChatViewController.swift +++ b/deltachat-ios/Chat/ChatViewController.swift @@ -2021,6 +2021,15 @@ extension ChatViewController { menuElements.append(action) } + private func isLinkTapped(indexPath: IndexPath, point: CGPoint) -> String? { + if let cell = tableView.cellForRow(at: indexPath) as? BaseMessageCell { + let label = cell.messageLabel.label + let localTouchLocation = tableView.convert(point, to: label) + return label.getCopyableLinkText(localTouchLocation) + } + return nil + } + // context menu for iOS 13+ override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { let messageId = messageIds[indexPath.row] @@ -2067,7 +2076,13 @@ extension ChatViewController { UIAction.menuAction(localizationKey: "forward", image: image, indexPath: indexPath, action: { self.forward(at: $0 ) }) ) - if let text = message.text, !text.isEmpty { + if let link = isLinkTapped(indexPath: indexPath, point: point) { + children.append( + UIAction.menuAction(localizationKey: "menu_copy_link_to_clipboard", systemImageName: "link", indexPath: indexPath, action: { _ in + UIPasteboard.general.string = link + }) + ) + } else if let text = message.text, !text.isEmpty { let copyTitle = message.file == nil ? "global_menu_edit_copy_desktop" : "menu_copy_text_to_clipboard" children.append( UIAction.menuAction(localizationKey: copyTitle, systemImageName: "doc.on.doc", indexPath: indexPath, action: { self.copyToClipboard(at: $0 ) }) diff --git a/deltachat-ios/Chat/Views/MessageLabel.swift b/deltachat-ios/Chat/Views/MessageLabel.swift index f23956e40..181ab1826 100644 --- a/deltachat-ios/Chat/Views/MessageLabel.swift +++ b/deltachat-ios/Chat/Views/MessageLabel.swift @@ -452,49 +452,65 @@ open class MessageLabel: UILabel { } - open func handleGesture(_ touchLocation: CGPoint) -> Bool { - - guard let index = stringIndex(at: touchLocation) else { return false } + internal func detectLink(_ touchLocation: CGPoint) -> (DetectorType, NewMessageTextCheckingType)? { + guard let index = stringIndex(at: touchLocation) else { return nil } for (detectorType, ranges) in rangesForDetectors { for (range, value) in ranges { if range.contains(index) { - handleGesture(for: detectorType, value: value) - return true + return (detectorType, value) } } } - return false + + return nil } - /// swiftlint:disable cyclomatic_complexity - private func handleGesture(for detectorType: DetectorType, value: NewMessageTextCheckingType) { + internal func getCopyableLinkText(_ touchLocation: CGPoint) -> String? { + guard let (_, value) = detectLink(touchLocation) else { return nil } + + switch value { + case let .link(url): + guard let url else { return nil } + return url.absoluteString + case let .phoneNumber(phoneNumber): + return phoneNumber + case let .custom(_, match): + return match + case .addressComponents, .date, .transitInfoComponents: + return nil + } + } + + internal func handleGesture(_ touchLocation: CGPoint) -> Bool { + guard let (detectorType, value) = detectLink(touchLocation) else { return false } + switch value { case let .addressComponents(addressComponents): var transformedAddressComponents = [String: String]() - guard let addressComponents = addressComponents else { return } + guard let addressComponents else { return false } addressComponents.forEach { (key, value) in transformedAddressComponents[key.rawValue] = value } handleAddress(transformedAddressComponents) case let .phoneNumber(phoneNumber): - guard let phoneNumber = phoneNumber else { return } + guard let phoneNumber else { return false } handlePhoneNumber(phoneNumber) case let .date(date): - guard let date = date else { return } + guard let date else { return false } handleDate(date) case let .link(url): - guard let url = url else { return } + guard let url else { return false } handleURL(url) case let .transitInfoComponents(transitInformation): var transformedTransitInformation = [String: String]() - guard let transitInformation = transitInformation else { return } + guard let transitInformation else { return false } transitInformation.forEach { (key, value) in transformedTransitInformation[key.rawValue] = value } handleTransitInformation(transformedTransitInformation) case let .custom(pattern, match): - guard let match = match else { return } + guard let match else { return false } switch detectorType { case .hashtag: handleHashtag(match) @@ -506,6 +522,8 @@ open class MessageLabel: UILabel { handleCustom(pattern, match: match) } } + + return true } private func handleAddress(_ addressComponents: [String: String]) { From 82e6c2373638800657ae9e944b291922d40050c0 Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Wed, 27 Nov 2024 12:29:42 +0100 Subject: [PATCH 02/15] Add WidgetExtension with Default-implementation (#2406) --- .../AccentColor.colorset/Contents.json | 11 ++ .../AppIcon.appiconset/Contents.json | 35 ++++ .../Assets.xcassets/Contents.json | 6 + .../WidgetBackground.colorset/Contents.json | 11 ++ MostRecentWebXDCWidget/Info.plist | 11 ++ .../MostRecentWebXDCWidget.swift | 77 ++++++++ .../MostRecentWebXDCWidgetBundle.swift | 10 + .../MostRecentWebXDCWidgetControl.swift | 47 +++++ deltachat-ios.xcodeproj/project.pbxproj | 172 +++++++++++++++++- .../MostRecentWebXDCWidgetExtension.xcscheme | 124 +++++++++++++ 10 files changed, 503 insertions(+), 1 deletion(-) create mode 100644 MostRecentWebXDCWidget/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 MostRecentWebXDCWidget/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 MostRecentWebXDCWidget/Assets.xcassets/Contents.json create mode 100644 MostRecentWebXDCWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json create mode 100644 MostRecentWebXDCWidget/Info.plist create mode 100644 MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift create mode 100644 MostRecentWebXDCWidget/MostRecentWebXDCWidgetBundle.swift create mode 100644 MostRecentWebXDCWidget/MostRecentWebXDCWidgetControl.swift create mode 100644 deltachat-ios.xcodeproj/xcshareddata/xcschemes/MostRecentWebXDCWidgetExtension.xcscheme diff --git a/MostRecentWebXDCWidget/Assets.xcassets/AccentColor.colorset/Contents.json b/MostRecentWebXDCWidget/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/MostRecentWebXDCWidget/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MostRecentWebXDCWidget/Assets.xcassets/AppIcon.appiconset/Contents.json b/MostRecentWebXDCWidget/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..230588010 --- /dev/null +++ b/MostRecentWebXDCWidget/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MostRecentWebXDCWidget/Assets.xcassets/Contents.json b/MostRecentWebXDCWidget/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/MostRecentWebXDCWidget/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MostRecentWebXDCWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/MostRecentWebXDCWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/MostRecentWebXDCWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MostRecentWebXDCWidget/Info.plist b/MostRecentWebXDCWidget/Info.plist new file mode 100644 index 000000000..0f118fb75 --- /dev/null +++ b/MostRecentWebXDCWidget/Info.plist @@ -0,0 +1,11 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift b/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift new file mode 100644 index 000000000..c2f9eadd7 --- /dev/null +++ b/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift @@ -0,0 +1,77 @@ +import WidgetKit +import SwiftUI + +struct Provider: TimelineProvider { + func placeholder(in context: Context) -> SimpleEntry { + SimpleEntry(date: Date(), emoji: "😀") + } + + func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { + let entry = SimpleEntry(date: Date(), emoji: "😀") + completion(entry) + } + + func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { + var entries: [SimpleEntry] = [] + + // Generate a timeline consisting of five entries an hour apart, starting from the current date. + let currentDate = Date() + for hourOffset in 0 ..< 5 { + let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! + let entry = SimpleEntry(date: entryDate, emoji: "😀") + entries.append(entry) + } + + let timeline = Timeline(entries: entries, policy: .atEnd) + completion(timeline) + } + +// func relevances() async -> WidgetRelevances { +// // Generate a list containing the contexts this widget is relevant in. +// } +} + +struct SimpleEntry: TimelineEntry { + let date: Date + let emoji: String +} + +struct MostRecentWebXDCWidgetEntryView : View { + var entry: Provider.Entry + + var body: some View { + VStack { + Text("Time:") + Text(entry.date, style: .time) + + Text("Emoji:") + Text(entry.emoji) + } + } +} + +struct MostRecentWebXDCWidget: Widget { + let kind: String = "MostRecentWebXDCWidget" + + var body: some WidgetConfiguration { + StaticConfiguration(kind: kind, provider: Provider()) { entry in + if #available(iOS 17.0, *) { + MostRecentWebXDCWidgetEntryView(entry: entry) + .containerBackground(.fill.tertiary, for: .widget) + } else { + MostRecentWebXDCWidgetEntryView(entry: entry) + .padding() + .background() + } + } + .configurationDisplayName("My Widget") + .description("This is an example widget.") + } +} + +#Preview(as: .systemSmall) { + MostRecentWebXDCWidget() +} timeline: { + SimpleEntry(date: .now, emoji: "😀") + SimpleEntry(date: .now, emoji: "🤩") +} diff --git a/MostRecentWebXDCWidget/MostRecentWebXDCWidgetBundle.swift b/MostRecentWebXDCWidget/MostRecentWebXDCWidgetBundle.swift new file mode 100644 index 000000000..a09e7a94a --- /dev/null +++ b/MostRecentWebXDCWidget/MostRecentWebXDCWidgetBundle.swift @@ -0,0 +1,10 @@ +import WidgetKit +import SwiftUI + +@main +struct MostRecentWebXDCWidgetBundle: WidgetBundle { + var body: some Widget { + MostRecentWebXDCWidget() + MostRecentWebXDCWidgetControl() + } +} diff --git a/MostRecentWebXDCWidget/MostRecentWebXDCWidgetControl.swift b/MostRecentWebXDCWidget/MostRecentWebXDCWidgetControl.swift new file mode 100644 index 000000000..988299c7c --- /dev/null +++ b/MostRecentWebXDCWidget/MostRecentWebXDCWidgetControl.swift @@ -0,0 +1,47 @@ +import AppIntents +import SwiftUI +import WidgetKit + +struct MostRecentWebXDCWidgetControl: ControlWidget { + var body: some ControlWidgetConfiguration { + StaticControlConfiguration( + kind: "chat.delta.MostRecentWebXDCWidget", + provider: Provider() + ) { value in + ControlWidgetToggle( + "Start Timer", + isOn: value, + action: StartTimerIntent() + ) { isRunning in + Label(isRunning ? "On" : "Off", systemImage: "timer") + } + } + .displayName("Timer") + .description("A an example control that runs a timer.") + } +} + +extension MostRecentWebXDCWidgetControl { + struct Provider: ControlValueProvider { + var previewValue: Bool { + false + } + + func currentValue() async throws -> Bool { + let isRunning = true // Check if the timer is running + return isRunning + } + } +} + +struct StartTimerIntent: SetValueIntent { + static let title: LocalizedStringResource = "Start a timer" + + @Parameter(title: "Timer is running") + var value: Bool + + func perform() async throws -> some IntentResult { + // Start / stop the timer based on `value`. + return .result() + } +} diff --git a/deltachat-ios.xcodeproj/project.pbxproj b/deltachat-ios.xcodeproj/project.pbxproj index 8fba093b3..1b737b82d 100644 --- a/deltachat-ios.xcodeproj/project.pbxproj +++ b/deltachat-ios.xcodeproj/project.pbxproj @@ -219,6 +219,9 @@ D84AED272B566C0700D753F6 /* ReactionsOverviewTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84AED262B566C0700D753F6 /* ReactionsOverviewTableViewCell.swift */; }; D85DF9782C4A96CB00A01408 /* UserDefaults+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85DF9772C4A96CB00A01408 /* UserDefaults+Extensions.swift */; }; D85DF9802C5250E200A01408 /* ProgressAlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85DF97F2C5250E200A01408 /* ProgressAlertHandler.swift */; }; + D878C4FD2CF72AA0009AF551 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D878C4FC2CF72AA0009AF551 /* WidgetKit.framework */; }; + D878C4FF2CF72AA0009AF551 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D878C4FE2CF72AA0009AF551 /* SwiftUI.framework */; }; + D878C50C2CF72AA1009AF551 /* MostRecentWebXDCWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D878C4FB2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; D8A072A02BED0FD8001A4C7C /* InstantOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A0729F2BED0FD8001A4C7C /* InstantOnboardingView.swift */; }; D8C19DCE2C1B456700B32F6D /* SendContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C19DCD2C1B456700B32F6D /* SendContactViewController.swift */; }; D8C19DD02C1C9FFE00B32F6D /* ContactCardPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C19DCF2C1C95A900B32F6D /* ContactCardPreview.swift */; }; @@ -246,6 +249,13 @@ remoteGlobalIDString = B2D0E4912B93A4B200791949; remoteInfo = DcNotificationService; }; + D878C50A2CF72AA1009AF551 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7A9FB1381FB061E2001FEA36 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D878C4FA2CF72AA0009AF551; + remoteInfo = MostRecentWebXDCWidgetExtension; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -267,6 +277,7 @@ dstSubfolderSpec = 13; files = ( B2D0E4992B93A4B200791949 /* DcNotificationService.appex in Embed Foundation Extensions */, + D878C50C2CF72AA1009AF551 /* MostRecentWebXDCWidgetExtension.appex in Embed Foundation Extensions */, 30E8F21A2447285600CE2C90 /* Delta Chat.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; @@ -607,6 +618,9 @@ D84AED262B566C0700D753F6 /* ReactionsOverviewTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsOverviewTableViewCell.swift; sourceTree = ""; }; D85DF9772C4A96CB00A01408 /* UserDefaults+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Extensions.swift"; sourceTree = ""; }; D85DF97F2C5250E200A01408 /* ProgressAlertHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressAlertHandler.swift; sourceTree = ""; }; + D878C4FB2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = MostRecentWebXDCWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + D878C4FC2CF72AA0009AF551 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + D878C4FE2CF72AA0009AF551 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; D8A0729F2BED0FD8001A4C7C /* InstantOnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantOnboardingView.swift; sourceTree = ""; }; D8C19DCD2C1B456700B32F6D /* SendContactViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendContactViewController.swift; sourceTree = ""; }; D8C19DCF2C1C95A900B32F6D /* ContactCardPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactCardPreview.swift; sourceTree = ""; }; @@ -621,6 +635,20 @@ E23C80FA36453692862D4A90 /* Pods_deltachat_iosTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_deltachat_iosTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + D878C50F2CF72AA1009AF551 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = D878C4FA2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + D878C5002CF72AA0009AF551 /* MostRecentWebXDCWidget */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (D878C50F2CF72AA1009AF551 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = MostRecentWebXDCWidget; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 30E8F20D2447285600CE2C90 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -647,6 +675,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D878C4F82CF72AA0009AF551 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D878C4FF2CF72AA0009AF551 /* SwiftUI.framework in Frameworks */, + D878C4FD2CF72AA0009AF551 /* WidgetKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -864,6 +901,7 @@ 7A9FB1421FB061E2001FEA36 /* deltachat-ios */, 30E8F2112447285600CE2C90 /* DcShare */, B2D0E4932B93A4B200791949 /* DcNotificationService */, + D878C5002CF72AA0009AF551 /* MostRecentWebXDCWidget */, 7A9FB1411FB061E2001FEA36 /* Products */, 7A9FB4F81FB084E6001FEA36 /* Frameworks */, AFE4D4B4B038293E63BC1537 /* Pods */, @@ -879,6 +917,7 @@ 7A9FB1401FB061E2001FEA36 /* deltachat-ios.app */, 30E8F2102447285600CE2C90 /* Delta Chat.appex */, B2D0E4922B93A4B200791949 /* DcNotificationService.appex */, + D878C4FB2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension.appex */, ); name = Products; sourceTree = ""; @@ -923,6 +962,8 @@ A6A81736FBF0A4844C6D619F /* Pods_DcShare.framework */, 12E18F2336264C265BB756C2 /* Pods_deltachat_ios.framework */, E23C80FA36453692862D4A90 /* Pods_deltachat_iosTests.framework */, + D878C4FC2CF72AA0009AF551 /* WidgetKit.framework */, + D878C4FE2CF72AA0009AF551 /* SwiftUI.framework */, ); name = Frameworks; sourceTree = ""; @@ -1231,6 +1272,7 @@ dependencies = ( 30E8F2192447285600CE2C90 /* PBXTargetDependency */, B2D0E4982B93A4B200791949 /* PBXTargetDependency */, + D878C50B2CF72AA1009AF551 /* PBXTargetDependency */, ); name = "deltachat-ios"; productName = "deltachat-ios"; @@ -1254,6 +1296,28 @@ productReference = B2D0E4922B93A4B200791949 /* DcNotificationService.appex */; productType = "com.apple.product-type.app-extension"; }; + D878C4FA2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = D878C5102CF72AA1009AF551 /* Build configuration list for PBXNativeTarget "MostRecentWebXDCWidgetExtension" */; + buildPhases = ( + D878C4F72CF72AA0009AF551 /* Sources */, + D878C4F82CF72AA0009AF551 /* Frameworks */, + D878C4F92CF72AA0009AF551 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + D878C5002CF72AA0009AF551 /* MostRecentWebXDCWidget */, + ); + name = MostRecentWebXDCWidgetExtension; + packageProductDependencies = ( + ); + productName = MostRecentWebXDCWidgetExtension; + productReference = D878C4FB2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -1261,7 +1325,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastSwiftUpdateCheck = 1520; + LastSwiftUpdateCheck = 1610; LastUpgradeCheck = 1500; ORGANIZATIONNAME = "merlinux GmbH"; TargetAttributes = { @@ -1292,6 +1356,9 @@ B2D0E4912B93A4B200791949 = { CreatedOnToolsVersion = 15.2; }; + D878C4FA2CF72AA0009AF551 = { + CreatedOnToolsVersion = 16.1; + }; }; }; buildConfigurationList = 7A9FB13B1FB061E2001FEA36 /* Build configuration list for PBXProject "deltachat-ios" */; @@ -1354,6 +1421,7 @@ 7A9FB13F1FB061E2001FEA36 /* deltachat-ios */, 30E8F20F2447285600CE2C90 /* DcShare */, B2D0E4912B93A4B200791949 /* DcNotificationService */, + D878C4FA2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension */, ); }; /* End PBXProject section */ @@ -1397,6 +1465,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D878C4F92CF72AA0009AF551 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -1739,6 +1814,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D878C4F72CF72AA0009AF551 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1752,6 +1834,11 @@ target = B2D0E4912B93A4B200791949 /* DcNotificationService */; targetProxy = B2D0E4972B93A4B200791949 /* PBXContainerItemProxy */; }; + D878C50B2CF72AA1009AF551 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D878C4FA2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension */; + targetProxy = D878C50A2CF72AA1009AF551 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -2349,6 +2436,80 @@ }; name = Release; }; + D878C50D2CF72AA1009AF551 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 8Y86453UA8; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MostRecentWebXDCWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = MostRecentWebXDCWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 merlinux GmbH. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = chat.delta.MostRecentWebXDCWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + D878C50E2CF72AA1009AF551 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 8Y86453UA8; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MostRecentWebXDCWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = MostRecentWebXDCWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 merlinux GmbH. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = chat.delta.MostRecentWebXDCWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -2388,6 +2549,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + D878C5102CF72AA1009AF551 /* Build configuration list for PBXNativeTarget "MostRecentWebXDCWidgetExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D878C50D2CF72AA1009AF551 /* Debug */, + D878C50E2CF72AA1009AF551 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 7A9FB1381FB061E2001FEA36 /* Project object */; diff --git a/deltachat-ios.xcodeproj/xcshareddata/xcschemes/MostRecentWebXDCWidgetExtension.xcscheme b/deltachat-ios.xcodeproj/xcshareddata/xcschemes/MostRecentWebXDCWidgetExtension.xcscheme new file mode 100644 index 000000000..b0ad9f7a3 --- /dev/null +++ b/deltachat-ios.xcodeproj/xcshareddata/xcschemes/MostRecentWebXDCWidgetExtension.xcscheme @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 24c10915a8aa92c1e1a1c416d1df1f3fbc3a882c Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Wed, 27 Nov 2024 12:37:30 +0100 Subject: [PATCH 03/15] Update CI to run on macOS 15 with Xcode 16.0 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f3c7dad87..4ca904641 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,12 +4,12 @@ on: jobs: build: - runs-on: macos-14 + runs-on: macos-15 steps: - uses: actions/checkout@v4 with: submodules: true show-progress: false - - run: sudo xcode-select --switch /Applications/Xcode_15.3.app/Contents/Developer + - run: sudo xcode-select --switch /Applications/Xcode_16.0.app/Contents/Developer - run: cargo install cargo-lipo - run: set -o pipefail && xcodebuild -workspace deltachat-ios.xcworkspace -scheme deltachat-ios -destination "platform=iOS Simulator,name=iPhone SE (3rd generation)" | xcbeautify --renderer github-actions From 95647b824c6590ef5deed09c2e1e09f5b955d35a Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Thu, 28 Nov 2024 12:32:37 +0100 Subject: [PATCH 04/15] [WIP] Fetch Most recent WebXDC-apps and show them in the widget (#2406) --- .../MostRecentWebXDCWidget.swift | 99 +++++++++++++------ deltachat-ios.xcodeproj/project.pbxproj | 21 +++- 2 files changed, 91 insertions(+), 29 deletions(-) diff --git a/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift b/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift index c2f9eadd7..ad41f4cfc 100644 --- a/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift +++ b/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift @@ -1,51 +1,101 @@ import WidgetKit import SwiftUI +import DcCore struct Provider: TimelineProvider { - func placeholder(in context: Context) -> SimpleEntry { - SimpleEntry(date: Date(), emoji: "😀") + func placeholder(in context: Context) -> UsedWebXDCEntry { + UsedWebXDCEntry(date: Date(), apps: []) } - func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { - let entry = SimpleEntry(date: Date(), emoji: "😀") + func getSnapshot(in context: Context, completion: @escaping (UsedWebXDCEntry) -> Void) { + let entry = UsedWebXDCEntry(date: Date(), apps: []) completion(entry) } - func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { - var entries: [SimpleEntry] = [] + func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { + var entries: [UsedWebXDCEntry] = [] + let dcAccounts = DcAccounts.shared + dcAccounts.openDatabase(writeable: false) + for accountId in dcAccounts.getAll() { + let dcContext = dcAccounts.get(id: accountId) + if dcContext.isOpen() == false { + do { + let secret = try KeychainManager.getAccountSecret(accountID: accountId) + _ = dcContext.open(passphrase: secret) + } catch { + debugPrint("Couldn't open \(error.localizedDescription)") + } + } + } + + let dcContext = dcAccounts.getSelected() + let chatId = 0 + let ignore = Int32(0) + let messageIds: [Int] = Array(dcContext.getChatMedia(chatId: chatId, messageType: DC_MSG_WEBXDC, messageType2: ignore, messageType3: ignore).reversed().prefix(upTo: 3)) + + let apps = messageIds.compactMap { + dcContext.getMessage(id: $0) + }.compactMap { msg in + let name = msg.getWebxdcAppName() + let image = msg.getWebxdcPreviewImage() + + return UsedWebXDCEntry.WebXDCApp(id: msg.id, image: image, title: name) + } // Generate a timeline consisting of five entries an hour apart, starting from the current date. let currentDate = Date() for hourOffset in 0 ..< 5 { let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! - let entry = SimpleEntry(date: entryDate, emoji: "😀") + // Get the four most recent entries + let entry = UsedWebXDCEntry(date: entryDate, apps: apps) entries.append(entry) } let timeline = Timeline(entries: entries, policy: .atEnd) completion(timeline) } - -// func relevances() async -> WidgetRelevances { -// // Generate a list containing the contexts this widget is relevant in. -// } } -struct SimpleEntry: TimelineEntry { +struct UsedWebXDCEntry: TimelineEntry { + let date: Date - let emoji: String + fileprivate let apps: [WebXDCApp] + + fileprivate struct WebXDCApp: Hashable, Identifiable { + var id: Int + + let image: UIImage? + let title: String + } } -struct MostRecentWebXDCWidgetEntryView : View { +struct MostRecentWebXDCWidgetEntryView: View { var entry: Provider.Entry var body: some View { - VStack { - Text("Time:") - Text(entry.date, style: .time) + if entry.apps.isEmpty { + Text("No apps (yet)") + } else { + VStack(alignment: .leading) { + ForEach(entry.apps) { app in - Text("Emoji:") - Text(entry.emoji) + Button { + print("open...") + // TODO: Open deeplink with chatId and messageId etc. + } label: { + HStack { + if let image = app.image { + Image(uiImage: image) + .resizable() + .frame(width: 32, height: 32) + .cornerRadius(4) + } + Text(app.title) + } + } + .buttonStyle(.plain) + } + } } } } @@ -64,14 +114,7 @@ struct MostRecentWebXDCWidget: Widget { .background() } } - .configurationDisplayName("My Widget") - .description("This is an example widget.") + .configurationDisplayName("Most Recent WebXDC-apps") + .description("Shows the n moth recent WebXDC-apps") } } - -#Preview(as: .systemSmall) { - MostRecentWebXDCWidget() -} timeline: { - SimpleEntry(date: .now, emoji: "😀") - SimpleEntry(date: .now, emoji: "🤩") -} diff --git a/deltachat-ios.xcodeproj/project.pbxproj b/deltachat-ios.xcodeproj/project.pbxproj index 1b737b82d..c13b2e2ae 100644 --- a/deltachat-ios.xcodeproj/project.pbxproj +++ b/deltachat-ios.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -226,6 +226,9 @@ D8C19DCE2C1B456700B32F6D /* SendContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C19DCD2C1B456700B32F6D /* SendContactViewController.swift */; }; D8C19DD02C1C9FFE00B32F6D /* ContactCardPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C19DCF2C1C95A900B32F6D /* ContactCardPreview.swift */; }; D8C1B0DD2CE7421C00C233A7 /* ShareProxyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C1B0DC2CE7421C00C233A7 /* ShareProxyViewController.swift */; }; + D8C7B68A2CF86F91003A6AD1 /* DcCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 304219D1243F588500516852 /* DcCore.framework */; }; + D8C7B68B2CF86F91003A6AD1 /* DcCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 304219D1243F588500516852 /* DcCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8C7B6982CF87F5E003A6AD1 /* DcMsg+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 304219D82440734A00516852 /* DcMsg+Extension.swift */; }; D8CDEFE02C087CDA00146773 /* ContactCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CDEFDF2C087CDA00146773 /* ContactCardCell.swift */; }; D8CDEFE22C087D1000146773 /* ContactCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CDEFE12C087D1000146773 /* ContactCardView.swift */; }; D8CF2DDC2CDD110F001C2352 /* ProxySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CF2DD92CDD110F001C2352 /* ProxySettingsViewController.swift */; }; @@ -283,6 +286,17 @@ name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; }; + D8C7B68C2CF86F91003A6AD1 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + D8C7B68B2CF86F91003A6AD1 /* DcCore.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -679,6 +693,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D8C7B68A2CF86F91003A6AD1 /* DcCore.framework in Frameworks */, D878C4FF2CF72AA0009AF551 /* SwiftUI.framework in Frameworks */, D878C4FD2CF72AA0009AF551 /* WidgetKit.framework in Frameworks */, ); @@ -1303,6 +1318,7 @@ D878C4F72CF72AA0009AF551 /* Sources */, D878C4F82CF72AA0009AF551 /* Frameworks */, D878C4F92CF72AA0009AF551 /* Resources */, + D8C7B68C2CF86F91003A6AD1 /* Embed Frameworks */, ); buildRules = ( ); @@ -1818,6 +1834,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D8C7B6982CF87F5E003A6AD1 /* DcMsg+Extension.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2444,6 +2461,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_ENTITLEMENTS = "deltachat-ios/deltachat-ios.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -2482,6 +2500,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_ENTITLEMENTS = "deltachat-ios/deltachat-ios.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; From bf40aaa85b7d01a1d56898e3741941e0da98c58b Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Thu, 28 Nov 2024 19:08:44 +0100 Subject: [PATCH 05/15] Just show icon and open the app when tapping on it (#2406) --- .../MostRecentWebXDCWidget.swift | 100 ++++++++++++------ 1 file changed, 67 insertions(+), 33 deletions(-) diff --git a/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift b/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift index ad41f4cfc..3bc773c3f 100644 --- a/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift +++ b/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift @@ -13,7 +13,6 @@ struct Provider: TimelineProvider { } func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { - var entries: [UsedWebXDCEntry] = [] let dcAccounts = DcAccounts.shared dcAccounts.openDatabase(writeable: false) for accountId in dcAccounts.getAll() { @@ -31,27 +30,38 @@ struct Provider: TimelineProvider { let dcContext = dcAccounts.getSelected() let chatId = 0 let ignore = Int32(0) - let messageIds: [Int] = Array(dcContext.getChatMedia(chatId: chatId, messageType: DC_MSG_WEBXDC, messageType2: ignore, messageType3: ignore).reversed().prefix(upTo: 3)) + + let limit: Int + switch context.family { + case .systemSmall: limit = 2 + case .systemMedium: limit = 4 + default: limit = 8 + } + + let messageIds: [Int] = Array(dcContext.getChatMedia(chatId: chatId, messageType: DC_MSG_WEBXDC, messageType2: ignore, messageType3: ignore).reversed().prefix(limit)) let apps = messageIds.compactMap { dcContext.getMessage(id: $0) }.compactMap { msg in let name = msg.getWebxdcAppName() let image = msg.getWebxdcPreviewImage() - - return UsedWebXDCEntry.WebXDCApp(id: msg.id, image: image, title: name) + let accountId = dcContext.id + let chatId = msg.chatId + + return WebXDCApp( + accountId: accountId, + chatId: chatId, + messageId: msg.id, + image: image, + title: name + ) } - // Generate a timeline consisting of five entries an hour apart, starting from the current date. let currentDate = Date() - for hourOffset in 0 ..< 5 { - let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! - // Get the four most recent entries - let entry = UsedWebXDCEntry(date: entryDate, apps: apps) - entries.append(entry) - } + let entry = UsedWebXDCEntry(date: currentDate, apps: apps) + let nextDate = Calendar.current.date(byAdding: .minute, value: 15, to: currentDate)! - let timeline = Timeline(entries: entries, policy: .atEnd) + let timeline = Timeline(entries: [entry], policy: .after(nextDate)) completion(timeline) } } @@ -59,13 +69,30 @@ struct Provider: TimelineProvider { struct UsedWebXDCEntry: TimelineEntry { let date: Date - fileprivate let apps: [WebXDCApp] + let apps: [WebXDCApp] +} + +struct WebXDCApp: Hashable, Identifiable { + var id: Int { messageId } + + let accountId: Int + let chatId: Int + let messageId: Int + + let image: UIImage? + let title: String - fileprivate struct WebXDCApp: Hashable, Identifiable { - var id: Int + var url: URL { + var urlComponents = URLComponents() + urlComponents.scheme = "chat.delta.deeplink" + urlComponents.host = "webxdc" + urlComponents.queryItems = [ + URLQueryItem(name: "msgId", value: "\(messageId)"), + URLQueryItem(name: "chatId", value: "\(chatId)"), + URLQueryItem(name: "accountId", value: "\(accountId)"), + ] - let image: UIImage? - let title: String + return urlComponents.url! } } @@ -76,30 +103,36 @@ struct MostRecentWebXDCWidgetEntryView: View { if entry.apps.isEmpty { Text("No apps (yet)") } else { + // TODO: Use Grid! VStack(alignment: .leading) { ForEach(entry.apps) { app in - - Button { - print("open...") - // TODO: Open deeplink with chatId and messageId etc. - } label: { - HStack { - if let image = app.image { - Image(uiImage: image) - .resizable() - .frame(width: 32, height: 32) - .cornerRadius(4) - } - Text(app.title) - } - } - .buttonStyle(.plain) + WebXDCAppView(app: app) } } } } } +struct WebXDCAppView: View { + var app: WebXDCApp + + var body: some View { + Link(destination: app.url) { + if let image = app.image { + Image(uiImage: image) + .resizable() + .frame(width: 56, height: 56) + .cornerRadius(12) + } else { + Color(.systemBackground) + .frame(width: 56, height: 56) + .cornerRadius(12) + } + } + } +} + +// TODO: Localization struct MostRecentWebXDCWidget: Widget { let kind: String = "MostRecentWebXDCWidget" @@ -114,6 +147,7 @@ struct MostRecentWebXDCWidget: Widget { .background() } } + .supportedFamilies([.systemSmall, .systemMedium]) .configurationDisplayName("Most Recent WebXDC-apps") .description("Shows the n moth recent WebXDC-apps") } From 9a133f528f77cf9007ce7f7405b212ddabb57f6a Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Thu, 28 Nov 2024 19:34:36 +0100 Subject: [PATCH 06/15] Show the "app icons" in a grid ($2406) --- MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift b/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift index 3bc773c3f..78a5a38c8 100644 --- a/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift +++ b/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift @@ -33,8 +33,8 @@ struct Provider: TimelineProvider { let limit: Int switch context.family { - case .systemSmall: limit = 2 - case .systemMedium: limit = 4 + case .systemSmall: limit = 4 + case .systemMedium: limit = 8 default: limit = 8 } @@ -103,8 +103,8 @@ struct MostRecentWebXDCWidgetEntryView: View { if entry.apps.isEmpty { Text("No apps (yet)") } else { - // TODO: Use Grid! - VStack(alignment: .leading) { + let rows = [GridItem(.fixed(56)), GridItem(.fixed(56))] + LazyHGrid(rows: rows) { ForEach(entry.apps) { app in WebXDCAppView(app: app) } From 7e3810a8aa8b421db275c29be01c556bbfd3e689 Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Thu, 5 Dec 2024 11:18:36 +0100 Subject: [PATCH 07/15] Add placeholders for preview when adding a widget (#2406) --- .../checklist.imageset/Contents.json | 12 ++++++++ .../checklist.imageset/checklist.png | Bin 0 -> 1022 bytes .../hello.imageset/Contents.json | 12 ++++++++ .../Assets.xcassets/hello.imageset/hello.png | Bin 0 -> 16283 bytes .../packabunchas.imageset/Contents.json | 12 ++++++++ .../packabunchas.imageset/packabunchas.png | Bin 0 -> 4485 bytes .../pixel.imageset/Contents.json | 12 ++++++++ .../Assets.xcassets/pixel.imageset/pixel.png | Bin 0 -> 682 bytes .../webxdc.imageset/Contents.json | 12 ++++++++ .../webxdc.imageset/webxdc.png | Bin 0 -> 18155 bytes .../MostRecentWebXDCWidget.swift | 27 ++++++++++++++++-- 11 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 MostRecentWebXDCWidget/Assets.xcassets/checklist.imageset/Contents.json create mode 100644 MostRecentWebXDCWidget/Assets.xcassets/checklist.imageset/checklist.png create mode 100644 MostRecentWebXDCWidget/Assets.xcassets/hello.imageset/Contents.json create mode 100644 MostRecentWebXDCWidget/Assets.xcassets/hello.imageset/hello.png create mode 100644 MostRecentWebXDCWidget/Assets.xcassets/packabunchas.imageset/Contents.json create mode 100644 MostRecentWebXDCWidget/Assets.xcassets/packabunchas.imageset/packabunchas.png create mode 100644 MostRecentWebXDCWidget/Assets.xcassets/pixel.imageset/Contents.json create mode 100644 MostRecentWebXDCWidget/Assets.xcassets/pixel.imageset/pixel.png create mode 100644 MostRecentWebXDCWidget/Assets.xcassets/webxdc.imageset/Contents.json create mode 100644 MostRecentWebXDCWidget/Assets.xcassets/webxdc.imageset/webxdc.png diff --git a/MostRecentWebXDCWidget/Assets.xcassets/checklist.imageset/Contents.json b/MostRecentWebXDCWidget/Assets.xcassets/checklist.imageset/Contents.json new file mode 100644 index 000000000..ccbddaba2 --- /dev/null +++ b/MostRecentWebXDCWidget/Assets.xcassets/checklist.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "checklist.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MostRecentWebXDCWidget/Assets.xcassets/checklist.imageset/checklist.png b/MostRecentWebXDCWidget/Assets.xcassets/checklist.imageset/checklist.png new file mode 100644 index 0000000000000000000000000000000000000000..e452412e4c3eb52fd1b86f1313fc3349f775e87c GIT binary patch literal 1022 zcmeAS@N?(olHy`uVBq!ia0y~yV3Yu17G|JG>4Go&fs{djPl)UP|Nnpd{r~9m|J~32 zAG!ax`SSm|{r~L`{AXDAUuE`xhDHAw=KPnb`^V7ppP~9cL&g8YY(5Ve7??XfT^vIy zZoRp|=)xQ*bNu7|(76qFn+#?+u&^pfI4oc=v|u^Fl;FV7%qXDeB(cIJmy02RfrC+j z<+)+l_2xgX-`c;u`~Q~gzjy;S`ER%PzD*2SD3To9lO(cO;;4q3VOJo6St*rn>>GdT zpv}H?pZVfT6XqFj+a6@F-sah2kux)n`*8#Y^N#qJI)%S8q8O}9;`ugjIlE*- ztI7Q)wwejt$q9!w&xZfIDrS?OR5Sg|L7Bt*^Xz&Wd!I#L5UzO8ztFhzS@emUTV{P< zV8W2G@93Oc(mVa%N&rnU?pYGA#^1B>r;yvWgoEeL&3JY?DetKL7aN0T-)o-!=exz^ zBi8)PTjG`7qRHuvWo|+}j=hHiuKzCmqfww9yL|hr+vx}-%{Rkh}9YkPJAo80I12?f`&^_Ts8wns5$fXUy}SZm#5MYME?BOCeu)KaYAu`o>URvDOcxn zjyp3>?EEakYgz8LNb&iC7?US+nmgO(2cax zJK^*F9-y8^QC`dc^96Y=*`Go+%1r5Qo6}r$6~*-Q6P6m~yq3&QmnqiUDQcXbP(6Kh zlF<`siBCM0ZF}JEM7S8lVmv+J^5T-V8g1t|pSBur=Y6vku+xBHkq=~tZ0`}6h$poE;k?FsSBY0{sP4>Tx#PX^-sxwj|$Hasvvo>@)0 z$i7YDQ}KZu=Q`dQxhLw`&zL=_2MT}s4$^;o!pXKjGDbHi+;@H^_38M6oo#=lOs-D& r&oevi#B^Tw(@=Lu<8k-(e~kWb&+aJbJ3JYfp&2|~{an^LB{Ts5A{O3Y literal 0 HcmV?d00001 diff --git a/MostRecentWebXDCWidget/Assets.xcassets/hello.imageset/Contents.json b/MostRecentWebXDCWidget/Assets.xcassets/hello.imageset/Contents.json new file mode 100644 index 000000000..2834f1755 --- /dev/null +++ b/MostRecentWebXDCWidget/Assets.xcassets/hello.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "hello.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MostRecentWebXDCWidget/Assets.xcassets/hello.imageset/hello.png b/MostRecentWebXDCWidget/Assets.xcassets/hello.imageset/hello.png new file mode 100644 index 0000000000000000000000000000000000000000..0a5ed8deabede31b3a237c2be261f9b221572af1 GIT binary patch literal 16283 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 literal 0 HcmV?d00001 diff --git a/MostRecentWebXDCWidget/Assets.xcassets/packabunchas.imageset/Contents.json b/MostRecentWebXDCWidget/Assets.xcassets/packabunchas.imageset/Contents.json new file mode 100644 index 000000000..720e758f6 --- /dev/null +++ b/MostRecentWebXDCWidget/Assets.xcassets/packabunchas.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "packabunchas.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MostRecentWebXDCWidget/Assets.xcassets/packabunchas.imageset/packabunchas.png b/MostRecentWebXDCWidget/Assets.xcassets/packabunchas.imageset/packabunchas.png new file mode 100644 index 0000000000000000000000000000000000000000..d25bed7b1a9bb318cb47a3156fa98d810bea7dab GIT binary patch literal 4485 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 cHk^ex-szcr^#qX3T1IDbHOV3W1Fy+FH2?qr literal 0 HcmV?d00001 diff --git a/MostRecentWebXDCWidget/Assets.xcassets/pixel.imageset/Contents.json b/MostRecentWebXDCWidget/Assets.xcassets/pixel.imageset/Contents.json new file mode 100644 index 000000000..53f779f84 --- /dev/null +++ b/MostRecentWebXDCWidget/Assets.xcassets/pixel.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "pixel.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MostRecentWebXDCWidget/Assets.xcassets/pixel.imageset/pixel.png b/MostRecentWebXDCWidget/Assets.xcassets/pixel.imageset/pixel.png new file mode 100644 index 0000000000000000000000000000000000000000..39f2762c5ed0a9325c00748105fba4e418a9a920 GIT binary patch literal 682 zcmeAS@N?(olHy`uVBq!ia0y~yU@8S+MrNQ$tK}4NAjKBo6XMFu&h+i)*Cwl;|3K+@ zPZ!6Kid#uP_*tGe{)o4o@PqTwS@uWv3dhoQY-TKau5d{1!o9f*cJMkUGUQ1pyeV#x z*}BhheaH(}ro9fQUL6YP;`w~QreAA{)6!qU1};Z_cHH0aXVbJL_f9?jSYadAfBdoS z+gi=}n)mweRO^OM6~BLae)KEbQ{DMbxkcZ9%=|L#_4Ymczg~*jU%k`j)%CYfJs&HY zxBsn3?Eby}cbuGljY96@*4n?@yz`@v-rF0!D*kLV`v-{OA1n57ey)8P^Qp!m=8A>9 zz}MS*Y?PwR)+hgKJ`Q!j>kn$DA7|Djo~*RF*WG`If8O@+y(aT^%2jIv9S5@S{gQjp z&DSUSr=NHnf4aLuX`c2svGN<+{;BSnU%I|@UlYur>U-68@6Szt|M+F~+d3dIE8gR$ zT}$%%LbzS!dif`Reku00)7|@_?(DSVpa0!DRq-t9=K9+Rk6HG&mv?>^KD>MBJ>`8D zu2lZ}6AVRzO#a9{!5b1xz8-1m#t=f2;pp82}6`uD$E(>@07hdJW$-`&stxPZbH z8`wph9g1OT6_1@z5 zzvAa#4~nT%n+M<`5TVpa1<;n{0>Zf44$rjF6*2U FngFONcC-Kh literal 0 HcmV?d00001 diff --git a/MostRecentWebXDCWidget/Assets.xcassets/webxdc.imageset/Contents.json b/MostRecentWebXDCWidget/Assets.xcassets/webxdc.imageset/Contents.json new file mode 100644 index 000000000..9a83266fe --- /dev/null +++ b/MostRecentWebXDCWidget/Assets.xcassets/webxdc.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "webxdc.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MostRecentWebXDCWidget/Assets.xcassets/webxdc.imageset/webxdc.png b/MostRecentWebXDCWidget/Assets.xcassets/webxdc.imageset/webxdc.png new file mode 100644 index 0000000000000000000000000000000000000000..ab6bdf6000cf90073fc5b6813820860fc7e9da82 GIT binary patch literal 18155 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 literal 0 HcmV?d00001 diff --git a/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift b/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift index 78a5a38c8..8271af1d5 100644 --- a/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift +++ b/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift @@ -4,11 +4,32 @@ import DcCore struct Provider: TimelineProvider { func placeholder(in context: Context) -> UsedWebXDCEntry { - UsedWebXDCEntry(date: Date(), apps: []) + let limit: Int + switch context.family { + case .systemSmall: + limit = 4 + case .systemMedium: + limit = 7 + default: + limit = 7 + } + + let apps = [ + WebXDCApp(accountId: 0, chatId: 0, messageId: 0, image: UIImage(named: "checklist"), title: "checklist"), + WebXDCApp(accountId: 0, chatId: 0, messageId: 1, image: UIImage(named: "hello"), title: "hello"), + WebXDCApp(accountId: 0, chatId: 0, messageId: 6, image: UIImage(named: "packabunchas"), title: "packabunchas"), + WebXDCApp(accountId: 0, chatId: 0, messageId: 3, image: UIImage(named: "webxdc"), title: "webxdc"), + WebXDCApp(accountId: 0, chatId: 0, messageId: 2, image: UIImage(named: "pixel"), title: "pixel"), + WebXDCApp(accountId: 0, chatId: 0, messageId: 4, image: UIImage(named: "checklist"), title: "checklist"), + WebXDCApp(accountId: 0, chatId: 0, messageId: 5, image: UIImage(named: "hello"), title: "hello"), + WebXDCApp(accountId: 0, chatId: 0, messageId: 7, image: UIImage(named: "webxdc"), title: "webxdc"), + ] + + return UsedWebXDCEntry(date: Date(), apps: Array(apps.prefix(limit))) } func getSnapshot(in context: Context, completion: @escaping (UsedWebXDCEntry) -> Void) { - let entry = UsedWebXDCEntry(date: Date(), apps: []) + let entry = placeholder(in: context) completion(entry) } @@ -106,7 +127,7 @@ struct MostRecentWebXDCWidgetEntryView: View { let rows = [GridItem(.fixed(56)), GridItem(.fixed(56))] LazyHGrid(rows: rows) { ForEach(entry.apps) { app in - WebXDCAppView(app: app) + WebXDCAppView(app: app).accessibilityLabel(Text(app.title)) } } } From c9b994eb80220c048209d0b46357503dbc42ed85 Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Thu, 5 Dec 2024 12:37:35 +0100 Subject: [PATCH 08/15] Remove lockscreen-control (#2406) --- .../MostRecentWebXDCWidgetBundle.swift | 1 - .../MostRecentWebXDCWidgetControl.swift | 47 ------------------- 2 files changed, 48 deletions(-) delete mode 100644 MostRecentWebXDCWidget/MostRecentWebXDCWidgetControl.swift diff --git a/MostRecentWebXDCWidget/MostRecentWebXDCWidgetBundle.swift b/MostRecentWebXDCWidget/MostRecentWebXDCWidgetBundle.swift index a09e7a94a..3893a69dc 100644 --- a/MostRecentWebXDCWidget/MostRecentWebXDCWidgetBundle.swift +++ b/MostRecentWebXDCWidget/MostRecentWebXDCWidgetBundle.swift @@ -5,6 +5,5 @@ import SwiftUI struct MostRecentWebXDCWidgetBundle: WidgetBundle { var body: some Widget { MostRecentWebXDCWidget() - MostRecentWebXDCWidgetControl() } } diff --git a/MostRecentWebXDCWidget/MostRecentWebXDCWidgetControl.swift b/MostRecentWebXDCWidget/MostRecentWebXDCWidgetControl.swift deleted file mode 100644 index 988299c7c..000000000 --- a/MostRecentWebXDCWidget/MostRecentWebXDCWidgetControl.swift +++ /dev/null @@ -1,47 +0,0 @@ -import AppIntents -import SwiftUI -import WidgetKit - -struct MostRecentWebXDCWidgetControl: ControlWidget { - var body: some ControlWidgetConfiguration { - StaticControlConfiguration( - kind: "chat.delta.MostRecentWebXDCWidget", - provider: Provider() - ) { value in - ControlWidgetToggle( - "Start Timer", - isOn: value, - action: StartTimerIntent() - ) { isRunning in - Label(isRunning ? "On" : "Off", systemImage: "timer") - } - } - .displayName("Timer") - .description("A an example control that runs a timer.") - } -} - -extension MostRecentWebXDCWidgetControl { - struct Provider: ControlValueProvider { - var previewValue: Bool { - false - } - - func currentValue() async throws -> Bool { - let isRunning = true // Check if the timer is running - return isRunning - } - } -} - -struct StartTimerIntent: SetValueIntent { - static let title: LocalizedStringResource = "Start a timer" - - @Parameter(title: "Timer is running") - var value: Bool - - func perform() async throws -> some IntentResult { - // Start / stop the timer based on `value`. - return .result() - } -} From 49e9431975cbf75a27b04b668d1d3022075c5fd4 Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Thu, 5 Dec 2024 14:42:34 +0100 Subject: [PATCH 09/15] Do TODO: Localization (#2406) --- MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift | 10 ++++++---- deltachat-ios.xcodeproj/project.pbxproj | 2 ++ deltachat-ios/en.lproj/Localizable.strings | 3 +++ scripts/untranslated.xml | 3 +++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift b/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift index 8271af1d5..95fb5c312 100644 --- a/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift +++ b/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift @@ -34,6 +34,9 @@ struct Provider: TimelineProvider { } func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { + /// ---------------------------------- + /// **DO NOT** START dcAccounts.startIo() IN WIDGET + /// ---------------------------------- let dcAccounts = DcAccounts.shared dcAccounts.openDatabase(writeable: false) for accountId in dcAccounts.getAll() { @@ -122,7 +125,7 @@ struct MostRecentWebXDCWidgetEntryView: View { var body: some View { if entry.apps.isEmpty { - Text("No apps (yet)") + Text(String.localized("widget_no_apps")) } else { let rows = [GridItem(.fixed(56)), GridItem(.fixed(56))] LazyHGrid(rows: rows) { @@ -153,7 +156,6 @@ struct WebXDCAppView: View { } } -// TODO: Localization struct MostRecentWebXDCWidget: Widget { let kind: String = "MostRecentWebXDCWidget" @@ -169,7 +171,7 @@ struct MostRecentWebXDCWidget: Widget { } } .supportedFamilies([.systemSmall, .systemMedium]) - .configurationDisplayName("Most Recent WebXDC-apps") - .description("Shows the n moth recent WebXDC-apps") + .configurationDisplayName(String.localized("widget_most_recent_apps_title")) + .description(String.localized("widget_most_recent_apps_description")) } } diff --git a/deltachat-ios.xcodeproj/project.pbxproj b/deltachat-ios.xcodeproj/project.pbxproj index c13b2e2ae..deb715d7b 100644 --- a/deltachat-ios.xcodeproj/project.pbxproj +++ b/deltachat-ios.xcodeproj/project.pbxproj @@ -222,6 +222,7 @@ D878C4FD2CF72AA0009AF551 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D878C4FC2CF72AA0009AF551 /* WidgetKit.framework */; }; D878C4FF2CF72AA0009AF551 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D878C4FE2CF72AA0009AF551 /* SwiftUI.framework */; }; D878C50C2CF72AA1009AF551 /* MostRecentWebXDCWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D878C4FB2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + D87C64672D01E4EF004472D6 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3060119E22DDE24000C1CE6F /* Localizable.strings */; }; D8A072A02BED0FD8001A4C7C /* InstantOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A0729F2BED0FD8001A4C7C /* InstantOnboardingView.swift */; }; D8C19DCE2C1B456700B32F6D /* SendContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C19DCD2C1B456700B32F6D /* SendContactViewController.swift */; }; D8C19DD02C1C9FFE00B32F6D /* ContactCardPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C19DCF2C1C95A900B32F6D /* ContactCardPreview.swift */; }; @@ -1485,6 +1486,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + D87C64672D01E4EF004472D6 /* Localizable.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/deltachat-ios/en.lproj/Localizable.strings b/deltachat-ios/en.lproj/Localizable.strings index 851566ad6..574fce047 100644 --- a/deltachat-ios/en.lproj/Localizable.strings +++ b/deltachat-ios/en.lproj/Localizable.strings @@ -1063,3 +1063,6 @@ "backup_successful_explain_ios" = "You can find the backup in the \"Delta Chat\" folder using the \"Files\" app.\n\nMove the backup out of this folder to keep it when deleting Delta Chat."; "location_denied" = "Location access denied"; "location_denied_explain_ios" = "In the system settings, enable \"Privacy/Location Services\" and set \"Delta Chat/Location\" to \"Always\" and \"Precise\"."; +"widget_no_apps" = "No apps."; +"widget_most_recent_apps_title" = "Recent Webxdc-apps"; +"widget_most_recent_apps_description" = "Shows the most recent Webxdc-apps."; diff --git a/scripts/untranslated.xml b/scripts/untranslated.xml index 46f445091..e56a8a26c 100644 --- a/scripts/untranslated.xml +++ b/scripts/untranslated.xml @@ -7,4 +7,7 @@ You can find the backup in the \"Delta Chat\" folder using the \"Files\" app.\n\nMove the backup out of this folder to keep it when deleting Delta Chat. Location access denied In the system settings, enable \"Privacy/Location Services\" and set \"Delta Chat/Location\" to \"Always\" and \"Precise\". + No apps. + Recent Webxdc-apps + Shows the most recent Webxdc-apps. From 3d7ede7b35fbb226e453f63499d158768d512237 Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Thu, 5 Dec 2024 15:28:30 +0100 Subject: [PATCH 10/15] Rename some things (#2406) --- .../AccentColor.colorset/Contents.json | 0 .../AppIcon.appiconset/Contents.json | 0 .../Assets.xcassets/Contents.json | 0 .../WidgetBackground.colorset/Contents.json | 0 .../checklist.imageset/Contents.json | 0 .../checklist.imageset/checklist.png | Bin .../hello.imageset/Contents.json | 0 .../Assets.xcassets/hello.imageset/hello.png | Bin .../packabunchas.imageset/Contents.json | 0 .../packabunchas.imageset/packabunchas.png | Bin .../pixel.imageset/Contents.json | 0 .../Assets.xcassets/pixel.imageset/pixel.png | Bin .../webxdc.imageset/Contents.json | 0 .../webxdc.imageset/webxdc.png | Bin .../DcWebxdcWidget.swift | 44 +++++++++--------- DcWebxdcWidget/DcWebxdcWidgetBundle.swift | 9 ++++ .../Info.plist | 0 .../MostRecentWebXDCWidgetBundle.swift | 9 ---- deltachat-ios.xcodeproj/project.pbxproj | 42 ++++++++--------- ...cheme => DcWebxdcWidgetExtension.xcscheme} | 8 ++-- 20 files changed, 56 insertions(+), 56 deletions(-) rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Assets.xcassets/AccentColor.colorset/Contents.json (100%) rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Assets.xcassets/Contents.json (100%) rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Assets.xcassets/WidgetBackground.colorset/Contents.json (100%) rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Assets.xcassets/checklist.imageset/Contents.json (100%) rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Assets.xcassets/checklist.imageset/checklist.png (100%) rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Assets.xcassets/hello.imageset/Contents.json (100%) rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Assets.xcassets/hello.imageset/hello.png (100%) rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Assets.xcassets/packabunchas.imageset/Contents.json (100%) rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Assets.xcassets/packabunchas.imageset/packabunchas.png (100%) rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Assets.xcassets/pixel.imageset/Contents.json (100%) rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Assets.xcassets/pixel.imageset/pixel.png (100%) rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Assets.xcassets/webxdc.imageset/Contents.json (100%) rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Assets.xcassets/webxdc.imageset/webxdc.png (100%) rename MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift => DcWebxdcWidget/DcWebxdcWidget.swift (81%) create mode 100644 DcWebxdcWidget/DcWebxdcWidgetBundle.swift rename {MostRecentWebXDCWidget => DcWebxdcWidget}/Info.plist (100%) delete mode 100644 MostRecentWebXDCWidget/MostRecentWebXDCWidgetBundle.swift rename deltachat-ios.xcodeproj/xcshareddata/xcschemes/{MostRecentWebXDCWidgetExtension.xcscheme => DcWebxdcWidgetExtension.xcscheme} (94%) diff --git a/MostRecentWebXDCWidget/Assets.xcassets/AccentColor.colorset/Contents.json b/DcWebxdcWidget/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from MostRecentWebXDCWidget/Assets.xcassets/AccentColor.colorset/Contents.json rename to DcWebxdcWidget/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/MostRecentWebXDCWidget/Assets.xcassets/AppIcon.appiconset/Contents.json b/DcWebxdcWidget/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from MostRecentWebXDCWidget/Assets.xcassets/AppIcon.appiconset/Contents.json rename to DcWebxdcWidget/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/MostRecentWebXDCWidget/Assets.xcassets/Contents.json b/DcWebxdcWidget/Assets.xcassets/Contents.json similarity index 100% rename from MostRecentWebXDCWidget/Assets.xcassets/Contents.json rename to DcWebxdcWidget/Assets.xcassets/Contents.json diff --git a/MostRecentWebXDCWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/DcWebxdcWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json similarity index 100% rename from MostRecentWebXDCWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json rename to DcWebxdcWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json diff --git a/MostRecentWebXDCWidget/Assets.xcassets/checklist.imageset/Contents.json b/DcWebxdcWidget/Assets.xcassets/checklist.imageset/Contents.json similarity index 100% rename from MostRecentWebXDCWidget/Assets.xcassets/checklist.imageset/Contents.json rename to DcWebxdcWidget/Assets.xcassets/checklist.imageset/Contents.json diff --git a/MostRecentWebXDCWidget/Assets.xcassets/checklist.imageset/checklist.png b/DcWebxdcWidget/Assets.xcassets/checklist.imageset/checklist.png similarity index 100% rename from MostRecentWebXDCWidget/Assets.xcassets/checklist.imageset/checklist.png rename to DcWebxdcWidget/Assets.xcassets/checklist.imageset/checklist.png diff --git a/MostRecentWebXDCWidget/Assets.xcassets/hello.imageset/Contents.json b/DcWebxdcWidget/Assets.xcassets/hello.imageset/Contents.json similarity index 100% rename from MostRecentWebXDCWidget/Assets.xcassets/hello.imageset/Contents.json rename to DcWebxdcWidget/Assets.xcassets/hello.imageset/Contents.json diff --git a/MostRecentWebXDCWidget/Assets.xcassets/hello.imageset/hello.png b/DcWebxdcWidget/Assets.xcassets/hello.imageset/hello.png similarity index 100% rename from MostRecentWebXDCWidget/Assets.xcassets/hello.imageset/hello.png rename to DcWebxdcWidget/Assets.xcassets/hello.imageset/hello.png diff --git a/MostRecentWebXDCWidget/Assets.xcassets/packabunchas.imageset/Contents.json b/DcWebxdcWidget/Assets.xcassets/packabunchas.imageset/Contents.json similarity index 100% rename from MostRecentWebXDCWidget/Assets.xcassets/packabunchas.imageset/Contents.json rename to DcWebxdcWidget/Assets.xcassets/packabunchas.imageset/Contents.json diff --git a/MostRecentWebXDCWidget/Assets.xcassets/packabunchas.imageset/packabunchas.png b/DcWebxdcWidget/Assets.xcassets/packabunchas.imageset/packabunchas.png similarity index 100% rename from MostRecentWebXDCWidget/Assets.xcassets/packabunchas.imageset/packabunchas.png rename to DcWebxdcWidget/Assets.xcassets/packabunchas.imageset/packabunchas.png diff --git a/MostRecentWebXDCWidget/Assets.xcassets/pixel.imageset/Contents.json b/DcWebxdcWidget/Assets.xcassets/pixel.imageset/Contents.json similarity index 100% rename from MostRecentWebXDCWidget/Assets.xcassets/pixel.imageset/Contents.json rename to DcWebxdcWidget/Assets.xcassets/pixel.imageset/Contents.json diff --git a/MostRecentWebXDCWidget/Assets.xcassets/pixel.imageset/pixel.png b/DcWebxdcWidget/Assets.xcassets/pixel.imageset/pixel.png similarity index 100% rename from MostRecentWebXDCWidget/Assets.xcassets/pixel.imageset/pixel.png rename to DcWebxdcWidget/Assets.xcassets/pixel.imageset/pixel.png diff --git a/MostRecentWebXDCWidget/Assets.xcassets/webxdc.imageset/Contents.json b/DcWebxdcWidget/Assets.xcassets/webxdc.imageset/Contents.json similarity index 100% rename from MostRecentWebXDCWidget/Assets.xcassets/webxdc.imageset/Contents.json rename to DcWebxdcWidget/Assets.xcassets/webxdc.imageset/Contents.json diff --git a/MostRecentWebXDCWidget/Assets.xcassets/webxdc.imageset/webxdc.png b/DcWebxdcWidget/Assets.xcassets/webxdc.imageset/webxdc.png similarity index 100% rename from MostRecentWebXDCWidget/Assets.xcassets/webxdc.imageset/webxdc.png rename to DcWebxdcWidget/Assets.xcassets/webxdc.imageset/webxdc.png diff --git a/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift b/DcWebxdcWidget/DcWebxdcWidget.swift similarity index 81% rename from MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift rename to DcWebxdcWidget/DcWebxdcWidget.swift index 95fb5c312..5c14a4fbf 100644 --- a/MostRecentWebXDCWidget/MostRecentWebXDCWidget.swift +++ b/DcWebxdcWidget/DcWebxdcWidget.swift @@ -3,7 +3,7 @@ import SwiftUI import DcCore struct Provider: TimelineProvider { - func placeholder(in context: Context) -> UsedWebXDCEntry { + func placeholder(in context: Context) -> UsedWebxdcEntry { let limit: Int switch context.family { case .systemSmall: @@ -15,20 +15,20 @@ struct Provider: TimelineProvider { } let apps = [ - WebXDCApp(accountId: 0, chatId: 0, messageId: 0, image: UIImage(named: "checklist"), title: "checklist"), - WebXDCApp(accountId: 0, chatId: 0, messageId: 1, image: UIImage(named: "hello"), title: "hello"), - WebXDCApp(accountId: 0, chatId: 0, messageId: 6, image: UIImage(named: "packabunchas"), title: "packabunchas"), - WebXDCApp(accountId: 0, chatId: 0, messageId: 3, image: UIImage(named: "webxdc"), title: "webxdc"), - WebXDCApp(accountId: 0, chatId: 0, messageId: 2, image: UIImage(named: "pixel"), title: "pixel"), - WebXDCApp(accountId: 0, chatId: 0, messageId: 4, image: UIImage(named: "checklist"), title: "checklist"), - WebXDCApp(accountId: 0, chatId: 0, messageId: 5, image: UIImage(named: "hello"), title: "hello"), - WebXDCApp(accountId: 0, chatId: 0, messageId: 7, image: UIImage(named: "webxdc"), title: "webxdc"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 0, image: UIImage(named: "checklist"), title: "checklist"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 1, image: UIImage(named: "hello"), title: "hello"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 6, image: UIImage(named: "packabunchas"), title: "packabunchas"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 3, image: UIImage(named: "webxdc"), title: "webxdc"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 2, image: UIImage(named: "pixel"), title: "pixel"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 4, image: UIImage(named: "checklist"), title: "checklist"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 5, image: UIImage(named: "hello"), title: "hello"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 7, image: UIImage(named: "webxdc"), title: "webxdc"), ] - return UsedWebXDCEntry(date: Date(), apps: Array(apps.prefix(limit))) + return UsedWebxdcEntry(date: Date(), apps: Array(apps.prefix(limit))) } - func getSnapshot(in context: Context, completion: @escaping (UsedWebXDCEntry) -> Void) { + func getSnapshot(in context: Context, completion: @escaping (UsedWebxdcEntry) -> Void) { let entry = placeholder(in: context) completion(entry) } @@ -72,7 +72,7 @@ struct Provider: TimelineProvider { let accountId = dcContext.id let chatId = msg.chatId - return WebXDCApp( + return WebxdcApp( accountId: accountId, chatId: chatId, messageId: msg.id, @@ -82,7 +82,7 @@ struct Provider: TimelineProvider { } let currentDate = Date() - let entry = UsedWebXDCEntry(date: currentDate, apps: apps) + let entry = UsedWebxdcEntry(date: currentDate, apps: apps) let nextDate = Calendar.current.date(byAdding: .minute, value: 15, to: currentDate)! let timeline = Timeline(entries: [entry], policy: .after(nextDate)) @@ -90,13 +90,13 @@ struct Provider: TimelineProvider { } } -struct UsedWebXDCEntry: TimelineEntry { +struct UsedWebxdcEntry: TimelineEntry { let date: Date - let apps: [WebXDCApp] + let apps: [WebxdcApp] } -struct WebXDCApp: Hashable, Identifiable { +struct WebxdcApp: Hashable, Identifiable { var id: Int { messageId } let accountId: Int @@ -120,7 +120,7 @@ struct WebXDCApp: Hashable, Identifiable { } } -struct MostRecentWebXDCWidgetEntryView: View { +struct DcWebxdcWidgetEntryView: View { var entry: Provider.Entry var body: some View { @@ -138,7 +138,7 @@ struct MostRecentWebXDCWidgetEntryView: View { } struct WebXDCAppView: View { - var app: WebXDCApp + var app: WebxdcApp var body: some View { Link(destination: app.url) { @@ -156,16 +156,16 @@ struct WebXDCAppView: View { } } -struct MostRecentWebXDCWidget: Widget { - let kind: String = "MostRecentWebXDCWidget" +struct DcWebxdcWidget: Widget { + let kind: String = "DcWebxdcWidget" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: Provider()) { entry in if #available(iOS 17.0, *) { - MostRecentWebXDCWidgetEntryView(entry: entry) + DcWebxdcWidgetEntryView(entry: entry) .containerBackground(.fill.tertiary, for: .widget) } else { - MostRecentWebXDCWidgetEntryView(entry: entry) + DcWebxdcWidgetEntryView(entry: entry) .padding() .background() } diff --git a/DcWebxdcWidget/DcWebxdcWidgetBundle.swift b/DcWebxdcWidget/DcWebxdcWidgetBundle.swift new file mode 100644 index 000000000..dc9bf32fb --- /dev/null +++ b/DcWebxdcWidget/DcWebxdcWidgetBundle.swift @@ -0,0 +1,9 @@ +import WidgetKit +import SwiftUI + +@main +struct DcWebxdcWidgetBundle: WidgetBundle { + var body: some Widget { + DcWebxdcWidget() + } +} diff --git a/MostRecentWebXDCWidget/Info.plist b/DcWebxdcWidget/Info.plist similarity index 100% rename from MostRecentWebXDCWidget/Info.plist rename to DcWebxdcWidget/Info.plist diff --git a/MostRecentWebXDCWidget/MostRecentWebXDCWidgetBundle.swift b/MostRecentWebXDCWidget/MostRecentWebXDCWidgetBundle.swift deleted file mode 100644 index 3893a69dc..000000000 --- a/MostRecentWebXDCWidget/MostRecentWebXDCWidgetBundle.swift +++ /dev/null @@ -1,9 +0,0 @@ -import WidgetKit -import SwiftUI - -@main -struct MostRecentWebXDCWidgetBundle: WidgetBundle { - var body: some Widget { - MostRecentWebXDCWidget() - } -} diff --git a/deltachat-ios.xcodeproj/project.pbxproj b/deltachat-ios.xcodeproj/project.pbxproj index deb715d7b..a80877e36 100644 --- a/deltachat-ios.xcodeproj/project.pbxproj +++ b/deltachat-ios.xcodeproj/project.pbxproj @@ -221,7 +221,7 @@ D85DF9802C5250E200A01408 /* ProgressAlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85DF97F2C5250E200A01408 /* ProgressAlertHandler.swift */; }; D878C4FD2CF72AA0009AF551 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D878C4FC2CF72AA0009AF551 /* WidgetKit.framework */; }; D878C4FF2CF72AA0009AF551 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D878C4FE2CF72AA0009AF551 /* SwiftUI.framework */; }; - D878C50C2CF72AA1009AF551 /* MostRecentWebXDCWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D878C4FB2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + D878C50C2CF72AA1009AF551 /* DcWebxdcWidget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D878C4FB2CF72AA0009AF551 /* DcWebxdcWidget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; D87C64672D01E4EF004472D6 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3060119E22DDE24000C1CE6F /* Localizable.strings */; }; D8A072A02BED0FD8001A4C7C /* InstantOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A0729F2BED0FD8001A4C7C /* InstantOnboardingView.swift */; }; D8C19DCE2C1B456700B32F6D /* SendContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C19DCD2C1B456700B32F6D /* SendContactViewController.swift */; }; @@ -281,7 +281,7 @@ dstSubfolderSpec = 13; files = ( B2D0E4992B93A4B200791949 /* DcNotificationService.appex in Embed Foundation Extensions */, - D878C50C2CF72AA1009AF551 /* MostRecentWebXDCWidgetExtension.appex in Embed Foundation Extensions */, + D878C50C2CF72AA1009AF551 /* DcWebxdcWidget.appex in Embed Foundation Extensions */, 30E8F21A2447285600CE2C90 /* Delta Chat.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; @@ -633,7 +633,7 @@ D84AED262B566C0700D753F6 /* ReactionsOverviewTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsOverviewTableViewCell.swift; sourceTree = ""; }; D85DF9772C4A96CB00A01408 /* UserDefaults+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Extensions.swift"; sourceTree = ""; }; D85DF97F2C5250E200A01408 /* ProgressAlertHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressAlertHandler.swift; sourceTree = ""; }; - D878C4FB2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = MostRecentWebXDCWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + D878C4FB2CF72AA0009AF551 /* DcWebxdcWidget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = DcWebxdcWidget.appex; sourceTree = BUILT_PRODUCTS_DIR; }; D878C4FC2CF72AA0009AF551 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; D878C4FE2CF72AA0009AF551 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; D8A0729F2BED0FD8001A4C7C /* InstantOnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantOnboardingView.swift; sourceTree = ""; }; @@ -656,12 +656,12 @@ membershipExceptions = ( Info.plist, ); - target = D878C4FA2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension */; + target = D878C4FA2CF72AA0009AF551 /* DcWebxdcWidget */; }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ - D878C5002CF72AA0009AF551 /* MostRecentWebXDCWidget */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (D878C50F2CF72AA1009AF551 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = MostRecentWebXDCWidget; sourceTree = ""; }; + D878C5002CF72AA0009AF551 /* DcWebxdcWidget */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (D878C50F2CF72AA1009AF551 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = DcWebxdcWidget; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -917,7 +917,7 @@ 7A9FB1421FB061E2001FEA36 /* deltachat-ios */, 30E8F2112447285600CE2C90 /* DcShare */, B2D0E4932B93A4B200791949 /* DcNotificationService */, - D878C5002CF72AA0009AF551 /* MostRecentWebXDCWidget */, + D878C5002CF72AA0009AF551 /* DcWebxdcWidget */, 7A9FB1411FB061E2001FEA36 /* Products */, 7A9FB4F81FB084E6001FEA36 /* Frameworks */, AFE4D4B4B038293E63BC1537 /* Pods */, @@ -933,7 +933,7 @@ 7A9FB1401FB061E2001FEA36 /* deltachat-ios.app */, 30E8F2102447285600CE2C90 /* Delta Chat.appex */, B2D0E4922B93A4B200791949 /* DcNotificationService.appex */, - D878C4FB2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension.appex */, + D878C4FB2CF72AA0009AF551 /* DcWebxdcWidget.appex */, ); name = Products; sourceTree = ""; @@ -1312,9 +1312,9 @@ productReference = B2D0E4922B93A4B200791949 /* DcNotificationService.appex */; productType = "com.apple.product-type.app-extension"; }; - D878C4FA2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension */ = { + D878C4FA2CF72AA0009AF551 /* DcWebxdcWidget */ = { isa = PBXNativeTarget; - buildConfigurationList = D878C5102CF72AA1009AF551 /* Build configuration list for PBXNativeTarget "MostRecentWebXDCWidgetExtension" */; + buildConfigurationList = D878C5102CF72AA1009AF551 /* Build configuration list for PBXNativeTarget "DcWebxdcWidget" */; buildPhases = ( D878C4F72CF72AA0009AF551 /* Sources */, D878C4F82CF72AA0009AF551 /* Frameworks */, @@ -1326,13 +1326,13 @@ dependencies = ( ); fileSystemSynchronizedGroups = ( - D878C5002CF72AA0009AF551 /* MostRecentWebXDCWidget */, + D878C5002CF72AA0009AF551 /* DcWebxdcWidget */, ); - name = MostRecentWebXDCWidgetExtension; + name = DcWebxdcWidget; packageProductDependencies = ( ); productName = MostRecentWebXDCWidgetExtension; - productReference = D878C4FB2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension.appex */; + productReference = D878C4FB2CF72AA0009AF551 /* DcWebxdcWidget.appex */; productType = "com.apple.product-type.app-extension"; }; /* End PBXNativeTarget section */ @@ -1438,7 +1438,7 @@ 7A9FB13F1FB061E2001FEA36 /* deltachat-ios */, 30E8F20F2447285600CE2C90 /* DcShare */, B2D0E4912B93A4B200791949 /* DcNotificationService */, - D878C4FA2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension */, + D878C4FA2CF72AA0009AF551 /* DcWebxdcWidget */, ); }; /* End PBXProject section */ @@ -1855,7 +1855,7 @@ }; D878C50B2CF72AA1009AF551 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = D878C4FA2CF72AA0009AF551 /* MostRecentWebXDCWidgetExtension */; + target = D878C4FA2CF72AA0009AF551 /* DcWebxdcWidget */; targetProxy = D878C50A2CF72AA1009AF551 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -2471,8 +2471,8 @@ ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = MostRecentWebXDCWidget/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = MostRecentWebXDCWidget; + INFOPLIST_FILE = DcWebxdcWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = DcWebxdcWidget; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 merlinux GmbH. All rights reserved."; IPHONEOS_DEPLOYMENT_TARGET = 18.1; LD_RUNPATH_SEARCH_PATHS = ( @@ -2484,7 +2484,7 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = chat.delta.MostRecentWebXDCWidget; + PRODUCT_BUNDLE_IDENTIFIER = chat.delta.DcWebxdcWidget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; @@ -2510,8 +2510,8 @@ ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = MostRecentWebXDCWidget/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = MostRecentWebXDCWidget; + INFOPLIST_FILE = DcWebxdcWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = DcWebxdcWidget; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 merlinux GmbH. All rights reserved."; IPHONEOS_DEPLOYMENT_TARGET = 18.1; LD_RUNPATH_SEARCH_PATHS = ( @@ -2522,7 +2522,7 @@ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = chat.delta.MostRecentWebXDCWidget; + PRODUCT_BUNDLE_IDENTIFIER = chat.delta.DcWebxdcWidget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -2570,7 +2570,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D878C5102CF72AA1009AF551 /* Build configuration list for PBXNativeTarget "MostRecentWebXDCWidgetExtension" */ = { + D878C5102CF72AA1009AF551 /* Build configuration list for PBXNativeTarget "DcWebxdcWidget" */ = { isa = XCConfigurationList; buildConfigurations = ( D878C50D2CF72AA1009AF551 /* Debug */, diff --git a/deltachat-ios.xcodeproj/xcshareddata/xcschemes/MostRecentWebXDCWidgetExtension.xcscheme b/deltachat-ios.xcodeproj/xcshareddata/xcschemes/DcWebxdcWidgetExtension.xcscheme similarity index 94% rename from deltachat-ios.xcodeproj/xcshareddata/xcschemes/MostRecentWebXDCWidgetExtension.xcscheme rename to deltachat-ios.xcodeproj/xcshareddata/xcschemes/DcWebxdcWidgetExtension.xcscheme index b0ad9f7a3..7f7b7418f 100644 --- a/deltachat-ios.xcodeproj/xcshareddata/xcschemes/MostRecentWebXDCWidgetExtension.xcscheme +++ b/deltachat-ios.xcodeproj/xcshareddata/xcschemes/DcWebxdcWidgetExtension.xcscheme @@ -17,8 +17,8 @@ @@ -63,8 +63,8 @@ From 7bc2dce1bad7d4e24d8a57749d9699af5eb0215f Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Thu, 12 Dec 2024 15:55:45 +0100 Subject: [PATCH 11/15] Use correct widget for run (#2406) --- .../xcshareddata/xcschemes/DcWebxdcWidgetExtension.xcscheme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deltachat-ios.xcodeproj/xcshareddata/xcschemes/DcWebxdcWidgetExtension.xcscheme b/deltachat-ios.xcodeproj/xcshareddata/xcschemes/DcWebxdcWidgetExtension.xcscheme index 7f7b7418f..de6f24785 100644 --- a/deltachat-ios.xcodeproj/xcshareddata/xcschemes/DcWebxdcWidgetExtension.xcscheme +++ b/deltachat-ios.xcodeproj/xcshareddata/xcschemes/DcWebxdcWidgetExtension.xcscheme @@ -85,7 +85,7 @@ Date: Fri, 13 Dec 2024 11:10:30 +0100 Subject: [PATCH 12/15] Guess who forgot to add a changelog entry (again!) (#2406) y u no github issues and milestones and such :D # Conflicts: # CHANGELOG.md # Conflicts: # CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97f17e967..58d603500 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,10 @@ ## Unreleased +- Access your most recent Webxdc-apps from the Homescreen using our shiny new widget (#2406) - Don't show message-input when forwarding (#2435) - Long-tap links for copying to clipboard (#2445) - ## v1.50.3 - Don't change order of proxies when selecting a new one (#2414) From 328cddff2965141bc49fdb1a2df408bd92e422f2 Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Tue, 17 Dec 2024 09:41:54 +0100 Subject: [PATCH 13/15] Remove unused assets (#2406) --- .../AccentColor.colorset/Contents.json | 11 ------ .../AppIcon.appiconset/Contents.json | 35 ------------------- 2 files changed, 46 deletions(-) delete mode 100644 DcWebxdcWidget/Assets.xcassets/AccentColor.colorset/Contents.json delete mode 100644 DcWebxdcWidget/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/DcWebxdcWidget/Assets.xcassets/AccentColor.colorset/Contents.json b/DcWebxdcWidget/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb8789700..000000000 --- a/DcWebxdcWidget/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/DcWebxdcWidget/Assets.xcassets/AppIcon.appiconset/Contents.json b/DcWebxdcWidget/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 230588010..000000000 --- a/DcWebxdcWidget/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "tinted" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} From 7fdd120c3a985e5db2fc17ed51d95a374346d58e Mon Sep 17 00:00:00 2001 From: zeitschlag Date: Tue, 17 Dec 2024 09:42:27 +0100 Subject: [PATCH 14/15] Update scripts/untranslated.xml Co-authored-by: bjoern --- scripts/untranslated.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/untranslated.xml b/scripts/untranslated.xml index e56a8a26c..defebba59 100644 --- a/scripts/untranslated.xml +++ b/scripts/untranslated.xml @@ -7,7 +7,7 @@ You can find the backup in the \"Delta Chat\" folder using the \"Files\" app.\n\nMove the backup out of this folder to keep it when deleting Delta Chat. Location access denied In the system settings, enable \"Privacy/Location Services\" and set \"Delta Chat/Location\" to \"Always\" and \"Precise\". - No apps. - Recent Webxdc-apps - Shows the most recent Webxdc-apps. + In-Chat Utilities or Games will be shown here + Shortcuts + Shows the most recent In-Chat Utilities and Games From 51e19ad9095c65665a1d697d369406afd73815b0 Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Tue, 17 Dec 2024 10:38:16 +0100 Subject: [PATCH 15/15] consider PR-feedback (#2406) --- DcWebxdcWidget/DcWebxdcWidget.swift | 15 +- deltachat-ios/bqi.lproj/Localizable.strings | 528 +++++++++--------- .../bqi.lproj/Localizable.stringsdict | 34 +- deltachat-ios/de.lproj/Localizable.strings | 2 +- deltachat-ios/en.lproj/Localizable.strings | 6 +- deltachat-ios/fa.lproj/Localizable.strings | 3 +- deltachat-ios/it.lproj/Localizable.strings | 2 +- 7 files changed, 290 insertions(+), 300 deletions(-) diff --git a/DcWebxdcWidget/DcWebxdcWidget.swift b/DcWebxdcWidget/DcWebxdcWidget.swift index 5c14a4fbf..c97f9c77a 100644 --- a/DcWebxdcWidget/DcWebxdcWidget.swift +++ b/DcWebxdcWidget/DcWebxdcWidget.swift @@ -35,22 +35,11 @@ struct Provider: TimelineProvider { func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { /// ---------------------------------- - /// **DO NOT** START dcAccounts.startIo() IN WIDGET + /// **DO NOT** start dcAccounts.startIo() in widget. + /// This works only for one process, and widgets may run concurrently to the main app in their own process. /// ---------------------------------- let dcAccounts = DcAccounts.shared dcAccounts.openDatabase(writeable: false) - for accountId in dcAccounts.getAll() { - let dcContext = dcAccounts.get(id: accountId) - if dcContext.isOpen() == false { - do { - let secret = try KeychainManager.getAccountSecret(accountID: accountId) - _ = dcContext.open(passphrase: secret) - } catch { - debugPrint("Couldn't open \(error.localizedDescription)") - } - } - } - let dcContext = dcAccounts.getSelected() let chatId = 0 let ignore = Int32(0) diff --git a/deltachat-ios/bqi.lproj/Localizable.strings b/deltachat-ios/bqi.lproj/Localizable.strings index b529a6138..0e2e27905 100644 --- a/deltachat-ios/bqi.lproj/Localizable.strings +++ b/deltachat-ios/bqi.lproj/Localizable.strings @@ -14,317 +14,317 @@ "default_value" = "پؽش فرز (%1$@)"; "default_value_as_above" = "پؽش فرز (جۊر روء)"; "custom" = "سفارشی"; -"none" = "hičkoyêki"; -"automatic" = "xotkār"; -"strict" = "saxtger"; -"open" = "gušiďên"; -"download" = "dānlod"; -"downloading" = "honêy dānlod êbu..."; -"open_attachment" = "gušiďên zamima"; -"join" = "avoďên"; -"rejoin" = "zênu avoďên"; -"delete" = "pāk kerdên"; -"info" = "zêbār"; -"update" = "vê ru kerdên"; -"emoji" = "êmuji"; -"attachment" = "zamima"; -"back" = "vorgaštên"; -"close" = "bastên"; +"none" = "هیچکویکی"; +"automatic" = "خوتکار"; +"strict" = "سختگر"; +"open" = "گۊشیڌن"; +"download" = "دانلود"; +"downloading" = "هونی دانلود ابۊ..."; +"open_attachment" = "گۊشیڌن زمینه"; +"join" = "ٱووڌن"; +"rejoin" = "ز نۊ ٱووڌن"; +"delete" = "پاک کردن"; +"info" = "زبار"; +"update" = "ورۊ کردن"; +"emoji" = "ایموجی"; +"attachment" = "زمینه"; +"back" = "وورگشتن"; +"close" = "بستن"; // "Resend" means "Sending the selected message(s) again to the same chat". The string is used in a menu and should be as short as possible. Resending may be needed after failures or to repost old messages to new members. -"resend" = "zênu bêfêšn"; +"resend" = "ز نۊ بفشن"; // Verb "to archive", as in "put a chat in the archive", not a noun "The Archive". -"archive" = "bāyêguvi"; +"archive" = "بایگۊوی"; // Verb "to unarchive", as in "remove a chat from the archive", opposite of the previous string -"unarchive" = "bêdar zêyďên zê bāyêguvi"; -"mute" = "bî dong"; -"save" = "zaft"; -"chat" = "goftoloft"; -"media" = "vārasgar"; -"profile" = "porofāyl"; -"main_menu" = "balgê asli"; -"start_chat" = "nāhāďên pā goftoloft"; -"show_full_message" = "nêšow dāďên payomê kāmêl"; +"unarchive" = "و در زیڌن ز بایگۊوی"; +"mute" = "بؽ دونگ"; +"save" = "زفت کردن"; +"chat" = "گوفت ۉ لوفت"; +"media" = "وارسگر"; +"profile" = "پوروفایل"; +"main_menu" = "نومگه ٱسلی"; +"start_chat" = "ناڌن پا گوفت ۉ لوفت"; +"show_full_message" = "نشۉ داڌن پیوم کامل..."; // Stay short here, say ~16 characters. The source string could also be "All Read", maybe that hint can make translations easier :) -"mark_all_as_read" = "poy nê xonde biďe bêza"; +"mark_all_as_read" = "پوی ن خونده بیڌه بزݩ"; // Shortest text for "Mark as being read". In english, this could be "Read" (past tense of "to read"), in german, this could be "Gelesen". -"mark_as_read_short" = "xonde vâbiďa"; +"mark_as_read_short" = "علامت و عونوان خونده بیڌه"; // Placeholder text when something is loading -"loading" = "honêy bār êvane..."; -"hide" = "bîďār kerdên"; -"activate" = "faāl kerdên"; -"load_remote_content" = "bār vani šîvātā zê dar"; -"always" = "hamiša"; -"once" = "him ya kêrat"; -"show_warning" = "nêšow dāďên hošdār"; -"show_password" = "nêšow dāďên razm"; -"hide_password" = "bîďār kerdên razm"; -"not_now" = "sako na"; -"never" = "hič"; -"one_moment" = "yadamow..."; -"done" = "anjom vâbi"; -"offline" = "āflāyn"; +"loading" = "هونی بار اونه..."; +"hide" = "بؽڌار کردن"; +"activate" = "فعال کردن"; +"load_remote_content" = "بار ونی شؽواتا ز در"; +"always" = "همیشه"; +"once" = "هیم ی کرت"; +"show_warning" = "نشۉ داڌن هوشدار"; +"show_password" = "نشۉ داڌن رزم"; +"hide_password" = "بؽڌار کردن رزم"; +"not_now" = "سکو ن"; +"never" = "هیچ"; +"one_moment" = "ی دمۉ..."; +"done" = "ٱنجوم وابی"; +"offline" = "آفلاین"; // For the next view or as "continue". Should be as short as possible. -"next" = "niyâyi"; -"error" = "xatā"; -"error_x" = "xatā: %1$@"; -"file_not_found" = "%1$@ najost"; -"copied_to_clipboard" = "mênê virga lefgiri vâbi."; -"contacts_headline" = "homdangal"; -"email_address" = "nêšuvi imêyl"; -"password" = "razm"; -"existing_password" = "razmê mowjud"; -"now" = "sako"; +"next" = "نیایی"; +"error" = "ختا"; +"error_x" = "ختا: %1$@"; +"file_not_found" = "%1$@ ن نجوست."; +"copied_to_clipboard" = "من ویرگه لف گیری وابی."; +"contacts_headline" = "هومدنگۉݩ"; +"email_address" = "نشۊوی ایمیل"; +"password" = "رزم"; +"existing_password" = "رزم مۉجۊڌ"; +"now" = "سکو"; // Headline for destructive actions with no undo. Could also be "Caution" or "Warning". -"danger" = "bêpā"; -"today" = "amru"; -"yesterday" = "duš"; -"this_week" = "i hafta"; -"this_month" = "i mā"; -"last_week" = "haftê zitari"; -"last_month" = "mā zitari"; +"danger" = "بپا"; +"today" = "ٱمرۊ"; +"yesterday" = "دۊش"; +"this_week" = "ای هفته"; +"this_month" = "ای ما"; +"last_week" = "هفته زیتری"; +"last_month" = "ما زیتری"; // Refers to the time a contact was last seen. Shown below contact name in the profile. The placeholder will be replaced by date or time, resulting in "Last seen at 12:13 AM" or "Last seen Nov 12" -"last_seen_at" = "ritorê dindâyi mênê %1$@"; +"last_seen_at" = "ریتور دیندایی من %1$@"; // Refers to the time a contact was last seen. Shown below contact name in the profile. The placeholder will be replaced by a relative point in time as "3 minutes ago" (see https://momentjs.com for more examples and languages) -"last_seen_relative" = "ritorê dindâyi %1$@"; -"last_seen_unknown" = "ritorê dindâyi: diyār nî"; +"last_seen_relative" = "ریتور دیندایی %1$@"; +"last_seen_unknown" = "ریتور دیندایی: دیار نؽ"; // Shown beside messages that are "N minutes old". Prefer short strings, or well-known abbreviations. // Shown beside messages that are "N hours old". Prefer short strings, or well-known abbreviations. // Short form for "N Items Selected" -"self" = "mo"; -"draft" = "pîš hîl"; -"image" = "šîvāt"; +"self" = "مو"; +"draft" = "پؽش هؽل"; +"image" = "شؽوات"; // Used in summaries as "Draft: Reply", similar as "Draft: Image". Use a noun here, not a verb (not: "to reply") -"reply_noun" = "vêlom"; -"gif" = "gif"; +"reply_noun" = "ولوم"; +"gif" = "گیف"; // "Stickers" as known from other messengers; in some languages, the English "Sticker" is fine. -"sticker" = "êstikêr"; -"images" = "šîvātā"; -"audio" = "dong"; -"voice_message" = "payomê dong"; -"video" = "film"; -"documents" = "sanadā"; -"contact" = "homdang"; -"camera" = "šîvātgėr"; +"sticker" = "استیکر"; +"images" = "شؽواتا"; +"audio" = "دونگ"; +"voice_message" = "پیوم دونگ"; +"video" = "فیلم"; +"documents" = "سندا"; +"contact" = "هومدنگ"; +"camera" = "شؽواتگر"; // As in "start a video recording" or "take a photo"; eg. the description of the "shutter button" in cameras -"capture" = "gêrîďên"; -"switch_camera" = "ālêštê šîvātgêr"; -"toggle_fullscreen" = "ālêštê vaziyat poy balga"; -"location" = "jāga"; -"locations" = "jāgayal"; -"gallery" = "šîvātmāl"; -"images_and_videos" = "šîvātā vo filmā"; -"file" = "fāyl"; -"files" = "fāylā"; +"capture" = "گرؽڌن"; +"switch_camera" = "آلشت شؽواتگر"; +"toggle_fullscreen" = "آلشت وزیت پوی بلگه"; +"location" = "جاگه"; +"locations" = "جاگه یل"; +"gallery" = "شؽوات مال"; +"images_and_videos" = "شؽواتا وو فیلما"; +"file" = "فایل"; +"files" = "فایلا"; // "App" is used to present "Webxdc App" (https://webxdc.org) in a user friendly way. Please stay close to the original term and keep it short (it is used in menus with few screen space). -"webxdc_app" = "barnoma"; +"webxdc_app" = "برنومه"; // plural of "App"; used to present "Webxdc App" (https://webxdc.org) in a user friendly way. Please stay close to the original term and keep it short (it is used in menus with few screen space). -"webxdc_apps" = "barnomêyal"; -"unknown" = "nāšênās"; -"green" = "sawz"; -"red" = "so\'r"; -"blue" = "kêvu"; -"purple" = "bênawš"; -"white" = "êsbîď"; -"zoom" = "gap kerdên"; -"extra_small" = "qalêvê kučir"; -"small" = "kučir"; -"normal" = "âdi"; -"large" = "gap"; -"extra_large" = "qalêvê gap"; -"fast" = "zêl"; -"slow" = "hovār"; +"webxdc_apps" = "برنومه یل"; +"unknown" = "ناشناس"; +"green" = "سوز"; +"red" = "سوئر"; +"blue" = "کوۊ"; +"purple" = "بنوش"; +"white" = "اسبؽڌ"; +"zoom" = "گپ کردن"; +"extra_small" = "قلوه کۊچیر"; +"small" = "کۊچیر"; +"normal" = "عادی"; +"large" = "گپ"; +"extra_large" = "قلوه گپ"; +"fast" = "زل"; +"slow" = "هووار"; // menu labels (or icon, buttons...) -"menu_new_contact" = "homdangê nu"; -"menu_new_chat" = "goftoloftê nu"; -"menu_new_group" = "jarga nu"; +"menu_new_contact" = "هومدنگ نۊ"; +"menu_new_chat" = "گوفت ۉ لوفت نۊ"; +"menu_new_group" = "جرگه نۊ"; // consider keeping the term "broadcast" as in WhatsApp or Telegram -"broadcast_list" = "nomga Broadcast"; -"broadcast_lists" = "nomgayal Broadcast"; -"new_broadcast_list" = "nomga Broadcast nu"; -"menu_send" = "fêšnāďên"; -"menu_edit_group" = "ālêštê jarga"; -"menu_group_name_and_image" = "nom vo šîvātê jarga"; -"menu_archive_chat" = "bāyêguvi kerdên goftoloft"; -"menu_unarchive_chat" = "bêdar zêyďên goftoloft zê bāyêguvi"; -"menu_add_attachment" = "êzāf kerdên zamima"; -"menu_leave_group" = "ra\'ďên zê jarga"; -"menu_delete_chat" = "pāk kerdên poy goftoloft"; +"broadcast_list" = "نومگه Broadcast"; +"broadcast_lists" = "نومگه یل Broadcast"; +"new_broadcast_list" = "نومگه Broadcast نۊ"; +"menu_send" = "فشناڌن"; +"menu_edit_group" = "آلشت جرگه"; +"menu_group_name_and_image" = "نوم وو شؽوات جرگه"; +"menu_archive_chat" = "بایگۊوی گوفت ۉ لوفت"; +"menu_unarchive_chat" = "و در زیڌن گوفت ۉ لوفت ز بایگۊوی"; +"menu_add_attachment" = "ازاف کردن زمینه"; +"menu_leave_group" = "رئڌن ز جرگه"; +"menu_delete_chat" = "پاک کردن گوفت ۉ لوفت"; // Command to delete all messages in a chat. The chat itself will not be deleted but will be empty afterwards, so make sure to be different from "Delete Chat" here. "Clear" is a verb here, "Empty Chat" would also be fine (eg. in German "Chat leeren") -"clear_chat" = "roftênê virgārê goftoloft"; -"menu_delete_messages" = "pāk kerdên payomā"; -"delete_contact" = "pāk kerdên homdang"; -"menu_delete_location" = "i jāga pāk bu?"; -"menu_copy_to_clipboard" = "lefgiri mênê virga"; -"menu_copy_link_to_clipboard" = "lefgiri ling"; -"menu_copy_text_to_clipboard" = "lefgiri hîl"; -"menu_copy_image_to_clipboard" = "lefgiri šîvāt"; -"menu_copy_email_to_clipboard" = "lefgiri imêyl"; -"paste_from_clipboard" = "jā vandên zê virga"; -"menu_reply" = "vêlom bê payom"; -"menu_mute" = "bî dong kerdên vārasuviyā"; -"menu_unmute" = "vā dong kerdên"; -"menu_all_media" = "poy vārasgarā"; +"clear_chat" = "روفتن ویرگار گوفت ۉ لوفت"; +"menu_delete_messages" = "پاک کردن پیوما"; +"delete_contact" = "پاک کردن هومدنگ"; +"menu_delete_location" = "ای جاگه پاک بۊ؟"; +"menu_copy_to_clipboard" = "لف گیری من ویرگه"; +"menu_copy_link_to_clipboard" = "لف گیری لینک"; +"menu_copy_text_to_clipboard" = "لف گیری هؽل"; +"menu_copy_image_to_clipboard" = "لف گیری شؽوات"; +"menu_copy_email_to_clipboard" = "لف گیری ایمیل"; +"paste_from_clipboard" = "جا وندن ز ویرگه"; +"menu_reply" = "ولوم ب پیوم"; +"menu_mute" = "بؽ دونگ کردن وارسۊویا"; +"menu_unmute" = "وا دونگ کردن"; +"menu_all_media" = "پوی وارسگرا"; // Command to jump to the original message corresponding to a gallery image or document -"show_in_chat" = "nêšow dāďên mênê goftoloft"; -"menu_share" = "yakrasuvi"; -"accept" = "qêvul"; -"menu_play" = "pêšk"; -"menu_pause" = "vāstāďên"; -"menu_scroll_to_bottom" = "ra\'ďên bê lam"; -"menu_scroll_to_top" = "ra\'ďên bê ro\'"; -"menu_help" = "hayâri"; -"menu_select_all" = "pêsandê poy"; -"select_chat" = "pêsandê goftoloft"; -"select_more" = "pêsandê bištar"; -"menu_edit_name" = "ālêštê nom"; -"menu_settings" = "sāmovā"; -"menu_advanced" = "pîš ra\'ďa"; -"menu_view_profile" = "niyaštên porofāyl"; -"menu_zoom_in" = "gap kerdên"; -"menu_zoom_out" = "kučir kerdên"; -"menu_save_log" = "zaft kerdên gozārêš"; -"menu_more_options" = "guzinêyalê bištar"; -"jump_to_message" = "ra\'ďên bê payom"; -"device_talk" = "payomā dasgā"; -"device_talk_subtitle" = "payomā mêni"; -"edit_contact" = "ālêštê homdang"; +"show_in_chat" = "نشۉ داڌن من گوفت ۉ لوفت"; +"menu_share" = "یک رسۊوی"; +"accept" = "قوۊل"; +"menu_play" = "پشک"; +"menu_pause" = "واڌاشتن"; +"menu_scroll_to_bottom" = "رئڌن ب لم"; +"menu_scroll_to_top" = "رئڌن ب روء"; +"menu_help" = "هیاری"; +"menu_select_all" = "پسند پوی"; +"select_chat" = "پسند گوفت ۉ لوفت"; +"select_more" = "پسند بیشتر"; +"menu_edit_name" = "آلشت نوم"; +"menu_settings" = "سامووا"; +"menu_advanced" = "پؽش رئڌه"; +"menu_view_profile" = "نیشتن پوروفایل"; +"menu_zoom_in" = "گپ کردن"; +"menu_zoom_out" = "کۊچیر کردن"; +"menu_save_log" = "زفت کردن گوزارش"; +"menu_more_options" = "گۊزینیل بیشتر"; +"jump_to_message" = "رئڌن ب پیوم"; +"device_talk" = "پیوما دسگا"; +"device_talk_subtitle" = "پیوما منی"; +"edit_contact" = "آلشت هومدنگ"; // Verb "to pin", making something sticky, not a noun or abbreviation for "pin number". -"pin_chat" = "disniďên goftoloft"; +"pin_chat" = "دیسنیڌن گوفت ۉ لوفت"; // Verb "to pin", making something sticky, not a noun or abbreviation for "pin number". -"pin" = "disniďên"; -"source_code" = "kodê bončak"; -"mute_for_one_hour" = "bî dong kerdên si 1 sāat"; -"mute_for_two_hours" = "bî dong kerdên si 2 sāat"; -"mute_for_one_day" = "bî dong kerdên si 1 ru"; -"mute_for_seven_days" = "bî dong kerdên si 7 ru"; -"mute_forever" = "bî dong kerdên si hamiša"; -"share_location_for_5_minutes" = "si 5 dîqa"; -"share_location_for_30_minutes" = "si 30 dîqa"; -"share_location_for_one_hour" = "si 1 sāat"; -"share_location_for_two_hours" = "si 2 sāat"; -"share_location_for_six_hours" = "si 6 sāat"; -"file_saved_to" = "fāyl mênê \"%1$@\" zaft vâbi."; -"videochat" = "goftoloftê vidiyoyi"; -"videochat_invitation" = "mokêš bê goftoloft vidiyoyi"; -"ask_start_chat_with" = "goftoloft vā %1$@?"; -"ask_delete_value" = "pāk kerdên %1$@?"; +"pin" = "دیسنیڌن"; +"source_code" = "کود بونچک"; +"mute_for_one_hour" = "بؽ دونگ کردن سی 1 ساعت"; +"mute_for_two_hours" = "بؽ دونگ کردن سی 2 ساعت"; +"mute_for_one_day" = "بؽ دونگ کردن سی 1 رۊ"; +"mute_for_seven_days" = "بؽ دونگ کردن سی 7 رۊ"; +"mute_forever" = "بؽ دونگ کردن سی همیشه"; +"share_location_for_5_minutes" = "سی 5 دؽقه"; +"share_location_for_30_minutes" = "سی 30 دؽقه"; +"share_location_for_one_hour" = "سی 1 ساعت"; +"share_location_for_two_hours" = "سی 2 ساعت"; +"share_location_for_six_hours" = "سی 6 ساعت"; +"file_saved_to" = "فایل من «%1$@» زفت وابی."; +"videochat" = "گوفت ۉ لوفت ویدیویی"; +"videochat_invitation" = "موکش و گوفت ۉ لوفت ویدیویی"; +"ask_start_chat_with" = "گوفت ۉ لوفت وا %1$@؟"; +"ask_delete_value" = "پاک کردن %1$@؟"; // %1$s is replaced by a comma-separated list of names -"ask_remove_members" = "pāk kerdên %1$@ zê jarga?"; +"ask_remove_members" = "پاک کردن %1$@ ز جرگه؟"; // %1$s is replaced by a comma-separated list of names -"ask_remove_from_broadcast" = "%1$@ zê nomga Broadcast pāk bu?"; -"open_url_confirmation" = "êxuy i lingê nê buguši?"; +"ask_remove_from_broadcast" = "%1$@ ز نومگه Broadcast پاک بۊ؟"; +"open_url_confirmation" = "اخۊی ای لینگن بۊگۊشی؟"; // contact list -"contacts_title" = "homdangal"; -"contacts_empty_hint" = "bî homdang."; -"chat_please_enter_message" = "ya payom bêza"; -"chat_record_slide_to_cancel" = "si raď kerdên bêkašês"; -"chat_share_with_title" = "yakrasuvi vā"; -"chat_input_placeholder" = "payom"; +"contacts_title" = "هومدنگۉݩ"; +"contacts_empty_hint" = "بؽ هومدنگ."; +"chat_please_enter_message" = "ی پیوم بزݩ."; +"chat_record_slide_to_cancel" = "سی رڌ کردن بکشس."; +"chat_share_with_title" = "یک رسۊوی وا"; +"chat_input_placeholder" = "پیوم"; "chat_request_label" = "darxāst"; -"chat_no_messages" = "payomi nî."; -"chat_self_talk_subtitle" = "payomāyî ke si xom fêšnāme."; +"chat_no_messages" = "پیومؽ نؽ."; +"chat_self_talk_subtitle" = "پیومایی ک سی خوم فشنام."; "saved_messages" = "payomā zaft biďa"; // Should match "Saved" from "Saved messages" "saved" = "zaft vâbi"; "save_as" = "zaft vê onvān"; -"retry_send" = "zênu si fêšnāďênê payom taqalā ko"; +"retry_send" = "ز نۊ سی فشناڌن پیوم تقلا کوݩ"; // mailing lists -"mailing_list" = "nomga posti"; +"mailing_list" = "نومگه پوستی"; // title shown above a list of chats where one should be selected (eg. when sharing files from a webxdc). the placeholder will be replaced by a file name -"send_file_to" = "fêšnāďên \"%1$@\" vê..."; +"send_file_to" = "فشناڌن \"%1$@\" و..."; // title shown above a list contacts where one should be selected (eg. when a webxdc attempts to send a message to a chat) -"send_message_to" = "fêšnāďên payom vê..."; -"show_location_traces" = "nêšow dāďên jā pā"; +"send_message_to" = "فشناڌن پیوم و..."; +"show_location_traces" = "نشۉ داڌن جا پا"; // search -"search" = "pitiniďên"; +"search" = "پیتینیڌن"; // create/edit groups, contact/group profile -"group_name" = "nomê jarga"; -"group_avatar" = "šîvātê jarga"; -"remove_group_image" = "pāk kerdên šîvātê jarga"; -"change_group_image" = "ālêštê šîvātê jarga"; -"group_create_button" = "vorkêlê jarga"; -"group_add_members" = "avordênê mêntor"; -"tab_contact" = "homdang"; -"tab_group" = "jarga"; -"tab_gallery" = "šîvātmāl"; -"tab_docs" = "sanadā"; -"tab_links" = "lingā"; -"tab_map" = "naqša"; -"media_preview" = "nêšow dāďên vārasgar"; -"send_message" = "fêšnāďên payom"; +"group_name" = "نوم جرگه"; +"group_avatar" = "شؽوات جرگه"; +"remove_group_image" = "پاک کردن شؽوات جرگه"; +"change_group_image" = "آلشت شؽوات جرگه"; +"group_create_button" = "وورکل جرگه"; +"group_add_members" = "ٱووردن منتور"; +"tab_contact" = "هومدنگ"; +"tab_group" = "جرگه"; +"tab_gallery" = "شؽوات مال"; +"tab_docs" = "سندا"; +"tab_links" = "لینگا"; +"tab_map" = "نقشه"; +"media_preview" = "نشۉ داڌن وارسگر"; +"send_message" = "فشناڌن پیوم"; // Headline in the "Connectivity" view. Placeholder will be replaced by the domain of the configured email-address. -"storage_on_domain" = "zaft kerdên mênê %1$@"; +"storage_on_domain" = "زفت کردن من %1$@"; // Shown in the title bar if the app is "Connecting"; prefer short strings. The ellipsis is a single character (…), not three (...) -"connectivity_connecting" = "honêy menpiz êbu..."; +"connectivity_connecting" = "هونی منپیز ابۊ..."; // Shown in the title bar if the app is "Updating" (eg. getting new/old message, sync things); prefer short strings. The ellipsis is a single character (…), not three (...) -"connectivity_updating" = "honêy vê ru êbu..."; +"connectivity_updating" = "هونی ورۊ ابۊ..."; // Shown in the setting if the app is "Connected" -"connectivity_connected" = "menpiz vâbi"; -"sending" = "honêy fêšnāďe êbu..."; +"connectivity_connected" = "منپیز وابی"; +"sending" = "هونی افشنه..."; // Subtitle in quota context of "Connetivity" view. Should be be plural always, no number is prefixed. -"messages" = "payomā"; -"sync_all" = "vê ru kerdên poy"; -"pref_your_name" = "nomê to"; -"pref_notifications_show" = "nêšow dāďên"; -"pref_language" = "zow"; -"pref_chats" = "goftoloftā"; +"messages" = "پیوما"; +"sync_all" = "ورۊ کردن پوی"; +"pref_your_name" = "نوم ایسا"; +"pref_notifications_show" = "نشۉ داڌن"; +"pref_language" = "زۉݩ"; +"pref_chats" = "گوفت ۉ لوفتا"; // deprecated -"pref_message_text_size" = "hêndā hîlê payom"; -"pref_log_header" = "gozārêš"; -"pref_background_btn_gallery" = "pêsand zê šîvātmāl"; -"pref_show_emails_all" = "poy"; +"pref_message_text_size" = "هندا هؽلا پیوم"; +"pref_log_header" = "گوزارش"; +"pref_background_btn_gallery" = "پسند ز شؽوات مال"; +"pref_show_emails_all" = "پوی"; // %1$s will be replaced by a human-readable number of bytes, eg. 32 KiB, 1 MiB -"up_to_x" = "tā saqfe %1$@"; +"up_to_x" = "تا سقف %1$@"; // %1$s will be replaced by a human-readable number of bytes, eg. 32 KiB, 1 MiB. Resulting string eg. "1 MiB message" -"n_bytes_message" = "payom %1$@"; -"profile_image_select" = "pêsandê šîvātê porofāyl"; -"profile_image_delete" = "pāk kerdên šîvātê porofāyl"; -"pref_edit_profile" = "ālêštê porofāyl"; +"n_bytes_message" = "پیوم %1$@"; +"profile_image_select" = "پسند شؽوات پوروفایل"; +"profile_image_delete" = "پاک کردن شؽوات پوروفایل"; +"pref_edit_profile" = "آلشت پوروفایل"; // Emoji picker and categories -"emoji_search_results" = "natijêyalê pitiniďên"; -"emoji_symbols" = "nemāďā"; -"emoji_flags" = "bêyraqā"; +"emoji_search_results" = "نتیجیل پیتینیڌن"; +"emoji_symbols" = "نمادا"; +"emoji_flags" = "بیرقا"; // automatically delete message -"delete_old_messages" = "pāk kerdên payomā qaďimi"; -"autodel_device_title" = "pāk kerdên payomā zê dasgā"; -"autodel_server_title" = "pāk kerdên payomā zê sêrvêr"; +"delete_old_messages" = "پاک کردن پیوما قڌیمی"; +"autodel_device_title" = "پاک کردن پیوما ز دسگا"; +"autodel_server_title" = "پاک کردن پیوما ز سرور"; // "At once" in the meaning of "Immediately", without any intervening time. -"autodel_at_once" = "ya kêrat"; -"after_30_seconds" = "ba\'ďê 30 sâniya"; -"after_1_minute" = "ba\'ďê 1 dîqa"; -"after_5_minutes" = "ba\'ďê 5 dîqa"; -"after_30_minutes" = "ba\'ďê 30 dîqa"; -"autodel_after_1_hour" = "ba\'ďê 1 sāat"; -"autodel_after_1_day" = "ba\'ďê 1 ru"; -"autodel_after_1_week" = "ba\'ďê 1 hafta"; -"after_5_weeks" = "ba\'ďê 5 hafta"; -"autodel_after_1_year" = "ba\'ďê 1 sāl"; +"autodel_at_once" = "ی کرت دیندا دانلود"; +"after_30_seconds" = "دیندا 30 سانیه"; +"after_1_minute" = "دیندا 1 دؽقه"; +"after_5_minutes" = "دیندا 5 دؽقه"; +"after_30_minutes" = "دیندا 30 دؽقه"; +"autodel_after_1_hour" = "دیندا 1 ساعت"; +"autodel_after_1_day" = "دیندا 1 رۊ"; +"autodel_after_1_week" = "دیندا 1 هفته"; +"after_5_weeks" = "دیندا 5 هفته"; +"autodel_after_1_year" = "دیندا 1 سال"; // "left" in the meaning of "exited"; %1$s will be replaced by name and address of the contact leaving the group -"group_left_by_other" = "%1$@ zê jarga ra\'ď."; -"qrshow_x_joining" = "%1$@ mêntor vâbi."; -"qrshow_x_verified" = "%1$@ taiď vâbi."; -"contact_verified" = "%1$@ taiď vâbi."; +"group_left_by_other" = "%1$@ ز جرگه رئڌ."; +"qrshow_x_joining" = "%1$@ ٱووڌ."; +"qrshow_x_verified" = "%1$@ تاییڌ وابی."; +"contact_verified" = "%1$@ تاییڌ وابی."; // Shown in contact profile. The placeholder will be replaced by the name of the contact that introduced the contact. -"verified_by" = "taiď vâbiďa vê das %1$@"; -"notify_reply_button" = "vėlom"; -"notify_new_message" = "payomė nu"; -"notify_name_and_message" = "nom vo payom"; -"notify_name_only" = "têynā nom"; -"notify_no_name_or_message" = "nom yā payomi nî"; -"global_menu_preferences_language_desktop" = "zow"; -"global_menu_file_desktop" = "fāyl"; -"global_menu_file_quit_desktop" = "vê dar zêyďên"; -"global_menu_edit_desktop" = "ālêšt"; -"global_menu_edit_cut_desktop" = "jā goro kerdên"; -"global_menu_edit_copy_desktop" = "lefgiri"; -"global_menu_edit_paste_desktop" = "jā vandên"; -"global_menu_file_open_desktop" = "gušiďênê dêltā čat"; -"delete_message_desktop" = "pāk kerdên payom"; -"remove_desktop" = "pāk kerdên"; -"save_desktop" = "zaft"; -"name_desktop" = "nom"; -"select_group_image_desktop" = "pêsandê šîvātê jarga"; -"message_detail_sent_desktop" = "fêšnāďe vâbi"; -"message_detail_received_desktop" = "gêrîďe vâbi"; -"show_window" = "nêšow dāďênê nimdari"; -"open_settings" = "gušiďênê sāmovā"; +"verified_by" = "تاییڌ وابیڌه و دس %1$@"; +"notify_reply_button" = "ولوم"; +"notify_new_message" = "پیوم نۊ"; +"notify_name_and_message" = "نوم وو پیوم"; +"notify_name_only" = "تینا نوم"; +"notify_no_name_or_message" = "بؽ نوم یا پیوم"; +"global_menu_preferences_language_desktop" = "زۉݩ"; +"global_menu_file_desktop" = "فایل"; +"global_menu_file_quit_desktop" = "و در زیڌن"; +"global_menu_edit_desktop" = "آلشت"; +"global_menu_edit_cut_desktop" = "جا گورو"; +"global_menu_edit_copy_desktop" = "لف گیری"; +"global_menu_edit_paste_desktop" = "جا وندن"; +"global_menu_file_open_desktop" = "گۊشیڌن دلتا چت"; +"delete_message_desktop" = "پاک کردن پیوم"; +"remove_desktop" = "پاک کردن"; +"save_desktop" = "زفت"; +"name_desktop" = "نوم"; +"select_group_image_desktop" = "پسند شؽوات جرگه"; +"message_detail_sent_desktop" = "فشناڌ"; +"message_detail_received_desktop" = "گرؽڌه وابی"; +"show_window" = "نشۉ داڌن نیمدری"; +"open_settings" = "گۊشیڌن سامووا"; diff --git a/deltachat-ios/bqi.lproj/Localizable.stringsdict b/deltachat-ios/bqi.lproj/Localizable.stringsdict index ecd0d9fea..b0427a3e6 100644 --- a/deltachat-ios/bqi.lproj/Localizable.stringsdict +++ b/deltachat-ios/bqi.lproj/Localizable.stringsdict @@ -13,9 +13,9 @@ NSStringFormatValueTypeKey d one - %d dîqa + %d دؽقه other - %d dîqa + %d دؽقه n_hours @@ -29,9 +29,9 @@ NSStringFormatValueTypeKey d one - %d sāat + %d ساعت other - %d sāat + %d ساعت n_chats @@ -45,9 +45,9 @@ NSStringFormatValueTypeKey d one - %d čat + %d چت other - %d čat + %d چت n_contacts @@ -61,9 +61,9 @@ NSStringFormatValueTypeKey d one - %d homdang + %d هومدنگ other - %d homdang + %d هومدنگ n_messages @@ -77,9 +77,9 @@ NSStringFormatValueTypeKey d one - %d payom + %d پیوم other - %d payom + %d پیوم n_members @@ -93,9 +93,9 @@ NSStringFormatValueTypeKey d one - %d mėntor + %d منتور other - %d mėntor + %d منتور n_selected @@ -109,9 +109,9 @@ NSStringFormatValueTypeKey d one - %d tā pêsand vâbi + %d تا پسند وابی other - %d tā pêsand vâbi + %d تا پسند وابی ask_delete_messages_simple @@ -127,7 +127,7 @@ one êxuy %d payom pāk bu? other - êxuy %d payom pāk bu? + اخۊی %d پیوم پاک بۊ؟ chat_n_new_messages @@ -141,9 +141,9 @@ NSStringFormatValueTypeKey d one - %d payomê nu + %d پیوم نۊ other - %d payomê nu + %d پیوم نۊ diff --git a/deltachat-ios/de.lproj/Localizable.strings b/deltachat-ios/de.lproj/Localizable.strings index a149baff8..161498b37 100644 --- a/deltachat-ios/de.lproj/Localizable.strings +++ b/deltachat-ios/de.lproj/Localizable.strings @@ -558,7 +558,7 @@ "switch_account" = "Profil wechseln"; "add_account" = "Profil hinzufügen"; "profile_tag" = "Private Bezeichnung"; -"profile_tag_hint" = "Familie, Arbeit, Freunde"; +"profile_tag_hint" = "z.B. Familie, Arbeit, Freunde"; "profile_tag_explain" = "Die Bezeichnung ist nur für Sie sichtbar um zwischen Profilen zu unterscheiden."; "delete_account" = "Profil löschen"; "delete_account_ask" = "Möchten Sie das Profil und alle Daten wirklich von diesem Gerät löschen?"; diff --git a/deltachat-ios/en.lproj/Localizable.strings b/deltachat-ios/en.lproj/Localizable.strings index 574fce047..4903f6b59 100644 --- a/deltachat-ios/en.lproj/Localizable.strings +++ b/deltachat-ios/en.lproj/Localizable.strings @@ -1063,6 +1063,6 @@ "backup_successful_explain_ios" = "You can find the backup in the \"Delta Chat\" folder using the \"Files\" app.\n\nMove the backup out of this folder to keep it when deleting Delta Chat."; "location_denied" = "Location access denied"; "location_denied_explain_ios" = "In the system settings, enable \"Privacy/Location Services\" and set \"Delta Chat/Location\" to \"Always\" and \"Precise\"."; -"widget_no_apps" = "No apps."; -"widget_most_recent_apps_title" = "Recent Webxdc-apps"; -"widget_most_recent_apps_description" = "Shows the most recent Webxdc-apps."; +"widget_no_apps" = "In-Chat Utilities or Games will be shown here"; +"widget_most_recent_apps_title" = "Shortcuts"; +"widget_most_recent_apps_description" = "Shows the most recent In-Chat Utilities and Games"; diff --git a/deltachat-ios/fa.lproj/Localizable.strings b/deltachat-ios/fa.lproj/Localizable.strings index b5094832e..b5aad1578 100644 --- a/deltachat-ios/fa.lproj/Localizable.strings +++ b/deltachat-ios/fa.lproj/Localizable.strings @@ -1074,4 +1074,5 @@ GNU GPL ورژن ۳ "perm_enable_bg_reminder_title" = "برای دریافت پیام‌ها به صورت پس زمینه در دلتاچت این جا را ضربه بزنید. "; "perm_enable_bg_already_done" = "قبلا اجازه دسترسی به فعالیت در پس زمینه را به دلتاچت داده‌اید. \n\n اگر پیام‌ها هنوز هم در شرایط پس زمینه نمی‌آمد لطفا تنظیم‌های سیستم را نیز بررسی نمایید. "; // device messages for updates -"update_1_50_android" = "چه چیز تازه است؟\n\n❤️‍🔥 منوی انتخاب ایموجی جدید با ایموجی‌های جدید\🎮n\n بهبود برنامه‌های چت: دریافت اعلان و بازکردن برنامه‌های پشتیبانی‌کننده در محتوا. مثلا یک خط جدید در تقویم را مستقیم باز کنید.\👍n\n زمانی که به پیام شما واکنشی داده می‌شود، خبردار می‌شوید.\n\n 🛠️ همچنین رفع اشکال‌ها و دیگر چیز‌ها در %1$@"; +"update_1_50_android" = "چه چیز تازه است؟\n\n❤️‍🔥 منوی انتخاب ایموجی جدید با ایموجی‌های جدید\🎮n\n بهبود برنامه‌های چت: دریافت اعلان و بازکردن برنامه‌های پشتیبانی‌کننده در محتوا. مثلا یک خط جدید در تقویم را مستقیم باز کنید.\👍n\n زمانی که به پیام شما واکنشی داده می‌شود، خبردار می‌شوید.\n\n 🛠️ همچنین رفع اشکال‌ها و دیگر چیز‌ها در +%1$@"; diff --git a/deltachat-ios/it.lproj/Localizable.strings b/deltachat-ios/it.lproj/Localizable.strings index b9873f555..0d3c328f7 100644 --- a/deltachat-ios/it.lproj/Localizable.strings +++ b/deltachat-ios/it.lproj/Localizable.strings @@ -548,7 +548,7 @@ "login_error_server" = "Per piacere inserisci un server / indirizzo IP valido"; "login_error_port" = "Per piacere inserisci una porta valida (1-65535)"; "login_error_required_fields" = "Per piacere inserisci un indirizzo e-mail e una password validi"; -"import_backup_title" = "Ripristina dal Backup"; +"import_backup_title" = "Ripristina da Backup"; "import_backup_ask" = "Backup trovato in \"%1$@\".\n\nVuoi importarlo e usare tutti i dati e le impostazioni salvate?"; "import_backup_no_backup_found" = "Nessun backup trovato\n\nCopia il backup in \"%1$@\" e riprova ancora. Altimenti premi \"Inizia a messaggiare\" per procedere con le impostazioni normali."; // Translators: %1$s will be replaced by the e-mail address