mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-09-09 06:00:43 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
89bec03bfa
@ -12139,3 +12139,46 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"Channel.AdminLog.ShowMoreMessages_1" = "Show %@ More Message";
|
"Channel.AdminLog.ShowMoreMessages_1" = "Show %@ More Message";
|
||||||
"Channel.AdminLog.ShowMoreMessages_any" = "Show %@ More Messages";
|
"Channel.AdminLog.ShowMoreMessages_any" = "Show %@ More Messages";
|
||||||
|
|
||||||
|
"CreatePoll.OptionCountFooterFormat_1" = "You can add {count} more option.";
|
||||||
|
"CreatePoll.OptionCountFooterFormat_any" = "You can add {count} more options.";
|
||||||
|
|
||||||
|
"Chat.AdminActionSheet.DeleteTitle_1" = "Delete 1 Message";
|
||||||
|
"Chat.AdminActionSheet.DeleteTitle_any" = "Delete %d Messages";
|
||||||
|
"Chat.AdminActionSheet.ReportSpam" = "Report Spam";
|
||||||
|
"Chat.AdminActionSheet.DeleteAllSingle" = "Delete All from %@";
|
||||||
|
"Chat.AdminActionSheet.DeleteAllMultiple" = "Delete All from Users";
|
||||||
|
"Chat.AdminActionSheet.BanSingle" = "Ban %@";
|
||||||
|
"Chat.AdminActionSheet.BanMultiple" = "Ban Users";
|
||||||
|
"Chat.AdminActionSheet.RestrictSingle" = "Ban %@";
|
||||||
|
"Chat.AdminActionSheet.RestrictMultiple" = "Ban Users";
|
||||||
|
"Chat.AdminActionSheet.RestrictSectionHeader" = "ADDITIONAL ACTIONS";
|
||||||
|
"Chat.AdminActionSheet.BanFooterSingle" = "Fully ban this user";
|
||||||
|
"Chat.AdminActionSheet.RestrictFooterSingle" = "Partially restrict this user";
|
||||||
|
"Chat.AdminActionSheet.BanFooterMultiple" = "Fully ban users";
|
||||||
|
"Chat.AdminActionSheet.RestrictFooterMultiple" = "Partially restrict users";
|
||||||
|
"Chat.AdminActionSheet.PermissionsSectionHeader" = "WHAT CAN THIS USER DO?";
|
||||||
|
"Chat.AdminActionSheet.ActionButton" = "Proceed";
|
||||||
|
|
||||||
|
"Chat.AdminAction.ToastMessagesDeletedTitleSingle" = "Message Deleted";
|
||||||
|
"Chat.AdminAction.ToastMessagesDeletedTitleMultiple" = "Messages Deleted";
|
||||||
|
"Chat.AdminAction.ToastMessagesDeletedTextSingle" = "Message Deleted.";
|
||||||
|
"Chat.AdminAction.ToastMessagesDeletedTextMultiple" = "Messages Deleted.";
|
||||||
|
"Chat.AdminAction.ToastReportedSpamText_1" = "**1** user reported for spam.";
|
||||||
|
"Chat.AdminAction.ToastReportedSpamText_any" = "**%d** users reported for spam.";
|
||||||
|
"Chat.AdminAction.ToastBannedText_1" = "**1** user banned.";
|
||||||
|
"Chat.AdminAction.ToastBannedText_any" = "**%d** users banned.";
|
||||||
|
"Chat.AdminAction.ToastRestrictedText_1" = "**1** user restricted.";
|
||||||
|
"Chat.AdminAction.ToastRestrictedText_any" = "**%d** users restricted.";
|
||||||
|
|
||||||
|
"Chat.MessageForwardInfo.StoryHeader" = "Forwarded story from";
|
||||||
|
"Chat.MessageForwardInfo.ExpiredStoryHeader" = "Expired story from";
|
||||||
|
"Chat.MessageForwardInfo.UnavailableStoryHeader" = "Expired story from";
|
||||||
|
"Chat.MessageForwardInfo.MessageHeader" = "Forwarded from";
|
||||||
|
|
||||||
|
"Chat.NavigationNoTopics" = "You have no unread topics";
|
||||||
|
|
||||||
|
"Chat.NextSuggestedChannelSwipeProgress" = "Swipe up to go to the next channel";
|
||||||
|
"Chat.NextSuggestedChannelSwipeAction" = "Release to go to the next channel";
|
||||||
|
"Chat.NextUnreadTopicSwipeProgress" = "Swipe up to go to the next topic";
|
||||||
|
"Chat.NextUnreadTopicSwipeAction" = "Release to go to the next topic";
|
||||||
|
@ -89,6 +89,7 @@ public enum AttachmentButtonType: Equatable {
|
|||||||
public protocol AttachmentContainable: ViewController {
|
public protocol AttachmentContainable: ViewController {
|
||||||
var requestAttachmentMenuExpansion: () -> Void { get set }
|
var requestAttachmentMenuExpansion: () -> Void { get set }
|
||||||
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void { get set }
|
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void { get set }
|
||||||
|
var parentController: () -> ViewController? { get set }
|
||||||
var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void { get set }
|
var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void { get set }
|
||||||
var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void { get set }
|
var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void { get set }
|
||||||
var cancelPanGesture: () -> Void { get set }
|
var cancelPanGesture: () -> Void { get set }
|
||||||
@ -560,6 +561,12 @@ public class AttachmentController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
controller.parentController = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return self.controller
|
||||||
|
}
|
||||||
controller.updateTabBarAlpha = { [weak self, weak controller] alpha, transition in
|
controller.updateTabBarAlpha = { [weak self, weak controller] alpha, transition in
|
||||||
if let strongSelf = self, strongSelf.currentControllers.contains(where: { $0 === controller }) {
|
if let strongSelf = self, strongSelf.currentControllers.contains(where: { $0 === controller }) {
|
||||||
strongSelf.panel.updateBackgroundAlpha(alpha, transition: transition)
|
strongSelf.panel.updateBackgroundAlpha(alpha, transition: transition)
|
||||||
|
@ -204,9 +204,19 @@ final class ComposePollScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var mappedSolution: String?
|
var mappedSolution: (String, [MessageTextEntity])?
|
||||||
if self.isQuiz && self.quizAnswerTextInputState.text.length != 0 {
|
if self.isQuiz && self.quizAnswerTextInputState.text.length != 0 {
|
||||||
mappedSolution = self.quizAnswerTextInputState.text.string
|
var solutionTextEntities: [MessageTextEntity] = []
|
||||||
|
for entity in generateChatInputTextEntities(self.quizAnswerTextInputState.text) {
|
||||||
|
switch entity.type {
|
||||||
|
case .CustomEmoji:
|
||||||
|
solutionTextEntities.append(entity)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mappedSolution = (self.quizAnswerTextInputState.text.string, solutionTextEntities)
|
||||||
}
|
}
|
||||||
|
|
||||||
var textEntities: [MessageTextEntity] = []
|
var textEntities: [MessageTextEntity] = []
|
||||||
@ -232,7 +242,7 @@ final class ComposePollScreenComponent: Component {
|
|||||||
totalVoters: nil,
|
totalVoters: nil,
|
||||||
recentVoters: [],
|
recentVoters: [],
|
||||||
solution: mappedSolution.flatMap { mappedSolution in
|
solution: mappedSolution.flatMap { mappedSolution in
|
||||||
return TelegramMediaPollResults.Solution(text: mappedSolution, entities: [])
|
return TelegramMediaPollResults.Solution(text: mappedSolution.0, entities: mappedSolution.1)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
deadlineTimeout: nil,
|
deadlineTimeout: nil,
|
||||||
@ -591,11 +601,21 @@ final class ComposePollScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
self.environment?.controller()?.presentInGlobalOverlay(c, with: a)
|
self.environment?.controller()?.presentInGlobalOverlay(c, with: a)
|
||||||
},
|
},
|
||||||
getNavigationController: { [weak self] in
|
getNavigationController: { [weak self] () -> NavigationController? in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return self.environment?.controller()?.navigationController as? NavigationController
|
guard let controller = self.environment?.controller() as? ComposePollScreen else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let navigationController = controller.navigationController as? NavigationController {
|
||||||
|
return navigationController
|
||||||
|
}
|
||||||
|
if let parentController = controller.parentController() {
|
||||||
|
return parentController.navigationController as? NavigationController
|
||||||
|
}
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
requestLayout: { [weak self] transition in
|
requestLayout: { [weak self] transition in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -668,14 +688,13 @@ final class ComposePollScreenComponent: Component {
|
|||||||
))))
|
))))
|
||||||
self.resetPollText = nil
|
self.resetPollText = nil
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
let pollTextSectionSize = self.pollTextSection.update(
|
let pollTextSectionSize = self.pollTextSection.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(ListSectionComponent(
|
component: AnyComponent(ListSectionComponent(
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
header: AnyComponent(MultilineTextComponent(
|
header: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "QUESTION",
|
string: environment.strings.CreatePoll_TextHeader,
|
||||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||||
textColor: environment.theme.list.freeTextColor
|
textColor: environment.theme.list.freeTextColor
|
||||||
)),
|
)),
|
||||||
@ -696,7 +715,7 @@ final class ComposePollScreenComponent: Component {
|
|||||||
transition.setFrame(view: pollTextSectionView, frame: pollTextSectionFrame)
|
transition.setFrame(view: pollTextSectionView, frame: pollTextSectionFrame)
|
||||||
|
|
||||||
if let itemView = pollTextSectionView.itemView(id: 0) as? ListComposePollOptionComponent.View {
|
if let itemView = pollTextSectionView.itemView(id: 0) as? ListComposePollOptionComponent.View {
|
||||||
itemView.updateCustomPlaceholder(value: "Ask a Question", size: itemView.bounds.size, transition: .immediate)
|
itemView.updateCustomPlaceholder(value: environment.strings.CreatePoll_TextPlaceholder, size: itemView.bounds.size, transition: .immediate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
contentHeight += pollTextSectionSize.height
|
contentHeight += pollTextSectionSize.height
|
||||||
@ -838,9 +857,9 @@ final class ComposePollScreenComponent: Component {
|
|||||||
for i in 0 ..< pollOptionsSectionReadyItems.count {
|
for i in 0 ..< pollOptionsSectionReadyItems.count {
|
||||||
let placeholder: String
|
let placeholder: String
|
||||||
if i == pollOptionsSectionReadyItems.count - 1 {
|
if i == pollOptionsSectionReadyItems.count - 1 {
|
||||||
placeholder = "Add an Option"
|
placeholder = environment.strings.CreatePoll_AddOption
|
||||||
} else {
|
} else {
|
||||||
placeholder = "Option"
|
placeholder = environment.strings.CreatePoll_OptionPlaceholder
|
||||||
}
|
}
|
||||||
|
|
||||||
if let itemView = pollOptionsSectionReadyItems[i].itemView.contents.view as? ListComposePollOptionComponent.View {
|
if let itemView = pollOptionsSectionReadyItems[i].itemView.contents.view as? ListComposePollOptionComponent.View {
|
||||||
@ -866,7 +885,7 @@ final class ComposePollScreenComponent: Component {
|
|||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(MultilineTextComponent(
|
component: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "POLL OPTIONS",
|
string: environment.strings.CreatePoll_OptionsHeader,
|
||||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||||
textColor: environment.theme.list.freeTextColor
|
textColor: environment.theme.list.freeTextColor
|
||||||
)),
|
)),
|
||||||
@ -917,26 +936,36 @@ final class ComposePollScreenComponent: Component {
|
|||||||
if pollOptionsLimitReached {
|
if pollOptionsLimitReached {
|
||||||
pollOptionsFooterTransition = pollOptionsFooterTransition.withAnimation(.none)
|
pollOptionsFooterTransition = pollOptionsFooterTransition.withAnimation(.none)
|
||||||
pollOptionsComponent = AnyComponent(MultilineTextComponent(
|
pollOptionsComponent = AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(string: "You have added the maximum number of options.", font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: environment.theme.list.freeTextColor)),
|
text: .plain(NSAttributedString(string: environment.strings.CreatePoll_AllOptionsAdded, font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: environment.theme.list.freeTextColor)),
|
||||||
maximumNumberOfLines: 0
|
maximumNumberOfLines: 0
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
|
let remainingCount = 10 - self.pollOptions.count
|
||||||
|
let rawString = environment.strings.CreatePoll_OptionCountFooterFormat(Int32(remainingCount))
|
||||||
|
|
||||||
var pollOptionsFooterItems: [AnimatedTextComponent.Item] = []
|
var pollOptionsFooterItems: [AnimatedTextComponent.Item] = []
|
||||||
pollOptionsFooterItems.append(AnimatedTextComponent.Item(
|
if let range = rawString.range(of: "{count}") {
|
||||||
id: 0,
|
if range.lowerBound != rawString.startIndex {
|
||||||
isUnbreakable: true,
|
pollOptionsFooterItems.append(AnimatedTextComponent.Item(
|
||||||
content: .text("You can add ")
|
id: 0,
|
||||||
))
|
isUnbreakable: true,
|
||||||
pollOptionsFooterItems.append(AnimatedTextComponent.Item(
|
content: .text(String(rawString[rawString.startIndex ..< range.lowerBound]))
|
||||||
id: 1,
|
))
|
||||||
isUnbreakable: true,
|
}
|
||||||
content: .number(10 - self.pollOptions.count, minDigits: 1)
|
pollOptionsFooterItems.append(AnimatedTextComponent.Item(
|
||||||
))
|
id: 1,
|
||||||
pollOptionsFooterItems.append(AnimatedTextComponent.Item(
|
isUnbreakable: true,
|
||||||
id: 2,
|
content: .number(remainingCount, minDigits: 1)
|
||||||
isUnbreakable: true,
|
))
|
||||||
content: .text(" more options.")
|
if range.upperBound != rawString.endIndex {
|
||||||
))
|
pollOptionsFooterItems.append(AnimatedTextComponent.Item(
|
||||||
|
id: 2,
|
||||||
|
isUnbreakable: true,
|
||||||
|
content: .text(String(rawString[range.upperBound ..< rawString.endIndex]))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pollOptionsComponent = AnyComponent(AnimatedTextComponent(
|
pollOptionsComponent = AnyComponent(AnimatedTextComponent(
|
||||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||||
color: environment.theme.list.freeTextColor,
|
color: environment.theme.list.freeTextColor,
|
||||||
@ -977,7 +1006,7 @@ final class ComposePollScreenComponent: Component {
|
|||||||
title: AnyComponent(VStack([
|
title: AnyComponent(VStack([
|
||||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Anonymous Voting",
|
string: environment.strings.CreatePoll_Anonymous,
|
||||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
textColor: environment.theme.list.itemPrimaryTextColor
|
textColor: environment.theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -998,7 +1027,7 @@ final class ComposePollScreenComponent: Component {
|
|||||||
title: AnyComponent(VStack([
|
title: AnyComponent(VStack([
|
||||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Multiple Answers",
|
string: environment.strings.CreatePoll_MultipleChoice,
|
||||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
textColor: environment.theme.list.itemPrimaryTextColor
|
textColor: environment.theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -1022,7 +1051,7 @@ final class ComposePollScreenComponent: Component {
|
|||||||
title: AnyComponent(VStack([
|
title: AnyComponent(VStack([
|
||||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Quiz Mode",
|
string: environment.strings.CreatePoll_Quiz,
|
||||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
textColor: environment.theme.list.itemPrimaryTextColor
|
textColor: environment.theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -1049,7 +1078,7 @@ final class ComposePollScreenComponent: Component {
|
|||||||
header: nil,
|
header: nil,
|
||||||
footer: AnyComponent(MultilineTextComponent(
|
footer: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Polls in Quiz Mode have one correct answer. Users can't revoke their answers.",
|
string: environment.strings.CreatePoll_QuizInfo,
|
||||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||||
textColor: environment.theme.list.freeTextColor
|
textColor: environment.theme.list.freeTextColor
|
||||||
)),
|
)),
|
||||||
@ -1078,7 +1107,7 @@ final class ComposePollScreenComponent: Component {
|
|||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
header: AnyComponent(MultilineTextComponent(
|
header: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "EXPLANATION",
|
string: environment.strings.CreatePoll_ExplanationHeader,
|
||||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||||
textColor: environment.theme.list.freeTextColor
|
textColor: environment.theme.list.freeTextColor
|
||||||
)),
|
)),
|
||||||
@ -1086,7 +1115,7 @@ final class ComposePollScreenComponent: Component {
|
|||||||
)),
|
)),
|
||||||
footer: AnyComponent(MultilineTextComponent(
|
footer: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Users will see this comment after choosing a wrong answer, good for educational purposes.",
|
string: environment.strings.CreatePoll_ExplanationInfo,
|
||||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||||
textColor: environment.theme.list.freeTextColor
|
textColor: environment.theme.list.freeTextColor
|
||||||
)),
|
)),
|
||||||
@ -1401,6 +1430,11 @@ final class ComposePollScreenComponent: Component {
|
|||||||
if sendButtonItem.isEnabled != isValid {
|
if sendButtonItem.isEnabled != isValid {
|
||||||
sendButtonItem.isEnabled = isValid
|
sendButtonItem.isEnabled = isValid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let controllerTitle = self.isQuiz ? presentationData.strings.CreatePoll_QuizTitle : presentationData.strings.CreatePoll_Title
|
||||||
|
if controller.title != controllerTitle {
|
||||||
|
controller.title = controllerTitle
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let currentEditingTag = self.currentEditingTag, previousEditingTag !== currentEditingTag, self.currentInputMode != .keyboard {
|
if let currentEditingTag = self.currentEditingTag, previousEditingTag !== currentEditingTag, self.currentInputMode != .keyboard {
|
||||||
@ -1450,6 +1484,9 @@ public class ComposePollScreen: ViewControllerComponentContainer, AttachmentCont
|
|||||||
}
|
}
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in
|
||||||
}
|
}
|
||||||
|
public var parentController: () -> ViewController? = {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in
|
||||||
}
|
}
|
||||||
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in
|
||||||
@ -1491,12 +1528,13 @@ public class ComposePollScreen: ViewControllerComponentContainer, AttachmentCont
|
|||||||
completion: completion
|
completion: completion
|
||||||
), navigationBarAppearance: .default, theme: .default)
|
), navigationBarAppearance: .default, theme: .default)
|
||||||
|
|
||||||
//TODO:localize
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
self.title = "New Poll"
|
|
||||||
|
|
||||||
self.navigationItem.setLeftBarButton(UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(self.cancelPressed)), animated: false)
|
self.title = isQuiz == true ? presentationData.strings.CreatePoll_QuizTitle : presentationData.strings.CreatePoll_Title
|
||||||
|
|
||||||
let sendButtonItem = UIBarButtonItem(title: "Send", style: .done, target: self, action: #selector(self.sendPressed))
|
self.navigationItem.setLeftBarButton(UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)), animated: false)
|
||||||
|
|
||||||
|
let sendButtonItem = UIBarButtonItem(title: presentationData.strings.CreatePoll_Create, style: .done, target: self, action: #selector(self.sendPressed))
|
||||||
self.sendButtonItem = sendButtonItem
|
self.sendButtonItem = sendButtonItem
|
||||||
self.navigationItem.setRightBarButton(sendButtonItem, animated: false)
|
self.navigationItem.setRightBarButton(sendButtonItem, animated: false)
|
||||||
sendButtonItem.isEnabled = false
|
sendButtonItem.isEnabled = false
|
||||||
|
@ -563,6 +563,9 @@ private final class CreatePollContext: AttachmentMediaPickerContext {
|
|||||||
public class CreatePollControllerImpl: ItemListController, AttachmentContainable {
|
public class CreatePollControllerImpl: ItemListController, AttachmentContainable {
|
||||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
|
public var parentController: () -> ViewController? = {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
|
@ -80,6 +80,9 @@ public final class LocationPickerController: ViewController, AttachmentContainab
|
|||||||
|
|
||||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
|
public var parentController: () -> ViewController? = {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
|
@ -152,6 +152,9 @@ private func preparedTransition(from fromEntries: [MediaGroupsEntry], to toEntri
|
|||||||
public final class MediaGroupsScreen: ViewController, AttachmentContainable {
|
public final class MediaGroupsScreen: ViewController, AttachmentContainable {
|
||||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
|
public var parentController: () -> ViewController? = {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
|
@ -202,6 +202,9 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
|
|
||||||
public var requestAttachmentMenuExpansion: () -> Void = { }
|
public var requestAttachmentMenuExpansion: () -> Void = { }
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
|
public var parentController: () -> ViewController? = {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
|
@ -2010,7 +2010,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
case let .category(value):
|
case let .category(value):
|
||||||
let resultSignal = self.context.engine.stickers.searchEmoji(emojiString: value)
|
let resultSignal = self.context.engine.stickers.searchEmoji(category: value)
|
||||||
|> mapToSignal { files, isFinalResult -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
|> mapToSignal { files, isFinalResult -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
||||||
var items: [EmojiPagerContentComponent.Item] = []
|
var items: [EmojiPagerContentComponent.Item] = []
|
||||||
|
|
||||||
@ -2082,11 +2082,11 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
fillWithLoadingPlaceholders: true,
|
fillWithLoadingPlaceholders: true,
|
||||||
items: []
|
items: []
|
||||||
)
|
)
|
||||||
], id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
], id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
version += 1
|
version += 1
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -385,6 +385,391 @@ func _internal_searchStickers(account: Account, query: [String], scope: SearchSt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _internal_searchStickers(account: Account, category: EmojiSearchCategories.Group, scope: SearchStickersScope = [.installed, .remote]) -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> {
|
||||||
|
if scope.isEmpty {
|
||||||
|
return .single(([], true))
|
||||||
|
}
|
||||||
|
|
||||||
|
var query = category.identifiers
|
||||||
|
if query == ["\u{2764}"] {
|
||||||
|
query = ["\u{2764}\u{FE0F}"]
|
||||||
|
}
|
||||||
|
|
||||||
|
return account.postbox.transaction { transaction -> ([FoundStickerItem], CachedStickerQueryResult?, Bool, SearchStickersConfiguration) in
|
||||||
|
let isPremium = transaction.getPeer(account.peerId)?.isPremium ?? false
|
||||||
|
|
||||||
|
var result: [FoundStickerItem] = []
|
||||||
|
if scope.contains(.installed) {
|
||||||
|
for entry in transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudSavedStickers) {
|
||||||
|
if let item = entry.contents.get(SavedStickerItem.self) {
|
||||||
|
if case .premium = category.kind {
|
||||||
|
if item.file.isPremiumSticker {
|
||||||
|
result.append(FoundStickerItem(file: item.file, stringRepresentations: item.stringRepresentations))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for representation in item.stringRepresentations {
|
||||||
|
for queryItem in query {
|
||||||
|
if representation.hasPrefix(queryItem) {
|
||||||
|
result.append(FoundStickerItem(file: item.file, stringRepresentations: item.stringRepresentations))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentItems = Set<MediaId>(result.map { $0.file.fileId })
|
||||||
|
var recentItems: [TelegramMediaFile] = []
|
||||||
|
var recentAnimatedItems: [TelegramMediaFile] = []
|
||||||
|
var recentItemsIds = Set<MediaId>()
|
||||||
|
var matchingRecentItemsIds = Set<MediaId>()
|
||||||
|
|
||||||
|
for entry in transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudRecentStickers) {
|
||||||
|
if let item = entry.contents.get(RecentMediaItem.self) {
|
||||||
|
let file = item.media
|
||||||
|
if file.isPremiumSticker && !isPremium {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !currentItems.contains(file.fileId) {
|
||||||
|
currentItems.insert(file.fileId)
|
||||||
|
|
||||||
|
for case let .Sticker(displayText, _, _) in file.attributes {
|
||||||
|
if case .premium = category.kind {
|
||||||
|
if file.isPremiumSticker {
|
||||||
|
matchingRecentItemsIds.insert(file.fileId)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for queryItem in query {
|
||||||
|
if displayText.hasPrefix(queryItem) {
|
||||||
|
matchingRecentItemsIds.insert(file.fileId)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recentItemsIds.insert(file.fileId)
|
||||||
|
if file.isAnimatedSticker || file.isVideoSticker {
|
||||||
|
recentAnimatedItems.append(file)
|
||||||
|
} else {
|
||||||
|
recentItems.append(file)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var searchQueries: [ItemCollectionSearchQuery] = query.map { queryItem -> ItemCollectionSearchQuery in
|
||||||
|
return .exact(ValueBoxKey(queryItem))
|
||||||
|
}
|
||||||
|
if query == ["\u{2764}"] {
|
||||||
|
searchQueries = [.any([ValueBoxKey("\u{2764}"), ValueBoxKey("\u{2764}\u{FE0F}")])]
|
||||||
|
}
|
||||||
|
|
||||||
|
var installedItems: [FoundStickerItem] = []
|
||||||
|
var installedAnimatedItems: [FoundStickerItem] = []
|
||||||
|
var installedPremiumItems: [FoundStickerItem] = []
|
||||||
|
|
||||||
|
if case .premium = category.kind {
|
||||||
|
for (_, _, items) in transaction.getCollectionsItems(namespace: Namespaces.ItemCollection.CloudStickerPacks) {
|
||||||
|
for item in items {
|
||||||
|
guard let item = item as? StickerPackItem else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if item.file.isPremiumSticker {
|
||||||
|
if currentItems.contains(item.file.fileId) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
currentItems.insert(item.file.fileId)
|
||||||
|
|
||||||
|
if !recentItemsIds.contains(item.file.fileId) {
|
||||||
|
if item.file.isPremiumSticker {
|
||||||
|
installedPremiumItems.append(FoundStickerItem(file: item.file, stringRepresentations: []))
|
||||||
|
} else if item.file.isAnimatedSticker || item.file.isVideoSticker {
|
||||||
|
installedAnimatedItems.append(FoundStickerItem(file: item.file, stringRepresentations: []))
|
||||||
|
} else {
|
||||||
|
installedItems.append(FoundStickerItem(file: item.file, stringRepresentations: []))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
matchingRecentItemsIds.insert(item.file.fileId)
|
||||||
|
if item.file.isAnimatedSticker || item.file.isVideoSticker {
|
||||||
|
recentAnimatedItems.append(item.file)
|
||||||
|
} else {
|
||||||
|
recentItems.append(item.file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for item in transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudAllPremiumStickers) {
|
||||||
|
guard let item = item.contents.get(RecentMediaItem.self) else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if currentItems.contains(item.media.fileId) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
currentItems.insert(item.media.fileId)
|
||||||
|
|
||||||
|
if !recentItemsIds.contains(item.media.fileId) {
|
||||||
|
if item.media.isPremiumSticker {
|
||||||
|
installedPremiumItems.append(FoundStickerItem(file: item.media, stringRepresentations: []))
|
||||||
|
} else if item.media.isAnimatedSticker || item.media.isVideoSticker {
|
||||||
|
installedAnimatedItems.append(FoundStickerItem(file: item.media, stringRepresentations: []))
|
||||||
|
} else {
|
||||||
|
installedItems.append(FoundStickerItem(file: item.media, stringRepresentations: []))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
matchingRecentItemsIds.insert(item.media.fileId)
|
||||||
|
if item.media.isAnimatedSticker || item.media.isVideoSticker {
|
||||||
|
recentAnimatedItems.append(item.media)
|
||||||
|
} else {
|
||||||
|
recentItems.append(item.media)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for searchQuery in searchQueries {
|
||||||
|
for item in transaction.searchItemCollection(namespace: Namespaces.ItemCollection.CloudStickerPacks, query: searchQuery) {
|
||||||
|
if let item = item as? StickerPackItem {
|
||||||
|
if !currentItems.contains(item.file.fileId) {
|
||||||
|
currentItems.insert(item.file.fileId)
|
||||||
|
|
||||||
|
let stringRepresentations = item.getStringRepresentationsOfIndexKeys()
|
||||||
|
if !recentItemsIds.contains(item.file.fileId) {
|
||||||
|
if item.file.isPremiumSticker {
|
||||||
|
installedPremiumItems.append(FoundStickerItem(file: item.file, stringRepresentations: stringRepresentations))
|
||||||
|
} else if item.file.isAnimatedSticker || item.file.isVideoSticker {
|
||||||
|
installedAnimatedItems.append(FoundStickerItem(file: item.file, stringRepresentations: stringRepresentations))
|
||||||
|
} else {
|
||||||
|
installedItems.append(FoundStickerItem(file: item.file, stringRepresentations: stringRepresentations))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
matchingRecentItemsIds.insert(item.file.fileId)
|
||||||
|
if item.file.isAnimatedSticker || item.file.isVideoSticker {
|
||||||
|
recentAnimatedItems.append(item.file)
|
||||||
|
} else {
|
||||||
|
recentItems.append(item.file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for file in recentAnimatedItems {
|
||||||
|
if file.isPremiumSticker && !isPremium {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if matchingRecentItemsIds.contains(file.fileId) {
|
||||||
|
result.append(FoundStickerItem(file: file, stringRepresentations: query))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for file in recentItems {
|
||||||
|
if file.isPremiumSticker && !isPremium {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if matchingRecentItemsIds.contains(file.fileId) {
|
||||||
|
result.append(FoundStickerItem(file: file, stringRepresentations: query))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append(contentsOf: installedPremiumItems)
|
||||||
|
result.append(contentsOf: installedAnimatedItems)
|
||||||
|
result.append(contentsOf: installedItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cached: CachedStickerQueryResult?
|
||||||
|
let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? AppConfiguration.defaultValue
|
||||||
|
let searchStickersConfiguration = SearchStickersConfiguration.with(appConfiguration: appConfiguration)
|
||||||
|
|
||||||
|
if case .premium = category.kind {
|
||||||
|
} else {
|
||||||
|
let combinedQuery = query.joined(separator: "")
|
||||||
|
cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerQueryResults, key: CachedStickerQueryResult.cacheKey(combinedQuery)))?.get(CachedStickerQueryResult.self)
|
||||||
|
|
||||||
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
|
||||||
|
if let currentCached = cached, currentTime > currentCached.timestamp + searchStickersConfiguration.cacheTimeout {
|
||||||
|
cached = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result, cached, isPremium, searchStickersConfiguration)
|
||||||
|
}
|
||||||
|
|> mapToSignal { localItems, cached, isPremium, searchStickersConfiguration -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> in
|
||||||
|
if !scope.contains(.remote) {
|
||||||
|
return .single((localItems, true))
|
||||||
|
}
|
||||||
|
if case .premium = category.kind {
|
||||||
|
return .single((localItems, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
var tempResult: [FoundStickerItem] = []
|
||||||
|
let currentItemIds = Set<MediaId>(localItems.map { $0.file.fileId })
|
||||||
|
|
||||||
|
var premiumItems: [FoundStickerItem] = []
|
||||||
|
var otherItems: [FoundStickerItem] = []
|
||||||
|
|
||||||
|
for item in localItems {
|
||||||
|
if item.file.isPremiumSticker {
|
||||||
|
premiumItems.append(item)
|
||||||
|
} else {
|
||||||
|
otherItems.append(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let cached = cached {
|
||||||
|
var cachedItems: [FoundStickerItem] = []
|
||||||
|
var cachedAnimatedItems: [FoundStickerItem] = []
|
||||||
|
var cachedPremiumItems: [FoundStickerItem] = []
|
||||||
|
|
||||||
|
for file in cached.items {
|
||||||
|
if !currentItemIds.contains(file.fileId) {
|
||||||
|
if file.isPremiumSticker {
|
||||||
|
cachedPremiumItems.append(FoundStickerItem(file: file, stringRepresentations: []))
|
||||||
|
} else if file.isAnimatedSticker || file.isVideoSticker {
|
||||||
|
cachedAnimatedItems.append(FoundStickerItem(file: file, stringRepresentations: []))
|
||||||
|
} else {
|
||||||
|
cachedItems.append(FoundStickerItem(file: file, stringRepresentations: []))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
otherItems.append(contentsOf: cachedAnimatedItems)
|
||||||
|
otherItems.append(contentsOf: cachedItems)
|
||||||
|
|
||||||
|
let allPremiumItems = premiumItems + cachedPremiumItems
|
||||||
|
let allOtherItems = otherItems + cachedAnimatedItems + cachedItems
|
||||||
|
|
||||||
|
if isPremium {
|
||||||
|
let batchCount = Int(searchStickersConfiguration.normalStickersPerPremiumCount)
|
||||||
|
if batchCount == 0 {
|
||||||
|
tempResult.append(contentsOf: allPremiumItems)
|
||||||
|
tempResult.append(contentsOf: allOtherItems)
|
||||||
|
} else {
|
||||||
|
if allPremiumItems.isEmpty {
|
||||||
|
tempResult.append(contentsOf: allOtherItems)
|
||||||
|
} else {
|
||||||
|
var i = 0
|
||||||
|
for premiumItem in allPremiumItems {
|
||||||
|
if i < allOtherItems.count {
|
||||||
|
for j in i ..< min(i + batchCount, allOtherItems.count) {
|
||||||
|
tempResult.append(allOtherItems[j])
|
||||||
|
}
|
||||||
|
i += batchCount
|
||||||
|
}
|
||||||
|
tempResult.append(premiumItem)
|
||||||
|
}
|
||||||
|
if i < allOtherItems.count {
|
||||||
|
for j in i ..< allOtherItems.count {
|
||||||
|
tempResult.append(allOtherItems[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tempResult.append(contentsOf: allOtherItems)
|
||||||
|
tempResult.append(contentsOf: allPremiumItems.prefix(max(0, Int(searchStickersConfiguration.premiumStickersCount))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let remote = account.network.request(Api.functions.messages.getStickers(emoticon: query.joined(separator: ""), hash: cached?.hash ?? 0))
|
||||||
|
|> `catch` { _ -> Signal<Api.messages.Stickers, NoError> in
|
||||||
|
return .single(.stickersNotModified)
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> in
|
||||||
|
return account.postbox.transaction { transaction -> (items: [FoundStickerItem], isFinalResult: Bool) in
|
||||||
|
switch result {
|
||||||
|
case let .stickers(hash, stickers):
|
||||||
|
var result: [FoundStickerItem] = []
|
||||||
|
let currentItemIds = Set<MediaId>(localItems.map { $0.file.fileId })
|
||||||
|
|
||||||
|
var premiumItems: [FoundStickerItem] = []
|
||||||
|
var otherItems: [FoundStickerItem] = []
|
||||||
|
|
||||||
|
for item in localItems {
|
||||||
|
if item.file.isPremiumSticker {
|
||||||
|
premiumItems.append(item)
|
||||||
|
} else {
|
||||||
|
otherItems.append(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var foundItems: [FoundStickerItem] = []
|
||||||
|
var foundAnimatedItems: [FoundStickerItem] = []
|
||||||
|
var foundPremiumItems: [FoundStickerItem] = []
|
||||||
|
|
||||||
|
var files: [TelegramMediaFile] = []
|
||||||
|
for sticker in stickers {
|
||||||
|
if let file = telegramMediaFileFromApiDocument(sticker), let id = file.id {
|
||||||
|
files.append(file)
|
||||||
|
if !currentItemIds.contains(id) {
|
||||||
|
if file.isPremiumSticker {
|
||||||
|
foundPremiumItems.append(FoundStickerItem(file: file, stringRepresentations: []))
|
||||||
|
} else if file.isAnimatedSticker || file.isVideoSticker {
|
||||||
|
foundAnimatedItems.append(FoundStickerItem(file: file, stringRepresentations: []))
|
||||||
|
} else {
|
||||||
|
foundItems.append(FoundStickerItem(file: file, stringRepresentations: []))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let allPremiumItems = premiumItems + foundPremiumItems
|
||||||
|
let allOtherItems = otherItems + foundAnimatedItems + foundItems
|
||||||
|
|
||||||
|
if isPremium {
|
||||||
|
let batchCount = Int(searchStickersConfiguration.normalStickersPerPremiumCount)
|
||||||
|
if batchCount == 0 {
|
||||||
|
result.append(contentsOf: allPremiumItems)
|
||||||
|
result.append(contentsOf: allOtherItems)
|
||||||
|
} else {
|
||||||
|
if allPremiumItems.isEmpty {
|
||||||
|
result.append(contentsOf: allOtherItems)
|
||||||
|
} else {
|
||||||
|
var i = 0
|
||||||
|
for premiumItem in allPremiumItems {
|
||||||
|
if i < allOtherItems.count {
|
||||||
|
for j in i ..< min(i + batchCount, allOtherItems.count) {
|
||||||
|
result.append(allOtherItems[j])
|
||||||
|
}
|
||||||
|
i += batchCount
|
||||||
|
}
|
||||||
|
result.append(premiumItem)
|
||||||
|
}
|
||||||
|
if i < allOtherItems.count {
|
||||||
|
for j in i ..< allOtherItems.count {
|
||||||
|
result.append(allOtherItems[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.append(contentsOf: allOtherItems)
|
||||||
|
result.append(contentsOf: allPremiumItems.prefix(max(0, Int(searchStickersConfiguration.premiumStickersCount))))
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
if let entry = CodableEntry(CachedStickerQueryResult(items: files, hash: hash, timestamp: currentTime)) {
|
||||||
|
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerQueryResults, key: CachedStickerQueryResult.cacheKey(query.joined(separator: ""))), entry: entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result, true)
|
||||||
|
case .stickersNotModified:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return (tempResult, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .single((tempResult, false))
|
||||||
|
|> then(remote)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func _internal_searchEmoji(account: Account, query: [String], scope: SearchStickersScope = [.installed, .remote]) -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> {
|
func _internal_searchEmoji(account: Account, query: [String], scope: SearchStickersScope = [.installed, .remote]) -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> {
|
||||||
if scope.isEmpty {
|
if scope.isEmpty {
|
||||||
return .single(([], true))
|
return .single(([], true))
|
||||||
|
@ -33,6 +33,10 @@ public extension TelegramEngine {
|
|||||||
public func searchStickers(query: [String], scope: SearchStickersScope = [.installed, .remote]) -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> {
|
public func searchStickers(query: [String], scope: SearchStickersScope = [.installed, .remote]) -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> {
|
||||||
return _internal_searchStickers(account: self.account, query: query, scope: scope)
|
return _internal_searchStickers(account: self.account, query: query, scope: scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func searchStickers(category: EmojiSearchCategories.Group, scope: SearchStickersScope = [.installed, .remote]) -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> {
|
||||||
|
return _internal_searchStickers(account: self.account, category: category, scope: scope)
|
||||||
|
}
|
||||||
|
|
||||||
public func searchStickerSetsRemotely(query: String) -> Signal<FoundStickerSets, NoError> {
|
public func searchStickerSetsRemotely(query: String) -> Signal<FoundStickerSets, NoError> {
|
||||||
return _internal_searchStickerSetsRemotely(network: self.account.network, query: query)
|
return _internal_searchStickerSetsRemotely(network: self.account.network, query: query)
|
||||||
@ -286,6 +290,13 @@ public extension TelegramEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func searchEmoji(category: EmojiSearchCategories.Group) -> Signal<(items: [TelegramMediaFile], isFinalResult: Bool), NoError> {
|
||||||
|
return _internal_searchEmoji(account: self.account, query: category.identifiers)
|
||||||
|
|> map { items, isFinalResult -> (items: [TelegramMediaFile], isFinalResult: Bool) in
|
||||||
|
return (items.map(\.file), isFinalResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func addRecentlyUsedSticker(fileReference: FileMediaReference) {
|
public func addRecentlyUsedSticker(fileReference: FileMediaReference) {
|
||||||
let _ = self.account.postbox.transaction({ transaction -> Void in
|
let _ = self.account.postbox.transaction({ transaction -> Void in
|
||||||
TelegramCore.addRecentlyUsedSticker(transaction: transaction, fileReference: fileReference)
|
TelegramCore.addRecentlyUsedSticker(transaction: transaction, fileReference: fileReference)
|
||||||
|
@ -651,16 +651,16 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
selectedPeers = self.optionReportSelectedPeers
|
selectedPeers = self.optionReportSelectedPeers
|
||||||
isExpanded = self.isOptionReportExpanded
|
isExpanded = self.isOptionReportExpanded
|
||||||
|
|
||||||
title = "Report Spam"
|
title = environment.strings.Chat_AdminActionSheet_ReportSpam
|
||||||
case .deleteAll:
|
case .deleteAll:
|
||||||
sectionId = "delete-all"
|
sectionId = "delete-all"
|
||||||
selectedPeers = self.optionDeleteAllSelectedPeers
|
selectedPeers = self.optionDeleteAllSelectedPeers
|
||||||
isExpanded = self.isOptionDeleteAllExpanded
|
isExpanded = self.isOptionDeleteAllExpanded
|
||||||
|
|
||||||
if component.peers.count == 1 {
|
if component.peers.count == 1 {
|
||||||
title = "Delete All from \(EnginePeer(component.peers[0].peer).compactDisplayTitle)"
|
title = environment.strings.Chat_AdminActionSheet_DeleteAllSingle(EnginePeer(component.peers[0].peer).compactDisplayTitle).string
|
||||||
} else {
|
} else {
|
||||||
title = "Delete All from Users"
|
title = environment.strings.Chat_AdminActionSheet_DeleteAllMultiple
|
||||||
}
|
}
|
||||||
case .ban:
|
case .ban:
|
||||||
sectionId = "ban"
|
sectionId = "ban"
|
||||||
@ -670,11 +670,11 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
let banTitle: String
|
let banTitle: String
|
||||||
let restrictTitle: String
|
let restrictTitle: String
|
||||||
if component.peers.count == 1 {
|
if component.peers.count == 1 {
|
||||||
banTitle = "Ban \(EnginePeer(component.peers[0].peer).compactDisplayTitle)"
|
banTitle = environment.strings.Chat_AdminActionSheet_BanSingle(EnginePeer(component.peers[0].peer).compactDisplayTitle).string
|
||||||
restrictTitle = "Restrict \(EnginePeer(component.peers[0].peer).compactDisplayTitle)"
|
restrictTitle = environment.strings.Chat_AdminActionSheet_RestrictSingle(EnginePeer(component.peers[0].peer).compactDisplayTitle).string
|
||||||
} else {
|
} else {
|
||||||
banTitle = "Ban Users"
|
banTitle = environment.strings.Chat_AdminActionSheet_BanMultiple
|
||||||
restrictTitle = "Restrict Users"
|
restrictTitle = environment.strings.Chat_AdminActionSheet_RestrictMultiple
|
||||||
}
|
}
|
||||||
title = self.isConfigurationExpanded ? restrictTitle : banTitle
|
title = self.isConfigurationExpanded ? restrictTitle : banTitle
|
||||||
}
|
}
|
||||||
@ -870,13 +870,7 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO:localize
|
let titleString: String = environment.strings.Chat_AdminActionSheet_DeleteTitle(Int32(component.messageCount))
|
||||||
let titleString: String
|
|
||||||
if component.messageCount == 1 {
|
|
||||||
titleString = "Delete 1 Message?"
|
|
||||||
} else {
|
|
||||||
titleString = "Delete \(component.messageCount) Messages?"
|
|
||||||
}
|
|
||||||
let titleSize = self.title.update(
|
let titleSize = self.title.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(MultilineTextComponent(
|
component: AnyComponent(MultilineTextComponent(
|
||||||
@ -928,7 +922,7 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
header: AnyComponent(MultilineTextComponent(
|
header: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "ADDITIONAL ACTIONS",
|
string: environment.strings.Chat_AdminActionSheet_RestrictSectionHeader,
|
||||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||||
textColor: environment.theme.list.freeTextColor
|
textColor: environment.theme.list.freeTextColor
|
||||||
)),
|
)),
|
||||||
@ -954,11 +948,11 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
let partiallyRestrictTitle: String
|
let partiallyRestrictTitle: String
|
||||||
let fullyBanTitle: String
|
let fullyBanTitle: String
|
||||||
if component.peers.count == 1 {
|
if component.peers.count == 1 {
|
||||||
partiallyRestrictTitle = "Partially restrict this user"
|
partiallyRestrictTitle = environment.strings.Chat_AdminActionSheet_RestrictFooterSingle
|
||||||
fullyBanTitle = "Fully ban this user"
|
fullyBanTitle = environment.strings.Chat_AdminActionSheet_BanFooterSingle
|
||||||
} else {
|
} else {
|
||||||
partiallyRestrictTitle = "Partially restrict users"
|
partiallyRestrictTitle = environment.strings.Chat_AdminActionSheet_RestrictFooterMultiple
|
||||||
fullyBanTitle = "Fully ban users"
|
fullyBanTitle = environment.strings.Chat_AdminActionSheet_BanFooterMultiple
|
||||||
}
|
}
|
||||||
|
|
||||||
let optionsFooterSize = self.optionsFooter.update(
|
let optionsFooterSize = self.optionsFooter.update(
|
||||||
@ -1029,7 +1023,7 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
case .sendMessages:
|
case .sendMessages:
|
||||||
itemTitle = AnyComponent(MultilineTextComponent(
|
itemTitle = AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Send Messages",
|
string: environment.strings.Channel_BanUser_PermissionSendMessages,
|
||||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
textColor: environment.theme.list.itemPrimaryTextColor
|
textColor: environment.theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -1041,7 +1035,7 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
itemTitle = AnyComponent(HStack([
|
itemTitle = AnyComponent(HStack([
|
||||||
AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
|
AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Send Media",
|
string: environment.strings.Channel_BanUser_PermissionSendMedia,
|
||||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
textColor: environment.theme.list.itemPrimaryTextColor
|
textColor: environment.theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -1056,7 +1050,7 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
} else {
|
} else {
|
||||||
itemTitle = AnyComponent(MultilineTextComponent(
|
itemTitle = AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Send Media",
|
string: environment.strings.Channel_BanUser_PermissionSendMedia,
|
||||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
textColor: environment.theme.list.itemPrimaryTextColor
|
textColor: environment.theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -1068,7 +1062,7 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
case .addUsers:
|
case .addUsers:
|
||||||
itemTitle = AnyComponent(MultilineTextComponent(
|
itemTitle = AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Add Users",
|
string: environment.strings.Channel_BanUser_PermissionAddMembers,
|
||||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
textColor: environment.theme.list.itemPrimaryTextColor
|
textColor: environment.theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -1078,7 +1072,7 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
case .pinMessages:
|
case .pinMessages:
|
||||||
itemTitle = AnyComponent(MultilineTextComponent(
|
itemTitle = AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Pin Messages",
|
string: environment.strings.Channel_EditAdmin_PermissionPinMessages,
|
||||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
textColor: environment.theme.list.itemPrimaryTextColor
|
textColor: environment.theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -1088,7 +1082,7 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
case .changeInfo:
|
case .changeInfo:
|
||||||
itemTitle = AnyComponent(MultilineTextComponent(
|
itemTitle = AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Change Chat Info",
|
string: environment.strings.Channel_BanUser_PermissionChangeGroupInfo,
|
||||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
textColor: environment.theme.list.itemPrimaryTextColor
|
textColor: environment.theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -1163,23 +1157,23 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
let mediaItemTitle: String
|
let mediaItemTitle: String
|
||||||
switch possibleMediaItem {
|
switch possibleMediaItem {
|
||||||
case .photos:
|
case .photos:
|
||||||
mediaItemTitle = "Send Photos"
|
mediaItemTitle = environment.strings.Channel_BanUser_PermissionSendPhoto
|
||||||
case .videos:
|
case .videos:
|
||||||
mediaItemTitle = "Send Videos"
|
mediaItemTitle = environment.strings.Channel_BanUser_PermissionSendVideo
|
||||||
case .stickersAndGifs:
|
case .stickersAndGifs:
|
||||||
mediaItemTitle = "Send Stickers & GIFs"
|
mediaItemTitle = environment.strings.Channel_BanUser_PermissionSendStickersAndGifs
|
||||||
case .music:
|
case .music:
|
||||||
mediaItemTitle = "Send Music"
|
mediaItemTitle = environment.strings.Channel_BanUser_PermissionSendMusic
|
||||||
case .files:
|
case .files:
|
||||||
mediaItemTitle = "Send Files"
|
mediaItemTitle = environment.strings.Channel_BanUser_PermissionSendFile
|
||||||
case .voiceMessages:
|
case .voiceMessages:
|
||||||
mediaItemTitle = "Send Voice Messages"
|
mediaItemTitle = environment.strings.Channel_BanUser_PermissionSendVoiceMessage
|
||||||
case .videoMessages:
|
case .videoMessages:
|
||||||
mediaItemTitle = "Send Video Messages"
|
mediaItemTitle = environment.strings.Channel_BanUser_PermissionSendVideoMessage
|
||||||
case .links:
|
case .links:
|
||||||
mediaItemTitle = "Embed Links"
|
mediaItemTitle = environment.strings.Channel_BanUser_PermissionEmbedLinks
|
||||||
case .polls:
|
case .polls:
|
||||||
mediaItemTitle = "Send Polls"
|
mediaItemTitle = environment.strings.Channel_BanUser_PermissionSendPolls
|
||||||
default:
|
default:
|
||||||
continue mediaRightsLoop
|
continue mediaRightsLoop
|
||||||
}
|
}
|
||||||
@ -1244,7 +1238,7 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
header: AnyComponent(MultilineTextComponent(
|
header: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "WHAT CAN THIS USER DO?",
|
string: environment.strings.Chat_AdminActionSheet_PermissionsSectionHeader,
|
||||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||||
textColor: environment.theme.list.freeTextColor
|
textColor: environment.theme.list.freeTextColor
|
||||||
)),
|
)),
|
||||||
@ -1318,7 +1312,7 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
content: AnyComponentWithIdentity(
|
content: AnyComponentWithIdentity(
|
||||||
id: AnyHashable(0),
|
id: AnyHashable(0),
|
||||||
component: AnyComponent(ButtonTextContentComponent(
|
component: AnyComponent(ButtonTextContentComponent(
|
||||||
text: "Proceed",
|
text: environment.strings.Chat_AdminActionSheet_ActionButton,
|
||||||
badge: 0,
|
badge: 0,
|
||||||
textColor: environment.theme.list.itemCheckColors.foregroundColor,
|
textColor: environment.theme.list.itemCheckColors.foregroundColor,
|
||||||
badgeBackground: environment.theme.list.itemCheckColors.foregroundColor,
|
badgeBackground: environment.theme.list.itemCheckColors.foregroundColor,
|
||||||
|
@ -483,7 +483,7 @@ final class AvatarEditorScreenComponent: Component {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
case let .category(value):
|
case let .category(value):
|
||||||
let resultSignal = context.engine.stickers.searchEmoji(emojiString: value)
|
let resultSignal = context.engine.stickers.searchEmoji(category: value)
|
||||||
|> mapToSignal { files, isFinalResult -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
|> mapToSignal { files, isFinalResult -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
||||||
var items: [EmojiPagerContentComponent.Item] = []
|
var items: [EmojiPagerContentComponent.Item] = []
|
||||||
|
|
||||||
@ -557,11 +557,11 @@ final class AvatarEditorScreenComponent: Component {
|
|||||||
fillWithLoadingPlaceholders: true,
|
fillWithLoadingPlaceholders: true,
|
||||||
items: []
|
items: []
|
||||||
)
|
)
|
||||||
], id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
], id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
version += 1
|
version += 1
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -264,21 +264,20 @@ public class ChatMessageForwardInfoNode: ASDisplayNode {
|
|||||||
titleColor = presentationData.theme.theme.chat.message.outgoing.accentTextColor
|
titleColor = presentationData.theme.theme.chat.message.outgoing.accentTextColor
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
if let storyData = storyData {
|
if let storyData = storyData {
|
||||||
switch storyData.storyType {
|
switch storyData.storyType {
|
||||||
case .regular:
|
case .regular:
|
||||||
titleString = PresentationStrings.FormattedString(string: "Forwarded story from", ranges: [])
|
titleString = PresentationStrings.FormattedString(string: presentationData.strings.Chat_MessageForwardInfo_StoryHeader, ranges: [])
|
||||||
authorString = peerString
|
authorString = peerString
|
||||||
case .expired:
|
case .expired:
|
||||||
titleString = PresentationStrings.FormattedString(string: "Expired story from", ranges: [])
|
titleString = PresentationStrings.FormattedString(string: presentationData.strings.Chat_MessageForwardInfo_ExpiredStoryHeader, ranges: [])
|
||||||
authorString = peerString
|
authorString = peerString
|
||||||
case .unavailable:
|
case .unavailable:
|
||||||
titleString = PresentationStrings.FormattedString(string: "Expired story from", ranges: [])
|
titleString = PresentationStrings.FormattedString(string: presentationData.strings.Chat_MessageForwardInfo_UnavailableStoryHeader, ranges: [])
|
||||||
authorString = peerString
|
authorString = peerString
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
titleString = PresentationStrings.FormattedString(string: "Forwarded from", ranges: [])
|
titleString = PresentationStrings.FormattedString(string: presentationData.strings.Chat_MessageForwardInfo_MessageHeader, ranges: [])
|
||||||
authorString = peerString
|
authorString = peerString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -309,7 +308,7 @@ public class ChatMessageForwardInfoNode: ASDisplayNode {
|
|||||||
titleString = strings.Message_GenericForwardedPsa(peerString)
|
titleString = strings.Message_GenericForwardedPsa(peerString)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
titleString = PresentationStrings.FormattedString(string: "Forwarded from", ranges: [])
|
titleString = PresentationStrings.FormattedString(string: presentationData.strings.Chat_MessageForwardInfo_MessageHeader, ranges: [])
|
||||||
authorString = peerString
|
authorString = peerString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -752,6 +752,7 @@ final class OverscrollContentsComponent: Component {
|
|||||||
let foregroundColor: UIColor
|
let foregroundColor: UIColor
|
||||||
let peer: EnginePeer?
|
let peer: EnginePeer?
|
||||||
let threadData: ChatOverscrollThreadData?
|
let threadData: ChatOverscrollThreadData?
|
||||||
|
let isForumThread: Bool
|
||||||
let unreadCount: Int
|
let unreadCount: Int
|
||||||
let location: TelegramEngine.NextUnreadChannelLocation
|
let location: TelegramEngine.NextUnreadChannelLocation
|
||||||
let expandOffset: CGFloat
|
let expandOffset: CGFloat
|
||||||
@ -766,6 +767,7 @@ final class OverscrollContentsComponent: Component {
|
|||||||
foregroundColor: UIColor,
|
foregroundColor: UIColor,
|
||||||
peer: EnginePeer?,
|
peer: EnginePeer?,
|
||||||
threadData: ChatOverscrollThreadData?,
|
threadData: ChatOverscrollThreadData?,
|
||||||
|
isForumThread: Bool,
|
||||||
unreadCount: Int,
|
unreadCount: Int,
|
||||||
location: TelegramEngine.NextUnreadChannelLocation,
|
location: TelegramEngine.NextUnreadChannelLocation,
|
||||||
expandOffset: CGFloat,
|
expandOffset: CGFloat,
|
||||||
@ -779,6 +781,7 @@ final class OverscrollContentsComponent: Component {
|
|||||||
self.foregroundColor = foregroundColor
|
self.foregroundColor = foregroundColor
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.threadData = threadData
|
self.threadData = threadData
|
||||||
|
self.isForumThread = isForumThread
|
||||||
self.unreadCount = unreadCount
|
self.unreadCount = unreadCount
|
||||||
self.location = location
|
self.location = location
|
||||||
self.expandOffset = expandOffset
|
self.expandOffset = expandOffset
|
||||||
@ -804,6 +807,9 @@ final class OverscrollContentsComponent: Component {
|
|||||||
if lhs.threadData != rhs.threadData {
|
if lhs.threadData != rhs.threadData {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.isForumThread != rhs.isForumThread {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.unreadCount != rhs.unreadCount {
|
if lhs.unreadCount != rhs.unreadCount {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -965,6 +971,8 @@ final class OverscrollContentsComponent: Component {
|
|||||||
titleText = threadData.data.info.title
|
titleText = threadData.data.info.title
|
||||||
} else if let peer = component.peer {
|
} else if let peer = component.peer {
|
||||||
titleText = peer.compactDisplayTitle
|
titleText = peer.compactDisplayTitle
|
||||||
|
} else if component.isForumThread {
|
||||||
|
titleText = component.context.sharedContext.currentPresentationData.with({ $0 }).strings.Chat_NavigationNoTopics
|
||||||
} else {
|
} else {
|
||||||
titleText = component.context.sharedContext.currentPresentationData.with({ $0 }).strings.Chat_NavigationNoChannels
|
titleText = component.context.sharedContext.currentPresentationData.with({ $0 }).strings.Chat_NavigationNoChannels
|
||||||
}
|
}
|
||||||
@ -1083,6 +1091,7 @@ public final class ChatOverscrollControl: CombinedComponent {
|
|||||||
let foregroundColor: UIColor
|
let foregroundColor: UIColor
|
||||||
let peer: EnginePeer?
|
let peer: EnginePeer?
|
||||||
let threadData: ChatOverscrollThreadData?
|
let threadData: ChatOverscrollThreadData?
|
||||||
|
let isForumThread: Bool
|
||||||
let unreadCount: Int
|
let unreadCount: Int
|
||||||
let location: TelegramEngine.NextUnreadChannelLocation
|
let location: TelegramEngine.NextUnreadChannelLocation
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
@ -1097,6 +1106,7 @@ public final class ChatOverscrollControl: CombinedComponent {
|
|||||||
foregroundColor: UIColor,
|
foregroundColor: UIColor,
|
||||||
peer: EnginePeer?,
|
peer: EnginePeer?,
|
||||||
threadData: ChatOverscrollThreadData?,
|
threadData: ChatOverscrollThreadData?,
|
||||||
|
isForumThread: Bool,
|
||||||
unreadCount: Int,
|
unreadCount: Int,
|
||||||
location: TelegramEngine.NextUnreadChannelLocation,
|
location: TelegramEngine.NextUnreadChannelLocation,
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
@ -1110,6 +1120,7 @@ public final class ChatOverscrollControl: CombinedComponent {
|
|||||||
self.foregroundColor = foregroundColor
|
self.foregroundColor = foregroundColor
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.threadData = threadData
|
self.threadData = threadData
|
||||||
|
self.isForumThread = isForumThread
|
||||||
self.unreadCount = unreadCount
|
self.unreadCount = unreadCount
|
||||||
self.location = location
|
self.location = location
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -1133,6 +1144,9 @@ public final class ChatOverscrollControl: CombinedComponent {
|
|||||||
if lhs.threadData != rhs.threadData {
|
if lhs.threadData != rhs.threadData {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.isForumThread != rhs.isForumThread {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.unreadCount != rhs.unreadCount {
|
if lhs.unreadCount != rhs.unreadCount {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -1171,6 +1185,7 @@ public final class ChatOverscrollControl: CombinedComponent {
|
|||||||
foregroundColor: context.component.foregroundColor,
|
foregroundColor: context.component.foregroundColor,
|
||||||
peer: context.component.peer,
|
peer: context.component.peer,
|
||||||
threadData: context.component.threadData,
|
threadData: context.component.threadData,
|
||||||
|
isForumThread: context.component.isForumThread,
|
||||||
unreadCount: context.component.unreadCount,
|
unreadCount: context.component.unreadCount,
|
||||||
location: context.component.location,
|
location: context.component.location,
|
||||||
expandOffset: context.component.expandDistance,
|
expandOffset: context.component.expandDistance,
|
||||||
|
@ -697,7 +697,10 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
premiumToastCounter += 1
|
premiumToastCounter += 1
|
||||||
let suggestSavedMessages = premiumToastCounter % 2 == 0
|
var suggestSavedMessages = premiumToastCounter % 2 == 0
|
||||||
|
if chatPeerId == nil {
|
||||||
|
suggestSavedMessages = false
|
||||||
|
}
|
||||||
let text: String
|
let text: String
|
||||||
let actionTitle: String
|
let actionTitle: String
|
||||||
if suggestSavedMessages {
|
if suggestSavedMessages {
|
||||||
@ -1091,7 +1094,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
case let .category(value):
|
case let .category(value):
|
||||||
let resultSignal = self.context.engine.stickers.searchEmoji(emojiString: value)
|
let resultSignal = self.context.engine.stickers.searchEmoji(category: value)
|
||||||
|> mapToSignal { files, isFinalResult -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
|> mapToSignal { files, isFinalResult -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
||||||
var items: [EmojiPagerContentComponent.Item] = []
|
var items: [EmojiPagerContentComponent.Item] = []
|
||||||
|
|
||||||
@ -1105,7 +1108,8 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
let item = EmojiPagerContentComponent.Item(
|
let item = EmojiPagerContentComponent.Item(
|
||||||
animationData: animationData,
|
animationData: animationData,
|
||||||
content: .animation(animationData),
|
content: .animation(animationData),
|
||||||
itemFile: itemFile, subgroupId: nil,
|
itemFile: itemFile,
|
||||||
|
subgroupId: nil,
|
||||||
icon: .none,
|
icon: .none,
|
||||||
tintMode: animationData.isTemplate ? .primary : .none
|
tintMode: animationData.isTemplate ? .primary : .none
|
||||||
)
|
)
|
||||||
@ -1162,10 +1166,10 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
fillWithLoadingPlaceholders: true,
|
fillWithLoadingPlaceholders: true,
|
||||||
items: []
|
items: []
|
||||||
)
|
)
|
||||||
], id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
], id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
version += 1
|
version += 1
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -1434,7 +1438,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
strongSelf.stickerSearchDisposable.set(nil)
|
strongSelf.stickerSearchDisposable.set(nil)
|
||||||
strongSelf.stickerSearchStateValue = EmojiSearchState(result: nil, isSearching: false)
|
strongSelf.stickerSearchStateValue = EmojiSearchState(result: nil, isSearching: false)
|
||||||
case let .category(value):
|
case let .category(value):
|
||||||
let resultSignal = strongSelf.context.engine.stickers.searchStickers(query: value, scope: [.installed, .remote])
|
let resultSignal = strongSelf.context.engine.stickers.searchStickers(category: value, scope: [.installed, .remote])
|
||||||
|> mapToSignal { files -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
|> mapToSignal { files -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
||||||
var items: [EmojiPagerContentComponent.Item] = []
|
var items: [EmojiPagerContentComponent.Item] = []
|
||||||
|
|
||||||
@ -1506,10 +1510,10 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
fillWithLoadingPlaceholders: true,
|
fillWithLoadingPlaceholders: true,
|
||||||
items: []
|
items: []
|
||||||
)
|
)
|
||||||
], id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
], id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.stickerSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
strongSelf.stickerSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
version += 1
|
version += 1
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -615,7 +615,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
case let .category(value):
|
case let .category(value):
|
||||||
let resultSignal = self.context.engine.stickers.searchEmoji(emojiString: value)
|
let resultSignal = self.context.engine.stickers.searchEmoji(category: value)
|
||||||
|> mapToSignal { files, isFinalResult -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
|> mapToSignal { files, isFinalResult -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
||||||
var items: [EmojiPagerContentComponent.Item] = []
|
var items: [EmojiPagerContentComponent.Item] = []
|
||||||
|
|
||||||
@ -687,11 +687,11 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
fillWithLoadingPlaceholders: true,
|
fillWithLoadingPlaceholders: true,
|
||||||
items: []
|
items: []
|
||||||
)
|
)
|
||||||
], id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
], id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
version += 1
|
version += 1
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -1744,7 +1744,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
private var textField: EmojiSearchTextField?
|
private var textField: EmojiSearchTextField?
|
||||||
|
|
||||||
private var tapRecognizer: UITapGestureRecognizer?
|
private var tapRecognizer: UITapGestureRecognizer?
|
||||||
private(set) var currentPresetSearchTerm: [String]?
|
private(set) var currentPresetSearchTerm: EmojiSearchCategories.Group?
|
||||||
|
|
||||||
private var params: Params?
|
private var params: Params?
|
||||||
|
|
||||||
@ -2570,7 +2570,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
|
|
||||||
public enum SearchQuery: Equatable {
|
public enum SearchQuery: Equatable {
|
||||||
case text(value: String, language: String)
|
case text(value: String, language: String)
|
||||||
case category(value: [String])
|
case category(value: EmojiSearchCategories.Group)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ItemContent: Equatable {
|
public enum ItemContent: Equatable {
|
||||||
|
@ -1658,7 +1658,6 @@ public extension EmojiPagerContentComponent {
|
|||||||
|
|
||||||
let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings
|
let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
let searchCategories: Signal<EmojiSearchCategories?, NoError>
|
let searchCategories: Signal<EmojiSearchCategories?, NoError>
|
||||||
switch subject {
|
switch subject {
|
||||||
case .groupPhotoEmojiSelection, .profilePhotoEmojiSelection:
|
case .groupPhotoEmojiSelection, .profilePhotoEmojiSelection:
|
||||||
|
@ -319,7 +319,7 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
case let .category(value):
|
case let .category(value):
|
||||||
let resultSignal = self.context.engine.stickers.searchEmoji(emojiString: value)
|
let resultSignal = self.context.engine.stickers.searchEmoji(category: value)
|
||||||
|> mapToSignal { files, isFinalResult -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
|> mapToSignal { files, isFinalResult -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
||||||
var items: [EmojiPagerContentComponent.Item] = []
|
var items: [EmojiPagerContentComponent.Item] = []
|
||||||
|
|
||||||
@ -333,7 +333,8 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode
|
|||||||
let item = EmojiPagerContentComponent.Item(
|
let item = EmojiPagerContentComponent.Item(
|
||||||
animationData: animationData,
|
animationData: animationData,
|
||||||
content: .animation(animationData),
|
content: .animation(animationData),
|
||||||
itemFile: itemFile, subgroupId: nil,
|
itemFile: itemFile,
|
||||||
|
subgroupId: nil,
|
||||||
icon: .none,
|
icon: .none,
|
||||||
tintMode: animationData.isTemplate ? .primary : .none
|
tintMode: animationData.isTemplate ? .primary : .none
|
||||||
)
|
)
|
||||||
@ -393,11 +394,11 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode
|
|||||||
fillWithLoadingPlaceholders: true,
|
fillWithLoadingPlaceholders: true,
|
||||||
items: []
|
items: []
|
||||||
)
|
)
|
||||||
], id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
], id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value), version: version, isPreset: false), isSearching: false)
|
self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value.id), version: version, isPreset: false), isSearching: false)
|
||||||
version += 1
|
version += 1
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ final class EmojiSearchSearchBarComponent: Component {
|
|||||||
let useOpaqueTheme: Bool
|
let useOpaqueTheme: Bool
|
||||||
let textInputState: TextInputState
|
let textInputState: TextInputState
|
||||||
let categories: EmojiSearchCategories?
|
let categories: EmojiSearchCategories?
|
||||||
let searchTermUpdated: ([String]?) -> Void
|
let searchTermUpdated: (EmojiSearchCategories.Group?) -> Void
|
||||||
let activateTextInput: () -> Void
|
let activateTextInput: () -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
@ -115,7 +115,7 @@ final class EmojiSearchSearchBarComponent: Component {
|
|||||||
useOpaqueTheme: Bool,
|
useOpaqueTheme: Bool,
|
||||||
textInputState: TextInputState,
|
textInputState: TextInputState,
|
||||||
categories: EmojiSearchCategories?,
|
categories: EmojiSearchCategories?,
|
||||||
searchTermUpdated: @escaping ([String]?) -> Void,
|
searchTermUpdated: @escaping (EmojiSearchCategories.Group?) -> Void,
|
||||||
activateTextInput: @escaping () -> Void
|
activateTextInput: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -366,7 +366,7 @@ final class EmojiSearchSearchBarComponent: Component {
|
|||||||
self.componentState?.updated(transition: .easeInOut(duration: 0.2))
|
self.componentState?.updated(transition: .easeInOut(duration: 0.2))
|
||||||
|
|
||||||
if let _ = self.selectedItem, let categories = component.categories, let group = categories.groups.first(where: { $0.id == itemId }) {
|
if let _ = self.selectedItem, let categories = component.categories, let group = categories.groups.first(where: { $0.id == itemId }) {
|
||||||
component.searchTermUpdated(group.identifiers)
|
component.searchTermUpdated(group)
|
||||||
|
|
||||||
if let itemComponentView = itemView.view.view {
|
if let itemComponentView = itemView.view.view {
|
||||||
var offset = self.scrollView.contentOffset.x
|
var offset = self.scrollView.contentOffset.x
|
||||||
|
@ -1081,7 +1081,7 @@ public final class GifPagerContentComponent: Component {
|
|||||||
case .text:
|
case .text:
|
||||||
break
|
break
|
||||||
case let .category(value):
|
case let .category(value):
|
||||||
component.inputInteraction.updateSearchQuery(value)
|
component.inputInteraction.updateSearchQuery(value.identifiers)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
self.visibleSearchHeader = visibleSearchHeader
|
self.visibleSearchHeader = visibleSearchHeader
|
||||||
|
@ -11,6 +11,9 @@ import AttachmentUI
|
|||||||
public class PremiumGiftAttachmentScreen: PremiumGiftScreen, AttachmentContainable {
|
public class PremiumGiftAttachmentScreen: PremiumGiftScreen, AttachmentContainable {
|
||||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
|
public var parentController: () -> ViewController? = {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
public var isContainerPanning: () -> Bool = { return false }
|
public var isContainerPanning: () -> Bool = { return false }
|
||||||
public var isContainerExpanded: () -> Bool = { return false }
|
public var isContainerExpanded: () -> Bool = { return false }
|
||||||
|
@ -1303,6 +1303,9 @@ public final class QuickReplySetupScreen: ViewControllerComponentContainer, Atta
|
|||||||
}
|
}
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in
|
||||||
}
|
}
|
||||||
|
public var parentController: () -> ViewController? = {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in
|
||||||
}
|
}
|
||||||
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in
|
||||||
|
@ -518,7 +518,7 @@ final class BusinessIntroSetupScreenComponent: Component {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
case let .category(value):
|
case let .category(value):
|
||||||
let resultSignal = component.context.engine.stickers.searchStickers(query: value, scope: [.installed, .remote])
|
let resultSignal = component.context.engine.stickers.searchStickers(category: value, scope: [.installed, .remote])
|
||||||
|> mapToSignal { files -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
|> mapToSignal { files -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
||||||
var items: [EmojiPagerContentComponent.Item] = []
|
var items: [EmojiPagerContentComponent.Item] = []
|
||||||
|
|
||||||
@ -589,13 +589,13 @@ final class BusinessIntroSetupScreenComponent: Component {
|
|||||||
fillWithLoadingPlaceholders: true,
|
fillWithLoadingPlaceholders: true,
|
||||||
items: []
|
items: []
|
||||||
)
|
)
|
||||||
], id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
], id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
if !self.isUpdating {
|
if !self.isUpdating {
|
||||||
self.state?.updated(transition: .immediate)
|
self.state?.updated(transition: .immediate)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.stickerSearchState = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
self.stickerSearchState = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
version += 1
|
version += 1
|
||||||
if !self.isUpdating {
|
if !self.isUpdating {
|
||||||
self.state?.updated(transition: .immediate)
|
self.state?.updated(transition: .immediate)
|
||||||
|
@ -337,6 +337,9 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina
|
|||||||
|
|
||||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
|
public var parentController: () -> ViewController? = {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
|
@ -1156,7 +1156,7 @@ public class StickerPickerScreen: ViewController {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
case let .category(value):
|
case let .category(value):
|
||||||
let resultSignal = context.engine.stickers.searchEmoji(emojiString: value)
|
let resultSignal = context.engine.stickers.searchEmoji(category: value)
|
||||||
|> mapToSignal { files, isFinalResult -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
|> mapToSignal { files, isFinalResult -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
||||||
var items: [EmojiPagerContentComponent.Item] = []
|
var items: [EmojiPagerContentComponent.Item] = []
|
||||||
|
|
||||||
@ -1227,10 +1227,10 @@ public class StickerPickerScreen: ViewController {
|
|||||||
fillWithLoadingPlaceholders: true,
|
fillWithLoadingPlaceholders: true,
|
||||||
items: []
|
items: []
|
||||||
)
|
)
|
||||||
], id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
], id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
version += 1
|
version += 1
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -1442,7 +1442,7 @@ public class StickerPickerScreen: ViewController {
|
|||||||
strongSelf.stickerSearchDisposable.set(nil)
|
strongSelf.stickerSearchDisposable.set(nil)
|
||||||
strongSelf.stickerSearchStateValue = EmojiSearchState(result: nil, isSearching: false)
|
strongSelf.stickerSearchStateValue = EmojiSearchState(result: nil, isSearching: false)
|
||||||
case let .category(value):
|
case let .category(value):
|
||||||
let resultSignal = context.engine.stickers.searchStickers(query: value, scope: [.installed, .remote])
|
let resultSignal = context.engine.stickers.searchStickers(category: value, scope: [.installed, .remote])
|
||||||
|> mapToSignal { files -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
|> mapToSignal { files -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
|
||||||
var items: [EmojiPagerContentComponent.Item] = []
|
var items: [EmojiPagerContentComponent.Item] = []
|
||||||
|
|
||||||
@ -1513,10 +1513,10 @@ public class StickerPickerScreen: ViewController {
|
|||||||
fillWithLoadingPlaceholders: true,
|
fillWithLoadingPlaceholders: true,
|
||||||
items: []
|
items: []
|
||||||
)
|
)
|
||||||
], id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
], id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.stickerSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
|
strongSelf.stickerSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value.id), version: version, isPreset: true), isSearching: false)
|
||||||
version += 1
|
version += 1
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -196,6 +196,9 @@ private final class AttachmentFileContext: AttachmentMediaPickerContext {
|
|||||||
class AttachmentFileControllerImpl: ItemListController, AttachmentFileController, AttachmentContainable {
|
class AttachmentFileControllerImpl: ItemListController, AttachmentFileController, AttachmentContainable {
|
||||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
|
public var parentController: () -> ViewController? = {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
|
@ -8451,7 +8451,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .entities(text: solution.text, entities: solution.entities), icon: .animation(name: "anim_infotip", delay: 0.2, tintColor: nil), location: .top, shouldDismissOnTouch: { point, _ in
|
let tooltipScreen = TooltipScreen(context: self.context, account: self.context.account, sharedContext: self.context.sharedContext, text: .entities(text: solution.text, entities: solution.entities), icon: .animation(name: "anim_infotip", delay: 0.2, tintColor: nil), location: .top, shouldDismissOnTouch: { point, _ in
|
||||||
return .ignore
|
return .ignore
|
||||||
}, openActiveTextItem: { [weak self] item, action in
|
}, openActiveTextItem: { [weak self] item, action in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
|
@ -26,10 +26,9 @@ extension ChatControllerImpl {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO:localize
|
var title: String? = messageIds.count == 1 ? self.presentationData.strings.Chat_AdminAction_ToastMessagesDeletedTitleSingle : self.presentationData.strings.Chat_AdminAction_ToastMessagesDeletedTitleMultiple
|
||||||
var title: String? = messageIds.count == 1 ? "Message Deleted" : "Messages Deleted"
|
|
||||||
if !result.deleteAllFromPeers.isEmpty {
|
if !result.deleteAllFromPeers.isEmpty {
|
||||||
title = "Messages Deleted"
|
title = self.presentationData.strings.Chat_AdminAction_ToastMessagesDeletedTitleMultiple
|
||||||
}
|
}
|
||||||
var text: String = ""
|
var text: String = ""
|
||||||
var undoRights: [EnginePeer.Id: InitialBannedRights] = [:]
|
var undoRights: [EnginePeer.Id: InitialBannedRights] = [:]
|
||||||
@ -38,21 +37,13 @@ extension ChatControllerImpl {
|
|||||||
if !text.isEmpty {
|
if !text.isEmpty {
|
||||||
text.append("\n")
|
text.append("\n")
|
||||||
}
|
}
|
||||||
if result.reportSpamPeers.count == 1 {
|
text.append(self.presentationData.strings.Chat_AdminAction_ToastReportedSpamText(Int32(result.reportSpamPeers.count)))
|
||||||
text.append("**1** user reported for spam.")
|
|
||||||
} else {
|
|
||||||
text.append("**\(result.reportSpamPeers.count)** users reported for spam.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !result.banPeers.isEmpty {
|
if !result.banPeers.isEmpty {
|
||||||
if !text.isEmpty {
|
if !text.isEmpty {
|
||||||
text.append("\n")
|
text.append("\n")
|
||||||
}
|
}
|
||||||
if result.banPeers.count == 1 {
|
text.append(self.presentationData.strings.Chat_AdminAction_ToastBannedText(Int32(result.banPeers.count)))
|
||||||
text.append("**1** user banned.")
|
|
||||||
} else {
|
|
||||||
text.append("**\(result.banPeers.count)** users banned.")
|
|
||||||
}
|
|
||||||
for id in result.banPeers {
|
for id in result.banPeers {
|
||||||
if let value = initialUserBannedRights[id] {
|
if let value = initialUserBannedRights[id] {
|
||||||
undoRights[id] = value
|
undoRights[id] = value
|
||||||
@ -63,11 +54,7 @@ extension ChatControllerImpl {
|
|||||||
if !text.isEmpty {
|
if !text.isEmpty {
|
||||||
text.append("\n")
|
text.append("\n")
|
||||||
}
|
}
|
||||||
if result.updateBannedRights.count == 1 {
|
text.append(self.presentationData.strings.Chat_AdminAction_ToastRestrictedText(Int32(result.updateBannedRights.count)))
|
||||||
text.append("**1** user restricted.")
|
|
||||||
} else {
|
|
||||||
text.append("**\(result.updateBannedRights.count)** users restricted.")
|
|
||||||
}
|
|
||||||
for (id, _) in result.updateBannedRights {
|
for (id, _) in result.updateBannedRights {
|
||||||
if let value = initialUserBannedRights[id] {
|
if let value = initialUserBannedRights[id] {
|
||||||
undoRights[id] = value
|
undoRights[id] = value
|
||||||
@ -97,9 +84,9 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if text.isEmpty {
|
if text.isEmpty {
|
||||||
text = messageIds.count == 1 ? "Message Deleted." : "Messages Deleted."
|
text = messageIds.count == 1 ? self.presentationData.strings.Chat_AdminAction_ToastMessagesDeletedTextSingle : self.presentationData.strings.Chat_AdminAction_ToastMessagesDeletedTextMultiple
|
||||||
if !result.deleteAllFromPeers.isEmpty {
|
if !result.deleteAllFromPeers.isEmpty {
|
||||||
text = "Messages Deleted."
|
text = self.presentationData.strings.Chat_AdminAction_ToastMessagesDeletedTextMultiple
|
||||||
}
|
}
|
||||||
title = nil
|
title = nil
|
||||||
}
|
}
|
||||||
|
@ -2243,12 +2243,11 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
|||||||
switch nextChannelToRead.location {
|
switch nextChannelToRead.location {
|
||||||
case .same:
|
case .same:
|
||||||
if let controllerNode = self.controllerInteraction.chatControllerNode() as? ChatControllerNode, let chatController = controllerNode.interfaceInteraction?.chatController() as? ChatControllerImpl, chatController.customChatNavigationStack != nil {
|
if let controllerNode = self.controllerInteraction.chatControllerNode() as? ChatControllerNode, let chatController = controllerNode.interfaceInteraction?.chatController() as? ChatControllerImpl, chatController.customChatNavigationStack != nil {
|
||||||
//TODO:localize
|
swipeText = (self.currentPresentationData.strings.Chat_NextSuggestedChannelSwipeProgress, [])
|
||||||
swipeText = ("Pull up to go to the next channel", [])
|
releaseText = (self.currentPresentationData.strings.Chat_NextSuggestedChannelSwipeAction, [])
|
||||||
releaseText = ("Release to go to the next channel", [])
|
|
||||||
} else if nextChannelToRead.threadData != nil {
|
} else if nextChannelToRead.threadData != nil {
|
||||||
swipeText = ("Pull up to go to the next topic", [])
|
swipeText = (self.currentPresentationData.strings.Chat_NextUnreadTopicSwipeProgress, [])
|
||||||
releaseText = ("Release to go to the next topic", [])
|
releaseText = (self.currentPresentationData.strings.Chat_NextUnreadTopicSwipeAction, [])
|
||||||
} else {
|
} else {
|
||||||
swipeText = (self.currentPresentationData.strings.Chat_NextChannelSameLocationSwipeProgress, [])
|
swipeText = (self.currentPresentationData.strings.Chat_NextChannelSameLocationSwipeProgress, [])
|
||||||
releaseText = (self.currentPresentationData.strings.Chat_NextChannelSameLocationSwipeAction, [])
|
releaseText = (self.currentPresentationData.strings.Chat_NextChannelSameLocationSwipeAction, [])
|
||||||
@ -2298,6 +2297,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
|||||||
data: threadData.data
|
data: threadData.data
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
isForumThread: self.chatLocation.threadId != nil,
|
||||||
unreadCount: self.nextChannelToRead?.unreadCount ?? 0,
|
unreadCount: self.nextChannelToRead?.unreadCount ?? 0,
|
||||||
location: self.nextChannelToRead?.location ?? .same,
|
location: self.nextChannelToRead?.location ?? .same,
|
||||||
context: self.context,
|
context: self.context,
|
||||||
|
@ -78,6 +78,9 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
|||||||
|
|
||||||
var requestAttachmentMenuExpansion: () -> Void = {}
|
var requestAttachmentMenuExpansion: () -> Void = {}
|
||||||
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
|
public var parentController: () -> ViewController? = {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
var cancelPanGesture: () -> Void = { }
|
var cancelPanGesture: () -> Void = { }
|
||||||
|
@ -601,7 +601,16 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let textSize: CGSize
|
let textSize: CGSize
|
||||||
if case .attributedString = self.text, let context = self.context {
|
|
||||||
|
var isTextWithEntities = false
|
||||||
|
switch self.text {
|
||||||
|
case .attributedString, .entities:
|
||||||
|
isTextWithEntities = true
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if isTextWithEntities, let context = self.context {
|
||||||
textSize = self.textView.update(
|
textSize = self.textView.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(MultilineTextWithEntitiesComponent(
|
component: AnyComponent(MultilineTextWithEntitiesComponent(
|
||||||
|
@ -258,6 +258,9 @@ public func generateWebAppThemeParams(_ presentationTheme: PresentationTheme) ->
|
|||||||
public final class WebAppController: ViewController, AttachmentContainable {
|
public final class WebAppController: ViewController, AttachmentContainable {
|
||||||
public var requestAttachmentMenuExpansion: () -> Void = { }
|
public var requestAttachmentMenuExpansion: () -> Void = { }
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
|
public var parentController: () -> ViewController? = {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user