diff --git a/Images.xcassets/Item List/AddItemIcon.imageset/Contents.json b/Images.xcassets/Item List/AddItemIcon.imageset/Contents.json index ffe65a7ec0..2e2e9b52c0 100644 --- a/Images.xcassets/Item List/AddItemIcon.imageset/Contents.json +++ b/Images.xcassets/Item List/AddItemIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ic_addoption@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ic_addoption@3x.png", - "scale" : "3x" + "filename" : "add.pdf" } ], "info" : { diff --git a/Images.xcassets/Item List/AddItemIcon.imageset/add.pdf b/Images.xcassets/Item List/AddItemIcon.imageset/add.pdf new file mode 100644 index 0000000000..931be96420 Binary files /dev/null and b/Images.xcassets/Item List/AddItemIcon.imageset/add.pdf differ diff --git a/Images.xcassets/Item List/AddItemIcon.imageset/ic_addoption@2x.png b/Images.xcassets/Item List/AddItemIcon.imageset/ic_addoption@2x.png deleted file mode 100644 index 4a17bc48eb..0000000000 Binary files a/Images.xcassets/Item List/AddItemIcon.imageset/ic_addoption@2x.png and /dev/null differ diff --git a/Images.xcassets/Item List/AddItemIcon.imageset/ic_addoption@3x.png b/Images.xcassets/Item List/AddItemIcon.imageset/ic_addoption@3x.png deleted file mode 100644 index 0c2b3becdf..0000000000 Binary files a/Images.xcassets/Item List/AddItemIcon.imageset/ic_addoption@3x.png and /dev/null differ diff --git a/Images.xcassets/Item List/RemoveItemIcon.imageset/Contents.json b/Images.xcassets/Item List/RemoveItemIcon.imageset/Contents.json index 7a4287b94c..8edcfb86ed 100644 --- a/Images.xcassets/Item List/RemoveItemIcon.imageset/Contents.json +++ b/Images.xcassets/Item List/RemoveItemIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ic_deleteotion@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ic_deleteotion@3x.png", - "scale" : "3x" + "filename" : "delete.pdf" } ], "info" : { diff --git a/Images.xcassets/Item List/RemoveItemIcon.imageset/delete.pdf b/Images.xcassets/Item List/RemoveItemIcon.imageset/delete.pdf new file mode 100644 index 0000000000..b73c6ec402 Binary files /dev/null and b/Images.xcassets/Item List/RemoveItemIcon.imageset/delete.pdf differ diff --git a/Images.xcassets/Item List/RemoveItemIcon.imageset/ic_deleteotion@2x.png b/Images.xcassets/Item List/RemoveItemIcon.imageset/ic_deleteotion@2x.png deleted file mode 100644 index a52d7ebf3c..0000000000 Binary files a/Images.xcassets/Item List/RemoveItemIcon.imageset/ic_deleteotion@2x.png and /dev/null differ diff --git a/Images.xcassets/Item List/RemoveItemIcon.imageset/ic_deleteotion@3x.png b/Images.xcassets/Item List/RemoveItemIcon.imageset/ic_deleteotion@3x.png deleted file mode 100644 index a4c5861aae..0000000000 Binary files a/Images.xcassets/Item List/RemoveItemIcon.imageset/ic_deleteotion@3x.png and /dev/null differ diff --git a/TelegramUI/CallRatingController.swift b/TelegramUI/CallRatingController.swift index 06dda015dd..82e3e8384a 100644 --- a/TelegramUI/CallRatingController.swift +++ b/TelegramUI/CallRatingController.swift @@ -336,7 +336,7 @@ private func rateCallAndSendLogs(account: Account, callId: CallId, starsCount: I let id = arc4random64() let name = "\(callId.id)_\(callId.accessHash).log" let path = callLogsPath(account: account) + "/" + name - let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)]) + let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)]) let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) return rateSignal diff --git a/TelegramUI/ChannelMemberCategoryListContext.swift b/TelegramUI/ChannelMemberCategoryListContext.swift index 2b1aba3508..b78c3cc773 100644 --- a/TelegramUI/ChannelMemberCategoryListContext.swift +++ b/TelegramUI/ChannelMemberCategoryListContext.swift @@ -4,7 +4,7 @@ import Postbox import SwiftSignalKit private let initialBatchSize: Int32 = 64 -private let emptyTimeout: Double = 2.0 * 60.0 +private let defaultEmptyTimeout: Double = 2.0 * 60.0 private let headUpdateTimeout: Double = 30.0 private let requestBatchSize: Int32 = 64 @@ -83,7 +83,7 @@ private final class ChannelMemberSingleCategoryListContext: ChannelMemberCategor var listStateValue: ChannelMemberListState { didSet { self.listStatePromise.set(.single(self.listStateValue)) - if case .admins = self.category, case .ready = self.listStateValue.loadingState { + if case .admins(nil) = self.category, case .ready = self.listStateValue.loadingState { let ids: Set = Set(self.listStateValue.list.map { $0.peer.id }) let previousIds: Set = Set(oldValue.list.map { $0.peer.id }) if ids != previousIds { @@ -288,8 +288,8 @@ private final class ChannelMemberSingleCategoryListContext: ChannelMemberCategor } } switch self.category { - case .admins: - if let updated = updated, let _ = updated.participant.adminInfo { + case let .admins(query): + if let updated = updated, let _ = updated.participant.adminInfo, (query == nil || updated.peer.indexName.matchesByTokens(query!)) { var found = false loop: for i in 0 ..< list.count { if list[i].peer.id == updated.peer.id { @@ -512,14 +512,16 @@ struct PeerChannelMemberCategoryControl { private final class PeerChannelMemberContextWithSubscribers { let context: ChannelMemberCategoryListContext + private let emptyTimeout: Double private let subscribers = Bag<(ChannelMemberListState) -> Void>() private let disposable = MetaDisposable() private let becameEmpty: () -> Void private var emptyTimer: SwiftSignalKit.Timer? - init(context: ChannelMemberCategoryListContext, becameEmpty: @escaping () -> Void) { + init(context: ChannelMemberCategoryListContext, emptyTimeout: Double, becameEmpty: @escaping () -> Void) { self.context = context + self.emptyTimeout = emptyTimeout self.becameEmpty = becameEmpty self.disposable.set((context.listState |> deliverOnMainQueue).start(next: { [weak self] value in @@ -539,7 +541,7 @@ private final class PeerChannelMemberContextWithSubscribers { private func resetAndBeginEmptyTimer() { self.context.reset(false) self.emptyTimer?.invalidate() - let emptyTimer = SwiftSignalKit.Timer(timeout: emptyTimeout, repeat: false, completion: { [weak self] in + let emptyTimer = SwiftSignalKit.Timer(timeout: self.emptyTimeout, repeat: false, completion: { [weak self] in if let strongSelf = self { if strongSelf.subscribers.isEmpty { strongSelf.becameEmpty() @@ -605,6 +607,13 @@ final class PeerChannelMemberCategoriesContext { return (current.subscribe(requestUpdate: requestUpdate, updated: updated), PeerChannelMemberCategoryControl(key: key)) } let context: ChannelMemberCategoryListContext + let emptyTimeout: Double + switch key { + case .admins(nil), .banned(nil), .recentSearch(nil), .restricted(nil), .restrictedAndBanned(nil): + emptyTimeout = defaultEmptyTimeout + default: + emptyTimeout = 0.0 + } switch key { case .recent, .recentSearch, .admins: let mappedCategory: ChannelMemberListCategory @@ -626,7 +635,7 @@ final class PeerChannelMemberCategoriesContext { case let .banned(query): context = ChannelMemberSingleCategoryListContext(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, peerId: self.peerId, category: .banned(query)) } - let contextWithSubscribers = PeerChannelMemberContextWithSubscribers(context: context, becameEmpty: { [weak self] in + let contextWithSubscribers = PeerChannelMemberContextWithSubscribers(context: context, emptyTimeout: emptyTimeout, becameEmpty: { [weak self] in assert(Queue.mainQueue().isCurrent()) if let strongSelf = self { strongSelf.contexts.removeValue(forKey: key) diff --git a/TelegramUI/ChannelMembersSearchContainerNode.swift b/TelegramUI/ChannelMembersSearchContainerNode.swift index de0da68883..5c40857d05 100644 --- a/TelegramUI/ChannelMembersSearchContainerNode.swift +++ b/TelegramUI/ChannelMembersSearchContainerNode.swift @@ -260,6 +260,19 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod } if peerId.namespace == Namespaces.Peer.CloudChannel { + if case .searchAdmins = mode { + return account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.updateMemberAdminRights(account: account, peerId: peerId, memberId: memberId, adminRights: TelegramChatAdminRights(flags: [])) + |> afterDisposed { + Queue.mainQueue().async { + updateState { state in + var state = state + state.removingParticipantIds.remove(memberId) + return state + } + } + } + } + return account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(account: account, peerId: peerId, memberId: memberId, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max)) |> afterDisposed { Queue.mainQueue().async { @@ -272,6 +285,21 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod } } + if case .searchAdmins = mode { + return removeGroupAdmin(account: account, peerId: peerId, adminId: memberId) + |> `catch` { _ -> Signal in + return .complete() + } + |> deliverOnMainQueue + |> afterDisposed { + updateState { state in + var state = state + state.removingParticipantIds.remove(memberId) + return state + } + } + } + return removePeerMember(account: account, peerId: peerId, memberId: memberId) |> deliverOnMainQueue |> afterDisposed { @@ -320,11 +348,10 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod let (disposable, _) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.admins(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, searchQuery: query, updated: { state in if case .ready = state.loadingState { subscriber.putNext(state.list) - subscriber.putCompletion() } }) return disposable - } |> runOn(Queue.mainQueue()) + } |> runOn(Queue.mainQueue()) foundMembers = .single([]) case .searchBanned: foundGroupMembers = Signal { subscriber in @@ -473,6 +500,10 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod peerActions.append(ParticipantRevealAction(type: .warning, title: themeAndStrings.1.GroupInfo_ActionRestrict, action: .restrict)) peerActions.append(ParticipantRevealAction(type: .destructive, title: themeAndStrings.1.Common_Delete, action: .remove)) } + } else if case .searchAdmins = mode { + if canRestrict { + peerActions.append(ParticipantRevealAction(type: .destructive, title: themeAndStrings.1.Common_Delete, action: .remove)) + } } switch mode { diff --git a/TelegramUI/ChatContextResultPeekContentNode.swift b/TelegramUI/ChatContextResultPeekContentNode.swift index 1b59cdb7cd..e02d49c0a7 100644 --- a/TelegramUI/ChatContextResultPeekContentNode.swift +++ b/TelegramUI/ChatContextResultPeekContentNode.swift @@ -158,7 +158,7 @@ private final class ChatContextResultPeekNode: ASDisplayNode, PeekControllerCont imageDimensions = content?.dimensions if let content = content, type == "gif", let thumbnailResource = imageResource , let dimensions = content.dimensions { - videoFileReference = .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])])) + videoFileReference = .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])])) imageResource = nil } case let .internalReference(_, _, _, title, _, image, file, _): diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index bd83bcb6aa..a3f11be218 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -207,6 +207,8 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal private weak var silentPostTooltipController: TooltipController? private weak var mediaRecordingModeTooltipController: TooltipController? + private weak var mediaRestrictedTooltipController: TooltipController? + private var mediaRestrictedTooltipControllerMode = true private var screenCaptureEventsDisposable: Disposable? private let chatAdditionalDataDisposable = MetaDisposable() @@ -2602,6 +2604,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal strongSelf.recordingModeFeedback?.error() let rect: CGRect? + let isStickers: Bool = subject == .stickers switch subject { case .stickers: rect = strongSelf.chatDisplayNode.frameForStickersButton() @@ -2609,14 +2612,16 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal rect = strongSelf.chatDisplayNode.frameForInputActionButton() } - if let tooltipController = strongSelf.mediaRecordingModeTooltipController { + if let tooltipController = strongSelf.mediaRestrictedTooltipController, strongSelf.mediaRestrictedTooltipControllerMode == isStickers { tooltipController.text = banDescription } else if let rect = rect { + strongSelf.mediaRestrictedTooltipController?.dismiss() let tooltipController = TooltipController(text: banDescription) - strongSelf.mediaRecordingModeTooltipController = tooltipController + strongSelf.mediaRestrictedTooltipController = tooltipController + strongSelf.mediaRestrictedTooltipControllerMode = isStickers tooltipController.dismissed = { [weak tooltipController] in - if let strongSelf = self, let tooltipController = tooltipController, strongSelf.mediaRecordingModeTooltipController === tooltipController { - strongSelf.mediaRecordingModeTooltipController = nil + if let strongSelf = self, let tooltipController = tooltipController, strongSelf.mediaRestrictedTooltipController === tooltipController { + strongSelf.mediaRestrictedTooltipController = nil } } strongSelf.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: { @@ -3904,7 +3909,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal for item in results { if let item = item { let fileId = arc4random64() - let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: fileId), partialReference: nil, resource: ICloudFileResource(urlData: item.urlData), previewRepresentations: [], mimeType: guessMimeTypeByFileExtension((item.fileName as NSString).pathExtension), size: item.fileSize, attributes: [.FileName(fileName: item.fileName)]) + let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: fileId), partialReference: nil, resource: ICloudFileResource(urlData: item.urlData), previewRepresentations: [], immediateThumbnailData: nil, mimeType: guessMimeTypeByFileExtension((item.fileName as NSString).pathExtension), size: item.fileSize, attributes: [.FileName(fileName: item.fileName)]) let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: replyMessageId, localGroupingKey: nil) messages.append(message) } @@ -4294,7 +4299,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal fileAttributes.append(.Sticker(displayText: "", packReference: nil, maskData: nil)) fileAttributes.append(.ImageSize(size: size)) - let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: [], mimeType: "image/webp", size: data.count, attributes: fileAttributes) + let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: "image/webp", size: data.count, attributes: fileAttributes) let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil) let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId @@ -4463,7 +4468,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } }) - strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], mimeType: "audio/ogg", size: data.compressedData.count, attributes: [.Audio(isVoice: true, duration: Int(data.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)]) + strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: data.compressedData.count, attributes: [.Audio(isVoice: true, duration: Int(data.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)]) strongSelf.recorderFeedback?.tap() strongSelf.recorderFeedback = nil @@ -4534,7 +4539,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } }) - self.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: recordedMediaPreview.resource, previewRepresentations: [], mimeType: "audio/ogg", size: Int(recordedMediaPreview.fileSize), attributes: [.Audio(isVoice: true, duration: Int(recordedMediaPreview.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: self.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)]) + self.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: recordedMediaPreview.resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int(recordedMediaPreview.fileSize), attributes: [.Audio(isVoice: true, duration: Int(recordedMediaPreview.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: self.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)]) } } diff --git a/TelegramUI/ChatMessageInteractiveFileNode.swift b/TelegramUI/ChatMessageInteractiveFileNode.swift index e88f7f9231..360b2aae8e 100644 --- a/TelegramUI/ChatMessageInteractiveFileNode.swift +++ b/TelegramUI/ChatMessageInteractiveFileNode.swift @@ -195,10 +195,10 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { statusUpdated = true } - let hasThumbnail = !file.previewRepresentations.isEmpty && !file.isMusic && !file.isVoice + let hasThumbnail = (!file.previewRepresentations.isEmpty || file.immediateThumbnailData != nil) && !file.isMusic && !file.isVoice if mediaUpdated { - if let _ = largestImageRepresentation(file.previewRepresentations) { + if largestImageRepresentation(file.previewRepresentations) != nil || file.immediateThumbnailData != nil { updateImageSignal = chatMessageImageFile(account: account, fileReference: .message(message: MessageReference(message), media: file), thumbnail: true) } diff --git a/TelegramUI/DebugController.swift b/TelegramUI/DebugController.swift index a5bc8e8dc4..61f4a69e4e 100644 --- a/TelegramUI/DebugController.swift +++ b/TelegramUI/DebugController.swift @@ -118,7 +118,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { let messages = logs.map { (name, path) -> EnqueueMessage in let id = arc4random64() - let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)]) + let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)]) return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) } let _ = enqueueMessages(account: arguments.account, peerId: peerId, messages: messages).start() @@ -140,7 +140,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { let messages = updatedLogs.map { (name, path) -> EnqueueMessage in let id = arc4random64() - let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)]) + let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)]) return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) } let _ = enqueueMessages(account: arguments.account, peerId: peerId, messages: messages).start() diff --git a/TelegramUI/GroupInfoController.swift b/TelegramUI/GroupInfoController.swift index 5982a87c74..9be05a5a28 100644 --- a/TelegramUI/GroupInfoController.swift +++ b/TelegramUI/GroupInfoController.swift @@ -1760,7 +1760,7 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl canEditGroupInfo = true } } else if let channel = view.peers[view.peerId] as? TelegramChannel { - if channel.hasPermission(.changeInfo) { + if channel.hasPermission(.changeInfo) || !(channel.adminRights?.flags ?? []).isEmpty { canEditGroupInfo = true } } diff --git a/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift b/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift index 6de738c72c..b2dbf4669f 100644 --- a/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift +++ b/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift @@ -209,7 +209,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode } imageDimensions = content?.dimensions if type == "gif", let thumbnailResource = imageResource, let content = content, let dimensions = content.dimensions { - videoFile = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]) + videoFile = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]) imageResource = nil } diff --git a/TelegramUI/LegacyComponentsStickers.swift b/TelegramUI/LegacyComponentsStickers.swift index 07f5f491db..4f360663a3 100644 --- a/TelegramUI/LegacyComponentsStickers.swift +++ b/TelegramUI/LegacyComponentsStickers.swift @@ -21,7 +21,7 @@ func stickerFromLegacyDocument(_ documentAttachment: TGDocumentMediaAttachment) fileReference = data } - return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: documentAttachment.documentId), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(documentAttachment.datacenterId), fileId: documentAttachment.documentId, accessHash: documentAttachment.accessHash, size: Int(documentAttachment.size), fileReference: fileReference, fileName: documentAttachment.fileName()), previewRepresentations: [], mimeType: documentAttachment.mimeType, size: Int(documentAttachment.size), attributes: attributes) + return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: documentAttachment.documentId), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(documentAttachment.datacenterId), fileId: documentAttachment.documentId, accessHash: documentAttachment.accessHash, size: Int(documentAttachment.size), fileReference: fileReference, fileName: documentAttachment.fileName()), previewRepresentations: [], immediateThumbnailData: nil, mimeType: documentAttachment.mimeType, size: Int(documentAttachment.size), attributes: attributes) } } return nil @@ -184,7 +184,7 @@ final class LegacyStickerImageDataSource: TGImageDataSource { attributes.append(.Sticker(displayText: "", packReference: .id(id: stickerPackId, accessHash: stickerPackAccessHash), maskData: nil)) } - return LegacyStickerImageDataTask(account: account, file: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: documentId), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: datacenterId, fileId: documentId, accessHash: accessHash, size: size, fileReference: nil, fileName: fileNameFromFileAttributes(attributes)), previewRepresentations: [], mimeType: "image/webp", size: size, attributes: attributes), small: !highQuality, fitSize: fitSize, completion: { image in + return LegacyStickerImageDataTask(account: account, file: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: documentId), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: datacenterId, fileId: documentId, accessHash: accessHash, size: size, fileReference: nil, fileName: fileNameFromFileAttributes(attributes)), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "image/webp", size: size, attributes: attributes), small: !highQuality, fitSize: fitSize, completion: { image in if let image = image { sharedImageCache.setImage(image, forKey: uri, attributes: nil) completion?(TGDataResource(image: image, decoded: true)) diff --git a/TelegramUI/LegacyInstantVideoController.swift b/TelegramUI/LegacyInstantVideoController.swift index 4d09c834fb..547080c9ae 100644 --- a/TelegramUI/LegacyInstantVideoController.swift +++ b/TelegramUI/LegacyInstantVideoController.swift @@ -158,7 +158,7 @@ func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, } } - let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, mimeType: "video/mp4", size: nil, attributes: [.FileName(fileName: "video.mp4"), .Video(duration: Int(finalDuration), size: finalDimensions, flags: [.instantRoundVideo])]) + let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.FileName(fileName: "video.mp4"), .Video(duration: Int(finalDuration), size: finalDimensions, flags: [.instantRoundVideo])]) let attributes: [MessageAttribute] = [] send(.message(text: "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil)) } diff --git a/TelegramUI/LegacyMediaPickers.swift b/TelegramUI/LegacyMediaPickers.swift index 2565e3a036..6618d50684 100644 --- a/TelegramUI/LegacyMediaPickers.swift +++ b/TelegramUI/LegacyMediaPickers.swift @@ -237,7 +237,7 @@ func legacyEnqueueGifMessage(account: Account, data: Data) -> Signal Signa arc4random_buf(&randomId, 8) let _ = try? heicData.write(to: URL(fileURLWithPath: tempFilePath + ".heic")) let resource = LocalFileReferenceMediaResource(localFilePath: tempFilePath + ".heic", randomId: randomId) - let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], mimeType: "image/heic", size: nil, attributes: [.FileName(fileName: "image.heic")]) + let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: "image/heic", size: nil, attributes: [.FileName(fileName: "image.heic")]) var attributes: [MessageAttribute] = [] if let timer = item.timer, timer > 0 && timer <= 60 { attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil)) @@ -336,13 +336,13 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa var randomId: Int64 = 0 arc4random_buf(&randomId, 8) let resource = LocalFileReferenceMediaResource(localFilePath: path, randomId: randomId) - let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], mimeType: mimeType, size: nil, attributes: [.FileName(fileName: name)]) + let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: mimeType, size: nil, attributes: [.FileName(fileName: name)]) messages.append(.message(text: caption ?? "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId)) case let .asset(asset): var randomId: Int64 = 0 arc4random_buf(&randomId, 8) let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: arc4random64()) - let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], mimeType: mimeType, size: nil, attributes: [.FileName(fileName: name)]) + let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: mimeType, size: nil, attributes: [.FileName(fileName: name)]) messages.append(.message(text: caption ?? "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId)) default: break @@ -424,7 +424,7 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa } } - let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, mimeType: "video/mp4", size: nil, attributes: fileAttributes) + let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes) var attributes: [MessageAttribute] = [] if let timer = item.timer, timer > 0 && timer <= 60 { attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil)) diff --git a/TelegramUI/PhotoResources.swift b/TelegramUI/PhotoResources.swift index 6bdc4754fa..4f89b9f2ce 100644 --- a/TelegramUI/PhotoResources.swift +++ b/TelegramUI/PhotoResources.swift @@ -130,6 +130,7 @@ private func chatMessageFileDatas(account: Account, fileReference: FileMediaRefe let fullSizeResource = fileReference.media.resource let maybeFullSize = account.postbox.mediaBox.resourceData(fullSizeResource, pathExtension: pathExtension) + let decodedThumbnailData = fileReference.media.immediateThumbnailData.flatMap(decodeTinyThumbnail) let signal = maybeFullSize |> take(1) @@ -138,18 +139,26 @@ private func chatMessageFileDatas(account: Account, fileReference: FileMediaRefe return .single((nil, maybeData.path, true)) } else { let fetchedThumbnail: Signal - if let thumbnailResource = thumbnailResource { + if !fetched, let _ = decodedThumbnailData { + fetchedThumbnail = .single(.local) + } else if let thumbnailResource = thumbnailResource { fetchedThumbnail = fetchedMediaResource(postbox: account.postbox, reference: fileReference.resourceReference(thumbnailResource), statsCategory: statsCategoryForFileWithAttributes(fileReference.media.attributes)) } else { fetchedThumbnail = .complete() } let thumbnail: Signal - if let thumbnailResource = thumbnailResource { + if !fetched, let decodedThumbnailData = decodedThumbnailData { + thumbnail = .single(decodedThumbnailData) + } else if let thumbnailResource = thumbnailResource { thumbnail = Signal { subscriber in let fetchedDisposable = fetchedThumbnail.start() let thumbnailDisposable = account.postbox.mediaBox.resourceData(thumbnailResource, pathExtension: pathExtension).start(next: { next in - subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])) + if next.size != 0, let data = try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []) { + subscriber.putNext(data) + } else { + subscriber.putNext(nil) + } }, error: subscriber.putError, completed: subscriber.putCompletion) return ActionDisposable { @@ -186,14 +195,21 @@ private let thumbnailGenerationMimeTypes: Set = Set([ private func chatMessageImageFileThumbnailDatas(account: Account, fileReference: FileMediaReference, pathExtension: String? = nil, progressive: Bool = false) -> Signal<(Data?, String?, Bool), NoError> { let thumbnailResource = smallestImageRepresentation(fileReference.media.previewRepresentations)?.resource + let decodedThumbnailData = fileReference.media.immediateThumbnailData.flatMap(decodeTinyThumbnail) if !thumbnailGenerationMimeTypes.contains(fileReference.media.mimeType) { - if let thumbnailResource = thumbnailResource { + if let decodedThumbnailData = decodedThumbnailData { + return .single((decodedThumbnailData, nil, false)) + } else if let thumbnailResource = thumbnailResource { let fetchedThumbnail: Signal = fetchedMediaResource(postbox: account.postbox, reference: fileReference.resourceReference(thumbnailResource)) return Signal { subscriber in let fetchedDisposable = fetchedThumbnail.start() let thumbnailDisposable = account.postbox.mediaBox.resourceData(thumbnailResource, pathExtension: pathExtension).start(next: { next in - subscriber.putNext(((next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])), nil, false)) + if next.size != 0, let data = try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []) { + subscriber.putNext((data, nil, false)) + } else { + subscriber.putNext((nil, nil, false)) + } }, error: subscriber.putError, completed: subscriber.putCompletion) return ActionDisposable { @@ -218,18 +234,26 @@ private func chatMessageImageFileThumbnailDatas(account: Account, fileReference: return .single((nil, maybeData.path, true)) } else { let fetchedThumbnail: Signal - if let thumbnailResource = thumbnailResource { + if let _ = fileReference.media.immediateThumbnailData { + fetchedThumbnail = .complete() + } else if let thumbnailResource = thumbnailResource { fetchedThumbnail = fetchedMediaResource(postbox: account.postbox, reference: fileReference.resourceReference(thumbnailResource)) } else { fetchedThumbnail = .complete() } let thumbnail: Signal - if let thumbnailResource = thumbnailResource { + if let decodedThumbnailData = decodedThumbnailData { + thumbnail = .single(decodedThumbnailData) + } else if let thumbnailResource = thumbnailResource { thumbnail = Signal { subscriber in let fetchedDisposable = fetchedThumbnail.start() let thumbnailDisposable = account.postbox.mediaBox.resourceData(thumbnailResource, pathExtension: pathExtension).start(next: { next in - subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])) + if next.size != 0, let data = try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []) { + subscriber.putNext(data) + } else { + subscriber.putNext(nil) + } }, error: subscriber.putError, completed: subscriber.putCompletion) return ActionDisposable { diff --git a/TelegramUI/WebSearchGalleryController.swift b/TelegramUI/WebSearchGalleryController.swift index 5e6a399cae..1097beb3ae 100644 --- a/TelegramUI/WebSearchGalleryController.swift +++ b/TelegramUI/WebSearchGalleryController.swift @@ -32,7 +32,7 @@ struct WebSearchGalleryEntry: Equatable { switch self.result { case let .externalReference(_, _, type, _, _, _, content, thumbnail, _): if let content = content, type == "gif", let thumbnailResource = thumbnail?.resource, let dimensions = content.dimensions { - let fileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])])) + let fileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])])) return WebSearchVideoGalleryItem(account: account, presentationData: presentationData, result: self.result, content: NativeVideoContent(id: .contextResult(self.result.queryId, self.result.id), fileReference: fileReference, streamVideo: false, loopVideo: true, enableSound: false, fetchAutomatically: true), controllerInteraction: controllerInteraction) } case let .internalReference(_, _, _, _, _, _, file, _):