mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-04-08 14:17:54 +00:00
Fixes
fix localeWithStrings globally (#30)
Fix badge on zoomed devices. closes #9
Hide channel bottom panel closes #27
Another attempt to fix badge on some Zoomed devices
Force System Share sheet tg://sg/debug
fixes for device badge
New Crowdin updates (#34)
* New translations sglocalizable.strings (Chinese Traditional)
* New translations sglocalizable.strings (Chinese Simplified)
* New translations sglocalizable.strings (Chinese Traditional)
Fix input panel hidden on selection (#31)
* added if check for selectionState != nil
* same order of subnodes
Revert "Fix input panel hidden on selection (#31)"
This reverts commit e8a8bb1496.
Fix input panel for channels Closes #37
Quickly share links with system's share menu
force tabbar when editing
increase height for correct animation
New translations sglocalizable.strings (Ukrainian) (#38)
Hide Post Story button
Fix 10.15.1
Fix archive option for long-tap
Enable in-app Safari
Disable some unsupported purchases
disableDeleteChatSwipeOption + refactor restart alert
Hide bot in suggestions list
Fix merge v11.0
Fix exceptions for safari webview controller
New Crowdin updates (#47)
* New translations sglocalizable.strings (Romanian)
* New translations sglocalizable.strings (French)
* New translations sglocalizable.strings (Spanish)
* New translations sglocalizable.strings (Afrikaans)
* New translations sglocalizable.strings (Arabic)
* New translations sglocalizable.strings (Catalan)
* New translations sglocalizable.strings (Czech)
* New translations sglocalizable.strings (Danish)
* New translations sglocalizable.strings (German)
* New translations sglocalizable.strings (Greek)
* New translations sglocalizable.strings (Finnish)
* New translations sglocalizable.strings (Hebrew)
* New translations sglocalizable.strings (Hungarian)
* New translations sglocalizable.strings (Italian)
* New translations sglocalizable.strings (Japanese)
* New translations sglocalizable.strings (Korean)
* New translations sglocalizable.strings (Dutch)
* New translations sglocalizable.strings (Norwegian)
* New translations sglocalizable.strings (Polish)
* New translations sglocalizable.strings (Portuguese)
* New translations sglocalizable.strings (Serbian (Cyrillic))
* New translations sglocalizable.strings (Swedish)
* New translations sglocalizable.strings (Turkish)
* New translations sglocalizable.strings (Vietnamese)
* New translations sglocalizable.strings (Indonesian)
* New translations sglocalizable.strings (Hindi)
* New translations sglocalizable.strings (Uzbek)
New Crowdin updates (#49)
* New translations sglocalizable.strings (Arabic)
* New translations sglocalizable.strings (Arabic)
New translations sglocalizable.strings (Russian) (#51)
Call confirmation
WIP Settings search
Settings Search
Localize placeholder
Update AccountUtils.swift
mark mutual contact
Align back context action to left
New Crowdin updates (#54)
* New translations sglocalizable.strings (Chinese Simplified)
* New translations sglocalizable.strings (Chinese Traditional)
* New translations sglocalizable.strings (Ukrainian)
Independent Playground app for simulator
New translations sglocalizable.strings (Ukrainian) (#55)
Playground UIKit base and controllers
Inject SwiftUI view with overflow to AsyncDisplayKit
Launch Playgound project on simulator
Create .swiftformat
Move Playground to example
Update .swiftformat
Init SwiftUIViewController
wip
New translations sglocalizable.strings (Chinese Traditional) (#57)
Xcode 16 fixes
Fix
New translations sglocalizable.strings (Italian) (#59)
New translations sglocalizable.strings (Chinese Simplified) (#63)
Force disable CallKit integration due to missing NSE Entitlement
Fix merge
Fix whole chat translator
Sweetpad config
Bump version
11.3.1 fixes
Mutual contact placement fix
Disable Video PIP swipe
Update versions.json
Fix PIP crash
495 lines
32 KiB
Swift
495 lines
32 KiB
Swift
import Foundation
|
|
import SGSimpleSettings
|
|
import Display
|
|
import AsyncDisplayKit
|
|
import Postbox
|
|
import TelegramCore
|
|
import SwiftSignalKit
|
|
import PassKit
|
|
import Lottie
|
|
import TelegramUIPreferences
|
|
import TelegramPresentationData
|
|
import AccountContext
|
|
import GalleryUI
|
|
import InstantPageUI
|
|
import LocationUI
|
|
import StickerPackPreviewUI
|
|
import PeerAvatarGalleryUI
|
|
import PeerInfoUI
|
|
import SettingsUI
|
|
import AlertUI
|
|
import PresentationDataUtils
|
|
import ShareController
|
|
import UndoUI
|
|
import WebsiteType
|
|
import GalleryData
|
|
import StoryContainerScreen
|
|
import WallpaperGalleryScreen
|
|
import BrowserUI
|
|
|
|
func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
|
var story: TelegramMediaStory?
|
|
for media in params.message.media {
|
|
if let media = media as? TelegramMediaStory {
|
|
story = media
|
|
} else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, content.story != nil {
|
|
story = content.story
|
|
}
|
|
}
|
|
|
|
if let story {
|
|
let navigationController = params.navigationController
|
|
let context = params.context
|
|
let storyContent = SingleStoryContentContextImpl(context: params.context, storyId: story.storyId, readGlobally: true)
|
|
let _ = (storyContent.state
|
|
|> take(1)
|
|
|> deliverOnMainQueue).startStandalone(next: { [weak navigationController] _ in
|
|
var transitionIn: StoryContainerScreen.TransitionIn? = nil
|
|
|
|
var selectedTransitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
|
selectedTransitionNode = params.transitionNode(params.message.id, story, true)
|
|
|
|
if let selectedTransitionNode {
|
|
var cornerRadius: CGFloat = 0.0
|
|
if let imageNode = selectedTransitionNode.0 as? TransformImageNode, let currentArguments = imageNode.currentArguments {
|
|
cornerRadius = currentArguments.corners.topLeft.radius
|
|
}
|
|
transitionIn = StoryContainerScreen.TransitionIn(
|
|
sourceView: selectedTransitionNode.0.view,
|
|
sourceRect: selectedTransitionNode.1,
|
|
sourceCornerRadius: cornerRadius,
|
|
sourceIsAvatar: false
|
|
)
|
|
}
|
|
|
|
let hiddenMediaSource = params.context.sharedContext.mediaManager.galleryHiddenMediaManager.addSource(.single(GalleryHiddenMediaId.chat(params.context.account.id, params.message.id, story)))
|
|
|
|
let storyContainerScreen = StoryContainerScreen(
|
|
context: context,
|
|
content: storyContent,
|
|
transitionIn: transitionIn,
|
|
transitionOut: { _, _ in
|
|
var transitionOut: StoryContainerScreen.TransitionOut? = nil
|
|
|
|
var selectedTransitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
|
selectedTransitionNode = params.transitionNode(params.message.id, story, true)
|
|
if let selectedTransitionNode {
|
|
var cornerRadius: CGFloat = 0.0
|
|
if let imageNode = selectedTransitionNode.0 as? TransformImageNode, let currentArguments = imageNode.currentArguments {
|
|
cornerRadius = currentArguments.corners.topLeft.radius
|
|
}
|
|
|
|
transitionOut = StoryContainerScreen.TransitionOut(
|
|
destinationView: selectedTransitionNode.0.view,
|
|
transitionView: StoryContainerScreen.TransitionView(
|
|
makeView: {
|
|
let view = UIView()
|
|
if let transitionView = selectedTransitionNode.2().0 {
|
|
transitionView.layer.anchorPoint = CGPoint()
|
|
view.addSubview(transitionView)
|
|
}
|
|
return view
|
|
},
|
|
updateView: { view, state, transition in
|
|
guard let view = view.subviews.first else {
|
|
return
|
|
}
|
|
if state.progress == 0.0 {
|
|
view.frame = CGRect(origin: CGPoint(), size: state.destinationSize)
|
|
}
|
|
|
|
let toScaleX = state.sourceSize.width / state.destinationSize.width
|
|
let toScaleY = state.sourceSize.height / state.destinationSize.height
|
|
let fromScaleX: CGFloat = 1.0
|
|
let fromScaleY: CGFloat = 1.0
|
|
let scaleX = toScaleX.interpolate(to: fromScaleX, amount: state.progress)
|
|
let scaleY = toScaleY.interpolate(to: fromScaleY, amount: state.progress)
|
|
transition.setTransform(view: view, transform: CATransform3DMakeScale(scaleX, scaleY, 1.0))
|
|
},
|
|
insertCloneTransitionView: { view in
|
|
params.addToTransitionSurface(view)
|
|
}
|
|
),
|
|
destinationRect: selectedTransitionNode.1,
|
|
destinationCornerRadius: cornerRadius,
|
|
destinationIsAvatar: false,
|
|
completed: {
|
|
params.context.sharedContext.mediaManager.galleryHiddenMediaManager.removeSource(hiddenMediaSource)
|
|
}
|
|
)
|
|
} else {
|
|
params.context.sharedContext.mediaManager.galleryHiddenMediaManager.removeSource(hiddenMediaSource)
|
|
}
|
|
|
|
return transitionOut
|
|
}
|
|
)
|
|
navigationController?.pushViewController(storyContainerScreen)
|
|
})
|
|
return true
|
|
}
|
|
|
|
if let mediaData = chatMessageGalleryControllerData(context: params.context, chatLocation: params.chatLocation, chatFilterTag: params.chatFilterTag, chatLocationContextHolder: params.chatLocationContextHolder, message: params.message, mediaIndex: params.mediaIndex, navigationController: params.navigationController, standalone: params.standalone, reverseMessageGalleryOrder: params.reverseMessageGalleryOrder, mode: params.mode, source: params.gallerySource, synchronousLoad: false, actionInteraction: params.actionInteraction) {
|
|
switch mediaData {
|
|
case let .url(url):
|
|
params.openUrl(url)
|
|
return true
|
|
case let .pass(file):
|
|
let _ = (params.context.account.postbox.mediaBox.resourceData(file.resource, option: .complete(waitUntilFetchStatus: true))
|
|
|> take(1)
|
|
|> deliverOnMainQueue).startStandalone(next: { data in
|
|
guard let navigationController = params.navigationController else {
|
|
return
|
|
}
|
|
if data.complete, let content = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
|
|
if let pass = try? PKPass(data: content), let controller = PKAddPassesViewController(pass: pass) {
|
|
if let window = navigationController.view.window {
|
|
controller.popoverPresentationController?.sourceView = window
|
|
controller.popoverPresentationController?.sourceRect = CGRect(origin: CGPoint(x: window.bounds.width / 2.0, y: window.bounds.size.height - 1.0), size: CGSize(width: 1.0, height: 1.0))
|
|
window.rootViewController?.present(controller, animated: true)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
return true
|
|
case let .instantPage(gallery, centralIndex, galleryMedia):
|
|
params.setupTemporaryHiddenMedia(gallery.hiddenMedia |> map { a -> Any? in a }, centralIndex, galleryMedia)
|
|
|
|
params.dismissInput()
|
|
params.present(gallery, InstantPageGalleryControllerPresentationArguments(transitionArguments: { entry in
|
|
var selectedTransitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
|
if entry.index == centralIndex {
|
|
selectedTransitionNode = params.transitionNode(params.message.id, galleryMedia, false)
|
|
}
|
|
if let selectedTransitionNode = selectedTransitionNode {
|
|
return GalleryTransitionArguments(transitionNode: selectedTransitionNode, addToTransitionSurface: params.addToTransitionSurface)
|
|
}
|
|
return nil
|
|
}), .window(.root))
|
|
return true
|
|
case .map:
|
|
params.dismissInput()
|
|
|
|
let controllerParams = LocationViewParams(sendLiveLocation: { location in
|
|
let outMessage: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: location), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
|
|
params.enqueueMessage(outMessage)
|
|
}, stopLiveLocation: { messageId in
|
|
params.context.liveLocationManager?.cancelLiveLocation(peerId: messageId?.peerId ?? params.message.id.peerId)
|
|
}, openUrl: params.openUrl, openPeer: { peer in
|
|
params.openPeer(peer._asPeer(), .info(nil))
|
|
}, showAll: params.modal)
|
|
let controller = LocationViewController(context: params.context, updatedPresentationData: params.updatedPresentationData, subject: EngineMessage(params.message), params: controllerParams)
|
|
controller.navigationPresentation = .modal
|
|
params.navigationController?.pushViewController(controller)
|
|
return true
|
|
case let .stickerPack(reference, previewIconFile):
|
|
let controller = StickerPackScreen(context: params.context, updatedPresentationData: params.updatedPresentationData, mainStickerPack: reference, stickerPacks: [reference], previewIconFile: previewIconFile, parentNavigationController: params.navigationController, sendSticker: params.sendSticker, sendEmoji: params.sendEmoji, actionPerformed: { actions in
|
|
let presentationData = params.context.sharedContext.currentPresentationData.with { $0 }
|
|
|
|
if actions.count > 1, let first = actions.first {
|
|
if case .add = first.2 {
|
|
params.navigationController?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.EmojiPackActionInfo_AddedTitle, text: presentationData.strings.EmojiPackActionInfo_MultipleAddedText(Int32(actions.count)), undo: false, info: first.0, topItem: first.1.first, context: params.context), elevatedLayout: false, animateInAsReplacement: false, action: { _ in
|
|
return true
|
|
}))
|
|
}
|
|
} else if let (info, items, action) = actions.first {
|
|
let isEmoji = info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks
|
|
|
|
var animateInAsReplacement = false
|
|
if let navigationController = params.navigationController {
|
|
for controller in navigationController.overlayControllers {
|
|
if let controller = controller as? UndoOverlayController {
|
|
controller.dismissWithCommitActionAndReplacementAnimation()
|
|
animateInAsReplacement = true
|
|
}
|
|
}
|
|
}
|
|
switch action {
|
|
case .add:
|
|
let controller = UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: isEmoji ? presentationData.strings.EmojiPackActionInfo_AddedTitle : presentationData.strings.StickerPackActionInfo_AddedTitle, text: isEmoji ? presentationData.strings.EmojiPackActionInfo_AddedText(info.title).string : presentationData.strings.StickerPackActionInfo_AddedText(info.title).string, undo: false, info: info, topItem: items.first, context: params.context), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in
|
|
return true
|
|
})
|
|
(params.navigationController?.topViewController as? ViewController)?.present(controller, in: .current)
|
|
case let .remove(positionInList):
|
|
let controller = UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: isEmoji ? presentationData.strings.EmojiPackActionInfo_RemovedTitle : presentationData.strings.StickerPackActionInfo_RemovedTitle, text: isEmoji ? presentationData.strings.EmojiPackActionInfo_RemovedText(info.title).string : presentationData.strings.StickerPackActionInfo_RemovedText(info.title).string, undo: true, info: info, topItem: items.first, context: params.context), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { action in
|
|
if case .undo = action {
|
|
let _ = params.context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).startStandalone()
|
|
}
|
|
return true
|
|
})
|
|
(params.navigationController?.topViewController as? ViewController)?.present(controller, in: .current)
|
|
}
|
|
}
|
|
}, getSourceRect: params.getSourceRect)
|
|
params.dismissInput()
|
|
params.present(controller, nil, .window(.root))
|
|
return true
|
|
case let .document(file, immediateShare):
|
|
params.dismissInput()
|
|
let presentationData = params.context.sharedContext.currentPresentationData.with { $0 }
|
|
if immediateShare {
|
|
let controller = ShareController(context: params.context, subject: .media(.standalone(media: file)), immediateExternalShare: true)
|
|
params.present(controller, nil, .window(.root))
|
|
} else if let rootController = params.navigationController?.view.window?.rootViewController {
|
|
let proceed = {
|
|
let canShare = !params.message.isCopyProtected()
|
|
var useBrowserScreen = false
|
|
if BrowserScreen.supportedDocumentMimeTypes.contains(file.mimeType) {
|
|
useBrowserScreen = true
|
|
} else if let fileName = file.fileName as? NSString, BrowserScreen.supportedDocumentExtensions.contains(fileName.pathExtension.lowercased()) {
|
|
useBrowserScreen = true
|
|
}
|
|
if useBrowserScreen {
|
|
if let navigationController = params.navigationController, let minimizedContainer = navigationController.minimizedContainer {
|
|
for controller in minimizedContainer.controllers {
|
|
if let controller = controller as? BrowserScreen, controller.subject.fileId == file.fileId {
|
|
navigationController.maximizeViewController(controller, animated: true)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
let subject: BrowserScreen.Subject
|
|
if file.mimeType == "application/pdf" {
|
|
subject = .pdfDocument(file: .message(message: MessageReference(params.message), media: file), canShare: canShare)
|
|
} else {
|
|
subject = .document(file: .message(message: MessageReference(params.message), media: file), canShare: canShare)
|
|
}
|
|
let controller = BrowserScreen(context: params.context, subject: subject)
|
|
controller.openDocument = { file, canShare in
|
|
controller.dismiss()
|
|
|
|
presentDocumentPreviewController(rootController: rootController, theme: presentationData.theme, strings: presentationData.strings, postbox: params.context.account.postbox, file: file, canShare: canShare)
|
|
}
|
|
params.navigationController?.pushViewController(controller)
|
|
} else {
|
|
presentDocumentPreviewController(rootController: rootController, theme: presentationData.theme, strings: presentationData.strings, postbox: params.context.account.postbox, file: file, canShare: canShare)
|
|
}
|
|
}
|
|
if file.mimeType.contains("image/svg") {
|
|
let presentationData = params.context.sharedContext.currentPresentationData.with { $0 }
|
|
params.present(textAlertController(context: params.context, title: nil, text: presentationData.strings.OpenFile_PotentiallyDangerousContentAlert, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.OpenFile_Proceed, action: { proceed() })] ), nil, .window(.root))
|
|
} else {
|
|
proceed()
|
|
}
|
|
}
|
|
return true
|
|
case let .audio(file):
|
|
let location: PeerMessagesPlaylistLocation
|
|
let playerType: MediaManagerPlayerType
|
|
var control = SharedMediaPlayerControlAction.playback(.play)
|
|
if case let .timecode(time) = params.mode {
|
|
control = .seek(time)
|
|
}
|
|
if (file.isVoice || file.isInstantVideo) && params.message.tags.contains(.voiceOrInstantVideo) {
|
|
if let playlistLocation = params.playlistLocation {
|
|
location = playlistLocation
|
|
} else if params.standalone {
|
|
location = .recentActions(params.message)
|
|
} else {
|
|
location = .messages(chatLocation: params.chatLocation ?? .peer(id: params.message.id.peerId), tagMask: .voiceOrInstantVideo, at: params.message.id)
|
|
}
|
|
playerType = .voice
|
|
} else if file.isMusic && params.message.tags.contains(.music) {
|
|
if let playlistLocation = params.playlistLocation {
|
|
location = playlistLocation
|
|
} else if params.standalone {
|
|
location = .recentActions(params.message)
|
|
} else {
|
|
location = .messages(chatLocation: params.chatLocation ?? .peer(id: params.message.id.peerId), tagMask: .music, at: params.message.id)
|
|
}
|
|
playerType = .music
|
|
} else {
|
|
if let playlistLocation = params.playlistLocation {
|
|
location = playlistLocation
|
|
} else if params.standalone {
|
|
location = .recentActions(params.message)
|
|
} else {
|
|
location = .singleMessage(params.message.id)
|
|
}
|
|
playerType = (file.isVoice || file.isInstantVideo) ? .voice : .file
|
|
}
|
|
params.context.sharedContext.mediaManager.setPlaylist((params.context.account, PeerMessagesMediaPlaylist(context: params.context, location: location, chatLocationContextHolder: params.chatLocationContextHolder)), type: playerType, control: control)
|
|
return true
|
|
case let .story(storyController):
|
|
params.dismissInput()
|
|
let _ = (storyController
|
|
|> deliverOnMainQueue).startStandalone(next: { storyController in
|
|
params.navigationController?.pushViewController(storyController)
|
|
})
|
|
case let .gallery(gallery):
|
|
params.dismissInput()
|
|
let _ = (gallery
|
|
|> deliverOnMainQueue).startStandalone(next: { gallery in
|
|
gallery.centralItemUpdated = { messageId in
|
|
params.centralItemUpdated?(messageId)
|
|
}
|
|
params.present(gallery, GalleryControllerPresentationArguments(transitionArguments: { messageId, media in
|
|
let selectedTransitionNode = params.transitionNode(messageId, media, false)
|
|
if let selectedTransitionNode = selectedTransitionNode {
|
|
return GalleryTransitionArguments(transitionNode: selectedTransitionNode, addToTransitionSurface: params.addToTransitionSurface)
|
|
}
|
|
return nil
|
|
}), params.message.adAttribute != nil ? .current : .window(.root))
|
|
})
|
|
return true
|
|
case let .secretGallery(gallery):
|
|
params.dismissInput()
|
|
params.present(gallery, GalleryControllerPresentationArguments(transitionArguments: { messageId, media in
|
|
let selectedTransitionNode = params.transitionNode(messageId, media, false)
|
|
if let selectedTransitionNode = selectedTransitionNode {
|
|
return GalleryTransitionArguments(transitionNode: selectedTransitionNode, addToTransitionSurface: params.addToTransitionSurface)
|
|
}
|
|
return nil
|
|
}), .window(.root))
|
|
return true
|
|
case let .other(otherMedia):
|
|
params.dismissInput()
|
|
if let contact = otherMedia as? TelegramMediaContact {
|
|
let paramsSignal: Signal<(EnginePeer?, Bool), NoError>
|
|
if let peerId = contact.peerId {
|
|
paramsSignal = params.context.engine.data.get(
|
|
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
|
|
TelegramEngine.EngineData.Item.Peer.IsContact(id: peerId)
|
|
)
|
|
} else {
|
|
paramsSignal = .single((nil, false))
|
|
}
|
|
|
|
let _ = (paramsSignal
|
|
|> deliverOnMainQueue).startStandalone(next: { peer, isContact in
|
|
let contactData: DeviceContactExtendedData
|
|
if let vCard = contact.vCardData, let vCardData = vCard.data(using: .utf8), let parsed = DeviceContactExtendedData(vcard: vCardData) {
|
|
contactData = parsed
|
|
} else {
|
|
contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contact.firstName, lastName: contact.lastName, phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: contact.phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
|
}
|
|
let controller = deviceContactInfoController(context: ShareControllerAppAccountContext(context: params.context), environment: ShareControllerAppEnvironment(sharedContext: params.context.sharedContext), updatedPresentationData: params.updatedPresentationData, subject: .vcard(peer?._asPeer(), nil, contactData), completed: nil, cancelled: nil)
|
|
params.navigationController?.pushViewController(controller)
|
|
})
|
|
return true
|
|
}
|
|
case let .chatAvatars(controller, media):
|
|
params.dismissInput()
|
|
params.chatAvatarHiddenMedia(controller.hiddenMedia |> map { value -> MessageId? in
|
|
if value != nil {
|
|
return params.message.id
|
|
} else {
|
|
return nil
|
|
}
|
|
}, media)
|
|
|
|
params.present(controller, AvatarGalleryControllerPresentationArguments(transitionArguments: { entry in
|
|
if let selectedTransitionNode = params.transitionNode(params.message.id, media, false) {
|
|
return GalleryTransitionArguments(transitionNode: selectedTransitionNode, addToTransitionSurface: params.addToTransitionSurface)
|
|
}
|
|
return nil
|
|
}), .window(.root))
|
|
case let .theme(media):
|
|
params.dismissInput()
|
|
let path = params.context.account.postbox.mediaBox.completedResourcePath(media.resource)
|
|
var previewTheme: PresentationTheme?
|
|
if let path = path, let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) {
|
|
previewTheme = makePresentationTheme(data: data)
|
|
}
|
|
|
|
guard let theme = previewTheme else {
|
|
return false
|
|
}
|
|
let controller = ThemePreviewController(context: params.context, previewTheme: theme, source: .media(.message(message: MessageReference(params.message), media: media)))
|
|
params.navigationController?.pushViewController(controller)
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func makeInstantPageControllerImpl(context: AccountContext, message: Message, sourcePeerType: MediaAutoDownloadPeerType?) -> ViewController? {
|
|
guard let (webpage, anchor) = instantPageAndAnchor(message: message) else {
|
|
return nil
|
|
}
|
|
let sourceLocation = InstantPageSourceLocation(userLocation: .peer(message.id.peerId), peerType: sourcePeerType ?? .channel)
|
|
return makeInstantPageControllerImpl(context: context, webPage: webpage, anchor: anchor, sourceLocation: sourceLocation)
|
|
}
|
|
|
|
func makeInstantPageControllerImpl(context: AccountContext, webPage: TelegramMediaWebpage, anchor: String?, sourceLocation: InstantPageSourceLocation) -> ViewController {
|
|
return BrowserScreen(context: context, subject: .instantPage(webPage: webPage, anchor: anchor, sourceLocation: sourceLocation, preloadedResources: nil))
|
|
}
|
|
|
|
func openChatWallpaperImpl(context: AccountContext, message: Message, present: @escaping (ViewController, Any?) -> Void) {
|
|
for media in message.media {
|
|
if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
|
|
let _ = (context.sharedContext.resolveUrl(context: context, peerId: nil, url: content.url, skipUrlAuth: true)
|
|
|> deliverOnMainQueue).startStandalone(next: { resolvedUrl in
|
|
if case let .wallpaper(parameter) = resolvedUrl {
|
|
let source: WallpaperListSource
|
|
switch parameter {
|
|
case let .slug(slug, options, colors, intensity, rotation):
|
|
source = .slug(slug, content.file, options, colors, intensity, rotation, message)
|
|
case let .color(color):
|
|
source = .wallpaper(.color(color.argb), nil, [], nil, nil, message)
|
|
case let .gradient(colors, rotation):
|
|
source = .wallpaper(.gradient(TelegramWallpaper.Gradient(id: nil, colors: colors, settings: WallpaperSettings(rotation: rotation))), nil, [], nil, rotation, message)
|
|
}
|
|
|
|
let controller = WallpaperGalleryController(context: context, source: source)
|
|
present(controller, nil)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func openChatTheme(context: AccountContext, message: Message, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
|
for media in message.media {
|
|
if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
|
|
let _ = (context.sharedContext.resolveUrl(context: context, peerId: nil, url: content.url, skipUrlAuth: true)
|
|
|> deliverOnMainQueue).startStandalone(next: { resolvedUrl in
|
|
var file: TelegramMediaFile?
|
|
var settings: TelegramThemeSettings?
|
|
let themeMimeType = "application/x-tgtheme-ios"
|
|
|
|
for attribute in content.attributes {
|
|
if case let .theme(attribute) = attribute {
|
|
if let attributeSettings = attribute.settings {
|
|
settings = attributeSettings
|
|
} else if let filteredFile = attribute.files.filter({ $0.mimeType == themeMimeType }).first {
|
|
file = filteredFile
|
|
}
|
|
}
|
|
}
|
|
|
|
if file == nil && settings == nil, let contentFile = content.file, contentFile.mimeType == themeMimeType {
|
|
file = contentFile
|
|
}
|
|
let displayUnsupportedAlert: () -> Void = {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
present(textAlertController(context: context, title: nil, text: presentationData.strings.Theme_Unsupported, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
|
}
|
|
if case let .theme(slug) = resolvedUrl {
|
|
if let file = file {
|
|
if let path = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) {
|
|
if let theme = makePresentationTheme(data: data) {
|
|
let controller = ThemePreviewController(context: context, previewTheme: theme, source: .slug(slug, file))
|
|
pushController(controller)
|
|
} else {
|
|
displayUnsupportedAlert()
|
|
}
|
|
}
|
|
} else if let settings = settings {
|
|
if let theme = makePresentationTheme(settings: settings, title: content.title) {
|
|
let controller = ThemePreviewController(context: context, previewTheme: theme, source: .themeSettings(slug, settings))
|
|
pushController(controller)
|
|
} else {
|
|
displayUnsupportedAlert()
|
|
}
|
|
} else {
|
|
displayUnsupportedAlert()
|
|
}
|
|
} else {
|
|
displayUnsupportedAlert()
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|