mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' into postbox-refactoring-1
This commit is contained in:
commit
200d908724
@ -5837,8 +5837,11 @@ Sorry for the inconvenience.";
|
|||||||
"VoiceChat.StartRecordingText" = "Do you want to start recording this chat and save the result into an audio file?\n\nOther members will see that the chat is being recorded.";
|
"VoiceChat.StartRecordingText" = "Do you want to start recording this chat and save the result into an audio file?\n\nOther members will see that the chat is being recorded.";
|
||||||
"LiveStream.StartRecordingText" = "Do you want to start recording this live stream and save the result into an audio file?\n\nOther members will see that the chat is being recorded.";
|
"LiveStream.StartRecordingText" = "Do you want to start recording this live stream and save the result into an audio file?\n\nOther members will see that the chat is being recorded.";
|
||||||
"VoiceChat.StartRecordingStart" = "Start";
|
"VoiceChat.StartRecordingStart" = "Start";
|
||||||
|
"VoiceChat.StartRecordingTextVideo" = "Do you want to start recording this chat and save the result into a video file?\n\nOther members will see that the chat is being recorded.";
|
||||||
|
"LiveStream.StartRecordingTextVideo" = "Do you want to start recording this live stream and save the result into a video file?\n\nOther members will see that the chat is being recorded.";
|
||||||
|
|
||||||
"VoiceChat.RecordingTitlePlaceholder" = "Audio Title (Optional)";
|
"VoiceChat.RecordingTitlePlaceholder" = "Audio Title (Optional)";
|
||||||
|
"VoiceChat.RecordingTitlePlaceholderVideo" = "Video Title (Optional)";
|
||||||
"VoiceChat.RecordingStarted" = "Voice chat recording started";
|
"VoiceChat.RecordingStarted" = "Voice chat recording started";
|
||||||
"LiveStream.RecordingStarted" = "Live stream recording started";
|
"LiveStream.RecordingStarted" = "Live stream recording started";
|
||||||
"VoiceChat.RecordingInProgress" = "Voice chat is being recorded";
|
"VoiceChat.RecordingInProgress" = "Voice chat is being recorded";
|
||||||
@ -6715,12 +6718,15 @@ Sorry for the inconvenience.";
|
|||||||
"UserInfo.ChangeColors" = "Change Colors";
|
"UserInfo.ChangeColors" = "Change Colors";
|
||||||
|
|
||||||
"Conversation.Theme.Title" = "Select Theme";
|
"Conversation.Theme.Title" = "Select Theme";
|
||||||
|
"Conversation.Theme.Subtitle" = "Theme will be also applied for %@";
|
||||||
"Conversation.Theme.Apply" = "Apply Theme";
|
"Conversation.Theme.Apply" = "Apply Theme";
|
||||||
"Conversation.Theme.NoTheme" = "No\nTheme";
|
"Conversation.Theme.NoTheme" = "No\nTheme";
|
||||||
"Conversation.Theme.Reset" = "Reset Theme for This Chat";
|
"Conversation.Theme.Reset" = "Reset Theme for This Chat";
|
||||||
"Conversation.Theme.DontSetTheme" = "Do Not Set Theme";
|
"Conversation.Theme.DontSetTheme" = "Do Not Set Theme";
|
||||||
"Conversation.Theme.SwitchToDark" = "Switch to dark appearance";
|
"Conversation.Theme.SwitchToDark" = "Switch to dark appearance";
|
||||||
"Conversation.Theme.SwitchToLight" = "Switch to light appearance";
|
"Conversation.Theme.SwitchToLight" = "Switch to light appearance";
|
||||||
|
"Conversation.Theme.PreviewDark" = "Tap to see how chat will appear to\n%@ when using night mode.";
|
||||||
|
"Conversation.Theme.PreviewLight" = "Tap to see how chat will appear to\n%@ when using day mode.";
|
||||||
"Conversation.Theme.DismissAlert" = "Do you want to apply the selected theme to the chat?";
|
"Conversation.Theme.DismissAlert" = "Do you want to apply the selected theme to the chat?";
|
||||||
"Conversation.Theme.DismissAlertApply" = "Apply";
|
"Conversation.Theme.DismissAlertApply" = "Apply";
|
||||||
|
|
||||||
|
@ -2657,38 +2657,48 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
let controller = VoiceChatRecordingSetupController(context: strongSelf.context, completion: { [weak self] videoOrientation in
|
let controller = VoiceChatRecordingSetupController(context: strongSelf.context, completion: { [weak self] videoOrientation in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.call.setShouldBeRecording(true, title: "", videoOrientation: videoOrientation)
|
let title: String
|
||||||
|
let text: String
|
||||||
|
let placeholder: String
|
||||||
|
if let _ = videoOrientation {
|
||||||
|
placeholder = strongSelf.presentationData.strings.VoiceChat_RecordingTitlePlaceholderVideo
|
||||||
|
} else {
|
||||||
|
placeholder = strongSelf.presentationData.strings.VoiceChat_RecordingTitlePlaceholder
|
||||||
|
}
|
||||||
|
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
|
||||||
|
title = strongSelf.presentationData.strings.LiveStream_StartRecordingTitle
|
||||||
|
if let _ = videoOrientation {
|
||||||
|
text = strongSelf.presentationData.strings.LiveStream_StartRecordingTextVideo
|
||||||
|
} else {
|
||||||
|
text = strongSelf.presentationData.strings.LiveStream_StartRecordingText
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
title = strongSelf.presentationData.strings.VoiceChat_StartRecordingTitle
|
||||||
|
if let _ = videoOrientation {
|
||||||
|
text = strongSelf.presentationData.strings.VoiceChat_StartRecordingTextVideo
|
||||||
|
} else {
|
||||||
|
text = strongSelf.presentationData.strings.VoiceChat_StartRecordingText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.presentUndoOverlay(content: .voiceChatRecording(text: text), action: { _ in return false })
|
let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: title, text: text, placeholder: placeholder, value: nil, maxLength: 40, apply: { title in
|
||||||
strongSelf.call.playTone(.recordingStarted)
|
if let strongSelf = self, let title = title {
|
||||||
|
strongSelf.call.setShouldBeRecording(true, title: title, videoOrientation: videoOrientation)
|
||||||
|
|
||||||
|
let text: String
|
||||||
|
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
|
||||||
|
text = strongSelf.presentationData.strings.LiveStream_RecordingStarted
|
||||||
|
} else {
|
||||||
|
text = strongSelf.presentationData.strings.VoiceChat_RecordingStarted
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.presentUndoOverlay(content: .voiceChatRecording(text: text), action: { _ in return false })
|
||||||
|
strongSelf.call.playTone(.recordingStarted)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
strongSelf.controller?.present(controller, in: .window(.root))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// let title: String
|
|
||||||
// let text: String
|
|
||||||
// if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
|
|
||||||
// title = strongSelf.presentationData.strings.LiveStream_StartRecordingTitle
|
|
||||||
// text = strongSelf.presentationData.strings.LiveStream_StartRecordingText
|
|
||||||
// } else {
|
|
||||||
// title = strongSelf.presentationData.strings.VoiceChat_StartRecordingTitle
|
|
||||||
// text = strongSelf.presentationData.strings.VoiceChat_StartRecordingText
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: title, text: text, placeholder: strongSelf.presentationData.strings.VoiceChat_RecordingTitlePlaceholder, value: nil, maxLength: 40, apply: { title in
|
|
||||||
// if let strongSelf = self, let title = title {
|
|
||||||
// strongSelf.call.setShouldBeRecording(true, title: title, videoOrientation: nil)
|
|
||||||
//
|
|
||||||
// let text: String
|
|
||||||
// if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
|
|
||||||
// text = strongSelf.presentationData.strings.LiveStream_RecordingStarted
|
|
||||||
// } else {
|
|
||||||
// text = strongSelf.presentationData.strings.VoiceChat_RecordingStarted
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// strongSelf.presentUndoOverlay(content: .voiceChatRecording(text: text), action: { _ in return false })
|
|
||||||
// strongSelf.call.playTone(.recordingStarted)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
self?.controller?.present(controller, in: .window(.root))
|
self?.controller?.present(controller, in: .window(.root))
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,9 @@ public struct EmojiInteraction: Equatable {
|
|||||||
guard let item = decodedData as? [String: Any] else {
|
guard let item = decodedData as? [String: Any] else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
guard let version = item["v"] as? Int, version == 1 else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
guard let animationsArray = item["a"] as? [Any] else {
|
guard let animationsArray = item["a"] as? [Any] else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,37 @@ public final class ApplicationSpecificTimestampNotice: NoticeEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class ApplicationSpecificTimestampAndCounterNotice: NoticeEntry {
|
||||||
|
public let counter: Int32
|
||||||
|
public let timestamp: Int32
|
||||||
|
|
||||||
|
public init(counter: Int32, timestamp: Int32) {
|
||||||
|
self.counter = counter
|
||||||
|
self.timestamp = timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(decoder: PostboxDecoder) {
|
||||||
|
self.counter = decoder.decodeInt32ForKey("v", orElse: 0)
|
||||||
|
self.timestamp = decoder.decodeInt32ForKey("t", orElse: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
|
encoder.encodeInt32(self.counter, forKey: "v")
|
||||||
|
encoder.encodeInt32(self.timestamp, forKey: "t")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func isEqual(to: NoticeEntry) -> Bool {
|
||||||
|
if let to = to as? ApplicationSpecificTimestampAndCounterNotice {
|
||||||
|
if self.counter != to.counter || self.timestamp != to.timestamp {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final class ApplicationSpecificInt64ArrayNotice: NoticeEntry {
|
public final class ApplicationSpecificInt64ArrayNotice: NoticeEntry {
|
||||||
public let values: [Int64]
|
public let values: [Int64]
|
||||||
|
|
||||||
@ -167,9 +198,10 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
|
|||||||
case locationProximityAlertTip = 20
|
case locationProximityAlertTip = 20
|
||||||
case nextChatSuggestionTip = 21
|
case nextChatSuggestionTip = 21
|
||||||
case dismissedTrendingStickerPacks = 22
|
case dismissedTrendingStickerPacks = 22
|
||||||
case chatSpecificThemesDarkPreviewTip = 23
|
|
||||||
case chatForwardOptionsTip = 24
|
case chatForwardOptionsTip = 24
|
||||||
case messageViewsPrivacyTips = 25
|
case messageViewsPrivacyTips = 25
|
||||||
|
case chatSpecificThemeLightPreviewTip = 26
|
||||||
|
case chatSpecificThemeDarkPreviewTip = 27
|
||||||
|
|
||||||
var key: ValueBoxKey {
|
var key: ValueBoxKey {
|
||||||
let v = ValueBoxKey(length: 4)
|
let v = ValueBoxKey(length: 4)
|
||||||
@ -314,8 +346,12 @@ private struct ApplicationSpecificNoticeKeys {
|
|||||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedTrendingStickerPacks.key)
|
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedTrendingStickerPacks.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func chatSpecificThemesDarkPreviewTip() -> NoticeEntryKey {
|
static func chatSpecificThemeLightPreviewTip() -> NoticeEntryKey {
|
||||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatSpecificThemesDarkPreviewTip.key)
|
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatSpecificThemeLightPreviewTip.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func chatSpecificThemeDarkPreviewTip() -> NoticeEntryKey {
|
||||||
|
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatSpecificThemeDarkPreviewTip.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func chatForwardOptionsTip() -> NoticeEntryKey {
|
static func chatForwardOptionsTip() -> NoticeEntryKey {
|
||||||
@ -850,26 +886,51 @@ public struct ApplicationSpecificNotice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func getChatSpecificThemesDarkPreviewTip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
|
public static func getChatSpecificThemeLightPreviewTip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<(Int32, Int32), NoError> {
|
||||||
return accountManager.transaction { transaction -> Int32 in
|
return accountManager.transaction { transaction -> (Int32, Int32) in
|
||||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatSpecificThemesDarkPreviewTip()) as? ApplicationSpecificCounterNotice {
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatSpecificThemeLightPreviewTip()) as? ApplicationSpecificTimestampAndCounterNotice {
|
||||||
return value.value
|
return (value.counter, value.timestamp)
|
||||||
} else {
|
} else {
|
||||||
return 0
|
return (0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func incrementChatSpecificThemesDarkPreviewTip(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int = 1) -> Signal<Int, NoError> {
|
public static func incrementChatSpecificThemeLightPreviewTip(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int = 1, timestamp: Int32) -> Signal<Int, NoError> {
|
||||||
return accountManager.transaction { transaction -> Int in
|
return accountManager.transaction { transaction -> Int in
|
||||||
var currentValue: Int32 = 0
|
var currentValue: Int32 = 0
|
||||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatSpecificThemesDarkPreviewTip()) as? ApplicationSpecificCounterNotice {
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatSpecificThemeLightPreviewTip()) as? ApplicationSpecificTimestampAndCounterNotice {
|
||||||
currentValue = value.value
|
currentValue = value.counter
|
||||||
}
|
}
|
||||||
let previousValue = currentValue
|
let previousValue = currentValue
|
||||||
currentValue += Int32(count)
|
currentValue += Int32(count)
|
||||||
|
|
||||||
transaction.setNotice(ApplicationSpecificNoticeKeys.chatSpecificThemesDarkPreviewTip(), ApplicationSpecificCounterNotice(value: currentValue))
|
transaction.setNotice(ApplicationSpecificNoticeKeys.chatSpecificThemeLightPreviewTip(), ApplicationSpecificTimestampAndCounterNotice(counter: currentValue, timestamp: timestamp))
|
||||||
|
|
||||||
|
return Int(previousValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func getChatSpecificThemeDarkPreviewTip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<(Int32, Int32), NoError> {
|
||||||
|
return accountManager.transaction { transaction -> (Int32, Int32) in
|
||||||
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatSpecificThemeDarkPreviewTip()) as? ApplicationSpecificTimestampAndCounterNotice {
|
||||||
|
return (value.counter, value.timestamp)
|
||||||
|
} else {
|
||||||
|
return (0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func incrementChatSpecificThemeDarkPreviewTip(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int = 1, timestamp: Int32) -> Signal<Int, NoError> {
|
||||||
|
return accountManager.transaction { transaction -> Int in
|
||||||
|
var currentValue: Int32 = 0
|
||||||
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatSpecificThemeDarkPreviewTip()) as? ApplicationSpecificTimestampAndCounterNotice {
|
||||||
|
currentValue = value.counter
|
||||||
|
}
|
||||||
|
let previousValue = currentValue
|
||||||
|
currentValue += Int32(count)
|
||||||
|
|
||||||
|
transaction.setNotice(ApplicationSpecificNoticeKeys.chatSpecificThemeDarkPreviewTip(), ApplicationSpecificTimestampAndCounterNotice(counter: currentValue, timestamp: timestamp))
|
||||||
|
|
||||||
return Int(previousValue)
|
return Int(previousValue)
|
||||||
}
|
}
|
||||||
|
@ -7435,15 +7435,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
strongSelf.chatDisplayNode.historyNode.forEachVisibleItemNode({ itemNode in
|
strongSelf.chatDisplayNode.historyNode.forEachVisibleItemNode({ itemNode in
|
||||||
if !found, let itemNode = itemNode as? ChatMessageAnimatedStickerItemNode, let item = itemNode.item {
|
if !found, let itemNode = itemNode as? ChatMessageAnimatedStickerItemNode, let item = itemNode.item {
|
||||||
if item.message.id == messageId {
|
if item.message.id == messageId {
|
||||||
for animation in interaction.animations {
|
itemNode.playEmojiInteraction(interaction)
|
||||||
if animation.timeOffset > 0.0 {
|
|
||||||
Queue.mainQueue().after(Double(animation.timeOffset)) {
|
|
||||||
itemNode.playAdditionalAnimation(index: animation.index)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
itemNode.playAdditionalAnimation(index: animation.index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -13436,7 +13428,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectedEmoticon: String?
|
let selectedEmoticon: String?
|
||||||
if let cachedData = cachedData as? CachedUserData {
|
if let cachedData = cachedData as? CachedUserData {
|
||||||
selectedEmoticon = cachedData.themeEmoticon
|
selectedEmoticon = cachedData.themeEmoticon
|
||||||
@ -13448,7 +13440,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
selectedEmoticon = nil
|
selectedEmoticon = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = ChatThemeScreen(context: context, updatedPresentationData: strongSelf.updatedPresentationData, animatedEmojiStickers: animatedEmojiStickers, initiallySelectedEmoticon: selectedEmoticon, previewTheme: { [weak self] emoticon, dark in
|
let controller = ChatThemeScreen(context: context, updatedPresentationData: strongSelf.updatedPresentationData, animatedEmojiStickers: animatedEmojiStickers, initiallySelectedEmoticon: selectedEmoticon, peerName: strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer?.compactDisplayTitle ?? "", previewTheme: { [weak self] emoticon, dark in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.presentCrossfadeSnapshot(delay: 0.2)
|
strongSelf.presentCrossfadeSnapshot(delay: 0.2)
|
||||||
strongSelf.themeEmoticonAndDarkAppearancePreviewPromise.set(.single((emoticon, dark)))
|
strongSelf.themeEmoticonAndDarkAppearancePreviewPromise.set(.single((emoticon, dark)))
|
||||||
|
@ -204,6 +204,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
|
|
||||||
private var forceStopAnimations = false
|
private var forceStopAnimations = false
|
||||||
|
|
||||||
|
private var hapticFeedback: HapticFeedback?
|
||||||
private var haptic: EmojiHaptic?
|
private var haptic: EmojiHaptic?
|
||||||
private var mediaPlayer: MediaPlayer?
|
private var mediaPlayer: MediaPlayer?
|
||||||
private let mediaStatusDisposable = MetaDisposable()
|
private let mediaStatusDisposable = MetaDisposable()
|
||||||
@ -1352,6 +1353,52 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
item.context.account.updateLocalInputActivity(peerId: PeerActivitySpace(peerId: item.message.id.peerId, category: .global), activity: .interactingWithEmoji(emoticon: textEmoji, messageId: item.message.id, interaction: EmojiInteraction(animations: animations)), isPresent: true)
|
item.context.account.updateLocalInputActivity(peerId: PeerActivitySpace(peerId: item.message.id.peerId, category: .global), activity: .interactingWithEmoji(emoticon: textEmoji, messageId: item.message.id, interaction: EmojiInteraction(animations: animations)), isPresent: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func playEmojiInteraction(_ interaction: EmojiInteraction) {
|
||||||
|
guard interaction.animations.count <= 7 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var hapticFeedback: HapticFeedback
|
||||||
|
if let current = self.hapticFeedback {
|
||||||
|
hapticFeedback = current
|
||||||
|
} else {
|
||||||
|
hapticFeedback = HapticFeedback()
|
||||||
|
self.hapticFeedback = hapticFeedback
|
||||||
|
}
|
||||||
|
|
||||||
|
var playHaptic = true
|
||||||
|
if let existingHaptic = self.haptic, existingHaptic.active {
|
||||||
|
playHaptic = false
|
||||||
|
}
|
||||||
|
hapticFeedback.prepareImpact(.light)
|
||||||
|
hapticFeedback.prepareImpact(.medium)
|
||||||
|
|
||||||
|
var index = 0
|
||||||
|
for animation in interaction.animations {
|
||||||
|
if animation.timeOffset > 0.0 {
|
||||||
|
Queue.mainQueue().after(Double(animation.timeOffset)) {
|
||||||
|
self.playAdditionalAnimation(index: animation.index)
|
||||||
|
if playHaptic {
|
||||||
|
let style: ImpactHapticFeedbackStyle
|
||||||
|
if index == 1 {
|
||||||
|
style = .medium
|
||||||
|
} else {
|
||||||
|
style = [.light, .medium].randomElement() ?? .medium
|
||||||
|
}
|
||||||
|
hapticFeedback.impact(style)
|
||||||
|
}
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.playAdditionalAnimation(index: animation.index)
|
||||||
|
if playHaptic {
|
||||||
|
hapticFeedback.impact(interaction.animations.count > 1 ? .light : .medium)
|
||||||
|
}
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func playAdditionalAnimation(index: Int) {
|
func playAdditionalAnimation(index: Int) {
|
||||||
guard let item = self.item else {
|
guard let item = self.item else {
|
||||||
return
|
return
|
||||||
@ -1523,8 +1570,32 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
firstScalar = UnicodeScalar(heart)!
|
firstScalar = UnicodeScalar(heart)!
|
||||||
}
|
}
|
||||||
return .optionalAction({
|
return .optionalAction({
|
||||||
|
var haptic: EmojiHaptic?
|
||||||
|
if let current = self.haptic {
|
||||||
|
haptic = current
|
||||||
|
} else {
|
||||||
|
if firstScalar.value == heart {
|
||||||
|
haptic = HeartbeatHaptic()
|
||||||
|
} else if firstScalar.value == coffin {
|
||||||
|
haptic = CoffinHaptic()
|
||||||
|
} else if firstScalar.value == peach {
|
||||||
|
haptic = PeachHaptic()
|
||||||
|
}
|
||||||
|
haptic?.enabled = true
|
||||||
|
self.haptic = haptic
|
||||||
|
}
|
||||||
|
|
||||||
if let animationItems = item.associatedData.additionalAnimatedEmojiStickers[originalTextEmoji] {
|
if let animationItems = item.associatedData.additionalAnimatedEmojiStickers[originalTextEmoji] {
|
||||||
let syncAnimations = item.message.id.peerId.namespace == Namespaces.Peer.CloudUser
|
let syncAnimations = item.message.id.peerId.namespace == Namespaces.Peer.CloudUser
|
||||||
|
let playHaptic = haptic == nil
|
||||||
|
|
||||||
|
var hapticFeedback: HapticFeedback
|
||||||
|
if let current = self.hapticFeedback {
|
||||||
|
hapticFeedback = current
|
||||||
|
} else {
|
||||||
|
hapticFeedback = HapticFeedback()
|
||||||
|
self.hapticFeedback = hapticFeedback
|
||||||
|
}
|
||||||
|
|
||||||
if syncAnimations {
|
if syncAnimations {
|
||||||
self.startAdditionalAnimationsCommitTimer()
|
self.startAdditionalAnimationsCommitTimer()
|
||||||
@ -1539,11 +1610,25 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
availableAnimations.removeValue(forKey: previousIndex)
|
availableAnimations.removeValue(forKey: previousIndex)
|
||||||
}
|
}
|
||||||
if let (_, previousTimestamp) = previousAnimation {
|
if let (_, previousTimestamp) = previousAnimation {
|
||||||
delay = min(0.2, max(0.0, previousTimestamp + 0.2 - timestamp))
|
delay = min(0.15, max(0.0, previousTimestamp + 0.15 - timestamp))
|
||||||
}
|
}
|
||||||
if let index = availableAnimations.randomElement()?.0 {
|
if let index = availableAnimations.randomElement()?.0 {
|
||||||
if delay > 0.0 {
|
if delay > 0.0 {
|
||||||
Queue.mainQueue().after(delay) {
|
Queue.mainQueue().after(delay) {
|
||||||
|
if playHaptic {
|
||||||
|
if previousAnimation == nil {
|
||||||
|
hapticFeedback.impact(.light)
|
||||||
|
} else {
|
||||||
|
let style: ImpactHapticFeedbackStyle
|
||||||
|
if self.enqueuedAdditionalAnimations.count == 1 {
|
||||||
|
style = .medium
|
||||||
|
} else {
|
||||||
|
style = [.light, .medium].randomElement() ?? .medium
|
||||||
|
}
|
||||||
|
hapticFeedback.impact(style)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if syncAnimations {
|
if syncAnimations {
|
||||||
self.enqueuedAdditionalAnimations.append((index, timestamp + delay))
|
self.enqueuedAdditionalAnimations.append((index, timestamp + delay))
|
||||||
}
|
}
|
||||||
@ -1554,6 +1639,20 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if playHaptic {
|
||||||
|
if previousAnimation == nil {
|
||||||
|
hapticFeedback.impact(.light)
|
||||||
|
} else {
|
||||||
|
let style: ImpactHapticFeedbackStyle
|
||||||
|
if self.enqueuedAdditionalAnimations.count == 1 {
|
||||||
|
style = .medium
|
||||||
|
} else {
|
||||||
|
style = [.light, .medium].randomElement() ?? .medium
|
||||||
|
}
|
||||||
|
hapticFeedback.impact(style)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if syncAnimations {
|
if syncAnimations {
|
||||||
self.enqueuedAdditionalAnimations.append((index, timestamp))
|
self.enqueuedAdditionalAnimations.append((index, timestamp))
|
||||||
}
|
}
|
||||||
@ -1582,22 +1681,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
strongSelf.mediaStatusDisposable.set((mediaPlayer.status
|
strongSelf.mediaStatusDisposable.set((mediaPlayer.status
|
||||||
|> deliverOnMainQueue).start(next: { [weak self, weak animationNode] status in
|
|> deliverOnMainQueue).start(next: { [weak self, weak animationNode] status in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
|
||||||
var haptic: EmojiHaptic?
|
|
||||||
if let current = strongSelf.haptic {
|
|
||||||
haptic = current
|
|
||||||
} else {
|
|
||||||
if firstScalar.value == heart {
|
|
||||||
haptic = HeartbeatHaptic()
|
|
||||||
} else if firstScalar.value == coffin {
|
|
||||||
haptic = CoffinHaptic()
|
|
||||||
} else if firstScalar.value == peach {
|
|
||||||
haptic = PeachHaptic()
|
|
||||||
}
|
|
||||||
haptic?.enabled = true
|
|
||||||
strongSelf.haptic = haptic
|
|
||||||
}
|
|
||||||
|
|
||||||
if let haptic = haptic, !haptic.active {
|
if let haptic = haptic, !haptic.active {
|
||||||
haptic.start(time: 0.0)
|
haptic.start(time: 0.0)
|
||||||
}
|
}
|
||||||
|
@ -893,8 +893,4 @@ public class ChatMessageItemView: ListViewItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override public var preferredAnimationCurve: (CGFloat) -> CGFloat {
|
|
||||||
return listViewAnimationCurveSystem
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@ public final class ChatMessageTransitionNode: ASDisplayNode {
|
|||||||
case videoMessage(VideoMessage)
|
case videoMessage(VideoMessage)
|
||||||
case mediaInput(MediaInput)
|
case mediaInput(MediaInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class DecorationItemNode: ASDisplayNode {
|
final class DecorationItemNode: ASDisplayNode {
|
||||||
let itemNode: ChatMessageItemView
|
let itemNode: ChatMessageItemView
|
||||||
private let contentNode: ASDisplayNode
|
private let contentNode: ASDisplayNode
|
||||||
@ -640,13 +640,15 @@ public final class ChatMessageTransitionNode: ASDisplayNode {
|
|||||||
func add(decorationNode: ASDisplayNode, itemNode: ChatMessageItemView) -> DecorationItemNode {
|
func add(decorationNode: ASDisplayNode, itemNode: ChatMessageItemView) -> DecorationItemNode {
|
||||||
let decorationItemNode = DecorationItemNode(itemNode: itemNode, contentNode: decorationNode, getContentAreaInScreenSpace: self.getContentAreaInScreenSpace)
|
let decorationItemNode = DecorationItemNode(itemNode: itemNode, contentNode: decorationNode, getContentAreaInScreenSpace: self.getContentAreaInScreenSpace)
|
||||||
decorationItemNode.updateLayout(size: self.bounds.size)
|
decorationItemNode.updateLayout(size: self.bounds.size)
|
||||||
|
|
||||||
self.decorationItemNodes.append(decorationItemNode)
|
self.decorationItemNodes.append(decorationItemNode)
|
||||||
|
self.addSubnode(decorationItemNode)
|
||||||
|
|
||||||
let overlayController = OverlayTransitionContainerController()
|
// let overlayController = OverlayTransitionContainerController()
|
||||||
overlayController.displayNode.isUserInteractionEnabled = false
|
// overlayController.displayNode.isUserInteractionEnabled = false
|
||||||
overlayController.displayNode.addSubnode(decorationItemNode)
|
// overlayController.displayNode.addSubnode(decorationItemNode)
|
||||||
decorationItemNode.overlayController = overlayController
|
// decorationItemNode.overlayController = overlayController
|
||||||
itemNode.item?.context.sharedContext.mainWindow?.presentInGlobalOverlay(overlayController)
|
// itemNode.item?.context.sharedContext.mainWindow?.presentInGlobalOverlay(overlayController)
|
||||||
|
|
||||||
return decorationItemNode
|
return decorationItemNode
|
||||||
}
|
}
|
||||||
|
@ -289,8 +289,8 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
actionTitle = item.presentationData.strings.Conversation_ViewGroup
|
actionTitle = item.presentationData.strings.Conversation_ViewGroup
|
||||||
case "telegram_message":
|
case "telegram_message":
|
||||||
actionTitle = item.presentationData.strings.Conversation_ViewMessage
|
actionTitle = item.presentationData.strings.Conversation_ViewMessage
|
||||||
case "telegram_voicechat", "telegram_livestream", "telegram_videochat":
|
case "telegram_voicechat", "telegram_videochat", "telegram_livestream":
|
||||||
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
|
if type == "telegram_livestream" {
|
||||||
title = item.presentationData.strings.Conversation_LiveStream
|
title = item.presentationData.strings.Conversation_LiveStream
|
||||||
} else {
|
} else {
|
||||||
title = item.presentationData.strings.Conversation_VoiceChat
|
title = item.presentationData.strings.Conversation_VoiceChat
|
||||||
|
@ -531,7 +531,7 @@ final class ChatThemeScreen: ViewController {
|
|||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let animatedEmojiStickers: [String: [StickerPackItem]]
|
private let animatedEmojiStickers: [String: [StickerPackItem]]
|
||||||
private let initiallySelectedEmoticon: String?
|
private let initiallySelectedEmoticon: String?
|
||||||
private let dismissByTapOutside: Bool
|
private let peerName: String
|
||||||
private let previewTheme: (String?, Bool?) -> Void
|
private let previewTheme: (String?, Bool?) -> Void
|
||||||
private let completion: (String?) -> Void
|
private let completion: (String?) -> Void
|
||||||
|
|
||||||
@ -548,12 +548,12 @@ final class ChatThemeScreen: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>), animatedEmojiStickers: [String: [StickerPackItem]], initiallySelectedEmoticon: String?, dismissByTapOutside: Bool = true, previewTheme: @escaping (String?, Bool?) -> Void, completion: @escaping (String?) -> Void) {
|
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>), animatedEmojiStickers: [String: [StickerPackItem]], initiallySelectedEmoticon: String?, peerName: String, previewTheme: @escaping (String?, Bool?) -> Void, completion: @escaping (String?) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.presentationData = updatedPresentationData.initial
|
self.presentationData = updatedPresentationData.initial
|
||||||
self.animatedEmojiStickers = animatedEmojiStickers
|
self.animatedEmojiStickers = animatedEmojiStickers
|
||||||
self.initiallySelectedEmoticon = initiallySelectedEmoticon
|
self.initiallySelectedEmoticon = initiallySelectedEmoticon
|
||||||
self.dismissByTapOutside = dismissByTapOutside
|
self.peerName = peerName
|
||||||
self.previewTheme = previewTheme
|
self.previewTheme = previewTheme
|
||||||
self.completion = completion
|
self.completion = completion
|
||||||
|
|
||||||
@ -583,7 +583,7 @@ final class ChatThemeScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
self.displayNode = ChatThemeScreenNode(context: self.context, presentationData: self.presentationData, controller: self, animatedEmojiStickers: self.animatedEmojiStickers, initiallySelectedEmoticon: self.initiallySelectedEmoticon, dismissByTapOutside: self.dismissByTapOutside)
|
self.displayNode = ChatThemeScreenNode(context: self.context, presentationData: self.presentationData, controller: self, animatedEmojiStickers: self.animatedEmojiStickers, initiallySelectedEmoticon: self.initiallySelectedEmoticon, peerName: self.peerName)
|
||||||
self.controllerNode.passthroughHitTestImpl = self.passthroughHitTestImpl
|
self.controllerNode.passthroughHitTestImpl = self.passthroughHitTestImpl
|
||||||
self.controllerNode.previewTheme = { [weak self] emoticon, dark in
|
self.controllerNode.previewTheme = { [weak self] emoticon, dark in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -676,7 +676,6 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private weak var controller: ChatThemeScreen?
|
private weak var controller: ChatThemeScreen?
|
||||||
private let dismissByTapOutside: Bool
|
|
||||||
|
|
||||||
private let dimNode: ASDisplayNode
|
private let dimNode: ASDisplayNode
|
||||||
private let wrappingScrollNode: ASScrollNode
|
private let wrappingScrollNode: ASScrollNode
|
||||||
@ -698,6 +697,8 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
private var enqueuedTransitions: [ThemeSettingsThemeItemNodeTransition] = []
|
private var enqueuedTransitions: [ThemeSettingsThemeItemNodeTransition] = []
|
||||||
private var initialized = false
|
private var initialized = false
|
||||||
|
|
||||||
|
private let peerName: String
|
||||||
|
|
||||||
private let initiallySelectedEmoticon: String?
|
private let initiallySelectedEmoticon: String?
|
||||||
private var selectedEmoticon: String? {
|
private var selectedEmoticon: String? {
|
||||||
didSet {
|
didSet {
|
||||||
@ -723,14 +724,14 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
var dismiss: (() -> Void)?
|
var dismiss: (() -> Void)?
|
||||||
var cancel: (() -> Void)?
|
var cancel: (() -> Void)?
|
||||||
|
|
||||||
init(context: AccountContext, presentationData: PresentationData, controller: ChatThemeScreen, animatedEmojiStickers: [String: [StickerPackItem]], initiallySelectedEmoticon: String?, dismissByTapOutside: Bool) {
|
init(context: AccountContext, presentationData: PresentationData, controller: ChatThemeScreen, animatedEmojiStickers: [String: [StickerPackItem]], initiallySelectedEmoticon: String?, peerName: String) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.initiallySelectedEmoticon = initiallySelectedEmoticon
|
self.initiallySelectedEmoticon = initiallySelectedEmoticon
|
||||||
|
self.peerName = peerName
|
||||||
self.selectedEmoticon = initiallySelectedEmoticon
|
self.selectedEmoticon = initiallySelectedEmoticon
|
||||||
self.selectedEmoticonPromise = ValuePromise(initiallySelectedEmoticon)
|
self.selectedEmoticonPromise = ValuePromise(initiallySelectedEmoticon)
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.dismissByTapOutside = dismissByTapOutside
|
|
||||||
|
|
||||||
self.wrappingScrollNode = ASScrollNode()
|
self.wrappingScrollNode = ASScrollNode()
|
||||||
self.wrappingScrollNode.view.alwaysBounceVertical = true
|
self.wrappingScrollNode.view.alwaysBounceVertical = true
|
||||||
@ -755,6 +756,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
|
|
||||||
let backgroundColor = self.presentationData.theme.actionSheet.itemBackgroundColor
|
let backgroundColor = self.presentationData.theme.actionSheet.itemBackgroundColor
|
||||||
let textColor = self.presentationData.theme.actionSheet.primaryTextColor
|
let textColor = self.presentationData.theme.actionSheet.primaryTextColor
|
||||||
|
let secondaryTextColor = self.presentationData.theme.actionSheet.secondaryTextColor
|
||||||
let blurStyle: UIBlurEffect.Style = self.presentationData.theme.actionSheet.backgroundType == .light ? .light : .dark
|
let blurStyle: UIBlurEffect.Style = self.presentationData.theme.actionSheet.backgroundType == .light ? .light : .dark
|
||||||
|
|
||||||
self.effectNode = ASDisplayNode(viewBlock: {
|
self.effectNode = ASDisplayNode(viewBlock: {
|
||||||
@ -764,11 +766,11 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
self.contentBackgroundNode = ASDisplayNode()
|
self.contentBackgroundNode = ASDisplayNode()
|
||||||
self.contentBackgroundNode.backgroundColor = backgroundColor
|
self.contentBackgroundNode.backgroundColor = backgroundColor
|
||||||
|
|
||||||
let title = self.presentationData.strings.Conversation_Theme_Title
|
|
||||||
self.titleNode = ASTextNode()
|
self.titleNode = ASTextNode()
|
||||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(17.0), textColor: textColor)
|
self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.Conversation_Theme_Title, font: Font.semibold(16.0), textColor: textColor)
|
||||||
|
|
||||||
self.textNode = ImmediateTextNode()
|
self.textNode = ImmediateTextNode()
|
||||||
|
self.textNode.attributedText = NSAttributedString(string: self.presentationData.strings.Conversation_Theme_Subtitle(peerName).string, font: Font.regular(12.0), textColor: secondaryTextColor)
|
||||||
|
|
||||||
self.cancelButton = HighlightableButtonNode()
|
self.cancelButton = HighlightableButtonNode()
|
||||||
self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal)
|
self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal)
|
||||||
@ -851,6 +853,11 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
doneButtonTitle = strongSelf.presentationData.strings.Conversation_Theme_Apply
|
doneButtonTitle = strongSelf.presentationData.strings.Conversation_Theme_Apply
|
||||||
}
|
}
|
||||||
strongSelf.doneButton.title = doneButtonTitle
|
strongSelf.doneButton.title = doneButtonTitle
|
||||||
|
|
||||||
|
strongSelf.themeSelectionsCount += 1
|
||||||
|
if strongSelf.themeSelectionsCount == 2 {
|
||||||
|
strongSelf.maybePresentPreviewTooltip()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let previousEntries = strongSelf.entries ?? []
|
let previousEntries = strongSelf.entries ?? []
|
||||||
@ -946,7 +953,8 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
let previousTheme = self.presentationData.theme
|
let previousTheme = self.presentationData.theme
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
|
|
||||||
self.titleNode.attributedText = NSAttributedString(string: self.titleNode.attributedText?.string ?? "", font: Font.bold(17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor)
|
self.titleNode.attributedText = NSAttributedString(string: self.titleNode.attributedText?.string ?? "", font: Font.semibold(16.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor)
|
||||||
|
self.textNode.attributedText = NSAttributedString(string: self.textNode.attributedText?.string ?? "", font: Font.regular(12.0), textColor: self.presentationData.theme.actionSheet.secondaryTextColor)
|
||||||
|
|
||||||
if previousTheme !== presentationData.theme, let (layout, navigationBarHeight) = self.containerLayout {
|
if previousTheme !== presentationData.theme, let (layout, navigationBarHeight) = self.containerLayout {
|
||||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||||
@ -1010,7 +1018,11 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
self.previewTheme?(self.selectedEmoticon, isDarkAppearance)
|
self.previewTheme?(self.selectedEmoticon, isDarkAppearance)
|
||||||
self.isDarkAppearance = isDarkAppearance
|
self.isDarkAppearance = isDarkAppearance
|
||||||
|
|
||||||
let _ = ApplicationSpecificNotice.incrementChatSpecificThemesDarkPreviewTip(accountManager: self.context.sharedContext.accountManager, count: 3).start()
|
if isDarkAppearance {
|
||||||
|
let _ = ApplicationSpecificNotice.incrementChatSpecificThemeDarkPreviewTip(accountManager: self.context.sharedContext.accountManager, count: 3, timestamp: Int32(Date().timeIntervalSince1970)).start()
|
||||||
|
} else {
|
||||||
|
let _ = ApplicationSpecificNotice.incrementChatSpecificThemeLightPreviewTip(accountManager: self.context.sharedContext.accountManager, count: 3, timestamp: Int32(Date().timeIntervalSince1970)).start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func animateCrossfade(animateIcon: Bool = true) {
|
private func animateCrossfade(animateIcon: Bool = true) {
|
||||||
@ -1069,20 +1081,40 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
self.bounds = targetBounds
|
self.bounds = targetBounds
|
||||||
self.dimNode.position = dimPosition
|
self.dimNode.position = dimPosition
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private var themeSelectionsCount = 0
|
||||||
|
private var displayedPreviewTooltip = false
|
||||||
|
private func maybePresentPreviewTooltip() {
|
||||||
|
guard !self.displayedPreviewTooltip, !self.animatedOut else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let frame = self.switchThemeButton.view.convert(self.switchThemeButton.bounds, to: self.view)
|
let frame = self.switchThemeButton.view.convert(self.switchThemeButton.bounds, to: self.view)
|
||||||
|
let currentTimestamp = Int32(Date().timeIntervalSince1970)
|
||||||
|
|
||||||
let _ = (ApplicationSpecificNotice.getChatSpecificThemesDarkPreviewTip(accountManager: self.context.sharedContext.accountManager)
|
let isDark = self.presentationData.theme.overallDarkAppearance
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] count in
|
|
||||||
if let strongSelf = self, count < 3 {
|
let signal: Signal<(Int32, Int32), NoError>
|
||||||
Queue.mainQueue().after(1.0) {
|
if isDark {
|
||||||
if !strongSelf.animatedOut {
|
signal = ApplicationSpecificNotice.getChatSpecificThemeLightPreviewTip(accountManager: self.context.sharedContext.accountManager)
|
||||||
strongSelf.present?(TooltipScreen(account: strongSelf.context.account, text: strongSelf.presentationData.theme.overallDarkAppearance ? strongSelf.presentationData.strings.Conversation_Theme_SwitchToLight : strongSelf.presentationData.strings.Conversation_Theme_SwitchToDark, style: .default, icon: nil, location: .point(frame.offsetBy(dx: 3.0, dy: 6.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in
|
} else {
|
||||||
return .dismiss(consume: false)
|
signal = ApplicationSpecificNotice.getChatSpecificThemeDarkPreviewTip(accountManager: self.context.sharedContext.accountManager)
|
||||||
}))
|
}
|
||||||
|
|
||||||
let _ = ApplicationSpecificNotice.incrementChatSpecificThemesDarkPreviewTip(accountManager: strongSelf.context.sharedContext.accountManager).start()
|
let _ = (signal
|
||||||
}
|
|> deliverOnMainQueue).start(next: { [weak self] count, timestamp in
|
||||||
|
if let strongSelf = self, count < 2 && currentTimestamp > timestamp + 24 * 60 * 60 {
|
||||||
|
strongSelf.displayedPreviewTooltip = true
|
||||||
|
|
||||||
|
strongSelf.present?(TooltipScreen(account: strongSelf.context.account, text: isDark ? strongSelf.presentationData.strings.Conversation_Theme_PreviewLight(strongSelf.peerName).string : strongSelf.presentationData.strings.Conversation_Theme_PreviewDark(strongSelf.peerName).string, style: .default, icon: nil, location: .point(frame.offsetBy(dx: 3.0, dy: 6.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in
|
||||||
|
return .dismiss(consume: false)
|
||||||
|
}))
|
||||||
|
|
||||||
|
if isDark {
|
||||||
|
let _ = ApplicationSpecificNotice.incrementChatSpecificThemeLightPreviewTip(accountManager: strongSelf.context.sharedContext.accountManager, timestamp: currentTimestamp).start()
|
||||||
|
} else {
|
||||||
|
let _ = ApplicationSpecificNotice.incrementChatSpecificThemeDarkPreviewTip(accountManager: strongSelf.context.sharedContext.accountManager, timestamp: currentTimestamp).start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1158,10 +1190,14 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
transition.updateFrame(node: self.wrappingScrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
transition.updateFrame(node: self.wrappingScrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||||
|
|
||||||
let titleSize = self.titleNode.measure(CGSize(width: width, height: titleHeight))
|
let titleSize = self.titleNode.measure(CGSize(width: width - 90.0, height: titleHeight))
|
||||||
let titleFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - titleSize.width) / 2.0), y: 18.0), size: titleSize)
|
let titleFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - titleSize.width) / 2.0), y: 11.0 + UIScreenPixel), size: titleSize)
|
||||||
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
||||||
|
|
||||||
|
let textSize = self.textNode.updateLayout(CGSize(width: width - 90.0, height: titleHeight))
|
||||||
|
let textFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - textSize.width) / 2.0), y: 31.0), size: textSize)
|
||||||
|
transition.updateFrame(node: self.textNode, frame: textFrame)
|
||||||
|
|
||||||
let switchThemeSize = CGSize(width: 44.0, height: 44.0)
|
let switchThemeSize = CGSize(width: 44.0, height: 44.0)
|
||||||
let switchThemeFrame = CGRect(origin: CGPoint(x: 3.0, y: 6.0), size: switchThemeSize)
|
let switchThemeFrame = CGRect(origin: CGPoint(x: 3.0, y: 6.0), size: switchThemeSize)
|
||||||
transition.updateFrame(node: self.switchThemeButton, frame: switchThemeFrame)
|
transition.updateFrame(node: self.switchThemeButton, frame: switchThemeFrame)
|
||||||
|
@ -28,6 +28,7 @@ private var telegramUIDeclaredEncodables: Void = {
|
|||||||
declareEncodable(ApplicationSpecificVariantNotice.self, f: { ApplicationSpecificVariantNotice(decoder: $0) })
|
declareEncodable(ApplicationSpecificVariantNotice.self, f: { ApplicationSpecificVariantNotice(decoder: $0) })
|
||||||
declareEncodable(ApplicationSpecificCounterNotice.self, f: { ApplicationSpecificCounterNotice(decoder: $0) })
|
declareEncodable(ApplicationSpecificCounterNotice.self, f: { ApplicationSpecificCounterNotice(decoder: $0) })
|
||||||
declareEncodable(ApplicationSpecificTimestampNotice.self, f: { ApplicationSpecificTimestampNotice(decoder: $0) })
|
declareEncodable(ApplicationSpecificTimestampNotice.self, f: { ApplicationSpecificTimestampNotice(decoder: $0) })
|
||||||
|
declareEncodable(ApplicationSpecificTimestampAndCounterNotice.self, f: { ApplicationSpecificTimestampAndCounterNotice(decoder: $0) })
|
||||||
declareEncodable(ApplicationSpecificInt64ArrayNotice.self, f: { ApplicationSpecificInt64ArrayNotice(decoder: $0) })
|
declareEncodable(ApplicationSpecificInt64ArrayNotice.self, f: { ApplicationSpecificInt64ArrayNotice(decoder: $0) })
|
||||||
//declareEncodable(CallListSettings.self, f: { CallListSettings(decoder: $0) })
|
//declareEncodable(CallListSettings.self, f: { CallListSettings(decoder: $0) })
|
||||||
//declareEncodable(VoiceCallSettings.self, f: { VoiceCallSettings(decoder: $0) })
|
//declareEncodable(VoiceCallSettings.self, f: { VoiceCallSettings(decoder: $0) })
|
||||||
|
@ -156,7 +156,7 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
var text = ""
|
var text = ""
|
||||||
var sourcePeer: (Bool, String)?
|
var sourcePeer: (Bool, String)?
|
||||||
for message in messages {
|
for message in messages {
|
||||||
if let author = message.effectiveAuthor, !uniquePeerIds.contains(author.id) {
|
if let author = message.forwardInfo?.author ?? message.effectiveAuthor, !uniquePeerIds.contains(author.id) {
|
||||||
uniquePeerIds.insert(author.id)
|
uniquePeerIds.insert(author.id)
|
||||||
if !authors.isEmpty {
|
if !authors.isEmpty {
|
||||||
authors.append(", ")
|
authors.append(", ")
|
||||||
@ -268,15 +268,20 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
|
|
||||||
let filteredMessages = self.messages
|
let filteredMessages = self.messages
|
||||||
|
|
||||||
|
var authors = self.authors ?? ""
|
||||||
|
if forwardOptionsState?.hideNames == true {
|
||||||
|
authors = self.strings.DialogList_You
|
||||||
|
}
|
||||||
|
|
||||||
var title = ""
|
var title = ""
|
||||||
var text = ""
|
var text = ""
|
||||||
if filteredMessages.count == 1, let message = filteredMessages.first {
|
if filteredMessages.count == 1, let message = filteredMessages.first {
|
||||||
title = self.strings.Conversation_ForwardOptions_ForwardTitleSingle
|
title = self.strings.Conversation_ForwardOptions_ForwardTitleSingle
|
||||||
let (string, _) = textStringForForwardedMessage(message, strings: strings)
|
let (string, _) = textStringForForwardedMessage(message, strings: strings)
|
||||||
text = "\(self.authors ?? ""): \(string)"
|
text = "\(authors): \(string)"
|
||||||
} else {
|
} else {
|
||||||
title = self.strings.Conversation_ForwardOptions_ForwardTitle(Int32(filteredMessages.count))
|
title = self.strings.Conversation_ForwardOptions_ForwardTitle(Int32(filteredMessages.count))
|
||||||
text = self.strings.Conversation_ForwardFrom(self.authors ?? "").string
|
text = self.strings.Conversation_ForwardFrom(authors).string
|
||||||
}
|
}
|
||||||
|
|
||||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(15.0), textColor: self.theme.chat.inputPanel.panelControlAccentColor)
|
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(15.0), textColor: self.theme.chat.inputPanel.panelControlAccentColor)
|
||||||
|
@ -624,10 +624,10 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
game = value
|
game = value
|
||||||
} else if queryItem.name == "post" {
|
} else if queryItem.name == "post" {
|
||||||
post = value
|
post = value
|
||||||
} else if queryItem.name == "voicechat" || queryItem.name == "videochat" || queryItem.name == "livestream" {
|
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
|
||||||
voiceChat = value
|
voiceChat = value
|
||||||
}
|
}
|
||||||
} else if queryItem.name == "voicechat" || queryItem.name == "videochat" || queryItem.name == "livestream" {
|
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
|
||||||
voiceChat = ""
|
voiceChat = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ private let validUrlSet: CharacterSet = {
|
|||||||
var set = CharacterSet(charactersIn: "a".unicodeScalars.first! ... "z".unicodeScalars.first!)
|
var set = CharacterSet(charactersIn: "a".unicodeScalars.first! ... "z".unicodeScalars.first!)
|
||||||
set.insert(charactersIn: "A".unicodeScalars.first! ... "Z".unicodeScalars.first!)
|
set.insert(charactersIn: "A".unicodeScalars.first! ... "Z".unicodeScalars.first!)
|
||||||
set.insert(charactersIn: "0".unicodeScalars.first! ... "9".unicodeScalars.first!)
|
set.insert(charactersIn: "0".unicodeScalars.first! ... "9".unicodeScalars.first!)
|
||||||
set.insert(charactersIn: ".?!@#$^&%*+=,:;'\"`<>()[]{}/\\|~ ")
|
set.insert(charactersIn: ".?!@#$^&%*-+=,:;'\"`<>()[]{}/\\|~ ")
|
||||||
return set
|
return set
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -136,10 +136,10 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
|||||||
return .peerName(peerName, .groupBotStart(value))
|
return .peerName(peerName, .groupBotStart(value))
|
||||||
} else if queryItem.name == "game" {
|
} else if queryItem.name == "game" {
|
||||||
return nil
|
return nil
|
||||||
} else if queryItem.name == "voicechat" || queryItem.name == "videochat" || queryItem.name == "livestream" {
|
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
|
||||||
return .peerName(peerName, .voiceChat(value))
|
return .peerName(peerName, .voiceChat(value))
|
||||||
}
|
}
|
||||||
} else if queryItem.name == "voicechat" || queryItem.name == "videochat" || queryItem.name == "livestream" {
|
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
|
||||||
return .peerName(peerName, .voiceChat(nil))
|
return .peerName(peerName, .voiceChat(nil))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"app": "8.0.1",
|
"app": "8.1",
|
||||||
"bazel": "4.0.0",
|
"bazel": "4.0.0",
|
||||||
"xcode": "12.5.1"
|
"xcode": "12.5.1"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user