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", "telegram_team_id",
) )
load("@build_bazel_rules_apple//apple:resources.bzl",
"swift_intent_library",
)
config_setting( config_setting(
name = "debug", name = "debug",
values = { values = {
@ -1186,7 +1190,7 @@ swift_library(
"//submodules/TelegramCore:TelegramCore", "//submodules/TelegramCore:TelegramCore",
"//submodules/SyncCore:SyncCore", "//submodules/SyncCore:SyncCore",
"//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider", "//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( swift_library(
name = "IntentsExtensionLib", name = "IntentsExtensionLib",
module_name = "IntentsExtensionLib", module_name = "IntentsExtensionLib",
srcs = glob([ srcs = glob([
"SiriIntents/**/*.swift", "SiriIntents/**/*.swift",
]), ]),
data = [ data = glob(["SiriIntents/*.lproj/Intents.intentdefinition"]),
#"SiriIntents/Intents.intentdefinition",
],
deps = [ deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/Postbox:Postbox", "//submodules/Postbox:Postbox",
@ -1282,7 +1291,8 @@ swift_library(
"//submodules/BuildConfig:BuildConfig", "//submodules/BuildConfig:BuildConfig",
"//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider", "//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider",
"//submodules/AppLockState:AppLockState", "//submodules/AppLockState:AppLockState",
"//:GeneratedSources", "//Telegram:GeneratedSources",
"//submodules/WidgetItems:WidgetItems",
], ],
) )

View File

@ -10,6 +10,7 @@ import OpenSSLEncryptionProvider
import AppLockState import AppLockState
import UIKit import UIKit
import GeneratedSources import GeneratedSources
import WidgetItems
private var accountCache: Account? 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) { 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: [ let presentationData = WidgetPresentationData.getForExtension()
NSLocalizedDescriptionKey: "Open Telegram and enter passcode to edit widget."
let error = NSError(domain: presentationData.generalLockedTitle, code: 1, userInfo: [
NSLocalizedDescriptionKey: presentationData.generalLockedText
]) ])
completion(nil, error) 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) { 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: [ let error = NSError(domain: "Locked", code: 1, userInfo: [
NSLocalizedDescriptionKey: "Open Telegram and enter passcode to edit widget." NSLocalizedDescriptionKey: "Open Telegram and enter passcode to edit widget."
]) ])

View File

@ -19,8 +19,6 @@
<dict> <dict>
<key>INIntentCategory</key> <key>INIntentCategory</key>
<string>information</string> <string>information</string>
<key>INIntentDescription</key>
<string>Display the latest message from the most important chats.</string>
<key>INIntentDescriptionID</key> <key>INIntentDescriptionID</key>
<string>jmsEbj</string> <string>jmsEbj</string>
<key>INIntentEligibleForWidgets</key> <key>INIntentEligibleForWidgets</key>
@ -137,8 +135,6 @@
<dict> <dict>
<key>INIntentCategory</key> <key>INIntentCategory</key>
<string>information</string> <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> <key>INIntentDescriptionID</key>
<string>DwL4WQ</string> <string>DwL4WQ</string>
<key>INIntentEligibleForWidgets</key> <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.AutoremoveTimerRemovedGroup" = "A group admin disabled the auto-delete timer";
"Conversation.AutoremoveTimerSetChannel" = "Messages in this channel will be automatically deleted after %1$@"; "Conversation.AutoremoveTimerSetChannel" = "Messages in this channel will be automatically deleted after %1$@";
"Conversation.AutoremoveTimerRemovedChannel" = "Messages in this channel will no longer be automatically deleted"; "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(\.widgetFamily) private var widgetFamily
@Environment(\.colorScheme) private var colorScheme @Environment(\.colorScheme) private var colorScheme
let data: PeersWidgetData let data: PeersWidgetData
let presentationData: WidgetPresentationData
private func linkForPeer(accountId: Int64, id: Int64) -> String { private func linkForPeer(accountId: Int64, id: Int64) -> String {
switch self.widgetFamily { switch self.widgetFamily {
@ -523,7 +524,6 @@ struct WidgetView: View {
case let .peer(peer): case let .peer(peer):
if let message = peer.peer.message { if let message = peer.peer.message {
text = message.text text = message.text
//TODO:localize
switch message.content { switch message.content {
case .text: case .text:
break break
@ -531,19 +531,19 @@ struct WidgetView: View {
if !message.text.isEmpty { if !message.text.isEmpty {
text = "🖼 \(message.text)" text = "🖼 \(message.text)"
} else { } else {
text = "🖼 Photo" text = "🖼 \(self.presentationData.messagePhoto)"
} }
case .video: case .video:
if !message.text.isEmpty { if !message.text.isEmpty {
text = "📹 \(message.text)" text = "📹 \(message.text)"
} else { } else {
text = "📹 Video" text = "📹 \(self.presentationData.messageVideo)"
} }
case .gif: case .gif:
if !message.text.isEmpty { if !message.text.isEmpty {
text = "\(message.text)" text = "\(message.text)"
} else { } else {
text = "Gif" text = "\(self.presentationData.messageAnimation)"
} }
case let .file(file): case let .file(file):
if !message.text.isEmpty { if !message.text.isEmpty {
@ -559,31 +559,37 @@ struct WidgetView: View {
} else if !music.artist.isEmpty { } else if !music.artist.isEmpty {
text = music.artist text = music.artist
} else { } else {
text = "Music" text = "Unknown Artist"
} }
case .voiceMessage: case .voiceMessage:
text = "🎤 Voice Message" text = "🎤 \(self.presentationData.messageVoice)"
case .videoMessage: case .videoMessage:
text = "Video Message" text = "\(self.presentationData.messageVideoMessage)"
case let .sticker(sticker): case let .sticker(sticker):
text = "\(sticker.altText) Sticker" text = "\(sticker.altText) \(presentationData.messageSticker)"
case let .call(call): case let .call(call):
if call.isVideo { if call.isVideo {
text = "Video Call" text = "\(self.presentationData.messageVideoCall)"
} else { } else {
text = "Voice Call" text = "\(self.presentationData.messageVoiceCall)"
} }
case .mapLocation: case .mapLocation:
text = "Location" text = "\(self.presentationData.messageLocation)"
case let .game(game): case let .game(game):
text = "🎮 \(game.title)" text = "🎮 \(game.title)"
case let .poll(poll): case let .poll(poll):
text = "📊 \(poll.title)" 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 let author = message.author {
if author.isMe { if author.isMe {
text = "You: \(text)" text = "\(presentationData.messageAuthorYou): \(text)"
} else { } else {
text = "\(author.title): \(text)" text = "\(author.title): \(text)"
} }
@ -594,7 +600,7 @@ struct WidgetView: View {
if index == 0 { if index == 0 {
text = "☀️ 23 °C\n☁️ Passing Clouds" text = "☀️ 23 °C\n☁️ Passing Clouds"
} else { } else {
text = "😂 Sticker" text = "😂 \(presentationData.messageSticker)"
text += "\n" text += "\n"
} }
case .placeholder: case .placeholder:
@ -754,40 +760,38 @@ struct WidgetView: View {
switch data { switch data {
case let .peers(peersValue): case let .peers(peersValue):
if peersValue.peers.isEmpty { if peersValue.peers.isEmpty {
text = "Long tap to edit widget" text = self.presentationData.widgetLongTapToEdit
} else { } else {
let date = Date(timeIntervalSince1970: Double(peersValue.updateTimestamp)) let date = Date(timeIntervalSince1970: Double(peersValue.updateTimestamp))
let calendar = Calendar.current let calendar = Calendar.current
//TODO:localize
if !calendar.isDate(Date(), inSameDayAs: date) { if !calendar.isDate(Date(), inSameDayAs: date) {
let formatter = DateFormatter() let formatter = DateFormatter()
formatter.dateStyle = .short formatter.dateStyle = .short
formatter.timeStyle = .none formatter.timeStyle = .none
text = "updated \(formatter.string(from: date))" text = self.presentationData.widgetUpdatedAt.replacingOccurrences(of: "{}", with: formatter.string(from: date))
} else { } else {
let formatter = DateFormatter() let formatter = DateFormatter()
formatter.dateStyle = .none formatter.dateStyle = .none
formatter.timeStyle = .short formatter.timeStyle = .short
text = "updated at \(formatter.string(from: date))" text = self.presentationData.widgetUpdatedTodayAt.replacingOccurrences(of: "{}", with: formatter.string(from: date))
} }
} }
case .preview: case .preview:
let date = Date() let date = Date()
let calendar = Calendar.current let calendar = Calendar.current
//TODO:localize
if !calendar.isDate(Date(), inSameDayAs: date) { if !calendar.isDate(Date(), inSameDayAs: date) {
let formatter = DateFormatter() let formatter = DateFormatter()
formatter.dateStyle = .short formatter.dateStyle = .short
formatter.timeStyle = .none formatter.timeStyle = .none
text = "updated \(formatter.string(from: date))" text = self.presentationData.widgetUpdatedAt.replacingOccurrences(of: "{}", with: formatter.string(from: date))
} else { } else {
let formatter = DateFormatter() let formatter = DateFormatter()
formatter.dateStyle = .none formatter.dateStyle = .none
formatter.timeStyle = .short formatter.timeStyle = .short
text = "updated at \(formatter.string(from: date))" text = self.presentationData.widgetUpdatedTodayAt.replacingOccurrences(of: "{}", with: formatter.string(from: date))
} }
default: default:
text = "Long tap to edit widget" text = self.presentationData.widgetLongTapToEdit
} }
return Text(text) return Text(text)
@ -795,6 +799,17 @@ struct WidgetView: View {
.foregroundColor(getUpdatedTextColor()) .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 { func getSeparatorColor() -> Color {
switch colorScheme { switch colorScheme {
case .light: case .light:
@ -848,6 +863,7 @@ struct WidgetView: View {
chatUpdateView(size: geometry.size) chatUpdateView(size: geometry.size)
}) })
}) })
.background(Rectangle().foregroundColor(getBackgroundColor()))
.padding(0.0) .padding(0.0)
.unredacted() .unredacted()
} }
@ -857,6 +873,7 @@ struct AvatarsWidgetView: View {
@Environment(\.widgetFamily) private var widgetFamily @Environment(\.widgetFamily) private var widgetFamily
@Environment(\.colorScheme) private var colorScheme @Environment(\.colorScheme) private var colorScheme
let data: PeersWidgetData let data: PeersWidgetData
let presentationData: WidgetPresentationData
func placeholder(geometry: GeometryProxy) -> some View { func placeholder(geometry: GeometryProxy) -> some View {
return Spacer() return Spacer()
@ -951,38 +968,6 @@ private let buildConfig: BuildConfig = {
return 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 { func getWidgetData(contents: SimpleEntry.Contents) -> PeersWidgetData {
switch contents { switch contents {
case .recent: case .recent:
@ -998,12 +983,14 @@ struct Static_Widget: Widget {
private let kind: String = "Static_Widget" private let kind: String = "Static_Widget"
public var body: some WidgetConfiguration { public var body: some WidgetConfiguration {
let presentationData = WidgetPresentationData.getForExtension()
return IntentConfiguration(kind: kind, intent: SelectFriendsIntent.self, provider: Provider(), content: { entry in 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]) .supportedFamilies([.systemMedium])
.configurationDisplayName("Chats") .configurationDisplayName(presentationData.widgetChatsGalleryTitle)
.description("Display the latest message from the most important chats.") .description(presentationData.widgetChatsGalleryDescription)
} }
} }
@ -1011,12 +998,14 @@ struct Static_AvatarsWidget: Widget {
private let kind: String = "Static_AvatarsWidget" private let kind: String = "Static_AvatarsWidget"
public var body: some WidgetConfiguration { public var body: some WidgetConfiguration {
let presentationData = WidgetPresentationData.getForExtension()
return IntentConfiguration(kind: kind, intent: SelectAvatarFriendsIntent.self, provider: AvatarsProvider(), content: { entry in 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]) .supportedFamilies([.systemMedium])
.configurationDisplayName("Shortcuts") .configurationDisplayName(presentationData.widgetShortcutsGalleryTitle)
.description("Display shortcuts of your most important chats to always have quick access to them.") .description(presentationData.widgetShortcutsGalleryDescription)
} }
} }

View File

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

View File

@ -112,9 +112,8 @@ private func peerAutoremoveSetupEntries(peer: Peer?, presentationData: Presentat
resolvedValue = state.changedValue ?? defaultValue resolvedValue = state.changedValue ?? defaultValue
//TODO:localize
entries.append(.header) entries.append(.header)
entries.append(.timeHeader("AUTO-DELETE MESSAGES")) entries.append(.timeHeader(presentationData.strings.AutoremoveSetup_TimeSectionHeader))
var availableValues: [Int32] = [ var availableValues: [Int32] = [
Int32.max, Int32.max,
@ -127,9 +126,9 @@ private func peerAutoremoveSetupEntries(peer: Peer?, presentationData: Presentat
} }
entries.append(.timeValue(resolvedValue, availableValues)) entries.append(.timeValue(resolvedValue, availableValues))
if let channel = peer as? TelegramChannel, case .broadcast = channel.info { 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 { } 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 return entries
@ -151,7 +150,6 @@ public func peerAutoremoveSetupScreen(context: AccountContext, peerId: PeerId, c
statePromise.set(stateValue.modify { f($0) }) statePromise.set(stateValue.modify { f($0) })
} }
var pushControllerImpl: ((ViewController) -> Void)?
var dismissImpl: (() -> Void)? var dismissImpl: (() -> Void)?
let actionsDisposable = DisposableSet() let actionsDisposable = DisposableSet()
@ -238,8 +236,7 @@ public func peerAutoremoveSetupScreen(context: AccountContext, peerId: PeerId, c
let isDebug = context.account.testingEnvironment let isDebug = context.account.testingEnvironment
//TODO:localize let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.AutoremoveSetup_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Auto-Deletion"), 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) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: peerAutoremoveSetupEntries(peer: peer, presentationData: presentationData, isDebug: isDebug, defaultValue: defaultValue, state: state), style: .blocks)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
@ -254,8 +251,5 @@ public func peerAutoremoveSetupScreen(context: AccountContext, peerId: PeerId, c
controller?.view.endEditing(true) controller?.view.endEditing(true)
controller?.dismiss() controller?.dismiss()
} }
pushControllerImpl = { [weak controller] c in
controller?.push(c)
}
return controller 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 titleLayouts = zip(0 ..< makeTitleNodeLayouts.count, makeTitleNodeLayouts).map { index, makeLayout -> (TextNodeLayout, () -> TextNode) in
let text: String let text: String
//TODO:localize
if item.availableValues[index] == Int32.max { if item.availableValues[index] == Int32.max {
text = "Never" text = item.presentationData.strings.AutoremoveSetup_TimerValueNever
} else { } 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))) 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 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) let res = sqlite3_step(statement)
if res != SQLITE_ROW && res != SQLITE_DONE { if res != SQLITE_ROW && res != SQLITE_DONE {
if res != SQLITE_MISUSE { if let error = sqlite3_errmsg(handle), let str = NSString(utf8String: error) {
if let error = sqlite3_errmsg(handle), let str = NSString(utf8String: error) { postboxLog("SQL error \(res): \(str) on step")
postboxLog("SQL error \(res): \(str) on step") } else {
} else { postboxLog("SQL error \(res) on step")
postboxLog("SQL error \(res) on step")
}
} }
if res == SQLITE_CORRUPT { 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 { func int32At(_ index: Int) -> Int32 {
@ -484,11 +490,20 @@ public final class SqliteValueBox: ValueBox {
private func isEncrypted(_ database: Database) -> Bool { private func isEncrypted(_ database: Database) -> Bool {
var statement: OpaquePointer? = nil var statement: OpaquePointer? = nil
let status = sqlite3_prepare_v2(database.handle, "SELECT * FROM sqlite_master LIMIT 1", -1, &statement, 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 { if status == SQLITE_NOTADB {
postboxLog("isEncrypted: status = SQLITE_NOTADB [\(self.databasePath)]")
return true return true
} }
let preparedStatement = SqlitePreparedStatement(statement: statement) 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() preparedStatement.destroy()
return true return true
} }
@ -602,14 +617,14 @@ public final class SqliteValueBox: ValueBox {
switch table.keyType { switch table.keyType {
case .binary: case .binary:
var resultCode: Bool 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 { if table.compactValuesOnCreation {
createStatement += " WITHOUT ROWID" createStatement += " WITHOUT ROWID"
} }
resultCode = database.execute(createStatement) resultCode = database.execute(createStatement)
assert(resultCode) assert(resultCode)
case .int64: 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) assert(resultCode)
} }
} }
@ -618,10 +633,10 @@ public final class SqliteValueBox: ValueBox {
precondition(self.queue.isCurrent()) precondition(self.queue.isCurrent())
if let _ = self.fullTextTables[table.id] { if let _ = self.fullTextTables[table.id] {
} else { } 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) precondition(resultCode)
self.fullTextTables[table.id] = table 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) precondition(resultCode)
} }
} }

View File

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

View File

@ -5626,9 +5626,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.presentAutoremoveSetup() strongSelf.presentAutoremoveSetup()
} else if let currentAutoremoveTimeout = currentAutoremoveTimeout, let rect = strongSelf.chatDisplayNode.frameForInputPanelAccessoryButton(.messageAutoremoveTimeout(currentAutoremoveTimeout)) { } else if let currentAutoremoveTimeout = currentAutoremoveTimeout, let rect = strongSelf.chatDisplayNode.frameForInputPanelAccessoryButton(.messageAutoremoveTimeout(currentAutoremoveTimeout)) {
//TODO:localize
let intervalText = timeIntervalString(strings: strongSelf.presentationData.strings, value: currentAutoremoveTimeout) 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() strongSelf.mediaRecordingModeTooltipController?.dismiss()
@ -7950,8 +7949,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
if chatPeer.canSetupAutoremoveTimeout(accountPeerId: strongSelf.context.account.peerId) { if chatPeer.canSetupAutoremoveTimeout(accountPeerId: strongSelf.context.account.peerId) {
//TODO:localize items.append(ActionSheetButtonItem(title: strongSelf.presentationInterfaceState.autoremoveTimeout == nil ? strongSelf.presentationData.strings.Conversation_AutoremoveActionEnable : strongSelf.presentationData.strings.Conversation_AutoremoveActionEdit, color: .accent, action: { [weak actionSheet] in
items.append(ActionSheetButtonItem(title: strongSelf.presentationInterfaceState.autoremoveTimeout == nil ? "Enable Auto-Delete" : "Edit Auto-Delete Settings", color: .accent, action: { [weak actionSheet] in
guard let actionSheet = actionSheet else { guard let actionSheet = actionSheet else {
return 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: [ actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in 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.chatDisplayNode.dismissInput()
strongSelf.present(actionSheet, in: .window(.root)) 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): case let .openChatInfo(expandAvatar):
let _ = self.presentVoiceMessageDiscardAlert(action: { let _ = self.presentVoiceMessageDiscardAlert(action: {

View File

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

View File

@ -256,7 +256,29 @@ final class WidgetDataContext {
self.widgetPresentationDataDisposable = (presentationData self.widgetPresentationDataDisposable = (presentationData
|> map { presentationData -> WidgetPresentationData in |> 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 |> distinctUntilChanged).start(next: { value in
let path = widgetPresentationDataPath(rootPath: basePath) 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 { enum CodingKeys: String, CodingKey {
case text case text
case image case image
@ -133,6 +141,7 @@ public struct WidgetDataPeer: Codable, Equatable {
case mapLocation case mapLocation
case game case game
case poll case poll
case autodeleteTimer
} }
case text case text
@ -148,6 +157,7 @@ public struct WidgetDataPeer: Codable, Equatable {
case mapLocation(MapLocation) case mapLocation(MapLocation)
case game(Game) case game(Game)
case poll(Poll) case poll(Poll)
case autodeleteTimer(AutodeleteTimer)
public init(from decoder: Decoder) throws { public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
@ -177,6 +187,8 @@ public struct WidgetDataPeer: Codable, Equatable {
self = .game(game) self = .game(game)
} else if let poll = try? container.decode(Poll.self, forKey: .poll) { } else if let poll = try? container.decode(Poll.self, forKey: .poll) {
self = .poll(poll) self = .poll(poll)
} else if let autodeleteTimer = try? container.decode(AutodeleteTimer.self, forKey: .autodeleteTimer) {
self = .autodeleteTimer(autodeleteTimer)
} else { } else {
throw DecodingError.generic throw DecodingError.generic
} }
@ -211,6 +223,8 @@ public struct WidgetDataPeer: Codable, Equatable {
try container.encode(game, forKey: .game) try container.encode(game, forKey: .game)
case let .poll(poll): case let .poll(poll):
try container.encode(poll, forKey: .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 struct WidgetPresentationData: Codable, Equatable {
public var applicationLockedString: String public var widgetChatsGalleryTitle: String
public var applicationStartRequiredString: String public var widgetChatsGalleryDescription: String
public var widgetGalleryTitle: String public var widgetShortcutsGalleryTitle: String
public var widgetGalleryDescription: String public var widgetShortcutsGalleryDescription: String
public init(applicationLockedString: String, applicationStartRequiredString: String, widgetGalleryTitle: String, widgetGalleryDescription: String) { public var widgetLongTapToEdit: String
self.applicationLockedString = applicationLockedString public var widgetUpdatedTodayAt: String
self.applicationStartRequiredString = applicationStartRequiredString public var widgetUpdatedAt: String
self.widgetGalleryTitle = widgetGalleryTitle
self.widgetGalleryDescription = widgetGalleryDescription 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 { public func widgetPresentationDataPath(rootPath: String) -> String {