mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Manual date formatting
This commit is contained in:
parent
c93c70ff84
commit
f852dca2c8
14 changed files with 308 additions and 91 deletions
|
|
@ -15832,3 +15832,5 @@ Error: %8$@";
|
|||
"Channel.AdminLog.MessageRankNameNew" = "changed member tag for %1$@:\n%2$@";
|
||||
"Channel.AdminLog.MessageRankUsernameNew" = "changed member tag for %1$@ (%2$@):\n%3$@";
|
||||
"Channel.AdminLog.MessageRankNew" = "changed member tag:\n%1$@";
|
||||
|
||||
"TextFormat.Date" = "Date";
|
||||
|
|
|
|||
|
|
@ -1220,6 +1220,8 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate, ASGestureRecog
|
|||
})
|
||||
strongSelf.present(controller)
|
||||
}
|
||||
}, openDateEditing: {
|
||||
|
||||
}, displaySlowmodeTooltip: { _, _ in
|
||||
}, displaySendMessageOptions: { [weak self] node, gesture in
|
||||
guard let strongSelf = self, let textInputPanelNode = strongSelf.textInputPanelNode else {
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ public final class ChatPanelInterfaceInteraction {
|
|||
public let updateInputLanguage: (@escaping (String?) -> String?) -> Void
|
||||
public let unarchiveChat: () -> Void
|
||||
public let openLinkEditing: () -> Void
|
||||
public let openDateEditing: () -> Void
|
||||
public let displaySlowmodeTooltip: (UIView, CGRect) -> Void
|
||||
public let displaySendMessageOptions: (ASDisplayNode, ContextGesture) -> Void
|
||||
public let openScheduledMessages: () -> Void
|
||||
|
|
@ -277,6 +278,7 @@ public final class ChatPanelInterfaceInteraction {
|
|||
updateInputLanguage: @escaping ((String?) -> String?) -> Void,
|
||||
unarchiveChat: @escaping () -> Void,
|
||||
openLinkEditing: @escaping () -> Void,
|
||||
openDateEditing: @escaping () -> Void,
|
||||
displaySlowmodeTooltip: @escaping (UIView, CGRect) -> Void,
|
||||
displaySendMessageOptions: @escaping (ASDisplayNode, ContextGesture) -> Void,
|
||||
openScheduledMessages: @escaping () -> Void,
|
||||
|
|
@ -405,6 +407,7 @@ public final class ChatPanelInterfaceInteraction {
|
|||
self.updateInputLanguage = updateInputLanguage
|
||||
self.unarchiveChat = unarchiveChat
|
||||
self.openLinkEditing = openLinkEditing
|
||||
self.openDateEditing = openDateEditing
|
||||
self.displaySlowmodeTooltip = displaySlowmodeTooltip
|
||||
self.displaySendMessageOptions = displaySendMessageOptions
|
||||
self.openScheduledMessages = openScheduledMessages
|
||||
|
|
@ -541,8 +544,8 @@ public final class ChatPanelInterfaceInteraction {
|
|||
}, requestStopPollInMessage: { _ in
|
||||
}, updateInputLanguage: { _ in
|
||||
}, unarchiveChat: {
|
||||
}, openLinkEditing: openLinkEditing,
|
||||
displaySlowmodeTooltip: { _, _ in
|
||||
}, openLinkEditing: openLinkEditing, openDateEditing: {
|
||||
}, displaySlowmodeTooltip: { _, _ in
|
||||
}, displaySendMessageOptions: { _, _ in
|
||||
}, openScheduledMessages: {
|
||||
}, openPeersNearby: {
|
||||
|
|
|
|||
|
|
@ -169,6 +169,57 @@ public func chatTextInputRemoveLinkAttribute(_ state: ChatTextInputState, select
|
|||
}
|
||||
}
|
||||
|
||||
public func chatTextInputAddDateAttribute(_ state: ChatTextInputState, selectionRange: Range<Int>, date: Int32) -> ChatTextInputState {
|
||||
if !selectionRange.isEmpty {
|
||||
let nsRange = NSRange(location: selectionRange.lowerBound, length: selectionRange.count)
|
||||
var linkRange = nsRange
|
||||
var attributesToRemove: [(NSAttributedString.Key, NSRange)] = []
|
||||
state.inputText.enumerateAttributes(in: nsRange, options: .longestEffectiveRangeNotRequired) { attributes, range, stop in
|
||||
for (key, _) in attributes {
|
||||
if key == ChatTextInputAttributes.date {
|
||||
attributesToRemove.append((key, range))
|
||||
linkRange = linkRange.union(range)
|
||||
} else {
|
||||
attributesToRemove.append((key, nsRange))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = NSMutableAttributedString(attributedString: state.inputText)
|
||||
for (attribute, range) in attributesToRemove {
|
||||
result.removeAttribute(attribute, range: range)
|
||||
}
|
||||
result.addAttribute(ChatTextInputAttributes.date, value: ChatTextInputTextDateAttribute(date: date), range: nsRange)
|
||||
return ChatTextInputState(inputText: result, selectionRange: selectionRange)
|
||||
} else {
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
public func chatTextInputRemoveDateAttribute(_ state: ChatTextInputState, selectionRange: Range<Int>) -> ChatTextInputState {
|
||||
if !selectionRange.isEmpty {
|
||||
let nsRange = NSRange(location: selectionRange.lowerBound, length: selectionRange.count)
|
||||
var attributesToRemove: [(NSAttributedString.Key, NSRange)] = []
|
||||
state.inputText.enumerateAttributes(in: nsRange, options: .longestEffectiveRangeNotRequired) { attributes, range, stop in
|
||||
for (key, _) in attributes {
|
||||
if key == ChatTextInputAttributes.date {
|
||||
attributesToRemove.append((key, range))
|
||||
} else {
|
||||
attributesToRemove.append((key, nsRange))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = NSMutableAttributedString(attributedString: state.inputText)
|
||||
for (attribute, range) in attributesToRemove {
|
||||
result.removeAttribute(attribute, range: range)
|
||||
}
|
||||
return ChatTextInputState(inputText: result, selectionRange: selectionRange)
|
||||
} else {
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
public func chatTextInputAddMentionAttribute(_ state: ChatTextInputState, peer: EnginePeer) -> ChatTextInputState {
|
||||
let inputText = NSMutableAttributedString(attributedString: state.inputText)
|
||||
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ public final class ChatRecentActionsController: TelegramBaseController {
|
|||
}, updateInputLanguage: { _ in
|
||||
}, unarchiveChat: {
|
||||
}, openLinkEditing: {
|
||||
}, openDateEditing: {
|
||||
}, displaySlowmodeTooltip: { _, _ in
|
||||
}, displaySendMessageOptions: { _, _ in
|
||||
}, openScheduledMessages: {
|
||||
|
|
|
|||
|
|
@ -652,6 +652,8 @@ public final class ChatTextInputPanelComponent: Component {
|
|||
},
|
||||
openLinkEditing: {
|
||||
},
|
||||
openDateEditing: {
|
||||
},
|
||||
displaySlowmodeTooltip: { _, _ in
|
||||
},
|
||||
displaySendMessageOptions: { [weak self] node, gesture in
|
||||
|
|
|
|||
|
|
@ -4838,6 +4838,11 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
strongSelf.formatAttributesLink(strongSelf)
|
||||
}
|
||||
},
|
||||
UIAction(title: self.strings?.TextFormat_Date ?? "Date", image: nil) { [weak self] (action) in
|
||||
if let strongSelf = self {
|
||||
strongSelf.formatAttributesDate(strongSelf)
|
||||
}
|
||||
},
|
||||
UIAction(title: self.strings?.TextFormat_Strikethrough ?? "Strikethrough", image: nil) { [weak self] (action) in
|
||||
if let strongSelf = self {
|
||||
strongSelf.formatAttributesStrikethrough(strongSelf)
|
||||
|
|
@ -4924,6 +4929,11 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
self.interfaceInteraction?.openLinkEditing()
|
||||
}
|
||||
|
||||
@objc public func formatAttributesDate(_ sender: Any) {
|
||||
self.inputMenu.back()
|
||||
self.interfaceInteraction?.openDateEditing()
|
||||
}
|
||||
|
||||
@objc public func formatAttributesStrikethrough(_ sender: Any) {
|
||||
self.inputMenu.back()
|
||||
self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||
private let cancel = ComponentView<Empty>()
|
||||
private let title = ComponentView<Empty>()
|
||||
private let button = ComponentView<Empty>()
|
||||
private let onlineButton = ComponentView<Empty>()
|
||||
private let secondaryButton = ComponentView<Empty>()
|
||||
|
||||
private var datePicker: DatePickerNode?
|
||||
|
||||
|
|
@ -154,7 +154,17 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||
self.environment = environment
|
||||
|
||||
if self.component == nil {
|
||||
self.updateMinimumDate(currentTime: component.currentTime, minimalTime: component.minimalTime)
|
||||
if case .format = component.mode {
|
||||
self.minDate = Date(timeIntervalSince1970: 0.0)
|
||||
self.maxDate = Date(timeIntervalSince1970: Double(Int32.max - 1))
|
||||
if let current = component.currentTime {
|
||||
self.date = Date(timeIntervalSince1970: Double(current))
|
||||
} else {
|
||||
self.date = Date()
|
||||
}
|
||||
} else {
|
||||
self.updateMinimumDate(currentTime: component.currentTime, minimalTime: component.minimalTime)
|
||||
}
|
||||
self.repeatPeriod = component.currentRepeatPeriod
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +218,9 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||
title = strings.Conversation_ScheduleMessage_Title
|
||||
case .reminders:
|
||||
title = strings.Conversation_SetReminder_Title
|
||||
case .format:
|
||||
//TODO:localize
|
||||
title = "Date"
|
||||
}
|
||||
let titleSize = self.title.update(
|
||||
transition: transition,
|
||||
|
|
@ -359,89 +372,94 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||
|
||||
contentHeight += 56.0
|
||||
|
||||
transition.setFrame(layer: self.bottomSeparator, frame: CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: CGSize(width: availableSize.width - sideInset * 2.0, height: UIScreenPixel)))
|
||||
self.bottomSeparator.backgroundColor = environment.theme.list.itemBlocksSeparatorColor.cgColor
|
||||
if self.bottomSeparator.superlayer == nil {
|
||||
self.layer.addSublayer(self.bottomSeparator)
|
||||
}
|
||||
|
||||
let repeatTitleSize = self.repeatTitle.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
Text(text: strings.ScheduleMessage_Repeat, font: Font.regular(17.0), color: environment.theme.actionSheet.primaryTextColor)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let repeatTitleFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight + 16.0), size: repeatTitleSize)
|
||||
if let timeTitleView = self.repeatTitle.view {
|
||||
if timeTitleView.superview == nil {
|
||||
self.addSubview(timeTitleView)
|
||||
}
|
||||
transition.setFrame(view: timeTitleView, frame: repeatTitleFrame)
|
||||
}
|
||||
|
||||
let repeatString: String
|
||||
if let repeatPeriod = self.repeatPeriod {
|
||||
switch repeatPeriod {
|
||||
case 86400:
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_Daily
|
||||
case 7 * 86400:
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_Weekly
|
||||
case 14 * 86400:
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_Biweekly
|
||||
case 30 * 86400:
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_Monthly
|
||||
case 91 * 86400:
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_3Months
|
||||
case 182 * 86400:
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_6Months
|
||||
case 365 * 86400:
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_Yearly
|
||||
default:
|
||||
repeatString = "\(repeatPeriod)s"
|
||||
}
|
||||
var repeatValueFrame = CGRect()
|
||||
if case .format = component.mode {
|
||||
|
||||
} else {
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_Never
|
||||
}
|
||||
|
||||
let repeatValueSize = self.repeatValue.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
PlainButtonComponent(
|
||||
content: AnyComponent(
|
||||
ButtonContentComponent(
|
||||
theme: environment.theme,
|
||||
text: repeatString,
|
||||
isActive: self.isPickingRepeatPeriod,
|
||||
isLocked: !component.context.isPremium
|
||||
)
|
||||
),
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if self.isPickingTime {
|
||||
self.isPickingTime = false
|
||||
} else {
|
||||
self.isPickingRepeatPeriod = !self.isPickingRepeatPeriod
|
||||
}
|
||||
self.state?.updated()
|
||||
}
|
||||
)
|
||||
),
|
||||
environment: {
|
||||
},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let repeatValueFrame = CGRect(origin: CGPoint(x: availableSize.width - sideInset - repeatValueSize.width, y: contentHeight + 10.0), size: repeatValueSize)
|
||||
if let repeatValueView = self.repeatValue.view {
|
||||
if repeatValueView.superview == nil {
|
||||
self.addSubview(repeatValueView)
|
||||
transition.setFrame(layer: self.bottomSeparator, frame: CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: CGSize(width: availableSize.width - sideInset * 2.0, height: UIScreenPixel)))
|
||||
self.bottomSeparator.backgroundColor = environment.theme.list.itemBlocksSeparatorColor.cgColor
|
||||
if self.bottomSeparator.superlayer == nil {
|
||||
self.layer.addSublayer(self.bottomSeparator)
|
||||
}
|
||||
transition.setFrame(view: repeatValueView, frame: repeatValueFrame)
|
||||
|
||||
let repeatTitleSize = self.repeatTitle.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
Text(text: strings.ScheduleMessage_Repeat, font: Font.regular(17.0), color: environment.theme.actionSheet.primaryTextColor)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let repeatTitleFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight + 16.0), size: repeatTitleSize)
|
||||
if let timeTitleView = self.repeatTitle.view {
|
||||
if timeTitleView.superview == nil {
|
||||
self.addSubview(timeTitleView)
|
||||
}
|
||||
transition.setFrame(view: timeTitleView, frame: repeatTitleFrame)
|
||||
}
|
||||
|
||||
let repeatString: String
|
||||
if let repeatPeriod = self.repeatPeriod {
|
||||
switch repeatPeriod {
|
||||
case 86400:
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_Daily
|
||||
case 7 * 86400:
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_Weekly
|
||||
case 14 * 86400:
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_Biweekly
|
||||
case 30 * 86400:
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_Monthly
|
||||
case 91 * 86400:
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_3Months
|
||||
case 182 * 86400:
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_6Months
|
||||
case 365 * 86400:
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_Yearly
|
||||
default:
|
||||
repeatString = "\(repeatPeriod)s"
|
||||
}
|
||||
} else {
|
||||
repeatString = strings.ScheduleMessage_RepeatPeriod_Never
|
||||
}
|
||||
|
||||
let repeatValueSize = self.repeatValue.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
PlainButtonComponent(
|
||||
content: AnyComponent(
|
||||
ButtonContentComponent(
|
||||
theme: environment.theme,
|
||||
text: repeatString,
|
||||
isActive: self.isPickingRepeatPeriod,
|
||||
isLocked: !component.context.isPremium
|
||||
)
|
||||
),
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if self.isPickingTime {
|
||||
self.isPickingTime = false
|
||||
} else {
|
||||
self.isPickingRepeatPeriod = !self.isPickingRepeatPeriod
|
||||
}
|
||||
self.state?.updated()
|
||||
}
|
||||
)
|
||||
),
|
||||
environment: {
|
||||
},
|
||||
containerSize: availableSize
|
||||
)
|
||||
repeatValueFrame = CGRect(origin: CGPoint(x: availableSize.width - sideInset - repeatValueSize.width, y: contentHeight + 10.0), size: repeatValueSize)
|
||||
if let repeatValueView = self.repeatValue.view {
|
||||
if repeatValueView.superview == nil {
|
||||
self.addSubview(repeatValueView)
|
||||
}
|
||||
transition.setFrame(view: repeatValueView, frame: repeatValueFrame)
|
||||
}
|
||||
contentHeight += 70.0
|
||||
}
|
||||
contentHeight += 70.0
|
||||
|
||||
let time = stringForMessageTimestamp(timestamp: Int32(date.timeIntervalSince1970), dateTimeFormat: environment.dateTimeFormat)
|
||||
let buttonTitle: String
|
||||
|
|
@ -462,6 +480,9 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||
} else {
|
||||
buttonTitle = strings.Conversation_SetReminder_RemindOn(self.dateFormatter.string(from: date), time).string
|
||||
}
|
||||
case .format:
|
||||
//TODO:localize
|
||||
buttonTitle = component.currentTime != nil ? "Edit Date" : "Add Date"
|
||||
}
|
||||
|
||||
let buttonSideInset: CGFloat = 30.0
|
||||
|
|
@ -504,10 +525,51 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||
}
|
||||
contentHeight += buttonSize.height
|
||||
|
||||
if case .scheduledMessages(true) = component.mode {
|
||||
if case .format = component.mode, component.currentTime != nil {
|
||||
contentHeight += 8.0
|
||||
|
||||
let buttonSize = self.onlineButton.update(
|
||||
let buttonSize = self.secondaryButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
style: .glass,
|
||||
color: environment.theme.list.itemDestructiveColor.withMultipliedAlpha(0.1),
|
||||
foreground: environment.theme.list.itemDestructiveColor,
|
||||
pressedColor: environment.theme.list.itemDestructiveColor.withMultipliedAlpha(0.8),
|
||||
),
|
||||
content: AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent(
|
||||
Text(text: "Remove Date", font: Font.semibold(17.0), color: environment.theme.list.itemDestructiveColor)
|
||||
)),
|
||||
isEnabled: true,
|
||||
displaysProgress: false,
|
||||
action: { [weak self] in
|
||||
guard let self, let component = self.component, let controller = self.environment?.controller() as? ChatScheduleTimeScreen else {
|
||||
return
|
||||
}
|
||||
controller.completion(
|
||||
ChatScheduleTimeScreen.Result(
|
||||
time: 0,
|
||||
repeatPeriod: nil
|
||||
)
|
||||
)
|
||||
component.dismiss()
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - buttonSideInset * 2.0, height: 52.0)
|
||||
)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: buttonSideInset, y: contentHeight), size: buttonSize)
|
||||
if let buttonView = self.secondaryButton.view {
|
||||
if buttonView.superview == nil {
|
||||
self.addSubview(buttonView)
|
||||
}
|
||||
transition.setFrame(view: buttonView, frame: buttonFrame)
|
||||
}
|
||||
contentHeight += buttonSize.height
|
||||
} else if case .scheduledMessages(true) = component.mode {
|
||||
contentHeight += 8.0
|
||||
|
||||
let buttonSize = self.secondaryButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
|
|
@ -538,7 +600,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
|||
containerSize: CGSize(width: availableSize.width - buttonSideInset * 2.0, height: 52.0)
|
||||
)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: buttonSideInset, y: contentHeight), size: buttonSize)
|
||||
if let buttonView = self.onlineButton.view {
|
||||
if let buttonView = self.secondaryButton.view {
|
||||
if buttonView.superview == nil {
|
||||
self.addSubview(buttonView)
|
||||
}
|
||||
|
|
@ -844,6 +906,7 @@ public class ChatScheduleTimeScreen: ViewControllerComponentContainer {
|
|||
public enum Mode: Equatable {
|
||||
case scheduledMessages(sendWhenOnlineAvailable: Bool)
|
||||
case reminders
|
||||
case format
|
||||
}
|
||||
|
||||
public struct Result {
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
|
|||
}, updateInputLanguage: { _ in
|
||||
}, unarchiveChat: {
|
||||
}, openLinkEditing: {
|
||||
}, openDateEditing: {
|
||||
}, displaySlowmodeTooltip: { _, _ in
|
||||
}, displaySendMessageOptions: { _, _ in
|
||||
}, openScheduledMessages: {
|
||||
|
|
|
|||
|
|
@ -723,6 +723,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
|||
})
|
||||
strongSelf.present(controller, nil)
|
||||
}
|
||||
}, openDateEditing: {
|
||||
}, displaySlowmodeTooltip: { _, _ in
|
||||
}, displaySendMessageOptions: { [weak self] node, gesture in
|
||||
guard let strongSelf = self else {
|
||||
|
|
|
|||
|
|
@ -3685,6 +3685,62 @@ extension ChatControllerImpl {
|
|||
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: false, { $0.updatedInputMode({ _ in return .none }) })
|
||||
}
|
||||
}, openDateEditing: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
var selectionRange: Range<Int>?
|
||||
var text: NSAttributedString?
|
||||
var inputMode: ChatInputMode?
|
||||
self.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in
|
||||
selectionRange = state.interfaceState.effectiveInputState.selectionRange
|
||||
if let selectionRange = selectionRange {
|
||||
text = state.interfaceState.effectiveInputState.inputText.attributedSubstring(from: NSRange(location: selectionRange.startIndex, length: selectionRange.count))
|
||||
}
|
||||
inputMode = state.inputMode
|
||||
return state
|
||||
})
|
||||
|
||||
var date: Int32?
|
||||
if let text {
|
||||
text.enumerateAttributes(in: NSMakeRange(0, text.length)) { attributes, _, _ in
|
||||
if let dateAttribute = attributes[ChatTextInputAttributes.date] as? ChatTextInputTextDateAttribute {
|
||||
date = dateAttribute.date
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let controller = ChatScheduleTimeScreen(
|
||||
context: self.context,
|
||||
mode: .format,
|
||||
currentTime: date,
|
||||
currentRepeatPeriod: nil,
|
||||
minimalTime: nil,
|
||||
isDark: false,
|
||||
completion: { [weak self] result in
|
||||
guard let self, let inputMode = inputMode, let selectionRange = selectionRange else {
|
||||
return
|
||||
}
|
||||
if result.time != 0 {
|
||||
self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
|
||||
return (chatTextInputAddDateAttribute(current, selectionRange: selectionRange, date: result.time), inputMode)
|
||||
}
|
||||
} else {
|
||||
self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
|
||||
return (chatTextInputRemoveDateAttribute(current, selectionRange: selectionRange), inputMode)
|
||||
}
|
||||
}
|
||||
|
||||
self.updateChatPresentationInterfaceState(animated: false, interactive: true, {
|
||||
return $0.updatedInputMode({ _ in return inputMode }).updatedInterfaceState({
|
||||
$0.withUpdatedEffectiveInputState(ChatTextInputState(inputText: $0.effectiveInputState.inputText, selectionRange: selectionRange.endIndex ..< selectionRange.endIndex))
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
self.push(controller)
|
||||
|
||||
self.updateChatPresentationInterfaceState(animated: false, interactive: false, { $0.updatedInputMode({ _ in return .none }) })
|
||||
}, displaySlowmodeTooltip: { [weak self] sourceView, nodeRect in
|
||||
guard let strongSelf = self, let slowmodeState = strongSelf.presentationInterfaceState.slowmodeState else {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -17,12 +17,13 @@ public struct ChatTextInputAttributes {
|
|||
public static let underline = NSAttributedString.Key(rawValue: "Attribute__Underline")
|
||||
public static let textMention = NSAttributedString.Key(rawValue: "Attribute__TextMention")
|
||||
public static let textUrl = NSAttributedString.Key(rawValue: "Attribute__TextUrl")
|
||||
public static let date = NSAttributedString.Key(rawValue: "Attribute__Date")
|
||||
public static let spoiler = NSAttributedString.Key(rawValue: "Attribute__Spoiler")
|
||||
public static let customEmoji = NSAttributedString.Key(rawValue: "Attribute__CustomEmoji")
|
||||
public static let block = NSAttributedString.Key(rawValue: "Attribute__Blockquote")
|
||||
public static let collapsedBlock = NSAttributedString.Key(rawValue: "Attribute__CollapsedBlockquote")
|
||||
|
||||
public static let allAttributes = [ChatTextInputAttributes.bold, ChatTextInputAttributes.italic, ChatTextInputAttributes.monospace, ChatTextInputAttributes.strikethrough, ChatTextInputAttributes.underline, ChatTextInputAttributes.textMention, ChatTextInputAttributes.textUrl, ChatTextInputAttributes.spoiler, ChatTextInputAttributes.customEmoji, ChatTextInputAttributes.block, ChatTextInputAttributes.collapsedBlock]
|
||||
public static let allAttributes = [ChatTextInputAttributes.bold, ChatTextInputAttributes.italic, ChatTextInputAttributes.monospace, ChatTextInputAttributes.strikethrough, ChatTextInputAttributes.underline, ChatTextInputAttributes.textMention, ChatTextInputAttributes.textUrl, ChatTextInputAttributes.date, ChatTextInputAttributes.spoiler, ChatTextInputAttributes.customEmoji, ChatTextInputAttributes.block, ChatTextInputAttributes.collapsedBlock]
|
||||
}
|
||||
|
||||
public let originalTextAttributeKey = NSAttributedString.Key(rawValue: "Attribute__OriginalText")
|
||||
|
|
@ -237,7 +238,7 @@ public func textAttributedStringForStateText(context: AnyObject, stateText: NSAt
|
|||
var fontAttributes: ChatTextFontAttributes = []
|
||||
|
||||
for (key, value) in attributes {
|
||||
if key == ChatTextInputAttributes.textMention || key == ChatTextInputAttributes.textUrl {
|
||||
if key == ChatTextInputAttributes.textMention || key == ChatTextInputAttributes.textUrl || key == ChatTextInputAttributes.date {
|
||||
result.addAttribute(key, value: value, range: range)
|
||||
result.addAttribute(NSAttributedString.Key.foregroundColor, value: accentTextColor, range: range)
|
||||
if accentTextColor.isEqual(textColor) {
|
||||
|
|
@ -363,6 +364,24 @@ public final class ChatTextInputTextUrlAttribute: NSObject {
|
|||
}
|
||||
}
|
||||
|
||||
public final class ChatTextInputTextDateAttribute: NSObject {
|
||||
public let date: Int32
|
||||
|
||||
public init(date: Int32) {
|
||||
self.date = date
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
override public func isEqual(_ object: Any?) -> Bool {
|
||||
if let other = object as? ChatTextInputTextDateAttribute {
|
||||
return self.date == other.date
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class ChatTextInputTextQuoteAttribute: NSObject {
|
||||
public enum Kind: Equatable {
|
||||
case quote
|
||||
|
|
@ -836,6 +855,7 @@ public func refreshChatTextInputAttributes(context: AnyObject, textView: UITextV
|
|||
textView.textStorage.removeAttribute(NSAttributedString.Key.strikethroughStyle, range: fullRange)
|
||||
textView.textStorage.removeAttribute(ChatTextInputAttributes.textMention, range: fullRange)
|
||||
textView.textStorage.removeAttribute(ChatTextInputAttributes.textUrl, range: fullRange)
|
||||
textView.textStorage.removeAttribute(ChatTextInputAttributes.date, range: fullRange)
|
||||
textView.textStorage.removeAttribute(ChatTextInputAttributes.spoiler, range: fullRange)
|
||||
textView.textStorage.removeAttribute(ChatTextInputAttributes.customEmoji, range: fullRange)
|
||||
textView.textStorage.removeAttribute(ChatTextInputAttributes.block, range: fullRange)
|
||||
|
|
@ -850,7 +870,7 @@ public func refreshChatTextInputAttributes(context: AnyObject, textView: UITextV
|
|||
var fontAttributes: ChatTextFontAttributes = []
|
||||
|
||||
for (key, value) in attributes {
|
||||
if key == ChatTextInputAttributes.textMention || key == ChatTextInputAttributes.textUrl {
|
||||
if key == ChatTextInputAttributes.textMention || key == ChatTextInputAttributes.textUrl || key == ChatTextInputAttributes.date {
|
||||
textView.textStorage.addAttribute(key, value: value, range: range)
|
||||
textView.textStorage.addAttribute(NSAttributedString.Key.foregroundColor, value: accentTextColor, range: range)
|
||||
|
||||
|
|
@ -964,6 +984,7 @@ public func refreshGenericTextInputAttributes(context: AnyObject, textView: UITe
|
|||
textView.textStorage.removeAttribute(NSAttributedString.Key.strikethroughStyle, range: fullRange)
|
||||
textView.textStorage.removeAttribute(ChatTextInputAttributes.textMention, range: fullRange)
|
||||
textView.textStorage.removeAttribute(ChatTextInputAttributes.textUrl, range: fullRange)
|
||||
textView.textStorage.removeAttribute(ChatTextInputAttributes.date, range: fullRange)
|
||||
textView.textStorage.removeAttribute(ChatTextInputAttributes.spoiler, range: fullRange)
|
||||
|
||||
textView.textStorage.addAttribute(NSAttributedString.Key.font, value: Font.regular(baseFontSize), range: fullRange)
|
||||
|
|
@ -973,7 +994,7 @@ public func refreshGenericTextInputAttributes(context: AnyObject, textView: UITe
|
|||
var fontAttributes: ChatTextFontAttributes = []
|
||||
|
||||
for (key, value) in attributes {
|
||||
if key == ChatTextInputAttributes.textMention || key == ChatTextInputAttributes.textUrl {
|
||||
if key == ChatTextInputAttributes.textMention || key == ChatTextInputAttributes.textUrl || key == ChatTextInputAttributes.date {
|
||||
textView.textStorage.addAttribute(key, value: value, range: range)
|
||||
textView.textStorage.addAttribute(NSAttributedString.Key.foregroundColor, value: theme.chat.inputPanel.panelControlAccentColor, range: range)
|
||||
|
||||
|
|
|
|||
|
|
@ -167,6 +167,8 @@ public func generateChatInputTextEntities(_ text: NSAttributedString, maxAnimate
|
|||
entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .TextMention(peerId: value.peerId)))
|
||||
} else if key == ChatTextInputAttributes.textUrl, let value = value as? ChatTextInputTextUrlAttribute {
|
||||
entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .TextUrl(url: value.url)))
|
||||
} else if key == ChatTextInputAttributes.date, let value = value as? ChatTextInputTextDateAttribute {
|
||||
entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .FormattedDate(format: nil, date: value.date)))
|
||||
} else if key == ChatTextInputAttributes.spoiler {
|
||||
entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .Spoiler))
|
||||
} else if key == ChatTextInputAttributes.customEmoji, let value = value as? ChatTextInputTextCustomEmojiAttribute {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ public func chatInputStateStringWithAppliedEntities(_ text: String, entities: [M
|
|||
string.addAttribute(ChatTextInputAttributes.textMention, value: ChatTextInputTextMentionAttribute(peerId: peerId), range: range)
|
||||
case let .TextUrl(url):
|
||||
string.addAttribute(ChatTextInputAttributes.textUrl, value: ChatTextInputTextUrlAttribute(url: url), range: range)
|
||||
case let .FormattedDate(_, date):
|
||||
string.addAttribute(ChatTextInputAttributes.date, value: ChatTextInputTextDateAttribute(date: date), range: range)
|
||||
case .Code:
|
||||
string.addAttribute(ChatTextInputAttributes.monospace, value: true as NSNumber, range: range)
|
||||
case .Strikethrough:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue