Update intents

This commit is contained in:
Ali 2021-02-16 23:07:37 +04:00
parent 9e7c9abceb
commit 933dcc17e7
19 changed files with 5232 additions and 4934 deletions

View File

@ -1,10 +0,0 @@
load("@build_bazel_rules_apple//apple:resources.bzl",
"swift_intent_library",
)
swift_intent_library(
name = "GeneratedSources",
src = "Intents.intentdefinition",
visibility = ["//visibility:public"],
)

View File

@ -32,6 +32,10 @@ load(
"telegram_team_id",
)
load("@build_bazel_rules_apple//apple:resources.bzl",
"swift_intent_library",
)
config_setting(
name = "debug",
values = {
@ -1186,7 +1190,7 @@ swift_library(
"//submodules/TelegramCore:TelegramCore",
"//submodules/SyncCore:SyncCore",
"//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider",
"//:GeneratedSources",
"//Telegram:GeneratedSources",
],
)
@ -1264,15 +1268,20 @@ plist_fragment(
)
)
swift_intent_library(
name = "GeneratedSources",
src = "SiriIntents/en.lproj/Intents.intentdefinition",
module_name = "GeneratedSources",
visibility = ["//visibility:public"],
)
swift_library(
name = "IntentsExtensionLib",
module_name = "IntentsExtensionLib",
srcs = glob([
"SiriIntents/**/*.swift",
]),
data = [
#"SiriIntents/Intents.intentdefinition",
],
data = glob(["SiriIntents/*.lproj/Intents.intentdefinition"]),
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/Postbox:Postbox",
@ -1282,7 +1291,8 @@ swift_library(
"//submodules/BuildConfig:BuildConfig",
"//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider",
"//submodules/AppLockState:AppLockState",
"//:GeneratedSources",
"//Telegram:GeneratedSources",
"//submodules/WidgetItems:WidgetItems",
],
)

View File

