mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
0bdfd79289
commit
564ee0d966
@ -46,13 +46,15 @@ public final class ContactMultiselectionControllerParams {
|
|||||||
public let options: [ContactListAdditionalOption]
|
public let options: [ContactListAdditionalOption]
|
||||||
public let filters: [ContactListFilter]
|
public let filters: [ContactListFilter]
|
||||||
public let alwaysEnabled: Bool
|
public let alwaysEnabled: Bool
|
||||||
|
public let limit: Int32?
|
||||||
|
|
||||||
public init(context: AccountContext, mode: ContactMultiselectionControllerMode, options: [ContactListAdditionalOption], filters: [ContactListFilter] = [.excludeSelf], alwaysEnabled: Bool = false) {
|
public init(context: AccountContext, mode: ContactMultiselectionControllerMode, options: [ContactListAdditionalOption], filters: [ContactListFilter] = [.excludeSelf], alwaysEnabled: Bool = false, limit: Int32? = nil) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.options = options
|
self.options = options
|
||||||
self.filters = filters
|
self.filters = filters
|
||||||
self.alwaysEnabled = alwaysEnabled
|
self.alwaysEnabled = alwaysEnabled
|
||||||
|
self.limit = limit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,18 +244,18 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if isUnread {
|
|
||||||
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_MarkAsRead, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MarkAsRead"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
if isUnread {
|
||||||
let _ = togglePeerUnreadMarkInteractively(postbox: context.account.postbox, viewTracker: context.account.viewTracker, peerId: peerId).start()
|
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_MarkAsRead, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MarkAsRead"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||||
f(.default)
|
let _ = togglePeerUnreadMarkInteractively(postbox: context.account.postbox, viewTracker: context.account.viewTracker, peerId: peerId).start()
|
||||||
})))
|
f(.default)
|
||||||
} else {
|
})))
|
||||||
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_MarkAsUnread, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MarkAsUnread"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
} else {
|
||||||
let _ = togglePeerUnreadMarkInteractively(postbox: context.account.postbox, viewTracker: context.account.viewTracker, peerId: peerId).start()
|
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_MarkAsUnread, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MarkAsUnread"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||||
f(.default)
|
let _ = togglePeerUnreadMarkInteractively(postbox: context.account.postbox, viewTracker: context.account.viewTracker, peerId: peerId).start()
|
||||||
})))
|
f(.default)
|
||||||
}
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
let groupAndIndex = transaction.getPeerChatListIndex(peerId)
|
let groupAndIndex = transaction.getPeerChatListIndex(peerId)
|
||||||
@ -288,17 +288,19 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
if case let .chatList(filter) = source {
|
// if {
|
||||||
let location: TogglePeerChatPinnedLocation
|
let location: TogglePeerChatPinnedLocation
|
||||||
if let filter = filter {
|
var chatListFilter: ChatListFilter?
|
||||||
location = .filter(filter.id)
|
if case let .chatList(filter) = source, let chatFilter = filter {
|
||||||
|
chatListFilter = chatFilter
|
||||||
|
location = .filter(chatFilter.id)
|
||||||
} else {
|
} else {
|
||||||
location = .group(group)
|
location = .group(group)
|
||||||
}
|
}
|
||||||
|
|
||||||
let isPinned = getPinnedItemIds(transaction: transaction, location: location).contains(.peer(peerId))
|
let isPinned = getPinnedItemIds(transaction: transaction, location: location).contains(.peer(peerId))
|
||||||
|
|
||||||
if isPinned || filter == nil || peerId.namespace != Namespaces.Peer.SecretChat {
|
if isPinned || chatListFilter == nil || peerId.namespace != Namespaces.Peer.SecretChat {
|
||||||
items.append(.action(ContextMenuActionItem(text: isPinned ? strings.ChatList_Context_Unpin : strings.ChatList_Context_Pin, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin" : "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
items.append(.action(ContextMenuActionItem(text: isPinned ? strings.ChatList_Context_Unpin : strings.ChatList_Context_Pin, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin" : "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||||
let _ = (toggleItemPinned(postbox: context.account.postbox, location: location, itemId: .peer(peerId))
|
let _ = (toggleItemPinned(postbox: context.account.postbox, location: location, itemId: .peer(peerId))
|
||||||
|> deliverOnMainQueue).start(next: { result in
|
|> deliverOnMainQueue).start(next: { result in
|
||||||
@ -312,7 +314,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
|||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
}
|
// }
|
||||||
|
|
||||||
if !isSavedMessages, let notificationSettings = transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings {
|
if !isSavedMessages, let notificationSettings = transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings {
|
||||||
var isMuted = false
|
var isMuted = false
|
||||||
|
@ -593,7 +593,7 @@ private func internalChatListFilterAddChatsController(context: AccountContext, f
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .chatSelection(title: presentationData.strings.ChatListFolder_IncludeChatsTitle, selectedChats: Set(filter.data.includePeers.peers), additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories), chatListFilters: allFilters), options: [], filters: [], alwaysEnabled: true))
|
let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .chatSelection(title: presentationData.strings.ChatListFolder_IncludeChatsTitle, selectedChats: Set(filter.data.includePeers.peers), additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories), chatListFilters: allFilters), options: [], filters: [], alwaysEnabled: true, limit: 100))
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
let _ = (controller.result
|
let _ = (controller.result
|
||||||
|> take(1)
|
|> take(1)
|
||||||
@ -680,7 +680,7 @@ private func internalChatListFilterExcludeChatsController(context: AccountContex
|
|||||||
selectedCategories.insert(AdditionalExcludeCategoryId.archived.rawValue)
|
selectedCategories.insert(AdditionalExcludeCategoryId.archived.rawValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .chatSelection(title: presentationData.strings.ChatListFolder_ExcludeChatsTitle, selectedChats: Set(filter.data.excludePeers), additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories), chatListFilters: allFilters), options: [], filters: [], alwaysEnabled: true))
|
let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .chatSelection(title: presentationData.strings.ChatListFolder_ExcludeChatsTitle, selectedChats: Set(filter.data.excludePeers), additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories), chatListFilters: allFilters), options: [], filters: [], alwaysEnabled: true, limit: 100))
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
let _ = (controller.result
|
let _ = (controller.result
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|
@ -306,16 +306,19 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
return .single([])
|
return .single([])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let accountPeer = self.context.account.postbox.loadedPeerWithId(self.context.account.peerId)
|
||||||
|
|> take(1)
|
||||||
|
|
||||||
self.suggestedFiltersDisposable.set((combineLatest(suggestedPeers, self.suggestedDates.get(), self.selectedFilterKeyPromise.get(), self.searchQuery.get())
|
self.suggestedFiltersDisposable.set((combineLatest(suggestedPeers, self.suggestedDates.get(), self.selectedFilterKeyPromise.get(), self.searchQuery.get(), accountPeer)
|
||||||
|> mapToSignal { peers, dates, selectedFilter, searchQuery -> Signal<([Peer], [(Date?, Date, String?)], ChatListSearchFilterEntryId?), NoError> in
|
|> mapToSignal { peers, dates, selectedFilter, searchQuery, accountPeer -> Signal<([Peer], [(Date?, Date, String?)], ChatListSearchFilterEntryId?, String?, Peer?), NoError> in
|
||||||
if searchQuery?.isEmpty ?? true {
|
if searchQuery?.isEmpty ?? true {
|
||||||
return .single((peers, dates, selectedFilter))
|
return .single((peers, dates, selectedFilter, searchQuery, accountPeer))
|
||||||
} else {
|
} else {
|
||||||
return (.complete() |> delay(0.25, queue: Queue.mainQueue()))
|
return (.complete() |> delay(0.25, queue: Queue.mainQueue()))
|
||||||
|> then(.single((peers, dates, selectedFilter)))
|
|> then(.single((peers, dates, selectedFilter, searchQuery, accountPeer)))
|
||||||
}
|
}
|
||||||
} |> map { peers, dates, selectedFilter -> [ChatListSearchFilter] in
|
} |> map { peers, dates, selectedFilter, searchQuery, accountPeer -> [ChatListSearchFilter] in
|
||||||
var suggestedFilters: [ChatListSearchFilter] = []
|
var suggestedFilters: [ChatListSearchFilter] = []
|
||||||
if !dates.isEmpty {
|
if !dates.isEmpty {
|
||||||
let formatter = DateFormatter()
|
let formatter = DateFormatter()
|
||||||
@ -327,8 +330,18 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
suggestedFilters.append(.date(minDate.flatMap { Int32($0.timeIntervalSince1970) }, Int32(maxDate.timeIntervalSince1970), title))
|
suggestedFilters.append(.date(minDate.flatMap { Int32($0.timeIntervalSince1970) }, Int32(maxDate.timeIntervalSince1970), title))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
if !peers.isEmpty && selectedFilter != .filter(ChatListSearchFilter.chats.id) {
|
if !peers.isEmpty && selectedFilter != .filter(ChatListSearchFilter.chats.id) {
|
||||||
|
var existingPeerIds = Set<PeerId>()
|
||||||
|
var peers = peers
|
||||||
|
if let accountPeer = accountPeer, let lowercasedQuery = searchQuery?.lowercased(), lowercasedQuery.count > 1 && (presentationData.strings.DialogList_SavedMessages.lowercased().hasPrefix(lowercasedQuery) || "saved messages".hasPrefix(lowercasedQuery)) {
|
||||||
|
peers.insert(accountPeer, at: 0)
|
||||||
|
}
|
||||||
|
|
||||||
for peer in peers {
|
for peer in peers {
|
||||||
|
if existingPeerIds.contains(peer.id) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
let isGroup: Bool
|
let isGroup: Bool
|
||||||
if peer.id.namespace == Namespaces.Peer.SecretChat {
|
if peer.id.namespace == Namespaces.Peer.SecretChat {
|
||||||
continue
|
continue
|
||||||
@ -339,8 +352,15 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
} else {
|
} else {
|
||||||
isGroup = false
|
isGroup = false
|
||||||
}
|
}
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
suggestedFilters.append(.peer(peer.id, isGroup, peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), peer.compactDisplayTitle))
|
var title: String = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
|
var compactDisplayTitle = peer.compactDisplayTitle
|
||||||
|
if peer.id == accountPeer?.id {
|
||||||
|
title = presentationData.strings.DialogList_SavedMessages
|
||||||
|
compactDisplayTitle = title
|
||||||
|
}
|
||||||
|
suggestedFilters.append(.peer(peer.id, isGroup, title, compactDisplayTitle))
|
||||||
|
existingPeerIds.insert(peer.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return suggestedFilters
|
return suggestedFilters
|
||||||
|
@ -441,6 +441,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
|||||||
}
|
}
|
||||||
|
|
||||||
if origin == nil {
|
if origin == nil {
|
||||||
|
self.editButton.isHidden = true
|
||||||
self.deleteButton.isHidden = true
|
self.deleteButton.isHidden = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -516,9 +517,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
|||||||
}
|
}
|
||||||
messageText = galleryCaptionStringWithAppliedEntities(message.text, entities: entities)
|
messageText = galleryCaptionStringWithAppliedEntities(message.text, entities: entities)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.editButton.isHidden = message.containsSecretMedia
|
|
||||||
|
|
||||||
if self.currentMessageText != messageText || canDelete != !self.deleteButton.isHidden || canShare != !self.actionButton.isHidden || canEdit != !self.editButton.isHidden || self.currentAuthorNameText != authorNameText || self.currentDateText != dateText {
|
if self.currentMessageText != messageText || canDelete != !self.deleteButton.isHidden || canShare != !self.actionButton.isHidden || canEdit != !self.editButton.isHidden || self.currentAuthorNameText != authorNameText || self.currentDateText != dateText {
|
||||||
self.currentMessageText = messageText
|
self.currentMessageText = messageText
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@ final class InstantPageWebEmbedNode: ASDisplayNode, InstantPageNode {
|
|||||||
configuration.userContentController = userController
|
configuration.userContentController = userController
|
||||||
|
|
||||||
let webView = WKWebView(frame: CGRect(origin: CGPoint(), size: frame.size), configuration: configuration)
|
let webView = WKWebView(frame: CGRect(origin: CGPoint(), size: frame.size), configuration: configuration)
|
||||||
|
webView.allowsBackForwardNavigationGestures = false
|
||||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
||||||
webView.allowsLinkPreview = false
|
webView.allowsLinkPreview = false
|
||||||
}
|
}
|
||||||
|
@ -309,8 +309,13 @@
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool onlyGroupableMedia = true;
|
bool onlyGroupableMedia = true;
|
||||||
for (TGMediaAsset *asset in strongSelf->_selectionContext.selectedItems)
|
for (TGMediaAsset *item in strongSelf->_selectionContext.selectedItems)
|
||||||
{
|
{
|
||||||
|
TGMediaAsset *asset = asset;
|
||||||
|
if ([asset isKindOfClass:[TGCameraCapturedVideo class]]) {
|
||||||
|
asset = [(TGCameraCapturedVideo *)item originalAsset];
|
||||||
|
}
|
||||||
|
|
||||||
if (asset.type == TGMediaAssetGifType)
|
if (asset.type == TGMediaAssetGifType)
|
||||||
{
|
{
|
||||||
onlyGroupableMedia = false;
|
onlyGroupableMedia = false;
|
||||||
|
@ -236,7 +236,12 @@
|
|||||||
|
|
||||||
- (SSignal *)_signalForItem:(id)item
|
- (SSignal *)_signalForItem:(id)item
|
||||||
{
|
{
|
||||||
SSignal *assetSignal = [TGMediaAssetImageSignals imageForAsset:item imageType:TGMediaAssetImageTypeThumbnail size:[_layoutMetrics imageSize]];
|
TGMediaAsset *concreteAsset = (TGMediaAsset *)item;
|
||||||
|
if ([concreteAsset isKindOfClass:[TGCameraCapturedVideo class]]) {
|
||||||
|
concreteAsset = [(TGCameraCapturedVideo *)item originalAsset];
|
||||||
|
}
|
||||||
|
|
||||||
|
SSignal *assetSignal = [TGMediaAssetImageSignals imageForAsset:concreteAsset imageType:TGMediaAssetImageTypeThumbnail size:[_layoutMetrics imageSize]];
|
||||||
if (self.editingContext == nil)
|
if (self.editingContext == nil)
|
||||||
return assetSignal;
|
return assetSignal;
|
||||||
|
|
||||||
|
@ -504,8 +504,13 @@
|
|||||||
if (_attributesDisposable == nil)
|
if (_attributesDisposable == nil)
|
||||||
_attributesDisposable = [[SMetaDisposable alloc] init];
|
_attributesDisposable = [[SMetaDisposable alloc] init];
|
||||||
|
|
||||||
|
TGMediaAsset *asset = item.asset;
|
||||||
|
if ([asset isKindOfClass:[TGCameraCapturedVideo class]]) {
|
||||||
|
asset = [(TGCameraCapturedVideo *)asset originalAsset];
|
||||||
|
}
|
||||||
|
|
||||||
_fileInfoLabel.text = nil;
|
_fileInfoLabel.text = nil;
|
||||||
[_attributesDisposable setDisposable:[[[TGMediaAssetImageSignals fileAttributesForAsset:item.asset] deliverOn:[SQueue mainQueue]] startWithNext:^(TGMediaAssetImageFileAttributes *next)
|
[_attributesDisposable setDisposable:[[[TGMediaAssetImageSignals fileAttributesForAsset:asset] deliverOn:[SQueue mainQueue]] startWithNext:^(TGMediaAssetImageFileAttributes *next)
|
||||||
{
|
{
|
||||||
__strong TGMediaPickerGalleryVideoItemView *strongSelf = weakSelf;
|
__strong TGMediaPickerGalleryVideoItemView *strongSelf = weakSelf;
|
||||||
if (strongSelf == nil)
|
if (strongSelf == nil)
|
||||||
|
@ -4,6 +4,16 @@ import Display
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import LegacyUI
|
import LegacyUI
|
||||||
|
|
||||||
|
private class DocumentPickerViewController: UIDocumentPickerViewController {
|
||||||
|
var didDisappear: (() -> Void)?
|
||||||
|
|
||||||
|
override func viewDidDisappear(_ animated: Bool) {
|
||||||
|
super.viewDidDisappear(animated)
|
||||||
|
|
||||||
|
self.didDisappear?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final class LegacyICloudFileController: LegacyController, UIDocumentPickerDelegate {
|
private final class LegacyICloudFileController: LegacyController, UIDocumentPickerDelegate {
|
||||||
let completion: ([URL]) -> Void
|
let completion: ([URL]) -> Void
|
||||||
|
|
||||||
@ -52,7 +62,10 @@ public func legacyICloudFilePicker(theme: PresentationTheme, mode: LegacyICloudF
|
|||||||
})
|
})
|
||||||
legacyController.statusBar.statusBarStyle = .Black
|
legacyController.statusBar.statusBarStyle = .Black
|
||||||
|
|
||||||
let controller = UIDocumentPickerViewController(documentTypes: documentTypes, in: mode.documentPickerMode)
|
let controller = DocumentPickerViewController(documentTypes: documentTypes, in: mode.documentPickerMode)
|
||||||
|
controller.didDisappear = {
|
||||||
|
dismissImpl?()
|
||||||
|
}
|
||||||
controller.delegate = legacyController
|
controller.delegate = legacyController
|
||||||
if #available(iOSApplicationExtension 11.0, iOS 11.0, *), case .default = mode {
|
if #available(iOSApplicationExtension 11.0, iOS 11.0, *), case .default = mode {
|
||||||
controller.allowsMultipleSelection = true
|
controller.allowsMultipleSelection = true
|
||||||
|
@ -429,6 +429,14 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var preset: TGMediaVideoConversionPreset = TGMediaVideoConversionPresetCompressedMedium
|
||||||
|
if let selectedPreset = adjustments?.preset {
|
||||||
|
preset = selectedPreset
|
||||||
|
}
|
||||||
|
if asAnimation {
|
||||||
|
preset = TGMediaVideoConversionPresetAnimation
|
||||||
|
}
|
||||||
|
|
||||||
if !asAnimation {
|
if !asAnimation {
|
||||||
finalDimensions = TGMediaVideoConverter.dimensions(for: finalDimensions, adjustments: adjustments, preset: TGMediaVideoConversionPresetCompressedMedium)
|
finalDimensions = TGMediaVideoConverter.dimensions(for: finalDimensions, adjustments: adjustments, preset: TGMediaVideoConversionPresetCompressedMedium)
|
||||||
}
|
}
|
||||||
@ -465,6 +473,8 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let estimatedSize = TGMediaVideoConverter.estimatedSize(for: preset, duration: finalDuration, hasAudio: !asAnimation)
|
||||||
|
|
||||||
var fileAttributes: [TelegramMediaFileAttribute] = []
|
var fileAttributes: [TelegramMediaFileAttribute] = []
|
||||||
fileAttributes.append(.FileName(fileName: fileName))
|
fileAttributes.append(.FileName(fileName: fileName))
|
||||||
if asAnimation {
|
if asAnimation {
|
||||||
@ -478,6 +488,9 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if estimatedSize > 5 * 1024 * 1024 {
|
||||||
|
fileAttributes.append(.hintFileIsLarge)
|
||||||
|
}
|
||||||
|
|
||||||
var attributes: [MessageAttribute] = []
|
var attributes: [MessageAttribute] = []
|
||||||
|
|
||||||
|
@ -110,7 +110,9 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var finalDuration: Double = CMTimeGetSeconds(asset.duration)
|
var finalDuration: Double = CMTimeGetSeconds(asset.duration)
|
||||||
let finalDimensions = TGMediaVideoConverter.dimensions(for: asset.originalSize, adjustments: adjustments, preset: adjustments?.preset ?? TGMediaVideoConversionPresetCompressedMedium)
|
|
||||||
|
let preset = adjustments?.preset ?? TGMediaVideoConversionPresetCompressedMedium
|
||||||
|
let finalDimensions = TGMediaVideoConverter.dimensions(for: asset.originalSize, adjustments: adjustments, preset: preset)
|
||||||
|
|
||||||
var resourceAdjustments: VideoMediaResourceAdjustments?
|
var resourceAdjustments: VideoMediaResourceAdjustments?
|
||||||
if let adjustments = adjustments {
|
if let adjustments = adjustments {
|
||||||
@ -123,8 +125,10 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
|||||||
resourceAdjustments = VideoMediaResourceAdjustments(data: adjustmentsData, digest: digest)
|
resourceAdjustments = VideoMediaResourceAdjustments(data: adjustmentsData, digest: digest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let estimatedSize = TGMediaVideoConverter.estimatedSize(for: preset, duration: finalDuration, hasAudio: true)
|
||||||
|
|
||||||
let resource = LocalFileVideoMediaResource(randomId: arc4random64(), path: asset.url.path, adjustments: resourceAdjustments)
|
let resource = LocalFileVideoMediaResource(randomId: arc4random64(), path: asset.url.path, adjustments: resourceAdjustments)
|
||||||
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .resource(.standalone(resource: resource)), mimeType: "video/mp4", attributes: [.Video(duration: Int(finalDuration), size: PixelDimensions(width: Int32(finalDimensions.width), height: Int32(finalDimensions.height)), flags: flags)], hintFileIsLarge: finalDuration > 3.0 * 60.0)
|
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .resource(.standalone(resource: resource)), mimeType: "video/mp4", attributes: [.Video(duration: Int(finalDuration), size: PixelDimensions(width: Int32(finalDimensions.width), height: Int32(finalDimensions.height)), flags: flags)], hintFileIsLarge: estimatedSize > 5 * 1024 * 1024)
|
||||||
|> mapError { _ -> Void in
|
|> mapError { _ -> Void in
|
||||||
return Void()
|
return Void()
|
||||||
}
|
}
|
||||||
@ -229,7 +233,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
|||||||
if let audioData = try? Data(contentsOf: url, options: [.mappedIfSafe]) {
|
if let audioData = try? Data(contentsOf: url, options: [.mappedIfSafe]) {
|
||||||
let fileName = url.lastPathComponent
|
let fileName = url.lastPathComponent
|
||||||
let duration = (value["duration"] as? NSNumber)?.doubleValue ?? 0.0
|
let duration = (value["duration"] as? NSNumber)?.doubleValue ?? 0.0
|
||||||
let isVoice = ((value["isVoice"] as? NSNumber)?.boolValue ?? false) || (duration.isZero && duration < 30.0)
|
let isVoice = ((value["isVoice"] as? NSNumber)?.boolValue ?? false)
|
||||||
let title = value["title"] as? String
|
let title = value["title"] as? String
|
||||||
let artist = value["artist"] as? String
|
let artist = value["artist"] as? String
|
||||||
|
|
||||||
@ -357,21 +361,23 @@ public func sentShareItems(account: Account, to peerIds: [PeerId], items: [Prepa
|
|||||||
var messages: [EnqueueMessage] = []
|
var messages: [EnqueueMessage] = []
|
||||||
var groupingKey: Int64?
|
var groupingKey: Int64?
|
||||||
var mediaTypes: (photo: Bool, video: Bool, music: Bool, other: Bool) = (false, false, false, false)
|
var mediaTypes: (photo: Bool, video: Bool, music: Bool, other: Bool) = (false, false, false, false)
|
||||||
for item in items {
|
if items.count > 1 {
|
||||||
if case let .media(result) = item, case let .media(media) = result {
|
for item in items {
|
||||||
if media.media is TelegramMediaImage {
|
if case let .media(result) = item, case let .media(media) = result {
|
||||||
mediaTypes.photo = true
|
if media.media is TelegramMediaImage {
|
||||||
} else if let media = media.media as? TelegramMediaFile {
|
mediaTypes.photo = true
|
||||||
if media.isVideo {
|
} else if let media = media.media as? TelegramMediaFile {
|
||||||
mediaTypes.video = true
|
if media.isVideo {
|
||||||
} else if let fileName = media.fileName, fileName.hasPrefix("mp3") || fileName.hasPrefix("m4a") {
|
mediaTypes.video = true
|
||||||
mediaTypes.music = true
|
} else if let fileName = media.fileName, fileName.hasPrefix("mp3") || fileName.hasPrefix("m4a") {
|
||||||
|
mediaTypes.music = true
|
||||||
|
} else {
|
||||||
|
mediaTypes.other = true
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
mediaTypes.other = true
|
mediaTypes = (false, false, false, false)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
mediaTypes = (false, false, false, false)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -604,16 +604,15 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
|||||||
hintSize = Int(size)
|
hintSize = Int(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
loop: for attr in file.attributes {
|
loop: for attribute in file.attributes {
|
||||||
switch attr {
|
switch attribute {
|
||||||
case .hintFileIsLarge:
|
case .hintFileIsLarge:
|
||||||
hintFileIsLarge = true
|
hintFileIsLarge = true
|
||||||
break loop
|
break loop
|
||||||
default:
|
default:
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let fileReference: AnyMediaReference
|
let fileReference: AnyMediaReference
|
||||||
if let partialReference = file.partialReference {
|
if let partialReference = file.partialReference {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
function initialize() {
|
function initialize() {
|
||||||
var controls = document.getElementsByClassName("controls")[0];
|
var controls = document.getElementsByClassName("vp-controls-wrapper")[0];
|
||||||
if (controls != null) {
|
if (controls != null) {
|
||||||
controls.style.display = "none";
|
controls.style.display = "none";
|
||||||
}
|
}
|
||||||
|
@ -457,12 +457,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if let _ = strongSelf.presentationInterfaceState.inputTextPanelState.mediaRecordingState {
|
if strongSelf.presentVoiceMessageDiscardAlert(action: action) {
|
||||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_DiscardVoiceMessageDescription, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Conversation_DiscardVoiceMessageAction, action: {
|
|
||||||
self?.stopMediaRecorder()
|
|
||||||
action()
|
|
||||||
})]), in: .window(.root))
|
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -472,6 +467,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) else {
|
guard let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.commitPurposefulAction()
|
strongSelf.commitPurposefulAction()
|
||||||
strongSelf.dismissAllTooltips()
|
strongSelf.dismissAllTooltips()
|
||||||
|
|
||||||
@ -7785,13 +7781,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
var groupingKey: Int64?
|
var groupingKey: Int64?
|
||||||
var fileTypes: (music: Bool, other: Bool) = (false, false)
|
var fileTypes: (music: Bool, other: Bool) = (false, false)
|
||||||
for item in results {
|
if results.count > 1 {
|
||||||
if let item = item {
|
for item in results {
|
||||||
let pathExtension = (item.fileName as NSString).pathExtension.lowercased()
|
if let item = item {
|
||||||
if ["mp3", "m4a"].contains(pathExtension) {
|
let pathExtension = (item.fileName as NSString).pathExtension.lowercased()
|
||||||
fileTypes.music = true
|
if ["mp3", "m4a"].contains(pathExtension) {
|
||||||
} else {
|
fileTypes.music = true
|
||||||
fileTypes.other = true
|
} else {
|
||||||
|
fileTypes.other = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11205,6 +11203,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self.present(controller, in: .window(.root))
|
self.present(controller, in: .window(.root))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func presentVoiceMessageDiscardAlert(action: @escaping () -> Void = {}) -> Bool {
|
||||||
|
if let _ = self.presentationInterfaceState.inputTextPanelState.mediaRecordingState {
|
||||||
|
self.present(textAlertController(context: self.context, title: nil, text: self.presentationData.strings.Conversation_DiscardVoiceMessageDescription, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Conversation_DiscardVoiceMessageAction, action: { [weak self] in
|
||||||
|
self?.stopMediaRecorder()
|
||||||
|
action()
|
||||||
|
})]), in: .window(.root))
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
private var effectiveNavigationController: NavigationController? {
|
private var effectiveNavigationController: NavigationController? {
|
||||||
if let navigationController = self.navigationController as? NavigationController {
|
if let navigationController = self.navigationController as? NavigationController {
|
||||||
return navigationController
|
return navigationController
|
||||||
|
@ -640,8 +640,16 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
self.refreshMediaProcessingManager.process = { [weak context] messageIds in
|
self.refreshMediaProcessingManager.process = { [weak context] messageIds in
|
||||||
context?.account.viewTracker.refreshSecretMediaMediaForMessageIds(messageIds: messageIds)
|
context?.account.viewTracker.refreshSecretMediaMediaForMessageIds(messageIds: messageIds)
|
||||||
}
|
}
|
||||||
self.messageMentionProcessingManager.process = { [weak context] messageIds in
|
|
||||||
context?.account.viewTracker.updateMarkMentionsSeenForMessageIds(messageIds: messageIds)
|
self.messageMentionProcessingManager.process = { [weak self, weak context] messageIds in
|
||||||
|
if let strongSelf = self {
|
||||||
|
let _ = (strongSelf.canReadHistory.get()
|
||||||
|
|> take(1)).start(next: { [weak context] canReadHistory in
|
||||||
|
if canReadHistory {
|
||||||
|
context?.account.viewTracker.updateMarkMentionsSeenForMessageIds(messageIds: messageIds)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.preloadPages = false
|
self.preloadPages = false
|
||||||
|
@ -1035,7 +1035,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
|||||||
badgeContent = .text(inset: 0.0, backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, text: string)
|
badgeContent = .text(inset: 0.0, backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, text: string)
|
||||||
}
|
}
|
||||||
var animated: Bool = animated
|
var animated: Bool = animated
|
||||||
if let updatingMedia = attributes.updatingMedia {
|
if let updatingMedia = attributes.updatingMedia, case .update = updatingMedia.media {
|
||||||
state = .progress(color: messageTheme.mediaOverlayControlColors.foregroundColor, lineWidth: nil, value: CGFloat(updatingMedia.progress), cancelEnabled: true)
|
state = .progress(color: messageTheme.mediaOverlayControlColors.foregroundColor, lineWidth: nil, value: CGFloat(updatingMedia.progress), cancelEnabled: true)
|
||||||
} else if var fetchStatus = self.fetchStatus {
|
} else if var fetchStatus = self.fetchStatus {
|
||||||
var playerPosition: Int32?
|
var playerPosition: Int32?
|
||||||
|
@ -160,7 +160,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
|
|||||||
peer = RenderedPeer(peer: channelPeer)
|
peer = RenderedPeer(peer: channelPeer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entries.append(.message(message, peer, nil, presentationData))
|
entries.append(.message(message, peer, searchResult.readStates[peer.peerId], presentationData))
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
|
@ -78,6 +78,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
|||||||
private var initialPeersDisposable: Disposable?
|
private var initialPeersDisposable: Disposable?
|
||||||
private let options: [ContactListAdditionalOption]
|
private let options: [ContactListAdditionalOption]
|
||||||
private let filters: [ContactListFilter]
|
private let filters: [ContactListFilter]
|
||||||
|
private let limit: Int32?
|
||||||
|
|
||||||
init(_ params: ContactMultiselectionControllerParams) {
|
init(_ params: ContactMultiselectionControllerParams) {
|
||||||
self.params = params
|
self.params = params
|
||||||
@ -85,6 +86,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
|||||||
self.mode = params.mode
|
self.mode = params.mode
|
||||||
self.options = params.options
|
self.options = params.options
|
||||||
self.filters = params.filters
|
self.filters = params.filters
|
||||||
|
self.limit = params.limit
|
||||||
self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
self.titleView = CounterContollerTitleView(theme: self.presentationData.theme)
|
self.titleView = CounterContollerTitleView(theme: self.presentationData.theme)
|
||||||
@ -228,6 +230,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
|||||||
self?.presentingViewController?.dismiss(animated: true, completion: nil)
|
self?.presentingViewController?.dismiss(animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let limit = self.limit
|
||||||
self.contactsNode.openPeer = { [weak self] peer in
|
self.contactsNode.openPeer = { [weak self] peer in
|
||||||
if let strongSelf = self, case let .peer(peer, _, _) = peer {
|
if let strongSelf = self, case let .peer(peer, _, _) = peer {
|
||||||
var updatedCount: Int?
|
var updatedCount: Int?
|
||||||
@ -261,8 +264,8 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .chats(chatsNode):
|
case let .chats(chatsNode):
|
||||||
chatsNode.updateState { state in
|
chatsNode.updateState { initialState in
|
||||||
var state = state
|
var state = initialState
|
||||||
if state.selectedPeerIds.contains(peer.id) {
|
if state.selectedPeerIds.contains(peer.id) {
|
||||||
state.selectedPeerIds.remove(peer.id)
|
state.selectedPeerIds.remove(peer.id)
|
||||||
removedTokenId = peer.id
|
removedTokenId = peer.id
|
||||||
@ -271,6 +274,12 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
|||||||
state.selectedPeerIds.insert(peer.id)
|
state.selectedPeerIds.insert(peer.id)
|
||||||
}
|
}
|
||||||
updatedCount = state.selectedPeerIds.count
|
updatedCount = state.selectedPeerIds.count
|
||||||
|
if let limit = limit, let count = updatedCount, count > limit {
|
||||||
|
updatedCount = nil
|
||||||
|
removedTokenId = nil
|
||||||
|
addedToken = nil
|
||||||
|
return initialState
|
||||||
|
}
|
||||||
var updatedState = ContactListNodeGroupSelectionState()
|
var updatedState = ContactListNodeGroupSelectionState()
|
||||||
for peerId in state.selectedPeerIds {
|
for peerId in state.selectedPeerIds {
|
||||||
updatedState = updatedState.withToggledPeerId(.peer(peerId))
|
updatedState = updatedState.withToggledPeerId(.peer(peerId))
|
||||||
|
@ -179,90 +179,21 @@ class StickerShimmerEffectNode: ASDisplayNode {
|
|||||||
|
|
||||||
context.setFillColor(UIColor.black.cgColor)
|
context.setFillColor(UIColor.black.cgColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let data = data, let unpackedData = TGGUnzipData(data, 5 * 1024 * 1024), let path = String(data: unpackedData, encoding: .utf8) {
|
if let data = data, let unpackedData = TGGUnzipData(data, 5 * 1024 * 1024), let path = String(data: unpackedData, encoding: .utf8) {
|
||||||
|
if data.count == 141 {
|
||||||
|
print()
|
||||||
|
}
|
||||||
|
var path = path
|
||||||
|
if !path.hasPrefix("z") {
|
||||||
|
path = "\(path)z"
|
||||||
|
}
|
||||||
let reader = PathDataReader(input: path)
|
let reader = PathDataReader(input: path)
|
||||||
let segments = reader.read()
|
let segments = reader.read()
|
||||||
|
|
||||||
var currentX: Double = 0.0
|
let scale = size.width / 512.0
|
||||||
var currentY: Double = 0.0
|
context.scaleBy(x: scale, y: scale)
|
||||||
|
renderPath(segments, context: context)
|
||||||
let mul: Double = Double(size.width) / 512.0
|
|
||||||
|
|
||||||
for segment in segments {
|
|
||||||
switch segment.type {
|
|
||||||
case .M, .m:
|
|
||||||
let x = segment.data[0]
|
|
||||||
let y = segment.data[1]
|
|
||||||
|
|
||||||
if segment.isAbsolute() {
|
|
||||||
currentX = x
|
|
||||||
currentY = y
|
|
||||||
} else {
|
|
||||||
currentX += x
|
|
||||||
currentY += y
|
|
||||||
}
|
|
||||||
|
|
||||||
context.move(to: CGPoint(x: currentX * mul, y: currentY * mul))
|
|
||||||
case .L, .l:
|
|
||||||
let x = segment.data[0]
|
|
||||||
let y = segment.data[1]
|
|
||||||
|
|
||||||
let effectiveX: Double
|
|
||||||
let effectiveY: Double
|
|
||||||
if segment.isAbsolute() {
|
|
||||||
effectiveX = x
|
|
||||||
effectiveY = y
|
|
||||||
} else {
|
|
||||||
effectiveX = currentX + x
|
|
||||||
effectiveY = currentY + y
|
|
||||||
}
|
|
||||||
|
|
||||||
currentX = effectiveX
|
|
||||||
currentY = effectiveY
|
|
||||||
|
|
||||||
context.addLine(to: CGPoint(x: effectiveX * mul, y: effectiveY * mul))
|
|
||||||
case .C, .c:
|
|
||||||
let x1 = segment.data[0]
|
|
||||||
let y1 = segment.data[1]
|
|
||||||
let x2 = segment.data[2]
|
|
||||||
let y2 = segment.data[3]
|
|
||||||
let x = segment.data[4]
|
|
||||||
let y = segment.data[5]
|
|
||||||
|
|
||||||
let effectiveX1: Double
|
|
||||||
let effectiveY1: Double
|
|
||||||
let effectiveX2: Double
|
|
||||||
let effectiveY2: Double
|
|
||||||
let effectiveX: Double
|
|
||||||
let effectiveY: Double
|
|
||||||
|
|
||||||
if segment.isAbsolute() {
|
|
||||||
effectiveX1 = x1
|
|
||||||
effectiveY1 = y1
|
|
||||||
effectiveX2 = x2
|
|
||||||
effectiveY2 = y2
|
|
||||||
effectiveX = x
|
|
||||||
effectiveY = y
|
|
||||||
} else {
|
|
||||||
effectiveX1 = currentX + x1
|
|
||||||
effectiveY1 = currentY + y1
|
|
||||||
effectiveX2 = currentX + x2
|
|
||||||
effectiveY2 = currentY + y2
|
|
||||||
effectiveX = currentX + x
|
|
||||||
effectiveY = currentY + y
|
|
||||||
}
|
|
||||||
|
|
||||||
currentX = effectiveX
|
|
||||||
currentY = effectiveY
|
|
||||||
|
|
||||||
context.addCurve(to: CGPoint(x: effectiveX * mul, y: effectiveY * mul), control1: CGPoint(x: effectiveX1 * mul, y: effectiveY1 * mul), control2: CGPoint(x: effectiveX2 * mul, y: effectiveY2 * mul))
|
|
||||||
case .z:
|
|
||||||
context.fillPath()
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), byRoundingCorners: [.topLeft, .topRight, .bottomLeft, .bottomRight], cornerRadii: CGSize(width: 10.0, height: 10.0))
|
let path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), byRoundingCorners: [.topLeft, .topRight, .bottomLeft, .bottomRight], cornerRadii: CGSize(width: 10.0, height: 10.0))
|
||||||
UIGraphicsPushContext(context)
|
UIGraphicsPushContext(context)
|
||||||
@ -348,6 +279,225 @@ open class PathSegment: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func renderPath(_ segments: [PathSegment], context: CGContext) {
|
||||||
|
var currentPoint: CGPoint?
|
||||||
|
var cubicPoint: CGPoint?
|
||||||
|
var quadrPoint: CGPoint?
|
||||||
|
var initialPoint: CGPoint?
|
||||||
|
|
||||||
|
func M(_ x: Double, y: Double) {
|
||||||
|
let point = CGPoint(x: CGFloat(x), y: CGFloat(y))
|
||||||
|
context.move(to: point)
|
||||||
|
setInitPoint(point)
|
||||||
|
}
|
||||||
|
|
||||||
|
func m(_ x: Double, y: Double) {
|
||||||
|
if let cur = currentPoint {
|
||||||
|
let next = CGPoint(x: CGFloat(x) + cur.x, y: CGFloat(y) + cur.y)
|
||||||
|
context.move(to: next)
|
||||||
|
setInitPoint(next)
|
||||||
|
} else {
|
||||||
|
M(x, y: y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func L(_ x: Double, y: Double) {
|
||||||
|
lineTo(CGPoint(x: CGFloat(x), y: CGFloat(y)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func l(_ x: Double, y: Double) {
|
||||||
|
if let cur = currentPoint {
|
||||||
|
lineTo(CGPoint(x: CGFloat(x) + cur.x, y: CGFloat(y) + cur.y))
|
||||||
|
} else {
|
||||||
|
L(x, y: y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func H(_ x: Double) {
|
||||||
|
if let cur = currentPoint {
|
||||||
|
lineTo(CGPoint(x: CGFloat(x), y: CGFloat(cur.y)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func h(_ x: Double) {
|
||||||
|
if let cur = currentPoint {
|
||||||
|
lineTo(CGPoint(x: CGFloat(x) + cur.x, y: CGFloat(cur.y)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func V(_ y: Double) {
|
||||||
|
if let cur = currentPoint {
|
||||||
|
lineTo(CGPoint(x: CGFloat(cur.x), y: CGFloat(y)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func v(_ y: Double) {
|
||||||
|
if let cur = currentPoint {
|
||||||
|
lineTo(CGPoint(x: CGFloat(cur.x), y: CGFloat(y) + cur.y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func lineTo(_ p: CGPoint) {
|
||||||
|
context.addLine(to: p)
|
||||||
|
setPoint(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func c(_ x1: Double, y1: Double, x2: Double, y2: Double, x: Double, y: Double) {
|
||||||
|
if let cur = currentPoint {
|
||||||
|
let endPoint = CGPoint(x: CGFloat(x) + cur.x, y: CGFloat(y) + cur.y)
|
||||||
|
let controlPoint1 = CGPoint(x: CGFloat(x1) + cur.x, y: CGFloat(y1) + cur.y)
|
||||||
|
let controlPoint2 = CGPoint(x: CGFloat(x2) + cur.x, y: CGFloat(y2) + cur.y)
|
||||||
|
context.addCurve(to: endPoint, control1: controlPoint1, control2: controlPoint2)
|
||||||
|
setCubicPoint(endPoint, cubic: controlPoint2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func C(_ x1: Double, y1: Double, x2: Double, y2: Double, x: Double, y: Double) {
|
||||||
|
let endPoint = CGPoint(x: CGFloat(x), y: CGFloat(y))
|
||||||
|
let controlPoint1 = CGPoint(x: CGFloat(x1), y: CGFloat(y1))
|
||||||
|
let controlPoint2 = CGPoint(x: CGFloat(x2), y: CGFloat(y2))
|
||||||
|
context.addCurve(to: endPoint, control1: controlPoint1, control2: controlPoint2)
|
||||||
|
setCubicPoint(endPoint, cubic: controlPoint2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func s(_ x2: Double, y2: Double, x: Double, y: Double) {
|
||||||
|
if let cur = currentPoint {
|
||||||
|
let nextCubic = CGPoint(x: CGFloat(x2) + cur.x, y: CGFloat(y2) + cur.y)
|
||||||
|
let next = CGPoint(x: CGFloat(x) + cur.x, y: CGFloat(y) + cur.y)
|
||||||
|
|
||||||
|
let xy1: CGPoint
|
||||||
|
if let curCubicVal = cubicPoint {
|
||||||
|
xy1 = CGPoint(x: CGFloat(2 * cur.x) - curCubicVal.x, y: CGFloat(2 * cur.y) - curCubicVal.y)
|
||||||
|
} else {
|
||||||
|
xy1 = cur
|
||||||
|
}
|
||||||
|
context.addCurve(to: next, control1: xy1, control2: nextCubic)
|
||||||
|
setCubicPoint(next, cubic: nextCubic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func S(_ x2: Double, y2: Double, x: Double, y: Double) {
|
||||||
|
if let cur = currentPoint {
|
||||||
|
let nextCubic = CGPoint(x: CGFloat(x2), y: CGFloat(y2))
|
||||||
|
let next = CGPoint(x: CGFloat(x), y: CGFloat(y))
|
||||||
|
let xy1: CGPoint
|
||||||
|
if let curCubicVal = cubicPoint {
|
||||||
|
xy1 = CGPoint(x: CGFloat(2 * cur.x) - curCubicVal.x, y: CGFloat(2 * cur.y) - curCubicVal.y)
|
||||||
|
} else {
|
||||||
|
xy1 = cur
|
||||||
|
}
|
||||||
|
context.addCurve(to: next, control1: xy1, control2: nextCubic)
|
||||||
|
setCubicPoint(next, cubic: nextCubic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func z() {
|
||||||
|
context.fillPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setQuadrPoint(_ p: CGPoint, quadr: CGPoint) {
|
||||||
|
currentPoint = p
|
||||||
|
quadrPoint = quadr
|
||||||
|
cubicPoint = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCubicPoint(_ p: CGPoint, cubic: CGPoint) {
|
||||||
|
currentPoint = p
|
||||||
|
cubicPoint = cubic
|
||||||
|
quadrPoint = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setInitPoint(_ p: CGPoint) {
|
||||||
|
setPoint(p)
|
||||||
|
initialPoint = p
|
||||||
|
}
|
||||||
|
|
||||||
|
func setPoint(_ p: CGPoint) {
|
||||||
|
currentPoint = p
|
||||||
|
cubicPoint = nil
|
||||||
|
quadrPoint = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for segment in segments {
|
||||||
|
var data = segment.data
|
||||||
|
switch segment.type {
|
||||||
|
case .M:
|
||||||
|
M(data[0], y: data[1])
|
||||||
|
data.removeSubrange(Range(uncheckedBounds: (lower: 0, upper: 2)))
|
||||||
|
while data.count >= 2 {
|
||||||
|
L(data[0], y: data[1])
|
||||||
|
data.removeSubrange((0 ..< 2))
|
||||||
|
}
|
||||||
|
case .m:
|
||||||
|
m(data[0], y: data[1])
|
||||||
|
data.removeSubrange((0 ..< 2))
|
||||||
|
while data.count >= 2 {
|
||||||
|
l(data[0], y: data[1])
|
||||||
|
data.removeSubrange((0 ..< 2))
|
||||||
|
}
|
||||||
|
case .L:
|
||||||
|
while data.count >= 2 {
|
||||||
|
L(data[0], y: data[1])
|
||||||
|
data.removeSubrange((0 ..< 2))
|
||||||
|
}
|
||||||
|
case .l:
|
||||||
|
while data.count >= 2 {
|
||||||
|
l(data[0], y: data[1])
|
||||||
|
data.removeSubrange((0 ..< 2))
|
||||||
|
}
|
||||||
|
case .H:
|
||||||
|
H(data[0])
|
||||||
|
case .h:
|
||||||
|
h(data[0])
|
||||||
|
case .V:
|
||||||
|
V(data[0])
|
||||||
|
case .v:
|
||||||
|
v(data[0])
|
||||||
|
case .C:
|
||||||
|
while data.count >= 6 {
|
||||||
|
C(data[0], y1: data[1], x2: data[2], y2: data[3], x: data[4], y: data[5])
|
||||||
|
data.removeSubrange((0 ..< 6))
|
||||||
|
}
|
||||||
|
case .c:
|
||||||
|
while data.count >= 6 {
|
||||||
|
c(data[0], y1: data[1], x2: data[2], y2: data[3], x: data[4], y: data[5])
|
||||||
|
data.removeSubrange((0 ..< 6))
|
||||||
|
}
|
||||||
|
case .S:
|
||||||
|
while data.count >= 4 {
|
||||||
|
S(data[0], y2: data[1], x: data[2], y: data[3])
|
||||||
|
data.removeSubrange((0 ..< 4))
|
||||||
|
}
|
||||||
|
case .s:
|
||||||
|
while data.count >= 4 {
|
||||||
|
s(data[0], y2: data[1], x: data[2], y: data[3])
|
||||||
|
data.removeSubrange((0 ..< 4))
|
||||||
|
}
|
||||||
|
// case .Q:
|
||||||
|
// Q(data[0], y1: data[1], x: data[2], y: data[3])
|
||||||
|
// case .q:
|
||||||
|
// q(data[0], y1: data[1], x: data[2], y: data[3])
|
||||||
|
// case .T:
|
||||||
|
// T(data[0], y: data[1])
|
||||||
|
// case .t:
|
||||||
|
// t(data[0], y: data[1])
|
||||||
|
// case .A:
|
||||||
|
// A(data[0], ry: data[1], angle: data[2], largeArc: num2bool(data[3]), sweep: num2bool(data[4]), x: data[5], y: data[6])
|
||||||
|
// case .a:
|
||||||
|
// a(data[0], ry: data[1], angle: data[2], largeArc: num2bool(data[3]), sweep: num2bool(data[4]), x: data[5], y: data[6])
|
||||||
|
// case .E:
|
||||||
|
// E(data[0], y: data[1], w: data[2], h: data[3], startAngle: data[4], arcAngle: data[5])
|
||||||
|
// case .e:
|
||||||
|
// e(data[0], y: data[1], w: data[2], h: data[3], startAngle: data[4], arcAngle: data[5])
|
||||||
|
case .z:
|
||||||
|
z()
|
||||||
|
default:
|
||||||
|
print("unknown")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class PathDataReader {
|
private class PathDataReader {
|
||||||
private let input: String
|
private let input: String
|
||||||
private var current: UnicodeScalar?
|
private var current: UnicodeScalar?
|
||||||
|
@ -224,6 +224,11 @@ final class VimeoEmbedImplementation: WebEmbedImplementation {
|
|||||||
playbackStatus = .buffering(initial: true, whilePlaying: false, progress: 0.0, display: true)
|
playbackStatus = .buffering(initial: true, whilePlaying: false, progress: 0.0, display: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if case .playing = playbackStatus, !self.started {
|
||||||
|
self.started = true
|
||||||
|
self.onPlaybackStarted?()
|
||||||
|
}
|
||||||
|
|
||||||
self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: Double(duration), dimensions: self.status.dimensions, timestamp: newTimestamp, baseRate: 1.0, seekId: self.status.seekId, status: playbackStatus, soundEnabled: true)
|
self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: Double(duration), dimensions: self.status.dimensions, timestamp: newTimestamp, baseRate: 1.0, seekId: self.status.seekId, status: playbackStatus, soundEnabled: true)
|
||||||
updateStatus(self.status)
|
updateStatus(self.status)
|
||||||
}
|
}
|
||||||
|
@ -136,6 +136,10 @@ final class WebEmbedPlayerNode: ASDisplayNode, WKNavigationDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
let webView = self.webView
|
||||||
|
Queue.mainQueue().after(1.0) {
|
||||||
|
print(webView.debugDescription)
|
||||||
|
}
|
||||||
func disableGestures(view: UIView) {
|
func disableGestures(view: UIView) {
|
||||||
if let recognizers = view.gestureRecognizers {
|
if let recognizers = view.gestureRecognizers {
|
||||||
for recognizer in recognizers {
|
for recognizer in recognizers {
|
||||||
@ -167,7 +171,6 @@ final class WebEmbedPlayerNode: ASDisplayNode, WKNavigationDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
||||||
print("w")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user