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
b92a46ee58
commit
bf94ea75e9
@ -8886,3 +8886,6 @@ Sorry for the inconvenience.";
|
||||
"Translation.Language.ta" = "Tamil";
|
||||
"Translation.Language.tr" = "Turkish";
|
||||
"Translation.Language.uz" = "Uzbek";
|
||||
|
||||
"Settings.RaiseToListen" = "Raise to Listen";
|
||||
"Settings.RaiseToListenInfo" = "Raise to Listen allows you to quickly listen and reply to incoming audio messages by raising the phone to your ear.";
|
||||
|
@ -954,7 +954,8 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
}
|
||||
}
|
||||
|
||||
let signal = TGMediaVideoConverter.convert(avatarAsset, adjustments: avatarAdjustments, watcher: nil, entityRenderer: entityRenderer)!
|
||||
let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4")
|
||||
let signal = TGMediaVideoConverter.convert(avatarAsset, adjustments: avatarAdjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)!
|
||||
|
||||
let signalDisposable = signal.start(next: { next in
|
||||
if let result = next as? TGMediaVideoConversionResult {
|
||||
@ -964,6 +965,8 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
||||
account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
|
||||
subscriber.putNext(resource)
|
||||
|
||||
EngineTempBox.shared.dispose(tempFile)
|
||||
}
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
|
@ -126,6 +126,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
private var selectedFilterPromise = Promise<ChatListSearchFilterEntry?>()
|
||||
private var transitionFraction: CGFloat = 0.0
|
||||
|
||||
private var appearanceTimestamp: Double?
|
||||
|
||||
private weak var copyProtectionTooltipController: TooltipController?
|
||||
|
||||
private lazy var hapticFeedback = { HapticFeedback() }()
|
||||
@ -306,6 +308,10 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
return
|
||||
}
|
||||
|
||||
if let appearanceTimestamp = strongSelf.appearanceTimestamp, CACurrentMediaTime() - appearanceTimestamp < 0.5 {
|
||||
return
|
||||
}
|
||||
|
||||
var key: ChatListSearchPaneKey?
|
||||
var date = strongSelf.currentSearchOptions.date
|
||||
var peer = strongSelf.currentSearchOptions.peer
|
||||
@ -667,6 +673,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
|
||||
if isFirstTime {
|
||||
self.filterContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.appearanceTimestamp = CACurrentMediaTime()
|
||||
}
|
||||
|
||||
var bottomIntrinsicInset = layout.intrinsicInsets.bottom
|
||||
|
@ -1848,7 +1848,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
entities = translation.entities
|
||||
}
|
||||
|
||||
messageString = stringWithAppliedEntities(trimToLineCount(messageText, lineCount: authorAttributedString == nil ? 2 : 1), entities: entities, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: italicTextFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message._asMessage())
|
||||
messageString = foldLineBreaks(stringWithAppliedEntities(messageText, entities: entities, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: italicTextFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message._asMessage()))
|
||||
} else if spoilers != nil || customEmojiRanges != nil {
|
||||
let mutableString = NSMutableAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor)
|
||||
if let spoilers = spoilers {
|
||||
|
@ -25,7 +25,6 @@ swift_library(
|
||||
"//submodules/GZip:GZip",
|
||||
"//third-party/ZipArchive:ZipArchive",
|
||||
"//submodules/InAppPurchaseManager:InAppPurchaseManager",
|
||||
"//submodules/Utils/DarwinDirStat",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -16,7 +16,6 @@ import AppBundle
|
||||
import ZipArchive
|
||||
import WebKit
|
||||
import InAppPurchaseManager
|
||||
import DarwinDirStat
|
||||
|
||||
@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
|
||||
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
||||
@ -948,15 +947,10 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case .resetTranslationStates:
|
||||
return ItemListActionItem(presentationData: presentationData, title: "Reset Translation States", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||
if let context = arguments.context {
|
||||
let size = statForDirectory(path: NSTemporaryDirectory())
|
||||
let controller = textAlertController(context: context, title: nil, text: "temp dir size \(size)", actions: [TextAlertAction(type: .genericAction, title: "OK", action: {})])
|
||||
arguments.presentController(controller, nil)
|
||||
let _ = context.engine.itemCache.clear(collectionIds: [
|
||||
ApplicationSpecificItemCacheCollectionId.translationState
|
||||
]).start()
|
||||
}
|
||||
// if let context = arguments.context {
|
||||
// let _ = context.engine.itemCache.clear(collectionIds: [
|
||||
// ApplicationSpecificItemCacheCollectionId.translationState
|
||||
// ]).start()
|
||||
// }
|
||||
})
|
||||
case .crash:
|
||||
return ItemListActionItem(presentationData: presentationData, title: "Crash", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||
@ -1545,30 +1539,3 @@ public func triggerDebugSendLogsUI(context: AccountContext, additionalInfo: Stri
|
||||
pushController(controller)
|
||||
})
|
||||
}
|
||||
|
||||
private func statForDirectory(path: String) -> Int64 {
|
||||
if #available(macOS 10.13, *) {
|
||||
var s = darwin_dirstat()
|
||||
var result = dirstat_np(path, 1, &s, MemoryLayout<darwin_dirstat>.size)
|
||||
if result != -1 {
|
||||
return Int64(s.total_size)
|
||||
} else {
|
||||
result = dirstat_np(path, 0, &s, MemoryLayout<darwin_dirstat>.size)
|
||||
if result != -1 {
|
||||
return Int64(s.total_size)
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let fileManager = FileManager.default
|
||||
let folderURL = URL(fileURLWithPath: path)
|
||||
var folderSize: Int64 = 0
|
||||
if let files = try? fileManager.contentsOfDirectory(at: folderURL, includingPropertiesForKeys: nil, options: []) {
|
||||
for file in files {
|
||||
folderSize += (fileSize(file.path) ?? 0)
|
||||
}
|
||||
}
|
||||
return folderSize
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ public final class InAppPurchaseManager: NSObject {
|
||||
}
|
||||
|
||||
public func pricePerMonth(_ monthsCount: Int) -> String {
|
||||
let price = self.skProduct.price.dividing(by: NSDecimalNumber(value: monthsCount)).prettyPrice().round(2)
|
||||
let price = self.skProduct.price.dividing(by: NSDecimalNumber(value: monthsCount)).round(2)
|
||||
return self.numberFormatter.string(from: price) ?? ""
|
||||
}
|
||||
|
||||
|
@ -16,11 +16,11 @@
|
||||
|
||||
@interface TGMediaVideoConverter : NSObject
|
||||
|
||||
+ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id<TGPhotoPaintEntityRenderer>)entityRenderer;
|
||||
+ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments watcher:(TGMediaVideoFileWatcher *)watcher inhibitAudio:(bool)inhibitAudio entityRenderer:(id<TGPhotoPaintEntityRenderer>)entityRenderer;
|
||||
+ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments path:(NSString *)path watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id<TGPhotoPaintEntityRenderer>)entityRenderer;
|
||||
+ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments path:(NSString *)path watcher:(TGMediaVideoFileWatcher *)watcher inhibitAudio:(bool)inhibitAudio entityRenderer:(id<TGPhotoPaintEntityRenderer>)entityRenderer;
|
||||
+ (SSignal *)hashForAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments;
|
||||
|
||||
+ (SSignal *)renderUIImage:(UIImage *)image duration:(NSTimeInterval)duration adjustments:(TGMediaVideoEditAdjustments *)adjustments watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id<TGPhotoPaintEntityRenderer>)entityRenderer;
|
||||
+ (SSignal *)renderUIImage:(UIImage *)image duration:(NSTimeInterval)duration adjustments:(TGMediaVideoEditAdjustments *)adjustments path:(NSString *)path watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id<TGPhotoPaintEntityRenderer>)entityRenderer;
|
||||
|
||||
+ (NSUInteger)estimatedSizeForPreset:(TGMediaVideoConversionPreset)preset duration:(NSTimeInterval)duration hasAudio:(bool)hasAudio;
|
||||
+ (TGMediaVideoConversionPreset)bestAvailablePresetForDimensions:(CGSize)dimensions;
|
||||
|
@ -1475,6 +1475,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
bool spoiler = [editingContext spoilerForItem:asset];
|
||||
|
||||
if ([asset isKindOfClass:[UIImage class]]) {
|
||||
if (intent == TGMediaAssetsControllerSendFileIntent)
|
||||
{
|
||||
@ -1513,6 +1515,10 @@
|
||||
if (groupedId != nil && !hasAnyTimers)
|
||||
dict[@"groupedId"] = groupedId;
|
||||
|
||||
if (spoiler) {
|
||||
dict[@"spoiler"] = @true;
|
||||
}
|
||||
|
||||
id generatedItem = descriptionGenerator(dict, caption, nil, nil);
|
||||
return generatedItem;
|
||||
}];
|
||||
@ -1560,6 +1566,10 @@
|
||||
if (groupedId != nil && !hasAnyTimers)
|
||||
dict[@"groupedId"] = groupedId;
|
||||
|
||||
if (spoiler) {
|
||||
dict[@"spoiler"] = @true;
|
||||
}
|
||||
|
||||
id generatedItem = descriptionGenerator(dict, caption, nil, nil);
|
||||
return generatedItem;
|
||||
}] catch:^SSignal *(__unused id error)
|
||||
@ -1636,6 +1646,10 @@
|
||||
else if (groupedId != nil && !hasAnyTimers)
|
||||
dict[@"groupedId"] = groupedId;
|
||||
|
||||
if (spoiler) {
|
||||
dict[@"spoiler"] = @true;
|
||||
}
|
||||
|
||||
id generatedItem = descriptionGenerator(dict, caption, nil, nil);
|
||||
return generatedItem;
|
||||
}];
|
||||
|
@ -101,12 +101,12 @@
|
||||
|
||||
@implementation TGMediaVideoConverter
|
||||
|
||||
+ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id<TGPhotoPaintEntityRenderer>)entityRenderer
|
||||
+ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments path:(NSString *)path watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id<TGPhotoPaintEntityRenderer>)entityRenderer
|
||||
{
|
||||
return [self convertAVAsset:avAsset adjustments:adjustments watcher:watcher inhibitAudio:false entityRenderer:entityRenderer];
|
||||
return [self convertAVAsset:avAsset adjustments:adjustments path:path watcher:watcher inhibitAudio:false entityRenderer:entityRenderer];
|
||||
}
|
||||
|
||||
+ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments watcher:(TGMediaVideoFileWatcher *)watcher inhibitAudio:(bool)inhibitAudio entityRenderer:(id<TGPhotoPaintEntityRenderer>)entityRenderer
|
||||
+ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments path:(NSString *)path watcher:(TGMediaVideoFileWatcher *)watcher inhibitAudio:(bool)inhibitAudio entityRenderer:(id<TGPhotoPaintEntityRenderer>)entityRenderer
|
||||
{
|
||||
if ([avAsset isKindOfClass:[NSURL class]]) {
|
||||
avAsset = [[AVURLAsset alloc] initWithURL:(NSURL *)avAsset options:nil];
|
||||
@ -116,7 +116,7 @@
|
||||
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||
{
|
||||
SAtomic *context = [[SAtomic alloc] initWithValue:[TGMediaVideoConversionContext contextWithQueue:queue subscriber:subscriber]];
|
||||
NSURL *outputUrl = [self _randomTemporaryURL];
|
||||
NSURL *outputUrl = [NSURL fileURLWithPath:path];
|
||||
|
||||
NSArray *requiredKeys = @[ @"tracks", @"duration" ];
|
||||
[avAsset loadValuesAsynchronouslyForKeys:requiredKeys completionHandler:^
|
||||
@ -222,14 +222,14 @@
|
||||
}];
|
||||
}
|
||||
|
||||
+ (SSignal *)renderUIImage:(UIImage *)image duration:(NSTimeInterval)duration adjustments:(TGMediaVideoEditAdjustments *)adjustments watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id<TGPhotoPaintEntityRenderer>)entityRenderer
|
||||
+ (SSignal *)renderUIImage:(UIImage *)image duration:(NSTimeInterval)duration adjustments:(TGMediaVideoEditAdjustments *)adjustments path:(NSString *)path watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id<TGPhotoPaintEntityRenderer>)entityRenderer
|
||||
{
|
||||
SQueue *queue = [[SQueue alloc] init];
|
||||
|
||||
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||
{
|
||||
SAtomic *context = [[SAtomic alloc] initWithValue:[TGMediaVideoConversionContext contextWithQueue:queue subscriber:subscriber]];
|
||||
NSURL *outputUrl = [self _randomTemporaryURL];
|
||||
NSURL *outputUrl = [NSURL fileURLWithPath:path];
|
||||
|
||||
NSString *path = TGComponentsPathForResource(@"blank", @"mp4");
|
||||
AVAsset *avAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:path] options:nil];
|
||||
@ -895,11 +895,6 @@
|
||||
return CGSizeMake(renderWidth, renderHeight);
|
||||
}
|
||||
|
||||
+ (NSURL *)_randomTemporaryURL
|
||||
{
|
||||
return [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"%x.mp4", (int)arc4random()]]];
|
||||
}
|
||||
|
||||
+ (NSUInteger)estimatedSizeForPreset:(TGMediaVideoConversionPreset)preset duration:(NSTimeInterval)duration hasAudio:(bool)hasAudio
|
||||
{
|
||||
NSInteger bitrate = [TGMediaVideoConversionPresetSettings _videoBitrateKbpsForPreset:preset];
|
||||
|
@ -648,6 +648,11 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f;
|
||||
[(TGPhotoEditorController *)self.parentViewController setInfoString:fileSize];
|
||||
}
|
||||
|
||||
+ (NSString *)_randomTemporaryPath
|
||||
{
|
||||
return [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"%x.mp4", (int)arc4random()]];
|
||||
}
|
||||
|
||||
- (void)generateVideoPreview
|
||||
{
|
||||
if (self.preset == _currentPreset)
|
||||
@ -665,6 +670,8 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f;
|
||||
TGVideoEditAdjustments *adjustments = [self.photoEditor exportAdjustments];
|
||||
adjustments = [adjustments editAdjustmentsWithPreset:self.preset maxDuration:TGPhotoQualityPreviewDuration];
|
||||
|
||||
NSString *path = [TGPhotoQualityController _randomTemporaryPath];
|
||||
|
||||
__block NSTimeInterval delay = 0.0;
|
||||
__weak TGPhotoQualityController *weakSelf = self;
|
||||
SSignal *convertSignal = [[assetSignal onNext:^(AVAsset *next) {
|
||||
@ -680,7 +687,7 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f;
|
||||
{
|
||||
return [[[[[SSignal single:avAsset] delay:delay onQueue:[SQueue concurrentDefaultQueue]] mapToSignal:^SSignal *(AVAsset *avAsset)
|
||||
{
|
||||
return [TGMediaVideoConverter convertAVAsset:avAsset adjustments:adjustments watcher:nil inhibitAudio:true entityRenderer:nil];
|
||||
return [TGMediaVideoConverter convertAVAsset:avAsset adjustments:adjustments path:path watcher:nil inhibitAudio:true entityRenderer:nil];
|
||||
}] onError:^(__unused id error) {
|
||||
delay = 1.0;
|
||||
}] retryIf:^bool(__unused id error)
|
||||
|
@ -1067,6 +1067,8 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate {
|
||||
|
||||
node.isHidden = true
|
||||
|
||||
self.textField.isUserInteractionEnabled = false
|
||||
|
||||
if !self.clearButton.isHidden {
|
||||
let xOffset = targetTextBackgroundFrame.width - self.textBackgroundNode.frame.width
|
||||
if !xOffset.isZero {
|
||||
@ -1118,10 +1120,11 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate {
|
||||
var backgroundCompleted = false
|
||||
var separatorCompleted = false
|
||||
var textBackgroundCompleted = false
|
||||
let intermediateCompletion: () -> Void = { [weak node] in
|
||||
let intermediateCompletion: () -> Void = { [weak node, weak self] in
|
||||
if backgroundCompleted && separatorCompleted && textBackgroundCompleted {
|
||||
completion()
|
||||
node?.isHidden = false
|
||||
self?.textField.isUserInteractionEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ private final class DataAndStorageControllerArguments {
|
||||
let openSaveIncoming: (AutomaticSaveIncomingPeerType) -> Void
|
||||
let toggleSaveEditedPhotos: (Bool) -> Void
|
||||
let togglePauseMusicOnRecording: (Bool) -> Void
|
||||
let toggleRaiseToListen: (Bool) -> Void
|
||||
let toggleAutoplayGifs: (Bool) -> Void
|
||||
let toggleAutoplayVideos: (Bool) -> Void
|
||||
let toggleDownloadInBackground: (Bool) -> Void
|
||||
@ -37,7 +38,7 @@ private final class DataAndStorageControllerArguments {
|
||||
let openIntents: () -> Void
|
||||
let toggleEnableSensitiveContent: (Bool) -> Void
|
||||
|
||||
init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, toggleVoiceUseLessData: @escaping (Bool) -> Void, openSaveIncoming: @escaping (AutomaticSaveIncomingPeerType) -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, togglePauseMusicOnRecording: @escaping (Bool) -> Void, toggleAutoplayGifs: @escaping (Bool) -> Void, toggleAutoplayVideos: @escaping (Bool) -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void, openBrowserSelection: @escaping () -> Void, openIntents: @escaping () -> Void, toggleEnableSensitiveContent: @escaping (Bool) -> Void) {
|
||||
init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, toggleVoiceUseLessData: @escaping (Bool) -> Void, openSaveIncoming: @escaping (AutomaticSaveIncomingPeerType) -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, togglePauseMusicOnRecording: @escaping (Bool) -> Void, toggleRaiseToListen: @escaping (Bool) -> Void, toggleAutoplayGifs: @escaping (Bool) -> Void, toggleAutoplayVideos: @escaping (Bool) -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void, openBrowserSelection: @escaping () -> Void, openIntents: @escaping () -> Void, toggleEnableSensitiveContent: @escaping (Bool) -> Void) {
|
||||
self.openStorageUsage = openStorageUsage
|
||||
self.openNetworkUsage = openNetworkUsage
|
||||
self.openProxy = openProxy
|
||||
@ -47,6 +48,7 @@ private final class DataAndStorageControllerArguments {
|
||||
self.openSaveIncoming = openSaveIncoming
|
||||
self.toggleSaveEditedPhotos = toggleSaveEditedPhotos
|
||||
self.togglePauseMusicOnRecording = togglePauseMusicOnRecording
|
||||
self.toggleRaiseToListen = toggleRaiseToListen
|
||||
self.toggleAutoplayGifs = toggleAutoplayGifs
|
||||
self.toggleAutoplayVideos = toggleAutoplayVideos
|
||||
self.toggleDownloadInBackground = toggleDownloadInBackground
|
||||
@ -74,6 +76,8 @@ public enum DataAndStorageEntryTag: ItemListItemTag, Equatable {
|
||||
case autoplayVideos
|
||||
case saveEditedPhotos
|
||||
case downloadInBackground
|
||||
case pauseMusicOnRecording
|
||||
case raiseToListen
|
||||
case autoSave(AutomaticSaveIncomingPeerType)
|
||||
|
||||
public func isEqual(to other: ItemListItemTag) -> Bool {
|
||||
@ -108,8 +112,10 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
||||
case otherHeader(PresentationTheme, String)
|
||||
case shareSheet(PresentationTheme, String)
|
||||
case saveEditedPhotos(PresentationTheme, String, Bool)
|
||||
case pauseMusicOnRecording(PresentationTheme, String, Bool)
|
||||
case openLinksIn(PresentationTheme, String, String)
|
||||
case pauseMusicOnRecording(PresentationTheme, String, Bool)
|
||||
case raiseToListen(PresentationTheme, String, Bool)
|
||||
case raiseToListenInfo(PresentationTheme, String)
|
||||
|
||||
case connectionHeader(PresentationTheme, String)
|
||||
case connectionProxy(PresentationTheme, String, String)
|
||||
@ -129,7 +135,7 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
||||
return DataAndStorageSection.voiceCalls.rawValue
|
||||
case .autoplayHeader, .autoplayGifs, .autoplayVideos:
|
||||
return DataAndStorageSection.autoPlay.rawValue
|
||||
case .otherHeader, .shareSheet, .saveEditedPhotos, .pauseMusicOnRecording, .openLinksIn:
|
||||
case .otherHeader, .shareSheet, .saveEditedPhotos, .openLinksIn, .pauseMusicOnRecording, .raiseToListen, .raiseToListenInfo:
|
||||
return DataAndStorageSection.other.rawValue
|
||||
case .connectionHeader, .connectionProxy:
|
||||
return DataAndStorageSection.connection.rawValue
|
||||
@ -152,14 +158,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
||||
return 4
|
||||
case .automaticDownloadReset:
|
||||
return 5
|
||||
|
||||
case .autoSaveHeader:
|
||||
return 6
|
||||
case let .autoSaveItem(index, _, _, _, _):
|
||||
return 7 + Int32(index)
|
||||
case .autoSaveInfo:
|
||||
return 20
|
||||
|
||||
case .downloadInBackground:
|
||||
return 21
|
||||
case .downloadInBackgroundInfo:
|
||||
@ -180,16 +184,20 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
||||
return 29
|
||||
case .saveEditedPhotos:
|
||||
return 31
|
||||
case .pauseMusicOnRecording:
|
||||
return 32
|
||||
case .openLinksIn:
|
||||
return 32
|
||||
case .pauseMusicOnRecording:
|
||||
return 33
|
||||
case .connectionHeader:
|
||||
case .raiseToListen:
|
||||
return 34
|
||||
case .connectionProxy:
|
||||
case .raiseToListenInfo:
|
||||
return 35
|
||||
case .enableSensitiveContent:
|
||||
case .connectionHeader:
|
||||
return 36
|
||||
case .connectionProxy:
|
||||
return 37
|
||||
case .enableSensitiveContent:
|
||||
return 38
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,14 +305,26 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .openLinksIn(lhsTheme, lhsText, lhsValue):
|
||||
if case let .openLinksIn(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .pauseMusicOnRecording(lhsTheme, lhsText, lhsValue):
|
||||
if case let .pauseMusicOnRecording(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .openLinksIn(lhsTheme, lhsText, lhsValue):
|
||||
if case let .openLinksIn(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
case let .raiseToListen(lhsTheme, lhsText, lhsValue):
|
||||
if case let .raiseToListen(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .raiseToListenInfo(lhsTheme, lhsText):
|
||||
if case let .raiseToListenInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -420,14 +440,20 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.toggleSaveEditedPhotos(value)
|
||||
}, tag: DataAndStorageEntryTag.saveEditedPhotos)
|
||||
case let .pauseMusicOnRecording(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.togglePauseMusicOnRecording(value)
|
||||
}, tag: DataAndStorageEntryTag.saveEditedPhotos)
|
||||
case let .openLinksIn(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openBrowserSelection()
|
||||
})
|
||||
case let .pauseMusicOnRecording(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.togglePauseMusicOnRecording(value)
|
||||
}, tag: DataAndStorageEntryTag.pauseMusicOnRecording)
|
||||
case let .raiseToListen(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.toggleRaiseToListen(value)
|
||||
}, tag: DataAndStorageEntryTag.raiseToListen)
|
||||
case let .raiseToListenInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
|
||||
case let .downloadInBackground(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.toggleDownloadInBackground(value)
|
||||
@ -646,8 +672,10 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat
|
||||
entries.append(.shareSheet(presentationData.theme, presentationData.strings.ChatSettings_IntentsSettings))
|
||||
}
|
||||
entries.append(.saveEditedPhotos(presentationData.theme, presentationData.strings.Settings_SaveEditedPhotos, data.generatedMediaStoreSettings.storeEditedPhotos))
|
||||
entries.append(.pauseMusicOnRecording(presentationData.theme, presentationData.strings.Settings_PauseMusicOnRecording, data.mediaInputSettings.pauseMusicOnRecording))
|
||||
entries.append(.openLinksIn(presentationData.theme, presentationData.strings.ChatSettings_OpenLinksIn, defaultWebBrowser))
|
||||
entries.append(.pauseMusicOnRecording(presentationData.theme, presentationData.strings.Settings_PauseMusicOnRecording, data.mediaInputSettings.pauseMusicOnRecording))
|
||||
entries.append(.raiseToListen(presentationData.theme, presentationData.strings.Settings_RaiseToListen, data.mediaInputSettings.enableRaiseToSpeak))
|
||||
entries.append(.raiseToListenInfo(presentationData.theme, presentationData.strings.Settings_RaiseToListenInfo))
|
||||
|
||||
let proxyValue: String
|
||||
if let proxySettings = data.proxySettings, let activeServer = proxySettings.activeServer, proxySettings.enabled {
|
||||
@ -878,6 +906,10 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da
|
||||
let _ = updateMediaInputSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||
return current.withUpdatedPauseMusicOnRecording(value)
|
||||
}).start()
|
||||
}, toggleRaiseToListen: { value in
|
||||
let _ = updateMediaInputSettingsInteractively(accountManager: context.sharedContext.accountManager, {
|
||||
$0.withUpdatedEnableRaiseToSpeak(value)
|
||||
}).start()
|
||||
}, toggleAutoplayGifs: { value in
|
||||
let _ = updateMediaDownloadSettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in
|
||||
var settings = settings
|
||||
|
@ -6296,6 +6296,8 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4")
|
||||
let uploadInterface = LegacyLiveUploadInterface(context: context)
|
||||
let signal: SSignal
|
||||
if let url = asset as? URL, url.absoluteString.hasSuffix(".jpg"), let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer {
|
||||
@ -6311,14 +6313,14 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
})
|
||||
signal = durationSignal.map(toSignal: { duration -> SSignal in
|
||||
if let duration = duration as? Double {
|
||||
return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: nil, entityRenderer: entityRenderer)!
|
||||
return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)!
|
||||
} else {
|
||||
return SSignal.single(nil)
|
||||
}
|
||||
})
|
||||
|
||||
} else if let asset = asset as? AVAsset {
|
||||
signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, watcher: uploadInterface, entityRenderer: entityRenderer)!
|
||||
signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, path: tempFile.path, watcher: uploadInterface, entityRenderer: entityRenderer)!
|
||||
} else {
|
||||
signal = SSignal.complete()
|
||||
}
|
||||
@ -6344,6 +6346,8 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
}
|
||||
account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
|
||||
subscriber.putNext(resource)
|
||||
|
||||
EngineTempBox.shared.dispose(tempFile)
|
||||
}
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
|
@ -418,7 +418,7 @@ public func foldLineBreaks(_ text: NSAttributedString) -> NSAttributedString {
|
||||
while true {
|
||||
if let range = remainingString.string.range(of: "\n") {
|
||||
let mappedRange = NSRange(range, in: remainingString.string)
|
||||
lines.append(remainingString.attributedSubstring(from: NSRange(location: 0, length: mappedRange.upperBound)))
|
||||
lines.append(remainingString.attributedSubstring(from: NSRange(location: 0, length: mappedRange.upperBound - 1)))
|
||||
remainingString.replaceCharacters(in: NSRange(location: 0, length: mappedRange.upperBound), with: "")
|
||||
} else {
|
||||
if lines.isEmpty {
|
||||
|
@ -77,7 +77,7 @@ final class BackgroundColorComponent: Component {
|
||||
let itemSize = CGSize(width: 30.0, height: 30.0)
|
||||
let sideInset: CGFloat = 12.0
|
||||
let height: CGFloat = 50.0
|
||||
let delta = (availableSize.width - sideInset * 2.0 - CGFloat(values.count) * itemSize.width) / CGFloat(values.count - 1)
|
||||
let delta = floorToScreenPixels((availableSize.width - sideInset * 2.0 - CGFloat(values.count) * itemSize.width) / CGFloat(values.count - 1))
|
||||
|
||||
for i in 0 ..< values.count {
|
||||
let view: ComponentView<Empty>
|
||||
|
@ -1136,7 +1136,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular)
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular, associatedData: item.associatedData)
|
||||
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
|
@ -391,7 +391,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: context.account.peerId, message: message, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, strings: presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: context.account.peerId, message: message, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, strings: presentationData.strings, associatedData: associatedData)
|
||||
|
||||
var webpageGalleryMediaCount: Int?
|
||||
for media in message.media {
|
||||
|
@ -1800,7 +1800,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
} else {
|
||||
dateFormat = .regular
|
||||
}
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: dateFormat)
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: dateFormat, associatedData: item.associatedData)
|
||||
|
||||
let statusType: ChatMessageDateAndStatusType
|
||||
if incoming {
|
||||
|
@ -167,7 +167,7 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, associatedData: item.associatedData)
|
||||
|
||||
let statusText: String
|
||||
if let callDuration = callDuration, callDuration > 1 {
|
||||
|
@ -188,7 +188,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, associatedData: item.associatedData)
|
||||
|
||||
let statusType: ChatMessageDateAndStatusType?
|
||||
switch position {
|
||||
|
@ -815,7 +815,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
edited = true
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: arguments.context.account.peerId, message: arguments.message, dateTimeFormat: arguments.presentationData.dateTimeFormat, nameDisplayOrder: arguments.presentationData.nameDisplayOrder, strings: arguments.presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: arguments.context.account.peerId, message: arguments.message, dateTimeFormat: arguments.presentationData.dateTimeFormat, nameDisplayOrder: arguments.presentationData.nameDisplayOrder, strings: arguments.presentationData.strings, associatedData: arguments.associatedData)
|
||||
|
||||
let displayReactionsInline = shouldDisplayInlineDateReactions(message: arguments.message, isPremium: arguments.associatedData.isPremium, forceInline: arguments.associatedData.forceInlineReactions)
|
||||
var reactionSettings: ChatMessageDateAndStatusNode.TrailingReactionSettings?
|
||||
|
@ -487,7 +487,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular)
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular, associatedData: item.associatedData)
|
||||
|
||||
let maxDateAndStatusWidth: CGFloat
|
||||
if case .bubble = statusDisplayType {
|
||||
|
@ -205,7 +205,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, associatedData: item.associatedData)
|
||||
|
||||
let statusType: ChatMessageDateAndStatusType?
|
||||
switch position {
|
||||
|
@ -238,7 +238,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, associatedData: item.associatedData)
|
||||
|
||||
let statusType: ChatMessageDateAndStatusType?
|
||||
switch preparePosition {
|
||||
|
@ -1069,7 +1069,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, associatedData: item.associatedData)
|
||||
|
||||
let statusType: ChatMessageDateAndStatusType?
|
||||
switch position {
|
||||
|
@ -71,7 +71,7 @@ class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, associatedData: item.associatedData)
|
||||
|
||||
let statusType: ChatMessageDateAndStatusType?
|
||||
switch position {
|
||||
|
@ -561,7 +561,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular)
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular, associatedData: item.associatedData)
|
||||
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
|
@ -181,7 +181,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
} else {
|
||||
dateFormat = .regular
|
||||
}
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: dateFormat)
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: dateFormat, associatedData: item.associatedData)
|
||||
|
||||
let statusType: ChatMessageDateAndStatusType?
|
||||
var displayStatus = false
|
||||
|
@ -226,7 +226,7 @@ final class ChatTranslationPanelNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
topLanguages.append(contentsOf: popularTranslationLanguages)
|
||||
topLanguages.append("")
|
||||
|
||||
var languages: [(String, String)] = []
|
||||
let languageLocale = Locale(identifier: langCode)
|
||||
@ -322,6 +322,8 @@ private final class TranslationContextReferenceContentSource: ContextReferenceCo
|
||||
}
|
||||
}
|
||||
|
||||
private let separatorHeight: CGFloat = 7.0
|
||||
|
||||
private final class TranslationLanguagesContextMenuContent: ContextControllerItemsContent {
|
||||
private final class BackButtonNode: HighlightTrackingButtonNode {
|
||||
let highlightBackgroundNode: ASDisplayNode
|
||||
@ -445,7 +447,7 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte
|
||||
self.addSubnode(self.titleLabelNode)
|
||||
|
||||
self.highligthedChanged = { [weak self] highlighted in
|
||||
guard let strongSelf = self else {
|
||||
guard let strongSelf = self, let language = strongSelf.language, !language.isEmpty else {
|
||||
return
|
||||
}
|
||||
if highlighted {
|
||||
@ -461,6 +463,9 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte
|
||||
}
|
||||
|
||||
@objc private func pressed() {
|
||||
guard let language = self.language, !language.isEmpty else {
|
||||
return
|
||||
}
|
||||
self.action()
|
||||
}
|
||||
|
||||
@ -476,7 +481,6 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte
|
||||
}
|
||||
|
||||
self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
|
||||
self.separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor
|
||||
|
||||
self.highlightBackgroundNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
@ -487,10 +491,17 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte
|
||||
let titleFrame = CGRect(origin: CGPoint(x: sideInset, y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
self.titleLabelNode.frame = titleFrame
|
||||
|
||||
if language == "" {
|
||||
self.separatorNode.backgroundColor = presentationData.theme.contextMenu.sectionSeparatorColor
|
||||
self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: separatorHeight))
|
||||
self.separatorNode.isHidden = false
|
||||
} else {
|
||||
self.separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor
|
||||
self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: size.height), size: CGSize(width: size.width, height: UIScreenPixel))
|
||||
self.separatorNode.isHidden = isLast
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
private let languages: [(String, String)]
|
||||
@ -537,26 +548,6 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte
|
||||
self.scrollNode.view.delegate = self
|
||||
|
||||
self.clipsToBounds = true
|
||||
|
||||
// self.stateDisposable = (self.listContext.state
|
||||
// |> deliverOnMainQueue).start(next: { [weak self] state in
|
||||
// guard let strongSelf = self else {
|
||||
// return
|
||||
// }
|
||||
// let updatedState = ItemsState(listState: state, readStats: strongSelf.state.readStats)
|
||||
// var animateIn = false
|
||||
// if strongSelf.state.item(at: 0) == nil && updatedState.item(at: 0) != nil {
|
||||
// animateIn = true
|
||||
// }
|
||||
// strongSelf.state = updatedState
|
||||
// strongSelf.animateIn = true
|
||||
// strongSelf.requestUpdate(strongSelf, animateIn ? .animated(duration: 0.2, curve: .easeInOut) : .immediate)
|
||||
// if animateIn {
|
||||
// for (_, itemNode) in strongSelf.itemNodes {
|
||||
// itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
@ -592,9 +583,21 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte
|
||||
let minVisibleIndex = max(0, Int(floor(visibleBounds.minY / itemHeight)))
|
||||
let maxVisibleIndex = Int(ceil(visibleBounds.maxY / itemHeight))
|
||||
|
||||
var separatorIndex = 0
|
||||
for i in 0 ..< self.languages.count {
|
||||
if self.languages[i].0.isEmpty {
|
||||
separatorIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if minVisibleIndex <= maxVisibleIndex {
|
||||
for index in minVisibleIndex ... maxVisibleIndex {
|
||||
let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: CGFloat(index) * itemHeight), size: CGSize(width: size.width, height: itemHeight))
|
||||
let height = self.languages[index].0.isEmpty ? separatorHeight : itemHeight
|
||||
var itemFrame = CGRect(origin: CGPoint(x: 0.0, y: CGFloat(index) * itemHeight), size: CGSize(width: size.width, height: height))
|
||||
if index > separatorIndex {
|
||||
itemFrame.origin.y += separatorHeight - itemHeight
|
||||
}
|
||||
|
||||
if index < self.languages.count {
|
||||
let (languageCode, displayTitle) = self.languages[index]
|
||||
@ -612,7 +615,7 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte
|
||||
self.scrollNode.addSubnode(itemNode)
|
||||
}
|
||||
|
||||
itemNode.update(size: itemFrame.size, presentationData: presentationData, language: languageCode, displayTitle: displayTitle, isLast: index == self.languages.count - 1, syncronousLoad: syncronousLoad)
|
||||
itemNode.update(size: itemFrame.size, presentationData: presentationData, language: languageCode, displayTitle: displayTitle, isLast: index == self.languages.count - 1 || index == separatorIndex - 1, syncronousLoad: syncronousLoad)
|
||||
itemNode.frame = itemFrame
|
||||
}
|
||||
}
|
||||
@ -745,7 +748,7 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte
|
||||
topContentHeight += backButtonFrame.height
|
||||
}
|
||||
if let separatorNode = self.separatorNode {
|
||||
let separatorFrame = CGRect(origin: CGPoint(x: 0.0, y: topContentHeight), size: CGSize(width: constrainedSize.width, height: 7.0))
|
||||
let separatorFrame = CGRect(origin: CGPoint(x: 0.0, y: topContentHeight), size: CGSize(width: constrainedSize.width, height: separatorHeight))
|
||||
separatorNode.backgroundColor = self.presentationData.theme.contextMenu.sectionSeparatorColor
|
||||
transition.updateFrame(node: separatorNode, frame: separatorFrame)
|
||||
topContentHeight += separatorFrame.height
|
||||
|
@ -534,6 +534,8 @@ public func createChannelController(context: AccountContext, mode: CreateChannel
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4")
|
||||
let uploadInterface = LegacyLiveUploadInterface(context: context)
|
||||
let signal: SSignal
|
||||
if let url = asset as? URL, url.absoluteString.hasSuffix(".jpg"), let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer {
|
||||
@ -549,14 +551,14 @@ public func createChannelController(context: AccountContext, mode: CreateChannel
|
||||
})
|
||||
signal = durationSignal.map(toSignal: { duration -> SSignal in
|
||||
if let duration = duration as? Double {
|
||||
return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: nil, entityRenderer: entityRenderer)!
|
||||
return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)!
|
||||
} else {
|
||||
return SSignal.single(nil)
|
||||
}
|
||||
})
|
||||
|
||||
} else if let asset = asset as? AVAsset {
|
||||
signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, watcher: uploadInterface, entityRenderer: entityRenderer)!
|
||||
signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, path: tempFile.path, watcher: uploadInterface, entityRenderer: entityRenderer)!
|
||||
} else {
|
||||
signal = SSignal.complete()
|
||||
}
|
||||
@ -582,6 +584,8 @@ public func createChannelController(context: AccountContext, mode: CreateChannel
|
||||
}
|
||||
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
|
||||
subscriber.putNext(resource)
|
||||
|
||||
EngineTempBox.shared.dispose(tempFile)
|
||||
}
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
|
@ -850,7 +850,6 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
||||
}
|
||||
|
||||
let signal = Signal<TelegramMediaResource?, UploadPeerPhotoError> { subscriber in
|
||||
|
||||
let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in
|
||||
if let paintingData = adjustments.paintingData, paintingData.hasAnimation {
|
||||
return LegacyPaintEntityRenderer(account: context.account, adjustments: adjustments)
|
||||
@ -858,6 +857,8 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4")
|
||||
let uploadInterface = LegacyLiveUploadInterface(context: context)
|
||||
let signal: SSignal
|
||||
if let url = asset as? URL, url.absoluteString.hasSuffix(".jpg"), let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer {
|
||||
@ -873,14 +874,14 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
||||
})
|
||||
signal = durationSignal.map(toSignal: { duration -> SSignal in
|
||||
if let duration = duration as? Double {
|
||||
return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: nil, entityRenderer: entityRenderer)!
|
||||
return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)!
|
||||
} else {
|
||||
return SSignal.single(nil)
|
||||
}
|
||||
})
|
||||
|
||||
} else if let asset = asset as? AVAsset {
|
||||
signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, watcher: uploadInterface, entityRenderer: entityRenderer)!
|
||||
signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, path: tempFile.path, watcher: uploadInterface, entityRenderer: entityRenderer)!
|
||||
} else {
|
||||
signal = SSignal.complete()
|
||||
}
|
||||
@ -906,6 +907,8 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
||||
}
|
||||
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
|
||||
subscriber.putNext(resource)
|
||||
|
||||
EngineTempBox.shared.dispose(tempFile)
|
||||
}
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
|
@ -265,7 +265,8 @@ public func fetchVideoLibraryMediaResource(account: Account, resource: VideoLibr
|
||||
return nil
|
||||
}
|
||||
}
|
||||
let signal = TGMediaVideoConverter.convert(avAsset, adjustments: adjustments, watcher: VideoConversionWatcher(update: { path, size in
|
||||
let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4")
|
||||
let signal = TGMediaVideoConverter.convert(avAsset, adjustments: adjustments, path: tempFile.path, watcher: VideoConversionWatcher(update: { path, size in
|
||||
var value = stat()
|
||||
if stat(path, &value) == 0 {
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) {
|
||||
@ -298,6 +299,8 @@ public func fetchVideoLibraryMediaResource(account: Account, resource: VideoLibr
|
||||
subscriber.putError(.generic)
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
|
||||
EngineTempBox.shared.dispose(tempFile)
|
||||
}
|
||||
}, error: { _ in
|
||||
subscriber.putError(.generic)
|
||||
@ -342,6 +345,7 @@ func fetchLocalFileVideoMediaResource(account: Account, resource: LocalFileVideo
|
||||
adjustments = TGVideoEditAdjustments(dictionary: dict)
|
||||
}
|
||||
}
|
||||
let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4")
|
||||
let updatedSize = Atomic<Int64>(value: 0)
|
||||
let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in
|
||||
if let paintingData = adjustments.paintingData, paintingData.hasAnimation {
|
||||
@ -366,7 +370,7 @@ func fetchLocalFileVideoMediaResource(account: Account, resource: LocalFileVideo
|
||||
|
||||
signal = durationSignal.map(toSignal: { duration -> SSignal in
|
||||
if let duration = duration as? Double {
|
||||
return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: VideoConversionWatcher(update: { path, size in
|
||||
return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, path: tempFile.path, watcher: VideoConversionWatcher(update: { path, size in
|
||||
var value = stat()
|
||||
if stat(path, &value) == 0 {
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) {
|
||||
@ -388,7 +392,7 @@ func fetchLocalFileVideoMediaResource(account: Account, resource: LocalFileVideo
|
||||
signal = SSignal.single(nil)
|
||||
}
|
||||
} else {
|
||||
signal = TGMediaVideoConverter.convert(avAsset, adjustments: adjustments, watcher: VideoConversionWatcher(update: { path, size in
|
||||
signal = TGMediaVideoConverter.convert(avAsset, adjustments: adjustments, path: tempFile.path, watcher: VideoConversionWatcher(update: { path, size in
|
||||
var value = stat()
|
||||
if stat(path, &value) == 0 {
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) {
|
||||
@ -430,6 +434,8 @@ func fetchLocalFileVideoMediaResource(account: Account, resource: LocalFileVideo
|
||||
subscriber.putNext(.dataPart(resourceOffset: range!.lowerBound, data: data, range: range!, complete: false))
|
||||
subscriber.putNext(.replaceHeader(data: data, range: 0 ..< 1024))
|
||||
subscriber.putNext(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: true))
|
||||
|
||||
EngineTempBox.shared.dispose(tempFile)
|
||||
}
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
|
@ -5,6 +5,7 @@ import Display
|
||||
import SwiftSignalKit
|
||||
import UniversalMediaPlayer
|
||||
import LegacyComponents
|
||||
import UIKitRuntimeUtils
|
||||
|
||||
private final class InstantVideoRadialStatusNodeParameters: NSObject {
|
||||
let color: UIColor
|
||||
@ -124,6 +125,8 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele
|
||||
strongSelf.statusValue = status
|
||||
}
|
||||
})
|
||||
|
||||
self.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
@ -2510,10 +2510,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
func updateAvatarIsHidden(entry: AvatarGalleryEntry?) {
|
||||
if let entry = entry {
|
||||
self.avatarListNode.avatarContainerNode.containerNode.isHidden = entry == self.avatarListNode.listContainerNode.galleryEntries.first
|
||||
self.editingContentNode.isHidden = entry == self.avatarListNode.listContainerNode.galleryEntries.first
|
||||
self.editingContentNode.avatarNode.isHidden = entry == self.avatarListNode.listContainerNode.galleryEntries.first
|
||||
} else {
|
||||
self.avatarListNode.avatarContainerNode.containerNode.isHidden = false
|
||||
self.editingContentNode.isHidden = false
|
||||
self.editingContentNode.avatarNode.isHidden = false
|
||||
}
|
||||
self.avatarListNode.listContainerNode.updateEntryIsHidden(entry: entry)
|
||||
}
|
||||
|
@ -7058,6 +7058,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4")
|
||||
let uploadInterface = LegacyLiveUploadInterface(context: context)
|
||||
let signal: SSignal
|
||||
if let url = asset as? URL, url.absoluteString.hasSuffix(".jpg"), let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer {
|
||||
@ -7073,14 +7075,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
})
|
||||
signal = durationSignal.map(toSignal: { duration -> SSignal in
|
||||
if let duration = duration as? Double {
|
||||
return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: nil, entityRenderer: entityRenderer)!
|
||||
return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)!
|
||||
} else {
|
||||
return SSignal.single(nil)
|
||||
}
|
||||
})
|
||||
|
||||
} else if let asset = asset as? AVAsset {
|
||||
signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, watcher: uploadInterface, entityRenderer: entityRenderer)!
|
||||
signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, path: tempFile.path, watcher: uploadInterface, entityRenderer: entityRenderer)!
|
||||
} else {
|
||||
signal = SSignal.complete()
|
||||
}
|
||||
@ -7106,6 +7107,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
|
||||
subscriber.putNext(resource)
|
||||
|
||||
EngineTempBox.shared.dispose(tempFile)
|
||||
}
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
|
@ -5,6 +5,7 @@ import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import TelegramStringFormatting
|
||||
import LocalizedPeerData
|
||||
import AccountContext
|
||||
|
||||
enum MessageTimestampStatusFormat {
|
||||
case regular
|
||||
@ -28,7 +29,7 @@ private func dateStringForDay(strings: PresentationStrings, dateTimeFormat: Pres
|
||||
}
|
||||
}
|
||||
|
||||
func stringForMessageTimestampStatus(accountPeerId: PeerId, message: Message, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, strings: PresentationStrings, format: MessageTimestampStatusFormat = .regular) -> String {
|
||||
func stringForMessageTimestampStatus(accountPeerId: PeerId, message: Message, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, strings: PresentationStrings, format: MessageTimestampStatusFormat = .regular, associatedData: ChatMessageItemAssociatedData) -> String {
|
||||
if let adAttribute = message.adAttribute {
|
||||
switch adAttribute.messageType {
|
||||
case .sponsored:
|
||||
@ -86,6 +87,10 @@ func stringForMessageTimestampStatus(accountPeerId: PeerId, message: Message, da
|
||||
}
|
||||
}
|
||||
|
||||
if let subject = associatedData.subject, case .forwardedMessages = subject {
|
||||
authorTitle = nil
|
||||
}
|
||||
|
||||
if case .regular = format {
|
||||
if let authorTitle = authorTitle, !authorTitle.isEmpty {
|
||||
dateText = "\(authorTitle), \(dateText)"
|
||||
|
@ -8,7 +8,7 @@ public struct MediaInputSettings: Codable, Equatable {
|
||||
public let pauseMusicOnRecording: Bool
|
||||
|
||||
public static var defaultSettings: MediaInputSettings {
|
||||
return MediaInputSettings(enableRaiseToSpeak: true, pauseMusicOnRecording: false)
|
||||
return MediaInputSettings(enableRaiseToSpeak: true, pauseMusicOnRecording: true)
|
||||
}
|
||||
|
||||
public init(enableRaiseToSpeak: Bool, pauseMusicOnRecording: Bool) {
|
||||
@ -20,14 +20,14 @@ public struct MediaInputSettings: Codable, Equatable {
|
||||
let container = try decoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
self.enableRaiseToSpeak = (try container.decode(Int32.self, forKey: "enableRaiseToSpeak")) != 0
|
||||
self.pauseMusicOnRecording = (try container.decodeIfPresent(Int32.self, forKey: "pauseMusicOnRecording") ?? 0) != 0
|
||||
self.pauseMusicOnRecording = (try container.decodeIfPresent(Int32.self, forKey: "pauseMusicOnRecording_v2") ?? 0) != 0
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
try container.encode((self.enableRaiseToSpeak ? 1 : 0) as Int32, forKey: "enableRaiseToSpeak")
|
||||
try container.encode((self.pauseMusicOnRecording ? 1 : 0) as Int32, forKey: "pauseMusicOnRecording")
|
||||
try container.encode((self.pauseMusicOnRecording ? 1 : 0) as Int32, forKey: "pauseMusicOnRecording_v2")
|
||||
}
|
||||
|
||||
public static func ==(lhs: MediaInputSettings, rhs: MediaInputSettings) -> Bool {
|
||||
|
Loading…
x
Reference in New Issue
Block a user