diff --git a/submodules/AccountContext/Sources/GlobalExperimentalSettings.swift b/submodules/AccountContext/Sources/GlobalExperimentalSettings.swift index 143bfa6a53..382d312ee1 100644 --- a/submodules/AccountContext/Sources/GlobalExperimentalSettings.swift +++ b/submodules/AccountContext/Sources/GlobalExperimentalSettings.swift @@ -3,4 +3,5 @@ import Foundation public struct GlobalExperimentalSettings { public static var isAppStoreBuild: Bool = false public static var enableFeed: Bool = false + public static var enableWIPStickers: Bool = false } diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index 98318f1d11..28fdc97dc7 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -991,7 +991,13 @@ private enum StatsEntry: ItemListNodeEntry { switch transaction { case let .proceeds(_, fromDate, toDate): title = NSAttributedString(string: presentationData.strings.Monetization_Transaction_Proceeds, font: font, textColor: theme.list.itemPrimaryTextColor) - detailText = "\(stringForMediumCompactDate(timestamp: fromDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)) – \(stringForMediumCompactDate(timestamp: toDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat))" + let fromDateString = stringForMediumCompactDate(timestamp: fromDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, withTime: false) + let toDateString = stringForMediumCompactDate(timestamp: toDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, withTime: false) + if fromDateString == toDateString { + detailText = stringForMediumCompactDate(timestamp: toDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, withTime: true) + } else { + detailText = "\(fromDateString) – \(toDateString)" + } case let .withdrawal(status, _, date, provider, _, _): title = NSAttributedString(string: presentationData.strings.Monetization_Transaction_Withdrawal(provider).string, font: font, textColor: theme.list.itemPrimaryTextColor) labelColor = theme.list.itemDestructiveColor @@ -999,7 +1005,7 @@ private enum StatsEntry: ItemListNodeEntry { case .succeed: detailText = stringForMediumCompactDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat) case .failed: - detailText = stringForMediumCompactDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat) + " – \(presentationData.strings.Monetization_Transaction_Failed)" + detailText = stringForMediumCompactDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, withTime: false) + " – \(presentationData.strings.Monetization_Transaction_Failed)" detailColor = .destructive case .pending: detailText = presentationData.strings.Monetization_Transaction_Pending diff --git a/submodules/StatisticsUI/Sources/MonetizationIntroScreen.swift b/submodules/StatisticsUI/Sources/MonetizationIntroScreen.swift index f2a2837892..d7faca7461 100644 --- a/submodules/StatisticsUI/Sources/MonetizationIntroScreen.swift +++ b/submodules/StatisticsUI/Sources/MonetizationIntroScreen.swift @@ -73,6 +73,8 @@ private final class SheetContent: CombinedComponent { let infoTitle = Child(MultilineTextWithEntitiesComponent.self) let infoText = Child(MultilineTextComponent.self) + let spaceRegex = try? NSRegularExpression(pattern: "\\[(.*?)\\]", options: []) + return { context in let environment = context.environment[EnvironmentType.self] let component = context.component @@ -146,8 +148,7 @@ private final class SheetContent: CombinedComponent { .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + title.size.height / 2.0)) ) contentSize.height += title.size.height - contentSize.height += spacing - + contentSize.height += spacing - 2.0 var items: [AnyComponentWithIdentity] = [] items.append( @@ -199,7 +200,7 @@ private final class SheetContent: CombinedComponent { .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + list.size.height / 2.0)) ) contentSize.height += list.size.height - contentSize.height += spacing - 9.0 + contentSize.height += spacing - 13.0 let infoTitleString = strings.Monetization_Intro_Info_Title let infoTitleAttributedString = NSMutableAttributedString(string: infoTitleString, font: titleFont, textColor: textColor) @@ -225,7 +226,21 @@ private final class SheetContent: CombinedComponent { } let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - let infoString = strings.Monetization_Intro_Info_Text + var infoString = strings.Monetization_Intro_Info_Text + if let spaceRegex { + let nsRange = NSRange(infoString.startIndex..., in: infoString) + let matches = spaceRegex.matches(in: infoString, options: [], range: nsRange) + var modifiedString = infoString + + for match in matches.reversed() { + let matchRange = Range(match.range, in: infoString)! + let matchedSubstring = String(infoString[matchRange]) + let replacedSubstring = matchedSubstring.replacingOccurrences(of: " ", with: "\u{00A0}") + modifiedString.replaceSubrange(matchRange, with: replacedSubstring) + } + infoString = modifiedString + } + let infoAttributedString = parseMarkdownIntoAttributedString(infoString, attributes: markdownAttributes).mutableCopy() as! NSMutableAttributedString if let range = infoAttributedString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 { infoAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: infoAttributedString.string)) @@ -583,7 +598,7 @@ private final class ParagraphComponent: CombinedComponent { .position(CGPoint(x: 47.0, y: textTopInset + 18.0)) ) - return CGSize(width: context.availableSize.width, height: textTopInset + title.size.height + text.size.height + 20.0) + return CGSize(width: context.availableSize.width, height: textTopInset + title.size.height + text.size.height + 18.0) } } } diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index 97125897d4..03660866f6 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -495,6 +495,9 @@ private final class StickerPackContainer: ASDisplayNode { if let (info, _, _) = strongSelf.currentStickerPack, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { canEdit = true } + if !GlobalExperimentalSettings.enableWIPStickers { + canEdit = false + } let accountPeerId = strongSelf.context.account.peerId return combineLatest( @@ -973,7 +976,7 @@ private final class StickerPackContainer: ASDisplayNode { case .none: buttonColor = self.presentationData.theme.list.itemAccentColor case let .result(info, _, installed): - if info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { + if GlobalExperimentalSettings.enableWIPStickers && info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { buttonColor = installed ? self.presentationData.theme.list.itemAccentColor : self.presentationData.theme.list.itemCheckColors.foregroundColor } else { buttonColor = installed ? self.presentationData.theme.list.itemDestructiveColor : self.presentationData.theme.list.itemCheckColors.foregroundColor @@ -1012,6 +1015,9 @@ private final class StickerPackContainer: ASDisplayNode { if let info = self.currentStickerPack?.0, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { isEditable = true } + if !GlobalExperimentalSettings.enableWIPStickers { + isEditable = false + } let transaction: StickerPackPreviewGridTransaction if reload { @@ -1100,7 +1106,7 @@ private final class StickerPackContainer: ASDisplayNode { } }))) - if let (info, packItems, _) = self.currentStickerPack, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { + if GlobalExperimentalSettings.enableWIPStickers, let (info, packItems, _) = self.currentStickerPack, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { //TODO:localize items.append(.separator) if packItems.count > 0 { @@ -1434,7 +1440,7 @@ private final class StickerPackContainer: ASDisplayNode { } self.requestDismiss() } else if let (info, _, installed) = self.currentStickerPack { - if installed, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { + if GlobalExperimentalSettings.enableWIPStickers, installed, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { self.updateIsEditing(!self.isEditing) return } @@ -1509,7 +1515,7 @@ private final class StickerPackContainer: ASDisplayNode { if let currentContents = self.currentContents, currentContents.count == 1, let content = currentContents.first, case let .result(info, _, installed) = content { if installed { let text: String - if info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { + if GlobalExperimentalSettings.enableWIPStickers, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { if self.isEditing { var updated = false if let current = self.buttonNode.attributedTitle(for: .normal)?.string, !current.isEmpty && current != self.presentationData.strings.Common_Done { @@ -1688,7 +1694,7 @@ private final class StickerPackContainer: ASDisplayNode { self.controller?.present(textAlertController(context: self.context, title: nil, text: self.presentationData.strings.StickerPack_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) self.controller?.dismiss(animated: true, completion: nil) case let .result(info, items, installed): - isEditable = info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) + isEditable = GlobalExperimentalSettings.enableWIPStickers && info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) self.onReady() if !items.isEmpty && self.currentStickerPack == nil { if let _ = self.validLayout, abs(self.expandScrollProgress - 1.0) < .ulpOfOne { @@ -1772,7 +1778,7 @@ private final class StickerPackContainer: ASDisplayNode { self.updateButton(count: count) } - if info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { + if GlobalExperimentalSettings.enableWIPStickers && info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { entries.append(.add) } } @@ -1873,7 +1879,7 @@ private final class StickerPackContainer: ASDisplayNode { self.currentEntries = entries if let controller = self.controller { - let transaction = StickerPackPreviewGridTransaction(previousList: previousEntries, list: entries, context: self.context, interaction: self.interaction, theme: self.presentationData.theme, strings: self.presentationData.strings, animationCache: controller.animationCache, animationRenderer: controller.animationRenderer, scrollToItem: nil, isEditable: info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji), isEditing: self.isEditing, invert: invert) + let transaction = StickerPackPreviewGridTransaction(previousList: previousEntries, list: entries, context: self.context, interaction: self.interaction, theme: self.presentationData.theme, strings: self.presentationData.strings, animationCache: controller.animationCache, animationRenderer: controller.animationRenderer, scrollToItem: nil, isEditable: GlobalExperimentalSettings.enableWIPStickers && info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji), isEditing: self.isEditing, invert: invert) self.enqueueTransaction(transaction) } } @@ -1933,7 +1939,7 @@ private final class StickerPackContainer: ASDisplayNode { actionAreaBottomInset = 2.0 } } - if let (info, _, isInstalled) = self.currentStickerPack, isInstalled, !info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { + if let (info, _, isInstalled) = self.currentStickerPack, isInstalled, GlobalExperimentalSettings.enableWIPStickers && (!info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji)) { buttonHeight = 42.0 actionAreaTopInset = 1.0 actionAreaBottomInset = 2.0 diff --git a/submodules/TelegramStringFormatting/Sources/DateFormat.swift b/submodules/TelegramStringFormatting/Sources/DateFormat.swift index 3d2a314fd0..430fde43d6 100644 --- a/submodules/TelegramStringFormatting/Sources/DateFormat.swift +++ b/submodules/TelegramStringFormatting/Sources/DateFormat.swift @@ -58,7 +58,7 @@ public func getDateTimeComponents(timestamp: Int32) -> (day: Int32, month: Int32 return (timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year, timeinfo.tm_hour, timeinfo.tm_min) } -public func stringForMediumCompactDate(timestamp: Int32, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat) -> String { +public func stringForMediumCompactDate(timestamp: Int32, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, withTime: Bool = true) -> String { var t: time_t = Int(timestamp) var timeinfo = tm() localtime_r(&t, &timeinfo); @@ -66,14 +66,18 @@ public func stringForMediumCompactDate(timestamp: Int32, strings: PresentationSt let day = timeinfo.tm_mday let month = monthAtIndex(Int(timeinfo.tm_mon), strings: strings) - let timeString = stringForShortTimestamp(hours: Int32(timeinfo.tm_hour), minutes: Int32(timeinfo.tm_min), dateTimeFormat: dateTimeFormat) - + let timeString: String + if withTime { + timeString = " \(stringForShortTimestamp(hours: Int32(timeinfo.tm_hour), minutes: Int32(timeinfo.tm_min), dateTimeFormat: dateTimeFormat))" + } else { + timeString = "" + } let dateString: String switch dateTimeFormat.dateFormat { case .monthFirst: - dateString = String(format: "%@ %02d %@", month, day, timeString) + dateString = String(format: "%@ %02d%@", month, day, timeString) case .dayFirst: - dateString = String(format: "%02d %@ %@", day, month, timeString) + dateString = String(format: "%02d %@%@", day, month, timeString) } return dateString } diff --git a/submodules/TelegramUI/Components/Ads/AdsInfoScreen/Sources/AdsInfoScreen.swift b/submodules/TelegramUI/Components/Ads/AdsInfoScreen/Sources/AdsInfoScreen.swift index 6f42174a73..f55bdaa96c 100644 --- a/submodules/TelegramUI/Components/Ads/AdsInfoScreen/Sources/AdsInfoScreen.swift +++ b/submodules/TelegramUI/Components/Ads/AdsInfoScreen/Sources/AdsInfoScreen.swift @@ -72,6 +72,8 @@ private final class ScrollContent: CombinedComponent { let infoBackground = Child(RoundedRectangle.self) let infoTitle = Child(MultilineTextComponent.self) let infoText = Child(MultilineTextComponent.self) + + let spaceRegex = try? NSRegularExpression(pattern: "\\[(.*?)\\]", options: []) return { context in let environment = context.environment[ViewControllerComponentContainer.Environment.self].value @@ -240,7 +242,20 @@ private final class ScrollContent: CombinedComponent { state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: linkColor)!, theme) } - let infoString = strings.AdsInfo_Launch_Text + var infoString = strings.AdsInfo_Launch_Text + if let spaceRegex { + let nsRange = NSRange(infoString.startIndex..., in: infoString) + let matches = spaceRegex.matches(in: infoString, options: [], range: nsRange) + var modifiedString = infoString + + for match in matches.reversed() { + let matchRange = Range(match.range, in: infoString)! + let matchedSubstring = String(infoString[matchRange]) + let replacedSubstring = matchedSubstring.replacingOccurrences(of: " ", with: "\u{00A0}") + modifiedString.replaceSubrange(matchRange, with: replacedSubstring) + } + infoString = modifiedString + } let infoAttributedString = parseMarkdownIntoAttributedString(infoString, attributes: markdownAttributes).mutableCopy() as! NSMutableAttributedString if let range = infoAttributedString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 { infoAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: infoAttributedString.string)) diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index 67c66c7251..febed02523 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -184,7 +184,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings - let stickerItems = EmojiPagerContentComponent.stickerInputData(context: context, animationCache: animationCache, animationRenderer: animationRenderer, stickerNamespaces: stickerNamespaces, stickerOrderedItemListCollectionIds: stickerOrderedItemListCollectionIds, chatPeerId: chatPeerId, hasSearch: hasSearch, hasTrending: hasTrending, forceHasPremium: false, hasEdit: true, hideBackground: hideBackground) + let stickerItems = EmojiPagerContentComponent.stickerInputData(context: context, animationCache: animationCache, animationRenderer: animationRenderer, stickerNamespaces: stickerNamespaces, stickerOrderedItemListCollectionIds: stickerOrderedItemListCollectionIds, chatPeerId: chatPeerId, hasSearch: hasSearch, hasTrending: hasTrending, forceHasPremium: false, hasEdit: GlobalExperimentalSettings.enableWIPStickers, hideBackground: hideBackground) let reactions: Signal<[String], NoError> = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.App()) |> map { appConfiguration -> [String] in