@ -10,6 +10,7 @@ import OpenSSLEncryptionProvider
import AppLockState
import UIKit
import GeneratedSources
import WidgetItems
private var accountCache: Account?
@ -771,8 +772,10 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
}
if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {
let error = NSError(domain: "Locked", code: 1, userInfo: [
NSLocalizedDescriptionKey: "Open Telegram and enter passcode to edit widget."
let presentationData = WidgetPresentationData.getForExtension()
let error = NSError(domain: presentationData.generalLockedTitle, code: 1, userInfo: [
NSLocalizedDescriptionKey: presentationData.generalLockedText
])
completion(nil, error)
@ -937,7 +940,9 @@ private final class WidgetIntentHandler {
}
if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {
//TODO:localize
let presentationData = WidgetPresentationData.getForExtension()
let error = NSError(domain: "Locked", code: 1, userInfo: [
NSLocalizedDescriptionKey: "Open Telegram and enter passcode to edit widget."
])

View File

@ -19,8 +19,6 @@
<dict>
<key>INIntentCategory</key>
<string>information</string>
<key>INIntentDescription</key>
<string>Display the latest message from the most important chats.</string>
<key>INIntentDescriptionID</key>
<string>jmsEbj</string>
<key>INIntentEligibleForWidgets</key>
@ -137,8 +135,6 @@
<dict>
<key>INIntentCategory</key>
<string>information</string>
<key>INIntentDescription</key>
<string>"Display shortcuts of your most important chats to always have quick access to them.</string>
<key>INIntentDescriptionID</key>
<string>DwL4WQ</string>
<key>INIntentEligibleForWidgets</key>

View File

@ -0,0 +1,333 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>INEnums</key>
<array/>
<key>INIntentDefinitionModelVersion</key>
<string>1.2</string>
<key>INIntentDefinitionNamespace</key>
<string>p74MWb</string>
<key>INIntentDefinitionSystemVersion</key>
<string>20C69</string>
<key>INIntentDefinitionToolsBuildVersion</key>
<string>12D4e</string>
<key>INIntentDefinitionToolsVersion</key>
<string>12.4</string>
<key>INIntents</key>
<array>
<dict>
<key>INIntentCategory</key>
<string>information</string>
<key>INIntentDescriptionID</key>
<string>jmsEbj</string>
<key>INIntentEligibleForWidgets</key>
<true/>
<key>INIntentIneligibleForSuggestions</key>
<true/>
<key>INIntentLastParameterTag</key>
<integer>19</integer>
<key>INIntentName</key>
<string>SelectFriends</string>
<key>INIntentParameters</key>
<array>
<dict>
<key>INIntentParameterArraySizes</key>
<array>
<dict>
<key>INIntentParameterArraySizeSize</key>
<integer>1</integer>
<key>INIntentParameterArraySizeSizeClass</key>
<string>Small</string>
</dict>
<dict>
<key>INIntentParameterArraySizeSize</key>
<integer>2</integer>
<key>INIntentParameterArraySizeSizeClass</key>
<string>Medium</string>
</dict>
<dict>
<key>INIntentParameterArraySizeSize</key>
<integer>8</integer>
<key>INIntentParameterArraySizeSizeClass</key>
<string>Large</string>
</dict>
</array>
<key>INIntentParameterConfigurable</key>
<true/>
<key>INIntentParameterDisplayName</key>
<string>ВЫБЕРИТЕ ЧАТЫ</string>
<key>INIntentParameterDisplayNameID</key>
<string>WIf4LD</string>
<key>INIntentParameterDisplayPriority</key>
<integer>1</integer>
<key>INIntentParameterFixedSizeArray</key>
<integer>1</integer>
<key>INIntentParameterName</key>
<string>friends</string>
<key>INIntentParameterObjectType</key>
<string>Friend</string>
<key>INIntentParameterObjectTypeNamespace</key>
<string>p74MWb</string>
<key>INIntentParameterPromptDialogs</key>
<array>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogFormatString</key>
<string>Search</string>
<key>INIntentParameterPromptDialogFormatStringID</key>
<string>ORCbLf</string>
<key>INIntentParameterPromptDialogType</key>
<string>Configuration</string>
</dict>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogType</key>
<string>Primary</string>
</dict>
</array>
<key>INIntentParameterRelationship</key>
<dict>
<key>INIntentParameterRelationshipPredicateName</key>
<string>EnumHasExactValue</string>
<key>INIntentParameterRelationshipPredicateValue</key>
<string>custom</string>
</dict>
<key>INIntentParameterSupportsDynamicEnumeration</key>
<true/>
<key>INIntentParameterSupportsMultipleValues</key>
<true/>
<key>INIntentParameterSupportsSearch</key>
<true/>
<key>INIntentParameterTag</key>
<integer>19</integer>
<key>INIntentParameterType</key>
<string>Object</string>
</dict>
</array>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
<dict>
<key>INIntentResponseCodeName</key>
<string>failure</string>
</dict>
</array>
</dict>
<key>INIntentTitle</key>
<string>Чаты</string>
<key>INIntentTitleID</key>
<string>lMot0c</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentVerb</key>
<string>View</string>
</dict>
<dict>
<key>INIntentCategory</key>
<string>information</string>
<key>INIntentDescriptionID</key>
<string>DwL4WQ</string>
<key>INIntentEligibleForWidgets</key>
<true/>
<key>INIntentIneligibleForSuggestions</key>
<true/>
<key>INIntentLastParameterTag</key>
<integer>19</integer>
<key>INIntentName</key>
<string>SelectAvatarFriends</string>
<key>INIntentParameters</key>
<array>
<dict>
<key>INIntentParameterArraySizes</key>
<array>
<dict>
<key>INIntentParameterArraySizeSize</key>
<integer>4</integer>
<key>INIntentParameterArraySizeSizeClass</key>
<string>Small</string>
</dict>
<dict>
<key>INIntentParameterArraySizeSize</key>
<integer>8</integer>
<key>INIntentParameterArraySizeSizeClass</key>
<string>Medium</string>
</dict>
<dict>
<key>INIntentParameterArraySizeSize</key>
<integer>16</integer>
<key>INIntentParameterArraySizeSizeClass</key>
<string>Large</string>
</dict>
</array>
<key>INIntentParameterConfigurable</key>
<true/>
<key>INIntentParameterDisplayName</key>
<string>ВЫБЕРИТЕ ЧАТЫ</string>
<key>INIntentParameterDisplayNameID</key>
<string>Jg5dYF</string>
<key>INIntentParameterDisplayPriority</key>
<integer>1</integer>
<key>INIntentParameterFixedSizeArray</key>
<integer>1</integer>
<key>INIntentParameterName</key>
<string>friends</string>
<key>INIntentParameterObjectType</key>
<string>Friend</string>
<key>INIntentParameterObjectTypeNamespace</key>
<string>p74MWb</string>
<key>INIntentParameterPromptDialogs</key>
<array>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogFormatString</key>
<string>Search</string>
<key>INIntentParameterPromptDialogFormatStringID</key>
<string>ORCbLf</string>
<key>INIntentParameterPromptDialogType</key>
<string>Configuration</string>
</dict>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogType</key>
<string>Primary</string>
</dict>
</array>
<key>INIntentParameterRelationship</key>
<dict>
<key>INIntentParameterRelationshipPredicateName</key>
<string>EnumHasExactValue</string>
<key>INIntentParameterRelationshipPredicateValue</key>
<string>custom</string>
</dict>
<key>INIntentParameterSupportsDynamicEnumeration</key>
<true/>
<key>INIntentParameterSupportsMultipleValues</key>
<true/>
<key>INIntentParameterSupportsSearch</key>
<true/>
<key>INIntentParameterTag</key>
<integer>19</integer>
<key>INIntentParameterType</key>
<string>Object</string>
</dict>
</array>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
<dict>
<key>INIntentResponseCodeName</key>
<string>failure</string>
</dict>
</array>
</dict>
<key>INIntentTitle</key>
<string>Шорткаты</string>
<key>INIntentTitleID</key>
<string>3Sbb7H</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentVerb</key>
<string>View</string>
</dict>
</array>
<key>INTypes</key>
<array>
<dict>
<key>INTypeDisplayName</key>
<string>Chat</string>
<key>INTypeDisplayNameID</key>
<string>zsoXow</string>
<key>INTypeLastPropertyTag</key>
<integer>100</integer>
<key>INTypeName</key>
<string>Friend</string>
<key>INTypeProperties</key>
<array>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>1</integer>
<key>INTypePropertyName</key>
<string>identifier</string>
<key>INTypePropertyTag</key>
<integer>1</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>2</integer>
<key>INTypePropertyName</key>
<string>displayString</string>
<key>INTypePropertyTag</key>
<integer>2</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>3</integer>
<key>INTypePropertyName</key>
<string>pronunciationHint</string>
<key>INTypePropertyTag</key>
<integer>3</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>4</integer>
<key>INTypePropertyName</key>
<string>alternativeSpeakableMatches</string>
<key>INTypePropertySupportsMultipleValues</key>
<true/>
<key>INTypePropertyTag</key>
<integer>4</integer>
<key>INTypePropertyType</key>
<string>SpeakableString</string>
</dict>
<dict>
<key>INTypePropertyDisplayName</key>
<string>Subtitle</string>
<key>INTypePropertyDisplayNameID</key>
<string>nNNpdC</string>
<key>INTypePropertyDisplayPriority</key>
<integer>5</integer>
<key>INTypePropertyName</key>
<string>subtitle</string>
<key>INTypePropertyTag</key>
<integer>100</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
</array>
</dict>
</array>
</dict>
</plist>

View File

@ -6102,3 +6102,31 @@ Sorry for the inconvenience.";
"Conversation.AutoremoveTimerRemovedGroup" = "A group admin disabled the auto-delete timer";
"Conversation.AutoremoveTimerSetChannel" = "Messages in this channel will be automatically deleted after %1$@";
"Conversation.AutoremoveTimerRemovedChannel" = "Messages in this channel will no longer be automatically deleted";
"AutoremoveSetup.Title" = "Auto-Deletion";
"AutoremoveSetup.TimeSectionHeader" = "AUTO-DELETE MESSAGES";
"AutoremoveSetup.TimerInfoChannel" = "Automatically delete messages sent in this channel after a certain period of time.";
"AutoremoveSetup.TimerInfoChat" = "Automatically delete messages sent in this chat after a certain period of time.";
"AutoremoveSetup.TimerValueNever" = "Never";
"AutoremoveSetup.TimerValueAfter" = "After %@";
"Conversation.ClearChannel" = "Clear Channel";
"Conversation.AutoremoveTimerSetToastText" = "Messages in this chat are automatically\ndeleted %@ after they have been sent.";
"Conversation.AutoremoveActionEnable" = "Enable Auto-Delete";
"Conversation.AutoremoveActionEdit" = "Edit Auto-Delete Settings";
"Widget.ChatsGalleryTitle" = "Chats";
"Widget.ChatsGalleryDescription" = "Display the latest message from the most important chats.";
"Widget.ShortcutsGalleryTitle" = "Shortcuts";
"Widget.ShortcutsGalleryDescription" = "Display shortcuts of your most important chats to always have quick access to them.";
"Widget.MessageAutoremoveTimerUpdated" = "Auto-delete timer updated";
"Widget.MessageAutoremoveTimerRemoved" = "Auto-delete timer disabled";
"Widget.LongTapToEdit" = "Tap or hold to edit widget.";
"Widget.UpdatedTodayAt" = "Updated at {}";
"Widget.UpdatedAt" = "Updated {}";
"Intents.ErrorLockedTitle" = "Locked";
"Intents.ErrorLockedText" = "Open Telegram and enter passcode to edit widget.";

View File

@ -435,6 +435,7 @@ struct WidgetView: View {
@Environment(\.widgetFamily) private var widgetFamily
@Environment(\.colorScheme) private var colorScheme
let data: PeersWidgetData
let presentationData: WidgetPresentationData
private func linkForPeer(accountId: Int64, id: Int64) -> String {
switch self.widgetFamily {
@ -523,7 +524,6 @@ struct WidgetView: View {
case let .peer(peer):
if let message = peer.peer.message {
text = message.text
//TODO:localize
switch message.content {
case .text:
break
@ -531,19 +531,19 @@ struct WidgetView: View {
if !message.text.isEmpty {
text = "🖼 \(message.text)"
} else {
text = "🖼 Photo"
text = "🖼 \(self.presentationData.messagePhoto)"
}
case .video:
if !message.text.isEmpty {
text = "📹 \(message.text)"
} else {
text = "📹 Video"
text = "📹 \(self.presentationData.messageVideo)"
}
case .gif:
if !message.text.isEmpty {
text = "\(message.text)"
} else {
text = "Gif"
text = "\(self.presentationData.messageAnimation)"
}
case let .file(file):
if !message.text.isEmpty {
@ -559,31 +559,37 @@ struct WidgetView: View {
} else if !music.artist.isEmpty {
text = music.artist
} else {
text = "Music"
text = "Unknown Artist"
}
case .voiceMessage:
text = "🎤 Voice Message"
text = "🎤 \(self.presentationData.messageVoice)"
case .videoMessage:
text = "Video Message"
text = "\(self.presentationData.messageVideoMessage)"
case let .sticker(sticker):
text = "\(sticker.altText) Sticker"
text = "\(sticker.altText) \(presentationData.messageSticker)"
case let .call(call):
if call.isVideo {
text = "Video Call"
text = "\(self.presentationData.messageVideoCall)"
} else {
text = "Voice Call"
text = "\(self.presentationData.messageVoiceCall)"
}
case .mapLocation:
text = "Location"
text = "\(self.presentationData.messageLocation)"
case let .game(game):
text = "🎮 \(game.title)"
case let .poll(poll):
text = "📊 \(poll.title)"
case let .autodeleteTimer(value):
if value.value != nil {
text = self.presentationData.autodeleteTimerUpdated
} else {
text = self.presentationData.autodeleteTimerRemoved
}
}
if let author = message.author {
if author.isMe {
text = "You: \(text)"
text = "\(presentationData.messageAuthorYou): \(text)"
} else {
text = "\(author.title): \(text)"
}
@ -594,7 +600,7 @@ struct WidgetView: View {
if index == 0 {
text = "☀️ 23 °C\n☁️ Passing Clouds"
} else {
text = "😂 Sticker"
text = "😂 \(presentationData.messageSticker)"
text += "\n"
}
case .placeholder:
@ -754,40 +760,38 @@ struct WidgetView: View {
switch data {
case let .peers(peersValue):
if peersValue.peers.isEmpty {
text = "Long tap to edit widget"
text = self.presentationData.widgetLongTapToEdit
} else {
let date = Date(timeIntervalSince1970: Double(peersValue.updateTimestamp))
let calendar = Calendar.current
//TODO:localize
if !calendar.isDate(Date(), inSameDayAs: date) {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .none
text = "updated \(formatter.string(from: date))"
text = self.presentationData.widgetUpdatedAt.replacingOccurrences(of: "{}", with: formatter.string(from: date))
} else {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .short
text = "updated at \(formatter.string(from: date))"
text = self.presentationData.widgetUpdatedTodayAt.replacingOccurrences(of: "{}", with: formatter.string(from: date))
}
}
case .preview:
let date = Date()
let calendar = Calendar.current
//TODO:localize
if !calendar.isDate(Date(), inSameDayAs: date) {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .none
text = "updated \(formatter.string(from: date))"
text = self.presentationData.widgetUpdatedAt.replacingOccurrences(of: "{}", with: formatter.string(from: date))
} else {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .short
text = "updated at \(formatter.string(from: date))"
text = self.presentationData.widgetUpdatedTodayAt.replacingOccurrences(of: "{}", with: formatter.string(from: date))
}
default:
text = "Long tap to edit widget"
text = self.presentationData.widgetLongTapToEdit
}
return Text(text)
@ -795,6 +799,17 @@ struct WidgetView: View {
.foregroundColor(getUpdatedTextColor())
}
func getBackgroundColor() -> Color {
switch colorScheme {
case .light:
return .white
case .dark:
return Color(.sRGB, red: 28.0 / 255.0, green: 28.0 / 255.0, blue: 30.0 / 255.0, opacity: 1.0)
@unknown default:
return .secondary
}
}
func getSeparatorColor() -> Color {
switch colorScheme {
case .light:
@ -848,6 +863,7 @@ struct WidgetView: View {
chatUpdateView(size: geometry.size)
})
})
.background(Rectangle().foregroundColor(getBackgroundColor()))
.padding(0.0)
.unredacted()
}
@ -857,6 +873,7 @@ struct AvatarsWidgetView: View {
@Environment(\.widgetFamily) private var widgetFamily
@Environment(\.colorScheme) private var colorScheme
let data: PeersWidgetData
let presentationData: WidgetPresentationData
func placeholder(geometry: GeometryProxy) -> some View {
return Spacer()
@ -951,38 +968,6 @@ private let buildConfig: BuildConfig = {
return buildConfig
}()
private extension WidgetPresentationData {
static var `default` = WidgetPresentationData(
applicationLockedString: "Unlock the app to use the widget",
applicationStartRequiredString: "Open the app to use the widget",
widgetGalleryTitle: "Telegram",
widgetGalleryDescription: ""
)
}
private let presentationData: WidgetPresentationData = {
let appBundleIdentifier = Bundle.main.bundleIdentifier!
guard let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else {
return WidgetPresentationData.default
}
let baseAppBundleId = String(appBundleIdentifier[..<lastDotRange.lowerBound])
let appGroupName = "group.\(baseAppBundleId)"
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
guard let appGroupUrl = maybeAppGroupUrl else {
return WidgetPresentationData.default
}
let rootPath = rootPathForBasePath(appGroupUrl.path)
if let data = try? Data(contentsOf: URL(fileURLWithPath: widgetPresentationDataPath(rootPath: rootPath))), let value = try? JSONDecoder().decode(WidgetPresentationData.self, from: data) {
return value
} else {
return WidgetPresentationData.default
}
}()
func getWidgetData(contents: SimpleEntry.Contents) -> PeersWidgetData {
switch contents {
case .recent:
@ -998,12 +983,14 @@ struct Static_Widget: Widget {
private let kind: String = "Static_Widget"
public var body: some WidgetConfiguration {
let presentationData = WidgetPresentationData.getForExtension()
return IntentConfiguration(kind: kind, intent: SelectFriendsIntent.self, provider: Provider(), content: { entry in
WidgetView(data: getWidgetData(contents: entry.contents))
WidgetView(data: getWidgetData(contents: entry.contents), presentationData: presentationData)
})
.supportedFamilies([.systemMedium])
.configurationDisplayName("Chats")
.description("Display the latest message from the most important chats.")
.configurationDisplayName(presentationData.widgetChatsGalleryTitle)
.description(presentationData.widgetChatsGalleryDescription)
}
}
@ -1011,12 +998,14 @@ struct Static_AvatarsWidget: Widget {
private let kind: String = "Static_AvatarsWidget"
public var body: some WidgetConfiguration {
let presentationData = WidgetPresentationData.getForExtension()
return IntentConfiguration(kind: kind, intent: SelectAvatarFriendsIntent.self, provider: AvatarsProvider(), content: { entry in
AvatarsWidgetView(data: getWidgetData(contents: entry.contents))
AvatarsWidgetView(data: getWidgetData(contents: entry.contents), presentationData: presentationData)
})
.supportedFamilies([.systemMedium])
.configurationDisplayName("Shortcuts")
.description("Display shortcuts of your most important chats to always have quick access to them.")
.configurationDisplayName(presentationData.widgetShortcutsGalleryTitle)
.description(presentationData.widgetShortcutsGalleryDescription)
}
}

View File

@ -126,7 +126,7 @@ class ChatListFilterSettingsHeaderItemNode: ListViewItemNode {
animationName = "MessageAutoRemove"
size = 260
insetDifference = 120
playbackMode = .count(2)
playbackMode = .once
additionalBottomInset = isHidden ? 8.0 : 16.0
}

View File

@ -112,9 +112,8 @@ private func peerAutoremoveSetupEntries(peer: Peer?, presentationData: Presentat
resolvedValue = state.changedValue ?? defaultValue
//TODO:localize
entries.append(.header)
entries.append(.timeHeader("AUTO-DELETE MESSAGES"))
entries.append(.timeHeader(presentationData.strings.AutoremoveSetup_TimeSectionHeader))
var availableValues: [Int32] = [
Int32.max,
@ -127,9 +126,9 @@ private func peerAutoremoveSetupEntries(peer: Peer?, presentationData: Presentat
}
entries.append(.timeValue(resolvedValue, availableValues))
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
entries.append(.timeComment("Automatically delete messages sent in this channel after a certain period of time."))
entries.append(.timeComment(presentationData.strings.AutoremoveSetup_TimerInfoChannel))
} else {
entries.append(.timeComment("Automatically delete messages sent in this chat after a certain period of time."))
entries.append(.timeComment(presentationData.strings.AutoremoveSetup_TimerInfoChat))
}
return entries
@ -151,7 +150,6 @@ public func peerAutoremoveSetupScreen(context: AccountContext, peerId: PeerId, c
statePromise.set(stateValue.modify { f($0) })
}
var pushControllerImpl: ((ViewController) -> Void)?
var dismissImpl: (() -> Void)?
let actionsDisposable = DisposableSet()
@ -238,8 +236,7 @@ public func peerAutoremoveSetupScreen(context: AccountContext, peerId: PeerId, c
let isDebug = context.account.testingEnvironment
//TODO:localize
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Auto-Deletion"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.AutoremoveSetup_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: peerAutoremoveSetupEntries(peer: peer, presentationData: presentationData, isDebug: isDebug, defaultValue: defaultValue, state: state), style: .blocks)
return (controllerState, (listState, arguments))
@ -254,8 +251,5 @@ public func peerAutoremoveSetupScreen(context: AccountContext, peerId: PeerId, c
controller?.view.endEditing(true)
controller?.dismiss()
}
pushControllerImpl = { [weak controller] c in
controller?.push(c)
}
return controller
}

View File

@ -193,11 +193,10 @@ class PeerRemoveTimeoutItemNode: ListViewItemNode, ItemListItemNode {
let titleLayouts = zip(0 ..< makeTitleNodeLayouts.count, makeTitleNodeLayouts).map { index, makeLayout -> (TextNodeLayout, () -> TextNode) in
let text: String
//TODO:localize
if item.availableValues[index] == Int32.max {
text = "Never"
text = item.presentationData.strings.AutoremoveSetup_TimerValueNever
} else {
text = "After \(timeIntervalString(strings: item.presentationData.strings, value: item.availableValues[index]))"
text = item.presentationData.strings.AutoremoveSetup_TimerValueAfter(timeIntervalString(strings: item.presentationData.strings, value: item.availableValues[index])).0
}
return makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: text, font: Font.regular(13.0), textColor: item.presentationData.theme.list.itemSecondaryTextColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0)))
}

View File

@ -66,15 +66,17 @@ struct SqlitePreparedStatement {
return res == SQLITE_ROW
}
func tryStep(handle: OpaquePointer?, _ initial: Bool = false, path: String?) -> Bool {
struct SqlError: Error {
var code: Int32
}
func tryStep(handle: OpaquePointer?, _ initial: Bool = false, path: String?) -> Result<Void, SqlError> {
let res = sqlite3_step(statement)
if res != SQLITE_ROW && res != SQLITE_DONE {
if res != SQLITE_MISUSE {
if let error = sqlite3_errmsg(handle), let str = NSString(utf8String: error) {
postboxLog("SQL error \(res): \(str) on step")
} else {
postboxLog("SQL error \(res) on step")
}
if let error = sqlite3_errmsg(handle), let str = NSString(utf8String: error) {
postboxLog("SQL error \(res): \(str) on step")
} else {
postboxLog("SQL error \(res) on step")
}
if res == SQLITE_CORRUPT {
@ -85,7 +87,11 @@ struct SqlitePreparedStatement {
}
}
}
return res == SQLITE_ROW || res == SQLITE_DONE
if res == SQLITE_ROW || res == SQLITE_DONE {
return .success(Void())
} else {
return .failure(SqlError(code: res))
}
}
func int32At(_ index: Int) -> Int32 {
@ -484,11 +490,20 @@ public final class SqliteValueBox: ValueBox {
private func isEncrypted(_ database: Database) -> Bool {
var statement: OpaquePointer? = nil
let status = sqlite3_prepare_v2(database.handle, "SELECT * FROM sqlite_master LIMIT 1", -1, &statement, nil)
if statement == nil {
postboxLog("isEncrypted: sqlite3_prepare_v2 status = \(status) [\(self.databasePath)]")
return true
}
if status == SQLITE_NOTADB {
postboxLog("isEncrypted: status = SQLITE_NOTADB [\(self.databasePath)]")
return true
}
let preparedStatement = SqlitePreparedStatement(statement: statement)
if !preparedStatement.tryStep(handle: database.handle, path: self.databasePath) {
switch preparedStatement.tryStep(handle: database.handle, path: self.databasePath) {
case .success:
break
case let .failure(error):
postboxLog("isEncrypted: tryStep result is \(error.code) [\(self.databasePath)]")
preparedStatement.destroy()
return true
}
@ -602,14 +617,14 @@ public final class SqliteValueBox: ValueBox {
switch table.keyType {
case .binary:
var resultCode: Bool
var createStatement = "CREATE TABLE t\(table.id) (key BLOB PRIMARY KEY, value BLOB)"
var createStatement = "CREATE TABLE IF NOT EXISTS t\(table.id) (key BLOB PRIMARY KEY, value BLOB)"
if table.compactValuesOnCreation {
createStatement += " WITHOUT ROWID"
}
resultCode = database.execute(createStatement)
assert(resultCode)
case .int64:
let resultCode = database.execute("CREATE TABLE t\(table.id) (key INTEGER PRIMARY KEY, value BLOB)")
let resultCode = database.execute("CREATE TABLE IF NOT EXISTS t\(table.id) (key INTEGER PRIMARY KEY, value BLOB)")
assert(resultCode)
}
}
@ -618,10 +633,10 @@ public final class SqliteValueBox: ValueBox {
precondition(self.queue.isCurrent())
if let _ = self.fullTextTables[table.id] {
} else {
var resultCode = self.database.execute("CREATE VIRTUAL TABLE ft\(table.id) USING fts5(collectionId, itemId, contents, tags)")
var resultCode = self.database.execute("CREATE VIRTUAL TABLE IF NOT EXISTS ft\(table.id) USING fts5(collectionId, itemId, contents, tags)")
precondition(resultCode)
self.fullTextTables[table.id] = table
resultCode = self.database.execute("INSERT INTO __meta_fulltext_tables(name) VALUES (\(table.id))")
resultCode = self.database.execute("INSERT OR IGNORE INTO __meta_fulltext_tables(name) VALUES (\(table.id))")
precondition(resultCode)
}
}

View File

@ -211,7 +211,7 @@ swift_library(
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
"//submodules/AnimatedNavigationStripeNode:AnimatedNavigationStripeNode",
"//submodules/AudioBlob:AudioBlob",
"//:GeneratedSources",
"//Telegram:GeneratedSources",
"//third-party/ZipArchive:ZipArchive",
"//submodules/ChatImportUI:ChatImportUI",
"//submodules/ChatHistoryImportTasks:ChatHistoryImportTasks",

View File

@ -5626,9 +5626,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.presentAutoremoveSetup()
} else if let currentAutoremoveTimeout = currentAutoremoveTimeout, let rect = strongSelf.chatDisplayNode.frameForInputPanelAccessoryButton(.messageAutoremoveTimeout(currentAutoremoveTimeout)) {
//TODO:localize
let intervalText = timeIntervalString(strings: strongSelf.presentationData.strings, value: currentAutoremoveTimeout)
let text: String = "Messages in this chat are automatically\ndeleted \(intervalText) after they have been sent."
let text: String = strongSelf.presentationData.strings.Conversation_AutoremoveTimerSetToastText(intervalText).0
strongSelf.mediaRecordingModeTooltipController?.dismiss()
@ -7950,8 +7949,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
if chatPeer.canSetupAutoremoveTimeout(accountPeerId: strongSelf.context.account.peerId) {
//TODO:localize
items.append(ActionSheetButtonItem(title: strongSelf.presentationInterfaceState.autoremoveTimeout == nil ? "Enable Auto-Delete" : "Edit Auto-Delete Settings", color: .accent, action: { [weak actionSheet] in
items.append(ActionSheetButtonItem(title: strongSelf.presentationInterfaceState.autoremoveTimeout == nil ? strongSelf.presentationData.strings.Conversation_AutoremoveActionEnable : strongSelf.presentationData.strings.Conversation_AutoremoveActionEdit, color: .accent, action: { [weak actionSheet] in
guard let actionSheet = actionSheet else {
return
}
@ -7965,81 +7963,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}))
}
}
/*if case .scheduledMessages = self.presentationInterfaceState.subject {
} else if canRemoveGlobally {
items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder))
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
beginClear(.forLocalPeer)
}))
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
guard let strongSelf = self else {
return
}
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationText, actions: [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
}),
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: {
beginClear(.forEveryone)
})
], parseMarkdown: true), in: .window(.root))
}))
} else {
if !isClearCache {
items.append(ActionSheetTextItem(title: text))
}
items.append(ActionSheetButtonItem(title: isClearCache ? self.presentationData.strings.Conversation_ClearCache : self.presentationData.strings.Conversation_ClearAll, color: isClearCache ? .accent : .destructive, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
guard let strongSelf = self else {
return
}
if isClearCache {
strongSelf.navigationButtonAction(.clearCache)
} else {
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
}),
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
beginClear(.forLocalPeer)
})
], parseMarkdown: true), in: .window(.root))
}
}))
}
if let peer = self.presentationInterfaceState.renderedPeer?.peer {
var currentAutoremoveTimeout: Int32? = self.presentationInterfaceState.autoremoveTimeout
var canSetupAutoremoveTimeout = false
if let _ = peer as? TelegramSecretChat {
} else if let group = peer as? TelegramGroup {
if case .creator = group.role {
canSetupAutoremoveTimeout = true
} else if case let .admin(rights, _) = group.role {
if rights.flags.contains(.canDeleteMessages) {
canSetupAutoremoveTimeout = true
}
}
} else if let user = self.presentationInterfaceState.renderedPeer?.peer as? TelegramUser {
if user.id != self.context.account.peerId && user.botInfo == nil {
canSetupAutoremoveTimeout = true
}
} else if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
if channel.hasPermission(.deleteAllMessages) {
canSetupAutoremoveTimeout = true
}
}
if canSetupAutoremoveTimeout {
}
}*/
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
@ -8050,163 +7973,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.chatDisplayNode.dismissInput()
strongSelf.present(actionSheet, in: .window(.root))
})
/*if peerId == self.context.account.peerId {
text = self.presentationData.strings.Conversation_ClearSelfHistory
} else if peerId.namespace == Namespaces.Peer.SecretChat {
text = self.presentationData.strings.Conversation_ClearSecretHistory
} else if peerId.namespace == Namespaces.Peer.CloudGroup || peerId.namespace == Namespaces.Peer.CloudChannel {
if let channel = peer.peer as? TelegramChannel, case .broadcast = channel.info {
isClearCache = true
text = self.presentationData.strings.Conversation_ClearCache
} else {
text = self.presentationData.strings.Conversation_ClearGroupHistory
}
} else {
text = self.presentationData.strings.Conversation_ClearPrivateHistory
}
let text: String
if peerId == self.context.account.peerId {
text = self.presentationData.strings.Conversation_ClearSelfHistory
} else if peerId.namespace == Namespaces.Peer.SecretChat {
text = self.presentationData.strings.Conversation_ClearSecretHistory
} else if peerId.namespace == Namespaces.Peer.CloudGroup || peerId.namespace == Namespaces.Peer.CloudChannel {
if let channel = peer.peer as? TelegramChannel, case .broadcast = channel.info {
isClearCache = true
text = self.presentationData.strings.Conversation_ClearCache
} else {
text = self.presentationData.strings.Conversation_ClearGroupHistory
}
} else {
text = self.presentationData.strings.Conversation_ClearPrivateHistory
}
var canRemoveGlobally = false
let limitsConfiguration = self.context.currentLimitsConfiguration.with { $0 }
if peerId.namespace == Namespaces.Peer.CloudUser && peerId != self.context.account.peerId {
if limitsConfiguration.maxMessageRevokeIntervalInPrivateChats == LimitsConfiguration.timeIntervalForever {
canRemoveGlobally = true
}
}
if let user = chatPeer as? TelegramUser, user.botInfo != nil {
canRemoveGlobally = false
}
let actionSheet = ActionSheetController(presentationData: self.presentationData)
var items: [ActionSheetItem] = []
if case .scheduledMessages = self.presentationInterfaceState.subject {
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ScheduledMessages_ClearAllConfirmation, color: .destructive, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
guard let strongSelf = self else {
return
}
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
}),
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
beginClear(.scheduledMessages)
})
], parseMarkdown: true), in: .window(.root))
}))
} else if canRemoveGlobally {
items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder))
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
beginClear(.forLocalPeer)
}))
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
guard let strongSelf = self else {
return
}
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationText, actions: [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
}),
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: {
beginClear(.forEveryone)
})
], parseMarkdown: true), in: .window(.root))
}))
} else {
if !isClearCache {
items.append(ActionSheetTextItem(title: text))
}
items.append(ActionSheetButtonItem(title: isClearCache ? self.presentationData.strings.Conversation_ClearCache : self.presentationData.strings.Conversation_ClearAll, color: isClearCache ? .accent : .destructive, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
guard let strongSelf = self else {
return
}
if isClearCache {
strongSelf.navigationButtonAction(.clearCache)
} else {
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
}),
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
beginClear(.forLocalPeer)
})
], parseMarkdown: true), in: .window(.root))
}
}))
}
if let peer = self.presentationInterfaceState.renderedPeer?.peer {
var currentAutoremoveTimeout: Int32? = self.presentationInterfaceState.autoremoveTimeout
var canSetupAutoremoveTimeout = false
if let _ = peer as? TelegramSecretChat {
} else if let group = peer as? TelegramGroup {
if case .creator = group.role {
canSetupAutoremoveTimeout = true
} else if case let .admin(rights, _) = group.role {
if rights.flags.contains(.canDeleteMessages) {
canSetupAutoremoveTimeout = true
}
}
} else if let user = self.presentationInterfaceState.renderedPeer?.peer as? TelegramUser {
if user.id != self.context.account.peerId && user.botInfo == nil {
canSetupAutoremoveTimeout = true
}
} else if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
if channel.hasPermission(.deleteAllMessages) {
canSetupAutoremoveTimeout = true
}
}
if canSetupAutoremoveTimeout {
//TODO:localize
items.append(ActionSheetButtonItem(title: currentAutoremoveTimeout == nil ? "Enable Auto-Delete" : "Edit Auto-Delete Settings", color: .accent, action: { [weak self, weak actionSheet] in
guard let actionSheet = actionSheet else {
return
}
guard let strongSelf = self else {
return
}
actionSheet.dismissAnimated()
strongSelf.presentAutoremoveSetup()
}))
}
}
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])])
self.chatDisplayNode.dismissInput()
self.present(actionSheet, in: .window(.root))*/
}
case let .openChatInfo(expandAvatar):
let _ = self.presentVoiceMessageDiscardAlert(action: {

View File

@ -50,8 +50,7 @@ func leftNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Cha
canClear = true
} else if let peer = peer as? TelegramChannel {
if case .broadcast = peer.info {
//TODO:localize
title = "Clear Channel"
title = strings.Conversation_ClearChannel
}
if peer.hasPermission(.changeInfo) {
canClear = true

View File

@ -256,7 +256,29 @@ final class WidgetDataContext {
self.widgetPresentationDataDisposable = (presentationData
|> map { presentationData -> WidgetPresentationData in
return WidgetPresentationData(applicationLockedString: presentationData.strings.Widget_ApplicationLocked, applicationStartRequiredString: presentationData.strings.Widget_ApplicationStartRequired, widgetGalleryTitle: presentationData.strings.Widget_GalleryTitle, widgetGalleryDescription: presentationData.strings.Widget_GalleryDescription)
return WidgetPresentationData(
widgetChatsGalleryTitle: presentationData.strings.Widget_ChatsGalleryTitle,
widgetChatsGalleryDescription: presentationData.strings.Widget_ChatsGalleryDescription,
widgetShortcutsGalleryTitle: presentationData.strings.Widget_ShortcutsGalleryTitle,
widgetShortcutsGalleryDescription: presentationData.strings.Widget_ShortcutsGalleryDescription,
widgetLongTapToEdit: presentationData.strings.Widget_LongTapToEdit,
widgetUpdatedTodayAt: presentationData.strings.Widget_UpdatedTodayAt,
widgetUpdatedAt: presentationData.strings.Widget_UpdatedAt,
messageAuthorYou: presentationData.strings.DialogList_You,
messagePhoto: presentationData.strings.Message_Photo,
messageVideo: presentationData.strings.Message_Video,
messageAnimation: presentationData.strings.Message_Animation,
messageVoice: presentationData.strings.Message_Audio,
messageVideoMessage: presentationData.strings.Message_VideoMessage,
messageSticker: presentationData.strings.Message_Sticker,
messageVoiceCall: presentationData.strings.Watch_Message_Call,
messageVideoCall: presentationData.strings.Watch_Message_Call,
messageLocation: presentationData.strings.Message_Location,
autodeleteTimerUpdated: presentationData.strings.Widget_MessageAutoremoveTimerUpdated,
autodeleteTimerRemoved: presentationData.strings.Widget_MessageAutoremoveTimerRemoved,
generalLockedTitle: presentationData.strings.Intents_ErrorLockedTitle,
generalLockedText: presentationData.strings.Intents_ErrorLockedText
)
}
|> distinctUntilChanged).start(next: { value in
let path = widgetPresentationDataPath(rootPath: basePath)

View File

@ -119,6 +119,14 @@ public struct WidgetDataPeer: Codable, Equatable {
}
}
public struct AutodeleteTimer: Codable, Equatable {
public var value: Int32?
public init(value: Int32?) {
self.value = value
}
}
enum CodingKeys: String, CodingKey {
case text
case image
@ -133,6 +141,7 @@ public struct WidgetDataPeer: Codable, Equatable {
case mapLocation
case game
case poll
case autodeleteTimer
}
case text
@ -148,6 +157,7 @@ public struct WidgetDataPeer: Codable, Equatable {
case mapLocation(MapLocation)
case game(Game)
case poll(Poll)
case autodeleteTimer(AutodeleteTimer)
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
@ -177,6 +187,8 @@ public struct WidgetDataPeer: Codable, Equatable {
self = .game(game)
} else if let poll = try? container.decode(Poll.self, forKey: .poll) {
self = .poll(poll)
} else if let autodeleteTimer = try? container.decode(AutodeleteTimer.self, forKey: .autodeleteTimer) {
self = .autodeleteTimer(autodeleteTimer)
} else {
throw DecodingError.generic
}
@ -211,6 +223,8 @@ public struct WidgetDataPeer: Codable, Equatable {
try container.encode(game, forKey: .game)
case let .poll(poll):
try container.encode(poll, forKey: .poll)
case let .autodeleteTimer(autodeleteTimer):
try container.encode(autodeleteTimer, forKey: .autodeleteTimer)
}
}
}
@ -260,17 +274,130 @@ public struct WidgetDataPeers: Codable, Equatable {
}
public struct WidgetPresentationData: Codable, Equatable {
public var applicationLockedString: String
public var applicationStartRequiredString: String
public var widgetGalleryTitle: String
public var widgetGalleryDescription: String
public var widgetChatsGalleryTitle: String
public var widgetChatsGalleryDescription: String
public var widgetShortcutsGalleryTitle: String
public var widgetShortcutsGalleryDescription: String
public init(applicationLockedString: String, applicationStartRequiredString: String, widgetGalleryTitle: String, widgetGalleryDescription: String) {
self.applicationLockedString = applicationLockedString
self.applicationStartRequiredString = applicationStartRequiredString
self.widgetGalleryTitle = widgetGalleryTitle
self.widgetGalleryDescription = widgetGalleryDescription
public var widgetLongTapToEdit: String
public var widgetUpdatedTodayAt: String
public var widgetUpdatedAt: String
public var messageAuthorYou: String
public var messagePhoto: String
public var messageVideo: String
public var messageAnimation: String
public var messageVoice: String
public var messageVideoMessage: String
public var messageSticker: String
public var messageVoiceCall: String
public var messageVideoCall: String
public var messageLocation: String
public var autodeleteTimerUpdated: String
public var autodeleteTimerRemoved: String
public var generalLockedTitle: String
public var generalLockedText: String
public init(
widgetChatsGalleryTitle: String,
widgetChatsGalleryDescription: String,
widgetShortcutsGalleryTitle: String,
widgetShortcutsGalleryDescription: String,
widgetLongTapToEdit: String,
widgetUpdatedTodayAt: String,
widgetUpdatedAt: String,
messageAuthorYou: String,
messagePhoto: String,
messageVideo: String,
messageAnimation: String,
messageVoice: String,
messageVideoMessage: String,
messageSticker: String,
messageVoiceCall: String,
messageVideoCall: String,
messageLocation: String,
autodeleteTimerUpdated: String,
autodeleteTimerRemoved: String,
generalLockedTitle: String,
generalLockedText: String
) {
self.widgetChatsGalleryTitle = widgetChatsGalleryTitle
self.widgetChatsGalleryDescription = widgetChatsGalleryDescription
self.widgetShortcutsGalleryTitle = widgetShortcutsGalleryTitle
self.widgetShortcutsGalleryDescription = widgetShortcutsGalleryDescription
self.widgetLongTapToEdit = widgetLongTapToEdit
self.widgetUpdatedTodayAt = widgetUpdatedTodayAt
self.widgetUpdatedAt = widgetUpdatedAt
self.messageAuthorYou = messageAuthorYou
self.messagePhoto = messagePhoto
self.messageVideo = messageVideo
self.messageAnimation = messageAnimation
self.messageVoice = messageVoice
self.messageVideoMessage = messageVideoMessage
self.messageSticker = messageSticker
self.messageVoiceCall = messageVoiceCall
self.messageVideoCall = messageVideoCall
self.messageLocation = messageLocation
self.autodeleteTimerUpdated = autodeleteTimerUpdated
self.autodeleteTimerRemoved = autodeleteTimerRemoved
self.generalLockedTitle = generalLockedTitle
self.generalLockedText = generalLockedText
}
public static func getForExtension() -> WidgetPresentationData {
let appBundleIdentifier = Bundle.main.bundleIdentifier!
guard let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else {
return WidgetPresentationData.default
}
let baseAppBundleId = String(appBundleIdentifier[..<lastDotRange.lowerBound])
let appGroupName = "group.\(baseAppBundleId)"
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
guard let appGroupUrl = maybeAppGroupUrl else {
return WidgetPresentationData.default
}
let rootPath = rootPathForBasePath(appGroupUrl.path)
if let data = try? Data(contentsOf: URL(fileURLWithPath: widgetPresentationDataPath(rootPath: rootPath))), let value = try? JSONDecoder().decode(WidgetPresentationData.self, from: data) {
return value
} else {
return WidgetPresentationData.default
}
}
}
public extension WidgetPresentationData {
static var `default` = WidgetPresentationData(
widgetChatsGalleryTitle: "Chats",
widgetChatsGalleryDescription: "Display the latest message from the most important chats.",
widgetShortcutsGalleryTitle: "Shortcuts",
widgetShortcutsGalleryDescription: "Display shortcuts of your most important chats to always have quick access to them.",
widgetLongTapToEdit: "Tap or hold to edit widget.",
widgetUpdatedTodayAt: "Updated at {}",
widgetUpdatedAt: "Updated {}",
messageAuthorYou: "You",
messagePhoto: "Photo",
messageVideo: "Video",
messageAnimation: "GIF",
messageVoice: "Voice Message",
messageVideoMessage: "Video Message",
messageSticker: "Sticker",
messageVoiceCall: "Call",
messageVideoCall: "Video Call",
messageLocation: "Map",
autodeleteTimerUpdated: "Auto-delete timer updated",
autodeleteTimerRemoved: "Auto-delete timer disabled",
generalLockedTitle: "Locked",
generalLockedText: "Open Telegram and enter passcode to edit widget."
)
}
private func rootPathForBasePath(_ appGroupPath: String) -> String {
return appGroupPath + "/telegram-data"
}
public func widgetPresentationDataPath(rootPath: String) -> String {