mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge commit 'c05a1dd17c'
This commit is contained in:
commit
99b46bd53a
28 changed files with 790 additions and 187 deletions
|
|
@ -1223,6 +1223,10 @@ private final class NotificationServiceHandler {
|
|||
content.userInfo["peerId"] = "\(peerId.toInt64())"
|
||||
content.userInfo["accountId"] = "\(recordId.int64)"
|
||||
|
||||
if let dataUrl = payloadJson["data_url"] as? String {
|
||||
content.userInfo["data_url"] = dataUrl
|
||||
}
|
||||
|
||||
if let silentString = payloadJson["silent"] as? String {
|
||||
if let silentValue = Int(silentString), silentValue != 0 {
|
||||
content.silent = true
|
||||
|
|
|
|||
|
|
@ -15752,6 +15752,7 @@ Error: %8$@";
|
|||
|
||||
"AuthConfirmation.LoginFail.Title" = "Login Failed";
|
||||
"AuthConfirmation.LoginFail.Text" = "Please try logging in to [%@]() again.";
|
||||
"AuthConfirmation.LoginFail.TextUnknown" = "Please try logging in again.";
|
||||
|
||||
"Notification.GroupCreatorChangePending" = "%2$@ will become the new main admin in 7 days if %1$@ does not return.";
|
||||
"Notification.GroupCreatorChangeApplied" = "%1$@ has transferred ownership of the group to %2$@.";
|
||||
|
|
@ -15909,7 +15910,7 @@ Error: %8$@";
|
|||
"RankInfo.Owner.Title" = "Owner Tag";
|
||||
"RankInfo.Owner.Text" = "This purple tag %1$@ is **%2$@'s** owner tag. **%3$@** is the owner of **%4$@**.";
|
||||
"RankInfo.ChangeInfo" = "Only admins can assign member tags in this group.";
|
||||
"RankInfo.MemberTag" = "Admin Tag";
|
||||
"RankInfo.MemberTag" = "Member Tag";
|
||||
"RankInfo.AdminTag" = "Admin Tag";
|
||||
"RankInfo.OwnerTag" = "Owner Tag";
|
||||
"RankInfo.SetMyTag" = "Set My Tag";
|
||||
|
|
@ -15927,7 +15928,20 @@ Error: %8$@";
|
|||
"BackgroundTasks.UploadingStories_any" = "Uploading %d Stories";
|
||||
"BackgroundTasks.StoryOpenAppToContinue" = "Open app to continue";
|
||||
"BackgroundTasks.StorySubtitle" = "Running...";
|
||||
"BackgroundTasks.StoryFinished" = "Finished";
|
||||
|
||||
"BackgroundTasks.UploadingMedia_1" = "Sending Message";
|
||||
"BackgroundTasks.UploadingMedia_any" = "Sending %d Messages";
|
||||
"BackgroundTasks.MediaSubtitle" = "Running...";
|
||||
"BackgroundTasks.MediaFinished" = "Finished";
|
||||
|
||||
"DisableSharing.Title" = "Disable Sharing";
|
||||
"DisableSharing.Screenshot.Title" = "No Screenshots";
|
||||
"DisableSharing.Screenshot.Text" = "Disable screenshots and screen recordings in this chat.";
|
||||
|
||||
"DisableSharing.Forwarding.Title" = "No Forwarding";
|
||||
"DisableSharing.Forwarding.Text" = "Disable message forwarding to other chats.";
|
||||
|
||||
"DisableSharing.Saving.Title" = "No Saving";
|
||||
"DisableSharing.Saving.Text" = "Disable copying text and saving photos and videos to Photos.";
|
||||
"DisableSharing.Confirm" = "Disable Sharing";
|
||||
|
|
|
|||
|
|
@ -441,7 +441,7 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||
return
|
||||
}
|
||||
var dismissImpl: (() -> Void)?
|
||||
let controller = AuthConfirmationScreen(context: self.context, subject: result, completion: { [weak self] accountContext, accountPeer, authResult in
|
||||
let controller = AuthConfirmationScreen(context: self.context, requestSubject: subject, subject: result, completion: { [weak self] accountContext, accountPeer, authResult in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
|
@ -503,6 +503,19 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||
case .decline:
|
||||
let _ = self.context.engine.messages.declineUrlAuth(url: url).start()
|
||||
self.webView.sendEvent(name: "oauth_result_failed", data: nil)
|
||||
case .failed:
|
||||
guard case let .request(domain, _, _, _, _, _) = result else {
|
||||
return
|
||||
}
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: presentationData.strings.AuthConfirmation_LoginFail_Title, text: presentationData.strings.AuthConfirmation_LoginFail_Text(domain).string, cancel: nil, destructive: false), action: { _ in return true })
|
||||
if let navigationController = self.getNavigationController() {
|
||||
(navigationController.topViewController as? ViewController)?.present(controller, in: .window(.root))
|
||||
}
|
||||
|
||||
let _ = self.context.engine.messages.declineUrlAuth(url: url).start()
|
||||
self.webView.sendEvent(name: "oauth_result_failed", data: nil)
|
||||
|
||||
HapticFeedback().error()
|
||||
}
|
||||
})
|
||||
dismissImpl = { [weak controller] in
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ public class ItemListAvatarAndNameInfoItem: ListViewItem, ItemListItem, ListItem
|
|||
if case .settings = mode {
|
||||
self.selectable = true
|
||||
} else {
|
||||
self.selectable = false
|
||||
self.selectable = action != nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ typedef enum
|
|||
- (bool)trimApplied;
|
||||
|
||||
- (bool)isCropAndRotationEqualWith:(id<TGMediaEditAdjustments>)adjustments;
|
||||
- (bool)isDefaultValuesForGif;
|
||||
|
||||
- (NSDictionary *)dictionary;
|
||||
|
||||
|
|
|
|||
|
|
@ -429,6 +429,11 @@ const NSTimeInterval TGVideoEditMaximumGifDuration = 30.5;
|
|||
return ![self cropAppliedForAvatar:forAvatar] && ![self toolsApplied] && ![self hasPainting] && !_sendAsGif && _preset == TGMediaVideoConversionPresetCompressedDefault;
|
||||
}
|
||||
|
||||
- (bool)isDefaultValuesForGif
|
||||
{
|
||||
return ![self cropAppliedForAvatar:false] && ![self toolsApplied] && ![self hasPainting];
|
||||
}
|
||||
|
||||
- (bool)isCropEqualWith:(id<TGMediaEditAdjustments>)adjusments
|
||||
{
|
||||
return (_CGRectEqualToRectWithEpsilon(self.cropRect, adjusments.cropRect, [self _cropRectEpsilon]));
|
||||
|
|
|
|||
|
|
@ -157,7 +157,19 @@ public func legacyStoryMediaEditor(context: AccountContext, item: TGMediaEditabl
|
|||
})
|
||||
}
|
||||
|
||||
public func legacyMediaEditor(context: AccountContext, peer: Peer, threadTitle: String?, media: AnyMediaReference, mode: LegacyMediaEditorMode, initialCaption: NSAttributedString, snapshots: [UIView], transitionCompletion: (() -> Void)?, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32, Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
public func legacyMediaEditor(
|
||||
context: AccountContext,
|
||||
peer: Peer,
|
||||
threadTitle: String?,
|
||||
media: AnyMediaReference,
|
||||
mode: LegacyMediaEditorMode,
|
||||
initialCaption: NSAttributedString,
|
||||
snapshots: [UIView],
|
||||
transitionCompletion: (() -> Void)?,
|
||||
getCaptionPanelView: @escaping () -> TGCaptionPanelView?,
|
||||
sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32, Bool) -> Void,
|
||||
present: @escaping (ViewController, Any?) -> Void
|
||||
) {
|
||||
let _ = (fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: media)
|
||||
|> deliverOnMainQueue).start(next: { (value, isImage) in
|
||||
guard case let .data(data) = value, data.complete else {
|
||||
|
|
|
|||
|
|
@ -374,7 +374,7 @@ public struct LegacyAssetPickerEnqueueMessage {
|
|||
public var isFile: Bool
|
||||
}
|
||||
|
||||
public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: Account, signals: [Any]) -> Signal<[LegacyAssetPickerEnqueueMessage], Void> {
|
||||
public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: Account, signals: [Any], originalMediaReference: AnyMediaReference? = nil) -> Signal<[LegacyAssetPickerEnqueueMessage], Void> {
|
||||
return Signal { subscriber in
|
||||
let disposable = SSignal.combineSignals(signals).start(next: { anyValues in
|
||||
var messages: [LegacyAssetPickerEnqueueMessage] = []
|
||||
|
|
@ -934,8 +934,16 @@ public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: A
|
|||
fileAttributes.append(.HasLinkedStickers)
|
||||
}
|
||||
|
||||
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], videoCover: videoCover, immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes, alternativeRepresentations: [])
|
||||
|
||||
let media: Media
|
||||
let mediaReference: AnyMediaReference
|
||||
if let adjustments, adjustments.isDefaultValuesForGif(), let originalMediaReference {
|
||||
media = originalMediaReference.media
|
||||
mediaReference = originalMediaReference
|
||||
} else {
|
||||
media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], videoCover: videoCover, immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes, alternativeRepresentations: [])
|
||||
mediaReference = .standalone(media: media)
|
||||
}
|
||||
|
||||
if let timer = item.timer, timer > 0 && (timer <= 60 || timer == viewOnceTimeout) {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
||||
}
|
||||
|
|
@ -987,7 +995,7 @@ public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: A
|
|||
)
|
||||
}
|
||||
} else {
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: item.groupedId, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets), uniqueId: item.uniqueId, isFile: asFile))
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: mediaReference, threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: item.groupedId, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets), uniqueId: item.uniqueId, isFile: asFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,8 +102,8 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
|
|||
if !skipSafari {
|
||||
options.append(OpenInOption(identifier: "safari", application: .safari, action: {
|
||||
var url = url
|
||||
if url.hasPrefix("http://") || url.hasPrefix("https://") {
|
||||
url = url.replacingOccurrences(of: "https://", with: "x-safari-https")
|
||||
if url.hasPrefix("https://") {
|
||||
url = url.replacingOccurrences(of: "https://", with: "x-safari-https://")
|
||||
}
|
||||
return .openUrl(url: url)
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -31,8 +31,9 @@ private final class ChannelAdminControllerArguments {
|
|||
let dismissInput: () -> Void
|
||||
let animateError: () -> Void
|
||||
let toggleIsOptionExpanded: (RightsItem.Sub) -> Void
|
||||
let openPeer: () -> Void
|
||||
|
||||
init(context: AccountContext, updateAdminRights: @escaping (Bool) -> Void, toggleRight: @escaping (RightsItem, TelegramChatAdminRightsFlags, Bool) -> Void, toggleRightWhileDisabled: @escaping (TelegramChatAdminRightsFlags, TelegramChatAdminRightsFlags) -> Void, transferOwnership: @escaping () -> Void, updateRank: @escaping (String, String) -> Void, updateFocusedOnRank: @escaping (Bool) -> Void, dismissAdmin: @escaping () -> Void, dismissInput: @escaping () -> Void, animateError: @escaping () -> Void, toggleIsOptionExpanded: @escaping (RightsItem.Sub) -> Void) {
|
||||
init(context: AccountContext, updateAdminRights: @escaping (Bool) -> Void, toggleRight: @escaping (RightsItem, TelegramChatAdminRightsFlags, Bool) -> Void, toggleRightWhileDisabled: @escaping (TelegramChatAdminRightsFlags, TelegramChatAdminRightsFlags) -> Void, transferOwnership: @escaping () -> Void, updateRank: @escaping (String, String) -> Void, updateFocusedOnRank: @escaping (Bool) -> Void, dismissAdmin: @escaping () -> Void, dismissInput: @escaping () -> Void, animateError: @escaping () -> Void, toggleIsOptionExpanded: @escaping (RightsItem.Sub) -> Void, openPeer: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.updateAdminRights = updateAdminRights
|
||||
self.toggleRight = toggleRight
|
||||
|
|
@ -44,6 +45,7 @@ private final class ChannelAdminControllerArguments {
|
|||
self.dismissInput = dismissInput
|
||||
self.animateError = animateError
|
||||
self.toggleIsOptionExpanded = toggleIsOptionExpanded
|
||||
self.openPeer = openPeer
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -367,6 +369,8 @@ private enum ChannelAdminEntry: ItemListNodeEntry {
|
|||
case let .info(_, _, dateTimeFormat, peer, presence):
|
||||
return ItemListAvatarAndNameInfoItem(itemContext: .accountContext(arguments.context), presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, memberCount: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: self.section, style: .blocks(withTopInset: true, withExtendedBottomInset: false), editingNameUpdated: { _ in
|
||||
}, avatarTapped: {
|
||||
}, action: {
|
||||
arguments.openPeer()
|
||||
})
|
||||
case let .rankTitle(_, text, count, limit):
|
||||
var accessoryText: ItemListSectionHeaderAccessoryText?
|
||||
|
|
@ -1219,6 +1223,16 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
|
|||
|
||||
return state
|
||||
}
|
||||
}, openPeer: {
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: adminId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
guard let peer else {
|
||||
return
|
||||
}
|
||||
if let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: updatedPresentationData, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
pushControllerImpl?(controller)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
|
||||
|
|
@ -1683,7 +1697,17 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
|
|||
})
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let rightNavigationButton: ItemListNavigationButton?
|
||||
if state.focusedOnRank {
|
||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
|
||||
rightButtonActionImpl()
|
||||
})
|
||||
footerItem = nil
|
||||
} else {
|
||||
rightNavigationButton = nil
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelAdminControllerEntries(presentationData: presentationData, state: state, accountPeerId: context.account.peerId, channelPeer: channelPeer, adminPeer: adminPeer, adminPresence: adminPresence, initialParticipant: initialParticipant, invite: invite, canEdit: canEdit), style: .blocks, focusItemTag: nil, ensureVisibleItemTag: nil, emptyStateItem: nil, footerItem: footerItem, animateChanges: true)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import PresentationDataUtils
|
|||
import ItemListAvatarAndNameInfoItem
|
||||
import OldChannelsController
|
||||
|
||||
private let rankMaxLength: Int32 = 16
|
||||
|
||||
private final class ChannelBannedMemberControllerArguments {
|
||||
let context: AccountContext
|
||||
let toggleRight: (TelegramChatBannedRightsFlags, Bool) -> Void
|
||||
|
|
@ -22,14 +24,24 @@ private final class ChannelBannedMemberControllerArguments {
|
|||
let toggleIsOptionExpanded: (TelegramChatBannedRightsFlags) -> Void
|
||||
let openTimeout: () -> Void
|
||||
let delete: () -> Void
|
||||
let openPeer: () -> Void
|
||||
let updateRank: (String, String) -> Void
|
||||
let updateFocusedOnRank: (Bool) -> Void
|
||||
let dismissInput: () -> Void
|
||||
let animateError: () -> Void
|
||||
|
||||
init(context: AccountContext, toggleRight: @escaping (TelegramChatBannedRightsFlags, Bool) -> Void, toggleRightWhileDisabled: @escaping (TelegramChatBannedRightsFlags) -> Void, toggleIsOptionExpanded: @escaping (TelegramChatBannedRightsFlags) -> Void, openTimeout: @escaping () -> Void, delete: @escaping () -> Void) {
|
||||
init(context: AccountContext, toggleRight: @escaping (TelegramChatBannedRightsFlags, Bool) -> Void, toggleRightWhileDisabled: @escaping (TelegramChatBannedRightsFlags) -> Void, toggleIsOptionExpanded: @escaping (TelegramChatBannedRightsFlags) -> Void, openTimeout: @escaping () -> Void, delete: @escaping () -> Void, openPeer: @escaping () -> Void, updateRank: @escaping (String, String) -> Void, updateFocusedOnRank: @escaping (Bool) -> Void, dismissInput: @escaping () -> Void, animateError: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.toggleRight = toggleRight
|
||||
self.toggleRightWhileDisabled = toggleRightWhileDisabled
|
||||
self.toggleIsOptionExpanded = toggleIsOptionExpanded
|
||||
self.openTimeout = openTimeout
|
||||
self.delete = delete
|
||||
self.openPeer = openPeer
|
||||
self.updateRank = updateRank
|
||||
self.updateFocusedOnRank = updateFocusedOnRank
|
||||
self.dismissInput = dismissInput
|
||||
self.animateError = animateError
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -38,6 +50,19 @@ private enum ChannelBannedMemberSection: Int32 {
|
|||
case rights
|
||||
case timeout
|
||||
case delete
|
||||
case rank
|
||||
}
|
||||
|
||||
private enum ChannelBannedMemberEntryTag: ItemListItemTag {
|
||||
case rank
|
||||
|
||||
func isEqual(to other: ItemListItemTag) -> Bool {
|
||||
if let other = other as? ChannelBannedMemberEntryTag, self == other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum ChannelBannedMemberEntryStableId: Hashable {
|
||||
|
|
@ -47,6 +72,10 @@ private enum ChannelBannedMemberEntryStableId: Hashable {
|
|||
case timeout
|
||||
case exceptionInfo
|
||||
case delete
|
||||
case rankTitle
|
||||
case rankPreview
|
||||
case rank
|
||||
case rankInfo
|
||||
}
|
||||
|
||||
private enum ChannelBannedMemberEntry: ItemListNodeEntry {
|
||||
|
|
@ -56,6 +85,10 @@ private enum ChannelBannedMemberEntry: ItemListNodeEntry {
|
|||
case timeout(PresentationTheme, String, String)
|
||||
case exceptionInfo(PresentationTheme, String)
|
||||
case delete(PresentationTheme, String)
|
||||
case rankTitle(PresentationTheme, String, Int32?, Int32)
|
||||
case rankPreview(PresentationTheme, PresentationStrings, EnginePeer, String, Bool)
|
||||
case rank(PresentationTheme, PresentationStrings, String, String, Bool)
|
||||
case rankInfo(PresentationTheme, String, Bool)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
|
|
@ -67,6 +100,8 @@ private enum ChannelBannedMemberEntry: ItemListNodeEntry {
|
|||
return ChannelBannedMemberSection.timeout.rawValue
|
||||
case .delete:
|
||||
return ChannelBannedMemberSection.delete.rawValue
|
||||
case .rankTitle, .rankPreview, .rank, .rankInfo:
|
||||
return ChannelBannedMemberSection.rank.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,6 +119,14 @@ private enum ChannelBannedMemberEntry: ItemListNodeEntry {
|
|||
return .exceptionInfo
|
||||
case .delete:
|
||||
return .delete
|
||||
case .rankTitle:
|
||||
return .rankTitle
|
||||
case .rankPreview:
|
||||
return .rankPreview
|
||||
case .rank:
|
||||
return .rank
|
||||
case .rankInfo:
|
||||
return .rankInfo
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -165,50 +208,109 @@ private enum ChannelBannedMemberEntry: ItemListNodeEntry {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case let .rankTitle(lhsTheme, lhsText, lhsCount, lhsLimit):
|
||||
if case let .rankTitle(rhsTheme, rhsText, rhsCount, rhsLimit) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsCount == rhsCount, lhsLimit == rhsLimit {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .rankPreview(lhsTheme, lhsStrings, lhsPeer, lhsRank, lhsIsOwner):
|
||||
if case let .rankPreview(rhsTheme, rhsStrings, rhsPeer, rhsRank, rhsIsOwner) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsPeer == rhsPeer, lhsRank == rhsRank, lhsIsOwner == rhsIsOwner {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .rank(lhsTheme, lhsStrings, lhsPlaceholder, lhsValue, lhsEnabled):
|
||||
if case let .rank(rhsTheme, rhsStrings, rhsPlaceholder, rhsValue, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsPlaceholder == rhsPlaceholder, lhsValue == rhsValue, lhsEnabled == rhsEnabled {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .rankInfo(lhsTheme, lhsText, lhsTrimBottomInset):
|
||||
if case let .rankInfo(rhsTheme, rhsText, rhsTrimBottomInset) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsTrimBottomInset == rhsTrimBottomInset {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func <(lhs: ChannelBannedMemberEntry, rhs: ChannelBannedMemberEntry) -> Bool {
|
||||
switch lhs {
|
||||
case .info:
|
||||
switch rhs {
|
||||
case .info:
|
||||
switch rhs {
|
||||
case .info:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case .rightsHeader:
|
||||
switch rhs {
|
||||
case .info, .rightsHeader:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case let .rightItem(_, lhsIndex, _, _, _, _, _, _):
|
||||
switch rhs {
|
||||
case .info, .rightsHeader:
|
||||
return false
|
||||
case let .rightItem(_, rhsIndex, _, _, _, _, _, _):
|
||||
return lhsIndex < rhsIndex
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case .timeout:
|
||||
switch rhs {
|
||||
case .delete, .exceptionInfo:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case .exceptionInfo:
|
||||
switch rhs {
|
||||
case .delete:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case .delete:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case .rightsHeader:
|
||||
switch rhs {
|
||||
case .info, .rightsHeader:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case let .rightItem(_, lhsIndex, _, _, _, _, _, _):
|
||||
switch rhs {
|
||||
case .info, .rightsHeader:
|
||||
return false
|
||||
case let .rightItem(_, rhsIndex, _, _, _, _, _, _):
|
||||
return lhsIndex < rhsIndex
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case .timeout:
|
||||
switch rhs {
|
||||
case .info, .rightsHeader, .rightItem, .timeout:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case .exceptionInfo:
|
||||
switch rhs {
|
||||
case .info, .rightsHeader, .rightItem, .timeout, .exceptionInfo:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case .delete:
|
||||
switch rhs {
|
||||
case .info, .rightsHeader, .rightItem, .timeout, .exceptionInfo, .delete:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case .rankTitle:
|
||||
switch rhs {
|
||||
case .info, .rightsHeader, .rightItem, .timeout, .delete, .rankTitle:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case .rankPreview:
|
||||
switch rhs {
|
||||
case .info, .rightsHeader, .rightItem, .timeout, .delete, .rankTitle, .rankPreview:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
|
||||
case .rank:
|
||||
switch rhs {
|
||||
case .info, .rightsHeader, .rightItem, .timeout, .delete, .rankTitle, .rankPreview, .rank:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
|
||||
case .rankInfo:
|
||||
switch rhs {
|
||||
case .info, .rightsHeader, .rightItem, .timeout, .delete, .rankTitle, .rankPreview, .rank, .rankInfo:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -218,6 +320,8 @@ private enum ChannelBannedMemberEntry: ItemListNodeEntry {
|
|||
case let .info(_, _, dateTimeFormat, peer, presence):
|
||||
return ItemListAvatarAndNameInfoItem(itemContext: .accountContext(arguments.context), presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, memberCount: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: self.section, style: .blocks(withTopInset: true, withExtendedBottomInset: false), editingNameUpdated: { _ in
|
||||
}, avatarTapped: {
|
||||
}, action: {
|
||||
arguments.openPeer()
|
||||
})
|
||||
case let .rightsHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
|
|
@ -265,6 +369,31 @@ private enum ChannelBannedMemberEntry: ItemListNodeEntry {
|
|||
return ItemListActionItem(presentationData: presentationData, systemStyle: .glass, title: text, kind: .destructive, alignment: .center, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.delete()
|
||||
})
|
||||
case let .rankTitle(_, text, count, limit):
|
||||
var accessoryText: ItemListSectionHeaderAccessoryText?
|
||||
if let count = count {
|
||||
accessoryText = ItemListSectionHeaderAccessoryText(value: "\(limit - count)", color: count > limit ? .destructive : .generic)
|
||||
}
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: accessoryText, sectionId: self.section)
|
||||
case let .rankPreview(_, _, peer, rank, _):
|
||||
let globalPresentationData = arguments.context.sharedContext.currentPresentationData.with { $0 }
|
||||
return arguments.context.sharedContext.makeChatRankPreviewItem(context: arguments.context, peer: peer, rank: rank, rankRole: .member, theme: presentationData.theme, strings: presentationData.strings, wallpaper: globalPresentationData.chatWallpaper, fontSize: globalPresentationData.chatFontSize, chatBubbleCorners: globalPresentationData.chatBubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, sectionId: self.section)
|
||||
case let .rank(_, _, placeholder, text, enabled):
|
||||
return ItemListSingleLineInputItem(presentationData: presentationData, systemStyle: .glass, title: NSAttributedString(string: "", textColor: .black), text: text, placeholder: placeholder, type: .regular(capitalization: false, autocorrection: true), spacing: 0.0, clearType: enabled ? .always : .none, enabled: enabled, tag: ChannelBannedMemberEntryTag.rank, sectionId: self.section, textUpdated: { updatedText in
|
||||
arguments.updateRank(text, updatedText)
|
||||
}, shouldUpdateText: { text in
|
||||
if text.containsEmoji {
|
||||
arguments.animateError()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, updatedFocus: { focus in
|
||||
arguments.updateFocusedOnRank(focus)
|
||||
}, action: {
|
||||
arguments.dismissInput()
|
||||
})
|
||||
case let .rankInfo(_, text, trimBottomInset):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section, additionalOuterInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: trimBottomInset ? -44.0 : 0.0, right: 0.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -275,6 +404,8 @@ private struct ChannelBannedMemberControllerState: Equatable {
|
|||
var updatedTimeout: Int32?
|
||||
var updating: Bool = false
|
||||
var expandedPermissions = Set<TelegramChatBannedRightsFlags>()
|
||||
var updatedRank: String?
|
||||
var focusedOnRank: Bool
|
||||
}
|
||||
|
||||
func completeRights(_ flags: TelegramChatBannedRightsFlags) -> TelegramChatBannedRightsFlags {
|
||||
|
|
@ -349,7 +480,22 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation
|
|||
index += 1
|
||||
}
|
||||
|
||||
if !editMember {
|
||||
if editMember {
|
||||
let currentRank: String?
|
||||
if let updatedRank = state.updatedRank {
|
||||
currentRank = updatedRank
|
||||
} else if let initialParticipant = initialParticipant {
|
||||
currentRank = initialParticipant.rank
|
||||
} else {
|
||||
currentRank = nil
|
||||
}
|
||||
|
||||
let rankEnabled = !state.updating
|
||||
entries.append(.rankTitle(presentationData.theme, presentationData.strings.Group_EditAdmin_MemberTagTitle.uppercased(), rankEnabled && state.focusedOnRank ? Int32(currentRank?.count ?? 0) : nil, rankMaxLength))
|
||||
entries.append(.rankPreview(presentationData.theme, presentationData.strings, member, currentRank ?? "0️⃣", false))
|
||||
entries.append(.rank(presentationData.theme, presentationData.strings, presentationData.strings.EditRank_Placeholder, currentRank ?? "", rankEnabled))
|
||||
entries.append(.rankInfo(presentationData.theme, presentationData.strings.Group_EditAdmin_MemberTagInfo(member.compactDisplayTitle).string, false))
|
||||
} else {
|
||||
entries.append(.timeout(presentationData.theme, presentationData.strings.GroupPermission_Duration, currentTimeoutString))
|
||||
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo?, _, _) = initialParticipant, let initialBannedBy = initialBannedBy {
|
||||
|
|
@ -411,7 +557,23 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation
|
|||
index += 1
|
||||
}
|
||||
|
||||
if !editMember {
|
||||
if editMember {
|
||||
let currentRank: String?
|
||||
if let updatedRank = state.updatedRank {
|
||||
currentRank = updatedRank
|
||||
} else if let initialParticipant = initialParticipant {
|
||||
currentRank = initialParticipant.rank
|
||||
} else {
|
||||
currentRank = nil
|
||||
}
|
||||
|
||||
let rankEnabled = !state.updating
|
||||
entries.append(.rankTitle(presentationData.theme, presentationData.strings.Group_EditAdmin_MemberTagTitle.uppercased(), rankEnabled && state.focusedOnRank ? Int32(currentRank?.count ?? 0) : nil, rankMaxLength))
|
||||
entries.append(.rankPreview(presentationData.theme, presentationData.strings, member, currentRank ?? "0️⃣", false))
|
||||
entries.append(.rank(presentationData.theme, presentationData.strings, presentationData.strings.EditRank_Placeholder, currentRank ?? "", rankEnabled))
|
||||
entries.append(.rankInfo(presentationData.theme, presentationData.strings.Group_EditAdmin_MemberTagInfo(member.compactDisplayTitle).string, false))
|
||||
|
||||
} else {
|
||||
entries.append(.timeout(presentationData.theme, presentationData.strings.GroupPermission_Duration, currentTimeoutString))
|
||||
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo?, _, _) = initialParticipant, let initialBannedBy = initialBannedBy {
|
||||
|
|
@ -425,7 +587,7 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation
|
|||
}
|
||||
|
||||
public func channelBannedMemberController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, memberId: PeerId, editMember: Bool = false, initialParticipant: ChannelParticipant?, updated: @escaping (TelegramChatBannedRights?) -> Void, upgradedToSupergroup: @escaping (PeerId, @escaping () -> Void) -> Void) -> ViewController {
|
||||
let initialState = ChannelBannedMemberControllerState(referenceTimestamp: Int32(Date().timeIntervalSince1970), updatedFlags: nil, updatedTimeout: nil, updating: false)
|
||||
let initialState = ChannelBannedMemberControllerState(referenceTimestamp: Int32(Date().timeIntervalSince1970), updatedFlags: nil, updatedTimeout: nil, updating: false, updatedRank: nil, focusedOnRank: false)
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
let updateState: ((ChannelBannedMemberControllerState) -> ChannelBannedMemberControllerState) -> Void = { f in
|
||||
|
|
@ -437,9 +599,15 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
|||
let updateRightsDisposable = MetaDisposable()
|
||||
actionsDisposable.add(updateRightsDisposable)
|
||||
|
||||
let updateRankDisposable = MetaDisposable()
|
||||
actionsDisposable.add(updateRankDisposable)
|
||||
|
||||
var dismissImpl: (() -> Void)?
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var dismissInputImpl: (() -> Void)?
|
||||
var errorImpl: (() -> Void)?
|
||||
var scrollToRankImpl: (() -> Void)?
|
||||
|
||||
let peerView = Promise<PeerView>()
|
||||
peerView.set(context.account.viewTracker.peerView(peerId))
|
||||
|
|
@ -471,8 +639,7 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
|||
} else {
|
||||
effectiveRightsFlags = defaultBannedRightsFlags
|
||||
}
|
||||
|
||||
|
||||
|
||||
if rights == .banSendMedia {
|
||||
if value {
|
||||
effectiveRightsFlags.remove(rights)
|
||||
|
|
@ -609,6 +776,38 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
|||
})
|
||||
])])
|
||||
presentControllerImpl?(actionSheet, nil)
|
||||
}, openPeer: {
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: memberId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
guard let peer else {
|
||||
return
|
||||
}
|
||||
if let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: updatedPresentationData, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
pushControllerImpl?(controller)
|
||||
}
|
||||
})
|
||||
}, updateRank: { previousRank, updatedRank in
|
||||
if updatedRank != previousRank {
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.updatedRank = updatedRank
|
||||
return state
|
||||
}
|
||||
}
|
||||
}, updateFocusedOnRank: { focusedOnRank in
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.focusedOnRank = focusedOnRank
|
||||
return state
|
||||
}
|
||||
|
||||
if focusedOnRank {
|
||||
scrollToRankImpl?()
|
||||
}
|
||||
}, dismissInput: {
|
||||
dismissInputImpl?()
|
||||
}, animateError: {
|
||||
errorImpl?()
|
||||
})
|
||||
|
||||
var peerDataItems: [TelegramEngine.EngineData.Item.Peer.Peer] = []
|
||||
|
|
@ -656,6 +855,16 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
|||
return
|
||||
}
|
||||
|
||||
var updateRank: String?
|
||||
updateState { current in
|
||||
updateRank = current.updatedRank?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
return current
|
||||
}
|
||||
if let updateRank = updateRank, updateRank.count > rankMaxLength || updateRank.containsEmoji {
|
||||
errorImpl?()
|
||||
return
|
||||
}
|
||||
|
||||
var resolvedRights: TelegramChatBannedRights?
|
||||
if let initialParticipant = initialParticipant {
|
||||
var updateFlags: TelegramChatBannedRightsFlags?
|
||||
|
|
@ -724,12 +933,27 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
|||
previousRights = banInfo?.rights
|
||||
}
|
||||
|
||||
let updateRankSignal: (PeerId) -> Signal<Void, NoError>
|
||||
if let updateRank {
|
||||
updateRankSignal = { peerId in
|
||||
return context.peerChannelMemberCategoriesContextsManager.updateMemberRank(engine: context.engine, peerId: peerId, memberId: memberId, rank: updateRank)
|
||||
|> `catch` { _ -> Signal<Void, NoError> in
|
||||
return .single(Void())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
updateRankSignal = { _ in return .complete() }
|
||||
}
|
||||
|
||||
if let resolvedRights = resolvedRights, previousRights != resolvedRights {
|
||||
let cleanResolvedRightsFlags = resolvedRights.flags.union(defaultBannedRightsFlags)
|
||||
let cleanResolvedRights = TelegramChatBannedRights(flags: cleanResolvedRightsFlags, untilDate: resolvedRights.untilDate)
|
||||
|
||||
|
||||
if cleanResolvedRights.flags.isEmpty && previousRights == nil {
|
||||
dismissImpl?()
|
||||
updateRankDisposable.set((updateRankSignal(peerId)
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
dismissImpl?()
|
||||
}))
|
||||
} else {
|
||||
let applyRights: () -> Void = {
|
||||
updateState { state in
|
||||
|
|
@ -756,9 +980,15 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
|||
guard let upgradedPeerId = upgradedPeerId else {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
let rankSignal = updateRankSignal(upgradedPeerId)
|
||||
|
||||
return context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: upgradedPeerId, memberId: memberId, bannedRights: cleanResolvedRights)
|
||||
|> mapToSignal { _ -> Signal<PeerId?, NoError> in
|
||||
return .complete()
|
||||
return rankSignal
|
||||
|> mapToSignal { _ -> Signal<PeerId?, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|> then(.single(upgradedPeerId))
|
||||
}
|
||||
|
|
@ -785,36 +1015,42 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
|||
}))
|
||||
} else {
|
||||
updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: memberId, bannedRights: cleanResolvedRights)
|
||||
|> deliverOnMainQueue).start(error: { _ in
|
||||
|
||||
}, completed: {
|
||||
if previousRights == nil {
|
||||
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
presentControllerImpl?(OverlayStatusController(theme: presentationData.theme, type: .genericSuccess(presentationData.strings.GroupPermission_AddSuccess, false)), nil)
|
||||
}
|
||||
updated(cleanResolvedRights.flags.isEmpty ? nil : cleanResolvedRights)
|
||||
dismissImpl?()
|
||||
}))
|
||||
|> deliverOnMainQueue).start(error: { _ in
|
||||
|
||||
}, completed: {
|
||||
if previousRights == nil {
|
||||
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
presentControllerImpl?(OverlayStatusController(theme: presentationData.theme, type: .genericSuccess(presentationData.strings.GroupPermission_AddSuccess, false)), nil)
|
||||
}
|
||||
updated(cleanResolvedRights.flags.isEmpty ? nil : cleanResolvedRights)
|
||||
dismissImpl?()
|
||||
}))
|
||||
|
||||
updateRankDisposable.set(updateRankSignal(peerId).start())
|
||||
}
|
||||
}
|
||||
|
||||
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
var items: [ActionSheetItem] = []
|
||||
items.append(ActionSheetTextItem(title: presentationData.strings.GroupPermission_ApplyAlertText(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string))
|
||||
items.append(ActionSheetButtonItem(title: presentationData.strings.GroupPermission_ApplyAlertAction, color: .accent, font: .default, enabled: true, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
applyRights()
|
||||
}))
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])])
|
||||
presentControllerImpl?(actionSheet, nil)
|
||||
applyRights()
|
||||
// let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
// let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
// var items: [ActionSheetItem] = []
|
||||
// items.append(ActionSheetTextItem(title: presentationData.strings.GroupPermission_ApplyAlertText(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string))
|
||||
// items.append(ActionSheetButtonItem(title: presentationData.strings.GroupPermission_ApplyAlertAction, color: .accent, font: .default, enabled: true, action: { [weak actionSheet] in
|
||||
// actionSheet?.dismissAnimated()
|
||||
// applyRights()
|
||||
// }))
|
||||
// actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||
// ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
// actionSheet?.dismissAnimated()
|
||||
// })
|
||||
// ])])
|
||||
// presentControllerImpl?(actionSheet, nil)
|
||||
}
|
||||
} else {
|
||||
dismissImpl?()
|
||||
updateRankDisposable.set((updateRankSignal(peerId)
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
dismissImpl?()
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -831,11 +1067,21 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
|||
}
|
||||
}
|
||||
|
||||
let footerItem = ChannelParticipantFooterItem(theme: presentationData.theme, title: footerButtonTitle, displayProgress: state.updating, action: {
|
||||
rightButtonActionImpl()
|
||||
})
|
||||
let rightNavigationButton: ItemListNavigationButton?
|
||||
let footerItem: ItemListControllerFooterItem?
|
||||
if state.focusedOnRank {
|
||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
|
||||
rightButtonActionImpl()
|
||||
})
|
||||
footerItem = nil
|
||||
} else {
|
||||
rightNavigationButton = nil
|
||||
footerItem = ChannelParticipantFooterItem(theme: presentationData.theme, title: footerButtonTitle, displayProgress: state.updating, action: {
|
||||
rightButtonActionImpl()
|
||||
})
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelBannedMemberControllerEntries(presentationData: presentationData, state: state, accountPeerId: context.account.peerId, channelPeer: channelPeer, memberPeer: memberPeer, memberPresence: memberPresence, initialParticipant: initialParticipant, initialBannedBy: initialBannedByPeer, editMember: editMember), style: .blocks, emptyStateItem: nil, footerItem: footerItem, animateChanges: true)
|
||||
|
||||
|
|
@ -856,5 +1102,41 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
|||
pushControllerImpl = { [weak controller] c in
|
||||
controller?.push(c)
|
||||
}
|
||||
|
||||
let hapticFeedback = HapticFeedback()
|
||||
errorImpl = { [weak controller] in
|
||||
hapticFeedback.error()
|
||||
controller?.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ItemListSingleLineInputItemNode {
|
||||
itemNode.animateError()
|
||||
}
|
||||
}
|
||||
}
|
||||
scrollToRankImpl = { [weak controller] in
|
||||
controller?.afterLayout({
|
||||
guard let controller = controller else {
|
||||
return
|
||||
}
|
||||
|
||||
var resultItemNode: ListViewItemNode?
|
||||
let _ = controller.frameForItemNode({ itemNode in
|
||||
if let itemNode = itemNode as? ItemListSingleLineInputItemNode {
|
||||
if let tag = itemNode.tag as? ChannelBannedMemberEntryTag, tag == .rank {
|
||||
resultItemNode = itemNode
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
if let resultItemNode = resultItemNode {
|
||||
Queue.mainQueue().after(0.1) {
|
||||
controller.ensureItemNodeVisible(resultItemNode, atTop: true)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
dismissInputImpl = { [weak controller] in
|
||||
controller?.view.endEditing(true)
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
|
|
|||
|
|
@ -685,9 +685,15 @@ public func channelMembersController(context: AccountContext, updatedPresentatio
|
|||
}))
|
||||
}, openParticipant: { participant, isGroup in
|
||||
if isGroup {
|
||||
let controller = channelBannedMemberController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, memberId: participant.peer.id, editMember: true, initialParticipant: participant.participant, updated: { rights in
|
||||
}, upgradedToSupergroup: { _, _ in })
|
||||
pushControllerImpl?(controller)
|
||||
if let _ = participant.participant.adminInfo {
|
||||
let controller = channelAdminController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, adminId: participant.participant.peerId, initialParticipant: participant.participant, updated: { _ in
|
||||
}, upgradedToSupergroup: { _, _ in }, transferedOwnership: { _ in })
|
||||
pushControllerImpl?(controller)
|
||||
} else {
|
||||
let controller = channelBannedMemberController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, memberId: participant.peer.id, editMember: true, initialParticipant: participant.participant, updated: { rights in
|
||||
}, upgradedToSupergroup: { _, _ in })
|
||||
pushControllerImpl?(controller)
|
||||
}
|
||||
} else {
|
||||
if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: participant.peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
pushControllerImpl?(infoController)
|
||||
|
|
|
|||
|
|
@ -5322,6 +5322,22 @@ public extension Api.functions.messages {
|
|||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func checkUrlAuthMatchCode(url: String, matchCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-911967477)
|
||||
serializeString(url, buffer: buffer, boxed: false)
|
||||
serializeString(matchCode, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.checkUrlAuthMatchCode", parameters: [("url", String(describing: url)), ("matchCode", String(describing: matchCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func clearAllDrafts() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
|
|
|
|||
|
|
@ -4329,6 +4329,8 @@ func replayFinalState(
|
|||
if !message.flags.contains(.Incoming), message.forwardInfo == nil {
|
||||
if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(message.id.peerId.namespace), let peer = transaction.getPeer(message.id.peerId), peer.isCopyProtectionEnabled {
|
||||
|
||||
} else if message.id.peerId.namespace == Namespaces.Peer.CloudUser, let cachedUserData = transaction.getPeerCachedData(peerId: message.id.peerId) as? CachedUserData, cachedUserData.flags.contains(.copyProtectionEnabled) || cachedUserData.flags.contains(.myCopyProtectionEnabled) {
|
||||
|
||||
} else {
|
||||
inner: for media in message.media {
|
||||
if let file = media as? TelegramMediaFile {
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ public enum MessageActionUrlAuthResult {
|
|||
|
||||
public static let requestWriteAccess = Flags(rawValue: 1 << 0)
|
||||
public static let requestPhoneNumber = Flags(rawValue: 1 << 1)
|
||||
public static let showMatchCodesFirst = Flags(rawValue: 1 << 2)
|
||||
}
|
||||
|
||||
public struct ClientData : Equatable {
|
||||
|
|
@ -244,25 +245,29 @@ func _internal_requestMessageActionUrlAuth(account: Account, subject: MessageAct
|
|||
return .default
|
||||
}
|
||||
switch result {
|
||||
case .urlAuthResultDefault:
|
||||
return .default
|
||||
case let .urlAuthResultAccepted(urlAuthResultAcceptedData):
|
||||
let url = urlAuthResultAcceptedData.url
|
||||
return .accepted(url: url)
|
||||
case let .urlAuthResultRequest(urlAuthResultRequestData):
|
||||
let (apiFlags, bot, domain) = (urlAuthResultRequestData.flags, urlAuthResultRequestData.bot, urlAuthResultRequestData.domain)
|
||||
var clientData: MessageActionUrlAuthResult.ClientData?
|
||||
if let browser = urlAuthResultRequestData.browser, let platform = urlAuthResultRequestData.platform, let ip = urlAuthResultRequestData.ip, let region = urlAuthResultRequestData.region {
|
||||
clientData = MessageActionUrlAuthResult.ClientData(browser: browser, platform: platform, ip: ip, region: region)
|
||||
}
|
||||
var flags: MessageActionUrlAuthResult.Flags = []
|
||||
if (apiFlags & (1 << 0)) != 0 {
|
||||
flags.insert(.requestWriteAccess)
|
||||
}
|
||||
if (apiFlags & (1 << 1)) != 0 {
|
||||
flags.insert(.requestPhoneNumber)
|
||||
}
|
||||
return .request(domain: domain, bot: TelegramUser(user: bot), clientData: clientData, flags: flags, matchCodes: urlAuthResultRequestData.matchCodes, userIdHint: urlAuthResultRequestData.userIdHint.flatMap { EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value($0)) })
|
||||
case .urlAuthResultDefault:
|
||||
return .default
|
||||
case let .urlAuthResultAccepted(urlAuthResultAcceptedData):
|
||||
let url = urlAuthResultAcceptedData.url
|
||||
return .accepted(url: url)
|
||||
case let .urlAuthResultRequest(urlAuthResultRequestData):
|
||||
let (apiFlags, bot, domain) = (urlAuthResultRequestData.flags, urlAuthResultRequestData.bot, urlAuthResultRequestData.domain)
|
||||
var clientData: MessageActionUrlAuthResult.ClientData?
|
||||
if let browser = urlAuthResultRequestData.browser, let platform = urlAuthResultRequestData.platform, let ip = urlAuthResultRequestData.ip, let region = urlAuthResultRequestData.region {
|
||||
clientData = MessageActionUrlAuthResult.ClientData(browser: browser, platform: platform, ip: ip, region: region)
|
||||
}
|
||||
var flags: MessageActionUrlAuthResult.Flags = []
|
||||
if (apiFlags & (1 << 0)) != 0 {
|
||||
flags.insert(.requestWriteAccess)
|
||||
}
|
||||
if (apiFlags & (1 << 1)) != 0 {
|
||||
flags.insert(.requestPhoneNumber)
|
||||
}
|
||||
|
||||
if (apiFlags & (1 << 5)) != 0 {
|
||||
flags.insert(.showMatchCodesFirst)
|
||||
}
|
||||
return .request(domain: domain, bot: TelegramUser(user: bot), clientData: clientData, flags: flags, matchCodes: urlAuthResultRequestData.matchCodes, userIdHint: urlAuthResultRequestData.userIdHint.flatMap { EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value($0)) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -327,3 +332,13 @@ func _internal_acceptMessageActionUrlAuth(account: Account, subject: MessageActi
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_checkUrlAuthMatchCode(account: Account, url: String, matchCode: String) -> Signal<Bool, NoError> {
|
||||
return account.network.request(Api.functions.messages.checkUrlAuthMatchCode(url: url, matchCode: matchCode))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|> map { result in
|
||||
return result == .boolTrue
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,6 +106,10 @@ public extension TelegramEngine {
|
|||
public func declineUrlAuth(url: String) -> Signal<Never, NoError> {
|
||||
return _internal_declineUrlAuth(account: self.account, url: url)
|
||||
}
|
||||
|
||||
public func checkUrlAuthMatchCode(url: String, matchCode: String) -> Signal<Bool, NoError> {
|
||||
return _internal_checkUrlAuthMatchCode(account: self.account, url: url, matchCode: matchCode)
|
||||
}
|
||||
|
||||
public func searchMessages(location: SearchMessagesLocation, query: String, state: SearchMessagesState?, centerId: MessageId? = nil, limit: Int32 = 100) -> Signal<(SearchMessagesResult, SearchMessagesState), NoError> {
|
||||
return _internal_searchMessages(account: self.account, location: location, query: query, state: state, centerId: centerId, limit: limit)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public struct PresentationResourcesRootController {
|
|||
|
||||
public static func navigationShareIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.navigationShareIcon.rawValue, { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.rootController.navigationBar.accentTextColor)
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.rootController.navigationBar.buttonColor)
|
||||
})
|
||||
// return theme.image(PresentationResourceKey.navigationShareIcon.rawValue, generateShareButtonImage)
|
||||
}
|
||||
|
|
@ -113,7 +113,7 @@ public struct PresentationResourcesRootController {
|
|||
|
||||
public static func navigationMoreCircledIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.navigationMoreCircledIcon.rawValue, { theme in
|
||||
generateTintedImage(image: UIImage(bundleImageName: "Chat List/NavigationMore"), color: theme.rootController.navigationBar.accentTextColor)
|
||||
generateTintedImage(image: UIImage(bundleImageName: "Chat List/NavigationMore"), color: theme.rootController.navigationBar.buttonColor)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -125,7 +125,7 @@ public struct PresentationResourcesRootController {
|
|||
|
||||
public static func navigationAddIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.navigationAddIcon.rawValue, { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddIcon"), color: theme.rootController.navigationBar.accentTextColor)
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddIcon"), color: theme.rootController.navigationBar.buttonColor)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ swift_library(
|
|||
"//submodules/AccountUtils",
|
||||
"//submodules/ActivityIndicator",
|
||||
"//submodules/TelegramUI/Components/PeerInfo/AccountPeerContextItem",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponentResourceContent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -25,22 +25,27 @@ import AccountUtils
|
|||
import GlassBackgroundComponent
|
||||
import AccountPeerContextItem
|
||||
import ActivityIndicator
|
||||
import LottieComponent
|
||||
import LottieComponentResourceContent
|
||||
|
||||
private final class AuthConfirmationSheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let requestSubject: MessageActionUrlSubject
|
||||
let subject: MessageActionUrlAuthResult
|
||||
let completion: (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void
|
||||
let cancel: (Bool) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
requestSubject: MessageActionUrlSubject,
|
||||
subject: MessageActionUrlAuthResult,
|
||||
completion: @escaping (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void,
|
||||
cancel: @escaping (Bool) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.requestSubject = requestSubject
|
||||
self.subject = subject
|
||||
self.completion = completion
|
||||
self.cancel = cancel
|
||||
|
|
@ -55,7 +60,11 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
|
||||
final class State: ComponentState {
|
||||
private let context: AccountContext
|
||||
private let requestSubject: MessageActionUrlSubject
|
||||
private let subject: MessageActionUrlAuthResult
|
||||
private let completion: (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void
|
||||
|
||||
private let disposables = DisposableSet()
|
||||
|
||||
var peer: EnginePeer?
|
||||
var forcedAccount: (AccountContext, EnginePeer)?
|
||||
|
|
@ -68,9 +77,11 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
var matchCodes: [String]?
|
||||
var selectedMatchCode: String?
|
||||
|
||||
init(context: AccountContext, subject: MessageActionUrlAuthResult) {
|
||||
init(context: AccountContext, requestSubject: MessageActionUrlSubject, subject: MessageActionUrlAuthResult, completion: @escaping (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void) {
|
||||
self.context = context
|
||||
self.requestSubject = requestSubject
|
||||
self.subject = subject
|
||||
self.completion = completion
|
||||
|
||||
super.init()
|
||||
|
||||
|
|
@ -82,6 +93,81 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
self.peer = peer
|
||||
self.updated()
|
||||
})
|
||||
|
||||
if case let .request(_, _, _, flags, matchCodes, _) = self.subject, let matchCodes {
|
||||
if flags.contains(.showMatchCodesFirst) {
|
||||
self.displayEmoji = true
|
||||
self.matchCodes = matchCodes.shuffled()
|
||||
} else {
|
||||
for code in matchCodes {
|
||||
var file: TelegramMediaFile?
|
||||
if let item = context.animatedEmojiStickersValue[code] {
|
||||
file = item.first?.file._parse()
|
||||
} else if let item = context.animatedEmojiStickersValue[code.strippedEmoji] {
|
||||
file = item.first?.file._parse()
|
||||
}
|
||||
if let file {
|
||||
self.disposables.add(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if case let .request(_, _, _, _, _, userIdHint) = self.subject, let userIdHint, userIdHint != context.account.peerId {
|
||||
let _ = (activeAccountsAndPeers(context: self.context, includePrimary: true)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] primary, other in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
for (accountContext, peer, _) in other {
|
||||
if peer.id == userIdHint {
|
||||
self.forcedAccount = (accountContext, peer)
|
||||
self.updated()
|
||||
|
||||
accountContext.account.shouldBeServiceTaskMaster.set(.single(.now))
|
||||
let _ = accountContext.engine.messages.requestMessageActionUrlAuth(subject: requestSubject).start()
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.disposables.dispose()
|
||||
|
||||
if !self.inProgress {
|
||||
if let (context, _) = self.forcedAccount {
|
||||
context.account.shouldBeServiceTaskMaster.set(.single(.never))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkMatchCode(_ matchCode: String) {
|
||||
guard case let .url(url, _) = self.requestSubject else {
|
||||
return
|
||||
}
|
||||
self.selectedMatchCode = matchCode
|
||||
self.updated(transition: .easeInOut(duration: 0.2))
|
||||
|
||||
let _ = (self.context.engine.messages.checkUrlAuthMatchCode(url: url, matchCode: matchCode)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if result {
|
||||
self.displayEmoji = false
|
||||
self.updated(transition: .spring(duration: 0.4))
|
||||
} else {
|
||||
let accountContext = self.forcedAccount?.0 ?? self.context
|
||||
guard let accountPeer = self.forcedAccount?.1 ?? self.peer else {
|
||||
return
|
||||
}
|
||||
|
||||
self.completion(accountContext, accountPeer, .failed)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func displayPhoneNumberConfirmation(commit: @escaping (Bool) -> Void) {
|
||||
|
|
@ -132,6 +218,10 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let (context, _) = self.forcedAccount {
|
||||
context.account.shouldBeServiceTaskMaster.set(.single(.never))
|
||||
}
|
||||
|
||||
self.forcedAccount = nil
|
||||
self.updated()
|
||||
}), true))
|
||||
|
|
@ -147,6 +237,12 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let (context, _) = self.forcedAccount {
|
||||
context.account.shouldBeServiceTaskMaster.set(.single(.never))
|
||||
}
|
||||
|
||||
accountContext.account.shouldBeServiceTaskMaster.set(.single(.now))
|
||||
|
||||
self.forcedAccount = (accountContext, peer)
|
||||
self.updated()
|
||||
}), true))
|
||||
|
|
@ -161,7 +257,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State(context: self.context, subject: self.subject)
|
||||
return State(context: self.context, requestSubject: self.requestSubject, subject: self.subject, completion: self.completion)
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
|
|
@ -191,13 +287,10 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
}
|
||||
|
||||
let presentationData = context.component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let _ = strings
|
||||
|
||||
guard case let .request(domain, bot, clientData, flags, matchCodes, userIdHint) = component.subject else {
|
||||
guard case let .request(domain, bot, clientData, flags, matchCodes, _) = component.subject else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
let _ = userIdHint
|
||||
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
|
||||
|
|
@ -241,7 +334,6 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
)
|
||||
context.add(accountButton
|
||||
.position(CGPoint(x: context.availableSize.width - 16.0 - accountButton.size.width / 2.0, y: 16.0 + accountButton.size.height / 2.0))
|
||||
.scale(state.displayEmoji ? 0.0 : 1.0)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -338,12 +430,28 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
))
|
||||
)
|
||||
}
|
||||
items.append(
|
||||
AnyComponentWithIdentity(id: "icon", component: AnyComponent(
|
||||
Text(text: code, font: Font.regular(32.0), color: .black)
|
||||
))
|
||||
)
|
||||
|
||||
var file: TelegramMediaFile?
|
||||
if let item = component.context.animatedEmojiStickersValue[code] {
|
||||
file = item.first?.file._parse()
|
||||
} else if let item = component.context.animatedEmojiStickersValue[code.strippedEmoji] {
|
||||
file = item.first?.file._parse()
|
||||
}
|
||||
if let file {
|
||||
items.append(
|
||||
AnyComponentWithIdentity(id: "animatedIcon", component: AnyComponent(
|
||||
LottieComponent(content: LottieComponent.ResourceContent(context: component.context, file: file, attemptSynchronously: true, providesPlaceholder: true), placeholderColor: theme.list.mediaPlaceholderColor, startingPosition: .begin, size: CGSize(width: 32.0, height: 32.0), loop: true, playOnce: nil)
|
||||
))
|
||||
)
|
||||
} else {
|
||||
items.append(
|
||||
AnyComponentWithIdentity(id: "staticIcon", component: AnyComponent(
|
||||
Text(text: code, font: Font.regular(32.0), color: .black)
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
let subject = component.subject
|
||||
let emoji = emojis[code].update(
|
||||
component: AnyComponent(
|
||||
PlainButtonComponent(
|
||||
|
|
@ -351,9 +459,17 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
ZStack(items)
|
||||
),
|
||||
minSize: emojiSize,
|
||||
action: {
|
||||
complete(code)
|
||||
action: { [weak state] in
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
if case let .request(_, _, _, flags, _, _) = subject, flags.contains(.showMatchCodesFirst) {
|
||||
state.checkMatchCode(code)
|
||||
} else {
|
||||
complete(code)
|
||||
}
|
||||
},
|
||||
isEnabled: state.selectedMatchCode == nil,
|
||||
animateAlpha: false,
|
||||
animateScale: true
|
||||
)
|
||||
|
|
@ -364,6 +480,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
)
|
||||
context.add(emoji
|
||||
.position(CGPoint(x: emojiOriginX + emojiSize.width / 2.0, y: contentHeight + emojiSize.height / 2.0))
|
||||
.opacity(state.selectedMatchCode != nil && state.selectedMatchCode != code ? 0.6 : 1.0)
|
||||
.appear(ComponentTransition.Appear({ _, view, transition in
|
||||
if !transition.animation.isImmediate {
|
||||
transition.animateAlpha(view: view, from: 0.0, to: 1.0, delay: emojiDelay)
|
||||
|
|
@ -631,17 +748,12 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
guard let state else {
|
||||
return
|
||||
}
|
||||
if state.displayEmoji {
|
||||
state.displayEmoji = false
|
||||
state.updated(transition: .spring(duration: 0.4))
|
||||
} else {
|
||||
let accountContext = state.forcedAccount?.0 ?? component.context
|
||||
guard let accountPeer = state.forcedAccount?.1 ?? state.peer else {
|
||||
return
|
||||
}
|
||||
component.completion(accountContext, accountPeer, .decline)
|
||||
component.cancel(true)
|
||||
let accountContext = state.forcedAccount?.0 ?? component.context
|
||||
guard let accountPeer = state.forcedAccount?.1 ?? state.peer else {
|
||||
return
|
||||
}
|
||||
component.completion(accountContext, accountPeer, .decline)
|
||||
component.cancel(true)
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: buttonWidth, height: 52.0),
|
||||
|
|
@ -669,12 +781,12 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
guard let state else {
|
||||
return
|
||||
}
|
||||
if let matchCodes, !matchCodes.isEmpty {
|
||||
if !flags.contains(.showMatchCodesFirst), let matchCodes, !matchCodes.isEmpty {
|
||||
state.displayEmoji = true
|
||||
state.matchCodes = matchCodes.shuffled()
|
||||
state.updated(transition: .spring(duration: 0.4))
|
||||
} else {
|
||||
complete(nil)
|
||||
complete(state.selectedMatchCode)
|
||||
}
|
||||
}
|
||||
),
|
||||
|
|
@ -699,15 +811,18 @@ private final class AuthConfirmationSheetComponent: CombinedComponent {
|
|||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let requestSubject: MessageActionUrlSubject
|
||||
let subject: MessageActionUrlAuthResult
|
||||
let completion: (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
requestSubject: MessageActionUrlSubject,
|
||||
subject: MessageActionUrlAuthResult,
|
||||
completion: @escaping (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.requestSubject = requestSubject
|
||||
self.subject = subject
|
||||
self.completion = completion
|
||||
}
|
||||
|
|
@ -731,6 +846,7 @@ private final class AuthConfirmationSheetComponent: CombinedComponent {
|
|||
component: SheetComponent<EnvironmentType>(
|
||||
content: AnyComponent<EnvironmentType>(AuthConfirmationSheetContent(
|
||||
context: context.component.context,
|
||||
requestSubject: context.component.requestSubject,
|
||||
subject: context.component.subject,
|
||||
completion: context.component.completion,
|
||||
cancel: { animate in
|
||||
|
|
@ -792,18 +908,22 @@ public class AuthConfirmationScreen: ViewControllerComponentContainer {
|
|||
public enum Result {
|
||||
case accept(allowWriteAccess: Bool, sharePhoneNumber: Bool, matchCode: String?)
|
||||
case decline
|
||||
case failed
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
private let requestSubject: MessageActionUrlSubject
|
||||
private let subject: MessageActionUrlAuthResult
|
||||
fileprivate let completion: (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
requestSubject: MessageActionUrlSubject,
|
||||
subject: MessageActionUrlAuthResult,
|
||||
completion: @escaping (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.requestSubject = requestSubject
|
||||
self.subject = subject
|
||||
self.completion = completion
|
||||
|
||||
|
|
@ -811,6 +931,7 @@ public class AuthConfirmationScreen: ViewControllerComponentContainer {
|
|||
context: context,
|
||||
component: AuthConfirmationSheetComponent(
|
||||
context: context,
|
||||
requestSubject: requestSubject,
|
||||
subject: subject,
|
||||
completion: completion
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1054,7 +1054,7 @@ public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode, Chat
|
|||
}
|
||||
|
||||
public func updatePeer(peer: Peer) {
|
||||
if let previousPeer = self.peer, previousPeer.nameColor != peer.nameColor || previousPeer.smallProfileImage != peer.smallProfileImage {
|
||||
if let previousPeer = self.peer, previousPeer.nameColor != peer.nameColor || previousPeer.smallProfileImage != peer.smallProfileImage || previousPeer.displayLetters != peer.displayLetters {
|
||||
self.peer = peer
|
||||
if peer.smallProfileImage != nil {
|
||||
self.avatarNode.setPeerV2(context: self.context, theme: self.presentationData.theme.theme, peer: EnginePeer(peer), authorOfMessage: self.messageReference, overrideImage: nil, emptyColor: .black, synchronousLoad: false, displayDimensions: CGSize(width: avatarHeaderSize(), height: avatarHeaderSize()))
|
||||
|
|
|
|||
|
|
@ -2343,7 +2343,13 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
|||
return result
|
||||
}, to: &text, entities: &entities)
|
||||
} else {
|
||||
|
||||
appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageRankNew(newRank), generateEntities: { index in
|
||||
var result: [MessageTextEntityType] = []
|
||||
if index == 0 {
|
||||
result.append(.Bold)
|
||||
}
|
||||
return result
|
||||
}, to: &text, entities: &entities)
|
||||
}
|
||||
var attributes: [MessageAttribute] = []
|
||||
if !entities.isEmpty {
|
||||
|
|
|
|||
|
|
@ -100,7 +100,6 @@ private final class PeerCopyProtectionInfoSheetContent: CombinedComponent {
|
|||
|
||||
let theme = environment.theme
|
||||
let strings = environment.strings
|
||||
let _ = strings
|
||||
|
||||
let sideInset: CGFloat = 30.0 + environment.safeInsets.left
|
||||
let textSideInset: CGFloat = 30.0 + environment.safeInsets.left
|
||||
|
|
@ -131,7 +130,7 @@ private final class PeerCopyProtectionInfoSheetContent: CombinedComponent {
|
|||
|
||||
let title = title.update(
|
||||
component: BalancedTextComponent(
|
||||
text: .plain(NSAttributedString(string: "Disable Sharing", font: titleFont, textColor: textColor)),
|
||||
text: .plain(NSAttributedString(string: strings.DisableSharing_Title, font: titleFont, textColor: textColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.1
|
||||
|
|
@ -150,9 +149,9 @@ private final class PeerCopyProtectionInfoSheetContent: CombinedComponent {
|
|||
AnyComponentWithIdentity(
|
||||
id: "screenshot",
|
||||
component: AnyComponent(InfoParagraphComponent(
|
||||
title: "No Screenshots",
|
||||
title: strings.DisableSharing_Screenshot_Title,
|
||||
titleColor: textColor,
|
||||
text: "Disable screenshots and screen recordings in this chat.",
|
||||
text: strings.DisableSharing_Screenshot_Text,
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Premium/CopyProtection/NoScreenshot",
|
||||
|
|
@ -164,9 +163,9 @@ private final class PeerCopyProtectionInfoSheetContent: CombinedComponent {
|
|||
AnyComponentWithIdentity(
|
||||
id: "forward",
|
||||
component: AnyComponent(InfoParagraphComponent(
|
||||
title: "No Forwarding",
|
||||
title: strings.DisableSharing_Forwarding_Title,
|
||||
titleColor: textColor,
|
||||
text: "Disable message forwarding to other chats.",
|
||||
text: strings.DisableSharing_Forwarding_Text,
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Premium/CopyProtection/NoForward",
|
||||
|
|
@ -178,9 +177,9 @@ private final class PeerCopyProtectionInfoSheetContent: CombinedComponent {
|
|||
AnyComponentWithIdentity(
|
||||
id: "save",
|
||||
component: AnyComponent(InfoParagraphComponent(
|
||||
title: "No Saving",
|
||||
title: strings.DisableSharing_Saving_Title,
|
||||
titleColor: textColor,
|
||||
text: "Disable copying text and saving photos and videos to Photos.",
|
||||
text: strings.DisableSharing_Saving_Text,
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Premium/CopyProtection/NoDownload",
|
||||
|
|
@ -227,7 +226,7 @@ private final class PeerCopyProtectionInfoSheetContent: CombinedComponent {
|
|||
if component.context.isPremium {
|
||||
buttonContent = AnyComponentWithIdentity(id: "disable", component: AnyComponent(
|
||||
ButtonTextContentComponent(
|
||||
text: "Disable Sharing",
|
||||
text: strings.DisableSharing_Confirm,
|
||||
badge: 0,
|
||||
textColor: theme.list.itemCheckColors.foregroundColor,
|
||||
badgeBackground: theme.list.itemCheckColors.foregroundColor,
|
||||
|
|
|
|||
|
|
@ -232,11 +232,14 @@ final class RankChatPreviewItemNode: ListViewItemNode {
|
|||
|
||||
var items: [ListViewItem] = []
|
||||
for messageItem in item.messageItems.reversed() {
|
||||
let authorPeerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(0))
|
||||
var userPeer = messageItem.peer._asPeer()
|
||||
|
||||
let updatedId = PeerId.Id._internalFromInt64Value(userPeer.id.id._internalGetInt64Value() - 7)
|
||||
let authorPeerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: updatedId)
|
||||
let groupPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(0))
|
||||
let groupPeer = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(0)), accessHash: nil, title: "", username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil)
|
||||
|
||||
var userPeer = messageItem.peer._asPeer()
|
||||
|
||||
if let user = userPeer as? TelegramUser {
|
||||
userPeer = TelegramUser(id: authorPeerId, accessHash: user.accessHash, firstName: user.firstName, lastName: user.lastName, username: "", phone: user.phone, photo: user.photo, botInfo: user.botInfo, restrictionInfo: user.restrictionInfo, flags: user.flags, emojiStatus: user.emojiStatus, usernames: user.usernames, storiesHidden: user.storiesHidden, nameColor: user.nameColor, backgroundEmojiId: user.backgroundEmojiId, profileColor: user.profileColor, profileBackgroundEmojiId: user.profileBackgroundEmojiId, subscriberCount: user.subscriberCount, verificationIconFileId: user.verificationIconFileId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2811,13 +2811,19 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||
let _ = (accountIdFromNotification(response.notification, sharedContext: self.sharedContextPromise.get())
|
||||
|> deliverOnMainQueue).start(next: { accountId in
|
||||
if response.actionIdentifier == UNNotificationDefaultActionIdentifier {
|
||||
if let (peerId, threadId) = peerIdFromNotification(response.notification) {
|
||||
var messageId: MessageId? = nil
|
||||
if response.notification.request.content.categoryIdentifier == "c" || response.notification.request.content.categoryIdentifier == "t" {
|
||||
messageId = messageIdFromNotification(peerId: peerId, notification: response.notification)
|
||||
if let dataUrl = response.notification.request.content.userInfo["data_url"] as? String {
|
||||
if let url = URL(string: dataUrl) {
|
||||
self.openUrlWhenReady(url: url)
|
||||
}
|
||||
} else {
|
||||
if let (peerId, threadId) = peerIdFromNotification(response.notification) {
|
||||
var messageId: MessageId? = nil
|
||||
if response.notification.request.content.categoryIdentifier == "c" || response.notification.request.content.categoryIdentifier == "t" {
|
||||
messageId = messageIdFromNotification(peerId: peerId, notification: response.notification)
|
||||
}
|
||||
let storyId = storyIdFromNotification(peerId: peerId, notification: response.notification)
|
||||
self.openChatWhenReady(accountId: accountId, peerId: peerId, threadId: threadId, messageId: messageId, storyId: storyId)
|
||||
}
|
||||
let storyId = storyIdFromNotification(peerId: peerId, notification: response.notification)
|
||||
self.openChatWhenReady(accountId: accountId, peerId: peerId, threadId: threadId, messageId: messageId, storyId: storyId)
|
||||
}
|
||||
completionHandler()
|
||||
} else if response.actionIdentifier == "reply", let (peerId, threadId) = peerIdFromNotification(response.notification), let accountId = accountId {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,17 @@ extension ChatControllerImpl {
|
|||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.enqueueMediaMessages(fromGallery: false, signals: signals, silentPosting: false, scheduleTime: nil, replyToSubject: nil, parameters: nil, getAnimatedTransitionSource: nil, completion: {})
|
||||
self.enqueueMediaMessages(
|
||||
fromGallery: false,
|
||||
signals: signals,
|
||||
originalMediaReference: file.abstract,
|
||||
silentPosting: false,
|
||||
scheduleTime: nil,
|
||||
replyToSubject: nil,
|
||||
parameters: nil,
|
||||
getAnimatedTransitionSource: nil,
|
||||
completion: {}
|
||||
)
|
||||
},
|
||||
present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
|
|
|
|||
|
|
@ -8452,18 +8452,38 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
})
|
||||
}
|
||||
|
||||
func enqueueMediaMessages(fromGallery: Bool = false, signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil, replyToSubject: ChatInterfaceState.ReplyMessageSubject? = nil, parameters: ChatSendMessageActionSheetController.SendParameters? = nil, getAnimatedTransitionSource: ((String) -> UIView?)? = nil, completion: @escaping () -> Void = {}) {
|
||||
func enqueueMediaMessages(
|
||||
fromGallery: Bool = false,
|
||||
signals: [Any]?,
|
||||
originalMediaReference: AnyMediaReference? = nil,
|
||||
silentPosting: Bool,
|
||||
scheduleTime: Int32? = nil,
|
||||
replyToSubject: ChatInterfaceState.ReplyMessageSubject? = nil,
|
||||
parameters: ChatSendMessageActionSheetController.SendParameters? = nil,
|
||||
getAnimatedTransitionSource: ((String) -> UIView?)? = nil,
|
||||
completion: @escaping () -> Void = {}
|
||||
) {
|
||||
if let _ = self.presentationInterfaceState.sendPaidMessageStars {
|
||||
self.presentPaidMessageAlertIfNeeded(count: Int32(signals?.count ?? 1), forceDark: fromGallery, completion: { [weak self] postpone in
|
||||
self?.commitEnqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, postpone: postpone, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
||||
})
|
||||
} else {
|
||||
self.commitEnqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, replyToSubject: replyToSubject, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
||||
self.commitEnqueueMediaMessages(signals: signals, originalMediaReference: originalMediaReference, silentPosting: silentPosting, scheduleTime: scheduleTime, replyToSubject: replyToSubject, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
private func commitEnqueueMediaMessages(signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil, postpone: Bool = false, replyToSubject: ChatInterfaceState.ReplyMessageSubject? = nil, parameters: ChatSendMessageActionSheetController.SendParameters? = nil, getAnimatedTransitionSource: ((String) -> UIView?)? = nil, completion: @escaping () -> Void = {}) {
|
||||
self.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(context: self.context, account: self.context.account, signals: signals!)
|
||||
private func commitEnqueueMediaMessages(
|
||||
signals: [Any]?,
|
||||
originalMediaReference: AnyMediaReference? = nil,
|
||||
silentPosting: Bool,
|
||||
scheduleTime: Int32? = nil,
|
||||
postpone: Bool = false,
|
||||
replyToSubject: ChatInterfaceState.ReplyMessageSubject? = nil,
|
||||
parameters: ChatSendMessageActionSheetController.SendParameters? = nil,
|
||||
getAnimatedTransitionSource: ((String) -> UIView?)? = nil,
|
||||
completion: @escaping () -> Void = {}
|
||||
) {
|
||||
self.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(context: self.context, account: self.context.account, signals: signals!, originalMediaReference: originalMediaReference)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] items in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1822,19 +1822,12 @@ func openResolvedUrlImpl(
|
|||
|> deliverOnMainQueue).start(next: { result in
|
||||
if case .request = result {
|
||||
var dismissImpl: (() -> Void)?
|
||||
let controller = AuthConfirmationScreen(context: context, subject: result, completion: { accountContext, accountPeer, authResult in
|
||||
let controller = AuthConfirmationScreen(context: context, requestSubject: subject, subject: result, completion: { accountContext, accountPeer, authResult in
|
||||
switch authResult {
|
||||
case let .accept(allowWriteAccess, sharePhoneNumber, matchCode):
|
||||
let signal: Signal<MessageActionUrlAuthResult, MessageActionUrlAuthError>
|
||||
if accountContext === context {
|
||||
signal = accountContext.engine.messages.acceptMessageActionUrlAuth(subject: subject, allowWriteAccess: allowWriteAccess, sharePhoneNumber: sharePhoneNumber, matchCode: matchCode)
|
||||
} else {
|
||||
accountContext.account.shouldBeServiceTaskMaster.set(.single(.now))
|
||||
signal = accountContext.engine.messages.requestMessageActionUrlAuth(subject: subject)
|
||||
|> castError(MessageActionUrlAuthError.self)
|
||||
|> mapToSignal { result in
|
||||
return accountContext.engine.messages.acceptMessageActionUrlAuth(subject: subject, allowWriteAccess: allowWriteAccess, sharePhoneNumber: sharePhoneNumber, matchCode: matchCode)
|
||||
} |> afterDisposed {
|
||||
let signal = accountContext.engine.messages.acceptMessageActionUrlAuth(subject: subject, allowWriteAccess: allowWriteAccess, sharePhoneNumber: sharePhoneNumber, matchCode: matchCode)
|
||||
|> afterDisposed {
|
||||
if accountContext !== context {
|
||||
accountContext.account.shouldBeServiceTaskMaster.set(.single(.never))
|
||||
}
|
||||
}
|
||||
|
|
@ -1851,8 +1844,13 @@ func openResolvedUrlImpl(
|
|||
} else {
|
||||
text = presentationData.strings.AuthConfirmation_LoginSuccess_Text(domain).string
|
||||
}
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: presentationData.strings.AuthConfirmation_LoginSuccess_Title, text: text, cancel: nil, destructive: false), action: { _ in return true })
|
||||
(navigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root))
|
||||
if var topViewController = navigationController?.topViewController as? ViewController {
|
||||
if let tabBarController = topViewController as? TabBarController, let controller = tabBarController.currentController {
|
||||
topViewController = controller
|
||||
}
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: presentationData.strings.AuthConfirmation_LoginSuccess_Title, text: text, cancel: nil, destructive: false), action: { _ in return true })
|
||||
topViewController.present(controller, in: .current)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1882,8 +1880,9 @@ func openResolvedUrlImpl(
|
|||
}
|
||||
}
|
||||
|
||||
let isWebUrl = url.hasPrefix("http:") || url.hasPrefix("https:")
|
||||
let openInOptions = availableOpenInOptions(context: context, item: .url(url: url))
|
||||
if let match = openInOptions.first(where: { $0.identifier == browserIdentifier }), case let .openUrl(openUrl) = match.action() {
|
||||
if isWebUrl, let match = openInOptions.first(where: { $0.identifier == browserIdentifier }), case let .openUrl(openUrl) = match.action() {
|
||||
context.sharedContext.openExternalUrl(context: context, urlContext: .external, url: openUrl, forceExternal: true, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
||||
} else {
|
||||
context.sharedContext.openExternalUrl(context: context, urlContext: .external, url: url, forceExternal: true, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
||||
|
|
@ -1892,21 +1891,52 @@ func openResolvedUrlImpl(
|
|||
}, error: { _ in
|
||||
dismissImpl?()
|
||||
|
||||
if case let .request(domain, _, _, _, _, _) = result {
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: presentationData.strings.AuthConfirmation_LoginFail_Title, text: presentationData.strings.AuthConfirmation_LoginFail_Text(domain).string, timeout: nil, customUndoText: nil), action: { _ in return true })
|
||||
(navigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root))
|
||||
Queue.mainQueue().after(0.3) {
|
||||
if case let .request(domain, _, _, _, _, _) = result {
|
||||
if var topViewController = navigationController?.topViewController as? ViewController {
|
||||
if let tabBarController = topViewController as? TabBarController, let controller = tabBarController.currentController {
|
||||
topViewController = controller
|
||||
}
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: presentationData.strings.AuthConfirmation_LoginFail_Title, text: presentationData.strings.AuthConfirmation_LoginFail_Text(domain).string, timeout: nil, customUndoText: nil), action: { _ in return true })
|
||||
topViewController.present(controller, in: .current)
|
||||
}
|
||||
}
|
||||
|
||||
HapticFeedback().error()
|
||||
}
|
||||
|
||||
HapticFeedback().error()
|
||||
})
|
||||
case .decline:
|
||||
let _ = context.engine.messages.declineUrlAuth(url: url).start()
|
||||
case .failed:
|
||||
dismissImpl?()
|
||||
|
||||
Queue.mainQueue().after(0.3) {
|
||||
if case let .request(domain, _, _, _, _, _) = result {
|
||||
if var topViewController = navigationController?.topViewController as? ViewController {
|
||||
if let tabBarController = topViewController as? TabBarController, let controller = tabBarController.currentController {
|
||||
topViewController = controller
|
||||
}
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: presentationData.strings.AuthConfirmation_LoginFail_Title, text: presentationData.strings.AuthConfirmation_LoginFail_Text(domain).string, timeout: nil, customUndoText: nil), action: { _ in return true })
|
||||
topViewController.present(controller, in: .current)
|
||||
}
|
||||
}
|
||||
|
||||
HapticFeedback().error()
|
||||
}
|
||||
}
|
||||
})
|
||||
navigationController?.pushViewController(controller)
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
} else {
|
||||
if var topViewController = navigationController?.topViewController as? ViewController {
|
||||
if let tabBarController = topViewController as? TabBarController, let controller = tabBarController.currentController {
|
||||
topViewController = controller
|
||||
}
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: presentationData.strings.AuthConfirmation_LoginFail_Title, text: presentationData.strings.AuthConfirmation_LoginFail_TextUnknown, timeout: nil, customUndoText: nil), action: { _ in return true })
|
||||
topViewController.present(controller, in: .current)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -514,7 +514,7 @@ public final class SharedWakeupManager {
|
|||
return
|
||||
}
|
||||
guard let self else {
|
||||
task.updateTitle(task.title, subtitle: "Finished")
|
||||
task.updateTitle(task.title, subtitle: presentationData.strings.BackgroundTasks_MediaFinished)
|
||||
task.setTaskCompleted(success: true)
|
||||
return
|
||||
}
|
||||
|
|
@ -562,7 +562,7 @@ public final class SharedWakeupManager {
|
|||
|
||||
Task { @MainActor [weak self] in
|
||||
guard let self else {
|
||||
task.updateTitle(task.title, subtitle: "Finished")
|
||||
task.updateTitle(task.title, subtitle: presentationData.strings.BackgroundTasks_MediaFinished)
|
||||
task.setTaskCompleted(success: true)
|
||||
return
|
||||
}
|
||||
|
|
@ -576,7 +576,7 @@ public final class SharedWakeupManager {
|
|||
|
||||
if self.backgroundProcessingTaskId != task.identifier || self.pendingMediaUploadsByKey.isEmpty {
|
||||
self.backgroundProcessingTaskProgressByKey = [:]
|
||||
task.updateTitle(task.title, subtitle: "Finished")
|
||||
task.updateTitle(task.title, subtitle: presentationData.strings.BackgroundTasks_MediaFinished)
|
||||
task.setTaskCompleted(success: true)
|
||||
if self.backgroundProcessingTaskId == task.identifier {
|
||||
self.backgroundProcessingTaskId = nil
|
||||
|
|
@ -690,7 +690,7 @@ public final class SharedWakeupManager {
|
|||
return
|
||||
}
|
||||
guard let self else {
|
||||
task.updateTitle(task.title, subtitle: "Finished")
|
||||
task.updateTitle(task.title, subtitle: presentationData.strings.BackgroundTasks_StoryFinished)
|
||||
task.setTaskCompleted(success: true)
|
||||
return
|
||||
}
|
||||
|
|
@ -738,7 +738,7 @@ public final class SharedWakeupManager {
|
|||
|
||||
Task { @MainActor [weak self] in
|
||||
guard let self else {
|
||||
task.updateTitle(task.title, subtitle: "Finished")
|
||||
task.updateTitle(task.title, subtitle: presentationData.strings.BackgroundTasks_StoryFinished)
|
||||
task.setTaskCompleted(success: true)
|
||||
return
|
||||
}
|
||||
|
|
@ -754,7 +754,7 @@ public final class SharedWakeupManager {
|
|||
|
||||
if self.backgroundStoryProcessingTaskId != task.identifier || self.pendingStoryUploadStatusesByKey.isEmpty {
|
||||
self.backgroundStoryProcessingTaskProgressByKey = [:]
|
||||
task.updateTitle(task.title, subtitle: "Finished")
|
||||
task.updateTitle(task.title, subtitle: presentationData.strings.BackgroundTasks_StoryFinished)
|
||||
task.setTaskCompleted(success: true)
|
||||
if self.backgroundStoryProcessingTaskId == task.identifier {
|
||||
self.backgroundStoryProcessingTaskId = nil
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue