Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2021-01-24 18:04:50 +03:00
commit bed5daf934
14 changed files with 3860 additions and 3660 deletions

View File

@ -17,7 +17,7 @@ There are several things we require from **all developers** for the moment.
2. Clone the project from GitHub:
```
git clone --recursive https://github.com/TelegramMessenger/Telegram-iOS.git
git clone --recursive -j8 https://github.com/TelegramMessenger/Telegram-iOS.git
```
3. Download Bazel 3.7.0
@ -75,7 +75,19 @@ python3 build-system/Make/Make.py \
--disableExtensions
```
Tip: use `--disableExtensions` when developing to speed up development by not building application extensions.
It is possible to generate a project that does not require any codesigning certificates to be installed: add `--disableProvisioningProfiles` flag:
```
python3 build-system/Make/Make.py \
--bazel="$HOME/bazel-dist/bazel" \
--cacheDir="$HOME/telegram-bazel-cache" \
generateProject \
--configurationPath="$HOME/telegram-configuration" \
--disableExtensions \
--disableProvisioningProfiles
```
Tip: use `--disableExtensions` when developing to speed up development by not building application extensions and the WatchOS app.
# Tips

View File

@ -41,6 +41,12 @@ bool_flag(
visibility = ["//visibility:public"],
)
bool_flag(
name = "disableProvisioningProfiles",
build_setting_default = False,
visibility = ["//visibility:public"],
)
config_setting(
name = "disableExtensionsSetting",
flag_values = {
@ -48,6 +54,13 @@ config_setting(
},
)
config_setting(
name = "disableProvisioningProfilesSetting",
flag_values = {
":disableProvisioningProfiles": "True",
},
)
genrule(
name = "empty",
outs = ["empty.swift"],
@ -277,6 +290,12 @@ official_unrestricted_voip_fragment = """
"""
unrestricted_voip_fragment = official_unrestricted_voip_fragment if telegram_bundle_id in official_bundle_ids else ""
official_carplay_fragment = """
<key>com.apple.developer.carplay-messaging</key>
<true/>
"""
carplay_fragment = official_carplay_fragment if telegram_bundle_id in official_bundle_ids else ""
telegram_entitlements_template = """
<key>com.apple.developer.icloud-services</key>
<array>
@ -304,7 +323,7 @@ telegram_entitlements_template = """
<string>{telegram_team_id}.{telegram_bundle_id}</string>
<key>com.apple.developer.icloud-container-environment</key>
<string>{telegram_icloud_environment}</string>
""" + apple_pay_merchants_fragment + unrestricted_voip_fragment
""" + apple_pay_merchants_fragment + unrestricted_voip_fragment + carplay_fragment
plist_fragment(
name = "TelegramEntitlements",
@ -486,7 +505,10 @@ watchos_extension(
":WatchExtensionNSExtensionInfoPlist",
],
minimum_os_version = "5.0",
provisioning_profile = "@build_configuration//provisioning:WatchExtension.mobileprovision",
provisioning_profile = select({
":disableProvisioningProfilesSetting": None,
"//conditions:default": "@build_configuration//provisioning:WatchExtension.mobileprovision",
}),
resources = [
":TelegramWatchExtensionResources",
],
@ -514,7 +536,10 @@ watchos_application(
":WatchAppCompanionInfoPlist",
],
minimum_os_version = "5.0",
provisioning_profile = "@build_configuration//provisioning:WatchApp.mobileprovision",
provisioning_profile = select({
":disableProvisioningProfilesSetting": None,
"//conditions:default": "@build_configuration//provisioning:WatchApp.mobileprovision",
}),
resources = [
":TelegramWatchAppResources",
":TelegramWatchAppAssets",
@ -1023,7 +1048,10 @@ ios_extension(
":AppNameInfoPlist",
],
minimum_os_version = "9.0",
provisioning_profile = "@build_configuration//provisioning:Share.mobileprovision",
provisioning_profile = select({
":disableProvisioningProfilesSetting": None,
"//conditions:default": "@build_configuration//provisioning:Share.mobileprovision",
}),
deps = [":ShareExtensionLib"],
frameworks = [
":TelegramUIFramework"
@ -1092,7 +1120,10 @@ ios_extension(
":AppNameInfoPlist",
],
minimum_os_version = "10.0",
provisioning_profile = "@build_configuration//provisioning:NotificationContent.mobileprovision",
provisioning_profile = select({
":disableProvisioningProfilesSetting": None,
"//conditions:default": "@build_configuration//provisioning:NotificationContent.mobileprovision",
}),
deps = [":NotificationContentExtensionLib"],
frameworks = [
":TelegramUIFramework"
@ -1175,7 +1206,10 @@ ios_extension(
],
minimum_os_version = "14.0",
provides_main = True,
provisioning_profile = "@build_configuration//provisioning:Widget.mobileprovision",
provisioning_profile = select({
":disableProvisioningProfilesSetting": None,
"//conditions:default": "@build_configuration//provisioning:Widget.mobileprovision",
}),
deps = [":WidgetExtensionLib"],
frameworks = [
":SwiftSignalKitFramework",
@ -1264,7 +1298,10 @@ ios_extension(
":AppNameInfoPlist",
],
minimum_os_version = "10.0",
provisioning_profile = "@build_configuration//provisioning:Intents.mobileprovision",
provisioning_profile = select({
":disableProvisioningProfilesSetting": None,
"//conditions:default": "@build_configuration//provisioning:Intents.mobileprovision",
}),
deps = [":IntentsExtensionLib"],
frameworks = [
":SwiftSignalKitFramework",
@ -1315,7 +1352,10 @@ ios_extension(
":AppNameInfoPlist",
],
minimum_os_version = "10.0",
provisioning_profile = "@build_configuration//provisioning:NotificationService.mobileprovision",
provisioning_profile = select({
":disableProvisioningProfilesSetting": None,
"//conditions:default": "@build_configuration//provisioning:NotificationService.mobileprovision",
}),
deps = ["//Telegram/NotificationService:NotificationServiceExtensionLib"],
frameworks = [
":MtProtoKitFramework",
@ -1501,7 +1541,10 @@ ios_application(
),
families = ["iphone", "ipad"],
minimum_os_version = "9.0",
provisioning_profile = "@build_configuration//provisioning:Telegram.mobileprovision",
provisioning_profile = select({
":disableProvisioningProfilesSetting": None,
"//conditions:default": "@build_configuration//provisioning:Telegram.mobileprovision",
}),
entitlements = ":TelegramEntitlements.entitlements",
infoplists = [
":TelegramInfoPlist",
@ -1538,7 +1581,10 @@ ios_application(
# ":WidgetExtension",
],
}),
watch_application = ":TelegramWatchApp",
watch_application = select({
":disableExtensionsSetting": None,
"//conditions:default": ":TelegramWatchApp",
}),
deps = [
":Main",
":Lib",

View File

@ -4276,7 +4276,9 @@ Unused sets are archived when you add more.";
"ChatList.DeleteForEveryoneConfirmationText" = "This will **delete all messages** in this chat for **both participants**.";
"ChatList.DeleteForEveryoneConfirmationAction" = "Delete All";
"ChatList.DeleteForAllMembers" = "Delete for all members";
"ChatList.DeleteForAllSubscribers" = "Delete for all subscribers";
"ChatList.DeleteForAllMembersConfirmationText" = "This will **delete all messages** in this chat for **all participants**.";
"ChatList.DeleteForAllSubscribersConfirmationText" = "This will **delete all messages** in this channel for **all subscribers**.";
"ChatList.DeleteSavedMessagesConfirmationTitle" = "Warning!";
"ChatList.DeleteSavedMessagesConfirmationText" = "This will **delete all messages** in this chat.";

View File

@ -297,12 +297,15 @@ def generate_project(arguments):
disable_extensions = False
if arguments.disableExtensions is not None:
disable_extensions = arguments.disableExtensions
if arguments.disableProvisioningProfiles is not None:
disable_provisioning_profiles = arguments.disableProvisioningProfiles
call_executable(['killall', 'Xcode'], check_result=False)
generate(
build_environment=bazel_command_line.build_environment,
disable_extensions=disable_extensions,
disable_provisioning_profiles=disable_provisioning_profiles,
configuration_path=bazel_command_line.configuration_path,
bazel_app_arguments=bazel_command_line.get_project_generation_arguments()
)
@ -439,6 +442,16 @@ if __name__ == '__main__':
'''
)
generateProjectParser.add_argument(
'--disableProvisioningProfiles',
action='store_true',
default=False,
help='''
This allows to build the project for simulator without having any codesigning identities installed.
Building for an actual device will fail.
'''
)
buildParser = subparsers.add_parser('build', help='Build the app')
buildParser.add_argument(
'--buildNumber',

View File

@ -10,7 +10,7 @@ def remove_directory(path):
shutil.rmtree(path)
def generate(build_environment: BuildEnvironment, disable_extensions, configuration_path, bazel_app_arguments):
def generate(build_environment: BuildEnvironment, disable_extensions, disable_provisioning_profiles, configuration_path, bazel_app_arguments):
project_path = os.path.join(build_environment.base_path, 'build-input/gen/project')
app_target = 'Telegram'
@ -43,8 +43,6 @@ def generate(build_environment: BuildEnvironment, disable_extensions, configurat
bazel_wrapper_arguments = []
bazel_wrapper_arguments += ['--override_repository=build_configuration={}'.format(configuration_path)]
if disable_extensions and False:
bazel_wrapper_arguments += ['--//Telegram:disableExtensions']
with open(bazel_wrapper_path, 'wb') as bazel_wrapper:
bazel_wrapper.write('''#!/bin/sh
@ -82,6 +80,8 @@ def generate(build_environment: BuildEnvironment, disable_extensions, configurat
bazel_build_arguments += ['--override_repository=build_configuration={}'.format(configuration_path)]
if disable_extensions:
bazel_build_arguments += ['--//Telegram:disableExtensions']
if disable_provisioning_profiles:
bazel_build_arguments += ['--//Telegram:disableProvisioningProfiles']
call_executable([
tulsi_path,

View File

@ -146,7 +146,7 @@ API_AVAILABLE(ios(10))
dataDict[@"device_token"] = [appToken base64EncodedStringWithOptions:0];
dataDict[@"device_token_type"] = @"voip";
}
float tzOffset = ([[NSTimeZone systemTimeZone] secondsFromGMT] / 3600.0);
float tzOffset = [[NSTimeZone systemTimeZone] secondsFromGMT];
dataDict[@"tz_offset"] = @((int)tzOffset);
if (signatureDict != nil) {
for (id<NSCopying> key in signatureDict.allKeys) {

View File

@ -23,6 +23,7 @@ swift_library(
"//submodules/ChatHistoryImportTasks:ChatHistoryImportTasks",
"//submodules/MimeTypes:MimeTypes",
"//submodules/ConfettiEffect:ConfettiEffect",
"//submodules/TelegramUniversalVideoContent:TelegramUniversalVideoContent",
],
visibility = [
"//visibility:public",

View File

@ -14,8 +14,15 @@ import AppBundle
import ZIPFoundation
import MimeTypes
import ConfettiEffect
import TelegramUniversalVideoContent
public final class ChatImportActivityScreen: ViewController {
private enum State {
case progress(CGFloat)
case error
case done
}
private final class Node: ViewControllerTracingNode {
private weak var controller: ChatImportActivityScreen?
@ -36,10 +43,10 @@ public final class ChatImportActivityScreen: ViewController {
private var validLayout: (ContainerViewLayout, CGFloat)?
private var totalProgress: CGFloat = 0.0
private let totalBytes: Int
private var isDone: Bool = false
private var state: State = .progress(0.0)
private var videoNode: UniversalVideoNode?
private var feedback: HapticFeedback?
init(controller: ChatImportActivityScreen, context: AccountContext, totalBytes: Int) {
@ -138,10 +145,32 @@ public final class ChatImportActivityScreen: ViewController {
strongSelf.doneAnimationNode.visibility = true
strongSelf.doneAnimationNode.isHidden = false
}
if let path = getAppBundle().path(forResource: "BlankVideo", ofType: "m4v"), let size = fileSize(path) {
let decoration = ChatBubbleVideoDecoration(corners: ImageCorners(), nativeSize: CGSize(width: 100.0, height: 100.0), contentMode: .aspectFit, backgroundColor: .black)
let dummyFile = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 1), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: 12345), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: size, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [])])
let videoContent = NativeVideoContent(id: .message(1, MediaId(namespace: 0, id: 1)), fileReference: .standalone(media: dummyFile), streamVideo: .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .black)
let videoNode = UniversalVideoNode(postbox: context.account.postbox, audioSession: context.sharedContext.mediaManager.audioSession, manager: context.sharedContext.mediaManager.universalVideoManager, decoration: decoration, content: videoContent, priority: .embedded)
videoNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 2.0, height: 2.0))
videoNode.alpha = 0.01
self.videoNode = videoNode
self.addSubnode(videoNode)
videoNode.canAttachContent = true
videoNode.play()
}
}
@objc private func statusButtonPressed() {
switch self.state {
case .done, .progress:
self.controller?.cancel()
case .error:
self.controller?.beginImport()
}
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
@ -157,20 +186,39 @@ public final class ChatImportActivityScreen: ViewController {
let progressStatusSpacing: CGFloat = 14.0
let statusButtonSpacing: CGFloat = 19.0
self.radialStatusText.attributedText = NSAttributedString(string: "\(Int(self.totalProgress * 100.0))%", font: Font.with(size: 42.0, design: .round, weight: .semibold), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
let effectiveProgress: CGFloat
switch state {
case let .progress(value):
effectiveProgress = value
case .error:
effectiveProgress = 0.0
case .done:
effectiveProgress = 1.0
}
self.radialStatusText.attributedText = NSAttributedString(string: "\(Int(effectiveProgress * 100.0))%", font: Font.with(size: 42.0, design: .round, weight: .semibold), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
let radialStatusTextSize = self.radialStatusText.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude))
self.progressText.attributedText = NSAttributedString(string: "\(dataSizeString(Int(self.totalProgress * CGFloat(self.totalBytes)))) of \(dataSizeString(Int(1.0 * CGFloat(self.totalBytes))))", font: Font.semibold(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
self.progressText.attributedText = NSAttributedString(string: "\(dataSizeString(Int(effectiveProgress * CGFloat(self.totalBytes)))) of \(dataSizeString(Int(1.0 * CGFloat(self.totalBytes))))", font: Font.semibold(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
let progressTextSize = self.progressText.updateLayout(CGSize(width: layout.size.width - 16.0 * 2.0, height: .greatestFiniteMagnitude))
switch self.state {
case .progress, .done:
self.statusButtonText.attributedText = NSAttributedString(string: "Done", font: Font.semibold(17.0), textColor: self.presentationData.theme.list.itemAccentColor)
case .error:
self.statusButtonText.attributedText = NSAttributedString(string: "Retry", font: Font.semibold(17.0), textColor: self.presentationData.theme.list.itemAccentColor)
}
let statusButtonTextSize = self.statusButtonText.updateLayout(CGSize(width: layout.size.width - 16.0 * 2.0, height: .greatestFiniteMagnitude))
if !self.isDone {
switch self.state {
case .progress:
self.statusText.attributedText = NSAttributedString(string: "Please keep this window open\nduring the import.", font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemSecondaryTextColor)
} else {
case .error:
self.statusText.attributedText = NSAttributedString(string: "An error occurred.", font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemDestructiveColor)
case .done:
self.statusText.attributedText = NSAttributedString(string: "This chat has been imported\nsuccessfully.", font: Font.semibold(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
}
let statusTextSize = self.statusText.updateLayout(CGSize(width: layout.size.width - 16.0 * 2.0, height: .greatestFiniteMagnitude))
let contentHeight: CGFloat
@ -201,35 +249,49 @@ public final class ChatImportActivityScreen: ViewController {
self.progressText.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - progressTextSize.width) / 2.0), y: self.radialStatus.frame.maxY + maxProgressTextSpacing), size: progressTextSize)
if self.isDone {
self.statusText.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - statusTextSize.width) / 2.0), y: self.progressText.frame.minY), size: statusTextSize)
} else {
if case .progress = self.state {
self.statusText.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - statusTextSize.width) / 2.0), y: self.progressText.frame.maxY + progressStatusSpacing), size: statusTextSize)
self.statusButtonText.isHidden = true
self.statusButton.isHidden = true
self.progressText.isHidden = false
} else {
self.statusText.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - statusTextSize.width) / 2.0), y: self.progressText.frame.minY), size: statusTextSize)
self.statusButtonText.isHidden = false
self.statusButton.isHidden = false
self.progressText.isHidden = true
}
let statusButtonTextFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - statusButtonTextSize.width) / 2.0), y: self.statusText.frame.maxY + statusButtonSpacing), size: statusButtonTextSize)
self.statusButtonText.frame = statusButtonTextFrame
self.statusButton.frame = statusButtonTextFrame.insetBy(dx: -10.0, dy: -10.0)
self.statusButtonText.isHidden = !self.isDone
self.statusButton.isHidden = !self.isDone
self.progressText.isHidden = self.isDone
if isFirstLayout {
self.updateProgress(totalProgress: self.totalProgress, isDone: self.isDone, animated: false)
self.updateState(state: self.state, animated: false)
}
}
func updateProgress(totalProgress: CGFloat, isDone: Bool, animated: Bool) {
self.totalProgress = totalProgress
let wasDone = self.isDone
self.isDone = isDone
func updateState(state: State, animated: Bool) {
var wasDone = false
if case .done = self.state {
wasDone = true
}
self.state = state
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .immediate)
self.radialStatus.transitionToState(.progress(color: self.presentationData.theme.list.itemAccentColor, lineWidth: 6.0, value: max(0.02, self.totalProgress), cancelEnabled: false, animateRotation: false), animated: animated, synchronous: true, completion: {})
if isDone {
self.radialCheck.transitionToState(.progress(color: .clear, lineWidth: 6.0, value: self.totalProgress, cancelEnabled: false, animateRotation: false), animated: false, synchronous: true, completion: {})
let effectiveProgress: CGFloat
switch state {
case let .progress(value):
effectiveProgress = value
case .error:
effectiveProgress = 0.0
case .done:
effectiveProgress = 1.0
}
self.radialStatus.transitionToState(.progress(color: self.presentationData.theme.list.itemAccentColor, lineWidth: 6.0, value: max(0.01, effectiveProgress), cancelEnabled: false, animateRotation: false), animated: animated, synchronous: true, completion: {})
if case .done = state {
self.radialCheck.transitionToState(.progress(color: .clear, lineWidth: 6.0, value: 1.0, cancelEnabled: false, animateRotation: false), animated: false, synchronous: true, completion: {})
self.radialCheck.transitionToState(.check(self.presentationData.theme.list.itemAccentColor), animated: animated, synchronous: true, completion: {})
self.radialStatus.layer.animateScale(from: 1.0, to: 1.05, duration: 0.07, delay: 0.0, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, additive: false, completion: { [weak self] _ in
guard let strongSelf = self else {
@ -274,7 +336,7 @@ public final class ChatImportActivityScreen: ViewController {
private let context: AccountContext
private var presentationData: PresentationData
fileprivate let cancel: () -> Void
private let peerId: PeerId
private var peerId: PeerId
private let archive: Archive
private let mainEntry: TempBoxFile
private let mainEntrySize: Int
@ -369,6 +431,12 @@ public final class ChatImportActivityScreen: ViewController {
case generic
}
for (key, value) in self.pendingEntries {
self.pendingEntries[key] = (value.0, 0.0)
}
self.controllerNode.updateState(state: .progress(0.0), animated: true)
let context = self.context
let archive = self.archive
let mainEntry = self.mainEntry
@ -385,7 +453,11 @@ public final class ChatImportActivityScreen: ViewController {
}
self.disposable.set((resolvedPeerId
|> mapToSignal { peerId -> Signal<ChatHistoryImport.Session, ImportError> in
|> mapToSignal { [weak self] peerId -> Signal<ChatHistoryImport.Session, ImportError> in
Queue.mainQueue().async {
self?.peerId = peerId
}
return ChatHistoryImport.initSession(account: context.account, peerId: peerId, file: mainEntry, mediaCount: Int32(otherEntries.count))
|> mapError { _ -> ImportError in
return .generic
@ -458,17 +530,17 @@ public final class ChatImportActivityScreen: ViewController {
if !strongSelf.otherEntries.isEmpty {
totalProgress = CGFloat(totalDoneBytes) / CGFloat(strongSelf.totalBytes)
}
strongSelf.controllerNode.updateProgress(totalProgress: totalProgress, isDone: false, animated: true)
strongSelf.controllerNode.updateState(state: .progress(totalProgress), animated: true)
}, error: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.controllerNode.updateProgress(totalProgress: 0.0, isDone: false, animated: true)
strongSelf.controllerNode.updateState(state: .error, animated: true)
}, completed: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.controllerNode.updateProgress(totalProgress: 1.0, isDone: true, animated: true)
strongSelf.controllerNode.updateState(state: .done, animated: true)
if let application = UIApplication.value(forKeyPath: #keyPath(UIApplication.shared)) as? UIApplication {
application.isIdleTimerDisabled = false

View File

@ -2144,15 +2144,18 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
if case .broadcast = channel.info {
canClear = false
deleteTitle = strongSelf.presentationData.strings.Channel_LeaveChannel
if channel.flags.contains(.isCreator) {
canRemoveGlobally = true
}
} else {
deleteTitle = strongSelf.presentationData.strings.Group_DeleteGroup
if channel.flags.contains(.isCreator) {
canRemoveGlobally = true
}
}
if let addressName = channel.addressName, !addressName.isEmpty {
canClear = false
}
if channel.flags.contains(.isCreator) {
canRemoveGlobally = true
}
} else if let group = chatPeer as? TelegramGroup {
if case .creator = group.role {
canRemoveGlobally = true
@ -2184,12 +2187,27 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
})
}))
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForAllMembers, color: .destructive, action: { [weak actionSheet] in
let deleteForAllText: String
if let channel = mainPeer as? TelegramChannel, case .broadcast = channel.info {
deleteForAllText = strongSelf.presentationData.strings.ChatList_DeleteForAllSubscribers
} else {
deleteForAllText = strongSelf.presentationData.strings.ChatList_DeleteForAllMembers
}
items.append(ActionSheetButtonItem(title: deleteForAllText, color: .destructive, action: { [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.ChannelInfo_DeleteGroupConfirmation, actions: [
let deleteForAllConfirmation: String
if let channel = mainPeer as? TelegramChannel, case .broadcast = channel.info {
deleteForAllConfirmation = strongSelf.presentationData.strings.ChannelInfo_DeleteChannelConfirmation
} else {
deleteForAllConfirmation = strongSelf.presentationData.strings.ChannelInfo_DeleteGroupConfirmation
}
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: deleteForAllConfirmation, actions: [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
}),
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: {
@ -2268,14 +2286,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
actionSheet?.dismissAnimated()
}))
} else {
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in
beginClear(.forEveryone)
actionSheet?.dismissAnimated()
}))
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in
beginClear(.forLocalPeer)
actionSheet?.dismissAnimated()
}))
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in
beginClear(.forEveryone)
actionSheet?.dismissAnimated()
}))
}
actionSheet.setItemGroups([
@ -2328,12 +2346,27 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
})
}))
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForAllMembers, color: .destructive, action: { [weak actionSheet] in
let deleteForAllText: String
if let channel = mainPeer as? TelegramChannel, case .broadcast = channel.info {
deleteForAllText = strongSelf.presentationData.strings.ChatList_DeleteForAllSubscribers
} else {
deleteForAllText = strongSelf.presentationData.strings.ChatList_DeleteForAllMembers
}
items.append(ActionSheetButtonItem(title: deleteForAllText, color: .destructive, action: { [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_DeleteForAllMembersConfirmationText, actions: [
let deleteForAllConfirmation: String
if let channel = mainPeer as? TelegramChannel, case .broadcast = channel.info {
deleteForAllConfirmation = strongSelf.presentationData.strings.ChatList_DeleteForAllSubscribersConfirmationText
} else {
deleteForAllConfirmation = strongSelf.presentationData.strings.ChatList_DeleteForAllMembersConfirmationText
}
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: deleteForAllConfirmation, actions: [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
}),
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: {
@ -2421,6 +2454,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
completion(true)
}))
} else {
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
self?.schedulePeerChatRemoval(peer: peer, type: .forLocalPeer, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
removed()
})
completion(true)
}))
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 {
@ -2438,13 +2478,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
})
], parseMarkdown: true), in: .window(.root))
}))
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
self?.schedulePeerChatRemoval(peer: peer, type: .forLocalPeer, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
removed()
})
completion(true)
}))
}
actionSheet.setItemGroups([
ActionSheetItemGroup(items: items),

Binary file not shown.

View File

@ -415,12 +415,31 @@ public class ShareRootControllerImpl {
beginShare()
return
}
guard let _ = archive["_chat.txt"] else {
let mainFileNames: [NSRegularExpression] = [
try! NSRegularExpression(pattern: "_chat\\.txt"),
try! NSRegularExpression(pattern: "KakaoTalkChats\\.txt"),
try! NSRegularExpression(pattern: "Talk_.*?\\.txt"),
]
var maybeMainFileName: String?
mainFileLoop: for entry in archive {
let entryFileName = entry.path(using: .utf8).replacingOccurrences(of: "/", with: "_").replacingOccurrences(of: "..", with: "_")
let fullRange = NSRange(entryFileName.startIndex ..< entryFileName.endIndex, in: entryFileName)
for expression in mainFileNames {
if expression.firstMatch(in: entryFileName, options: [], range: fullRange) != nil {
maybeMainFileName = entryFileName
break mainFileLoop
}
}
}
guard let mainFileName = maybeMainFileName else {
beginShare()
return
}
let photoRegex = try! NSRegularExpression(pattern: "[\\d]+-PHOTO-.*?\\.jpg")
let photoRegex = try! NSRegularExpression(pattern: ".*?\\.jpg")
let videoRegex = try! NSRegularExpression(pattern: "[\\d]+-VIDEO-.*?\\.mp4")
let stickerRegex = try! NSRegularExpression(pattern: "[\\d]+-STICKER-.*?\\.webp")
let voiceRegex = try! NSRegularExpression(pattern: "[\\d]+-AUDIO-.*?\\.opus")
@ -435,7 +454,7 @@ public class ShareRootControllerImpl {
continue
}
let tempFile = TempBox.shared.tempFile(fileName: entryPath)
if entryPath == "_chat.txt" {
if entryPath == mainFileName {
let _ = try archive.extract(entry, to: URL(fileURLWithPath: tempFile.path))
mainFile = tempFile
} else {

View File

@ -1,5 +1,5 @@
{
"app": "7.3.1",
"app": "7.4",
"bazel": "3.7.0",
"xcode": "12.3"
}