mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 11:23:48 +00:00
[WIP] Business
This commit is contained in:
parent
50339b3c4c
commit
0ba75f81de
@ -1441,7 +1441,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
self.textNode = TextNodeWithEntities()
|
||||
self.textNode.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.textNode.displaysAsynchronously = true
|
||||
self.textNode.textNode.layer.anchorPoint = CGPoint()
|
||||
self.textNode.textNode.anchorPoint = CGPoint()
|
||||
|
||||
self.inputActivitiesNode = ChatListInputActivitiesNode()
|
||||
self.inputActivitiesNode.isUserInteractionEnabled = false
|
||||
|
@ -314,19 +314,37 @@ public final class TelegramBusinessHours: Equatable, Codable {
|
||||
public func splitIntoWeekDays() -> [WeekDay] {
|
||||
var mappedDays: [[WorkingTimeInterval]] = Array(repeating: [], count: 7)
|
||||
|
||||
var weekMinutes = IndexSet()
|
||||
for interval in self.weeklyTimeIntervals {
|
||||
let startDayIndex = interval.startMinute / (24 * 60)
|
||||
if startDayIndex < 0 || startDayIndex >= 7 {
|
||||
continue
|
||||
weekMinutes.insert(integersIn: interval.startMinute ..< interval.endMinute)
|
||||
}
|
||||
|
||||
for i in 0 ..< mappedDays.count {
|
||||
let dayRange = i * 24 * 60 ..< (i + 1) * 24 * 60
|
||||
var removeMinutes = IndexSet()
|
||||
inner: for range in weekMinutes.rangeView {
|
||||
if range.lowerBound >= dayRange.upperBound {
|
||||
break inner
|
||||
} else {
|
||||
let clippedRange: Range<Int>
|
||||
if range.lowerBound == dayRange.lowerBound {
|
||||
clippedRange = range.lowerBound ..< min(range.upperBound, dayRange.upperBound)
|
||||
} else {
|
||||
clippedRange = range.lowerBound ..< min(range.upperBound, dayRange.upperBound + 12 * 60)
|
||||
}
|
||||
|
||||
let startTimeInsideDay = clippedRange.lowerBound - i * (24 * 60)
|
||||
let endTimeInsideDay = clippedRange.upperBound - i * (24 * 60)
|
||||
|
||||
mappedDays[i].append(WorkingTimeInterval(
|
||||
startMinute: startTimeInsideDay,
|
||||
endMinute: endTimeInsideDay
|
||||
))
|
||||
removeMinutes.insert(integersIn: clippedRange)
|
||||
}
|
||||
}
|
||||
|
||||
let startTimeInsideDay = interval.startMinute - startDayIndex * (24 * 60)
|
||||
let endTimeInsideDay = interval.endMinute - startDayIndex * (24 * 60)
|
||||
|
||||
mappedDays[startDayIndex].append(WorkingTimeInterval(
|
||||
startMinute: startTimeInsideDay,
|
||||
endMinute: endTimeInsideDay
|
||||
))
|
||||
weekMinutes.subtract(removeMinutes)
|
||||
}
|
||||
|
||||
return mappedDays.map { day -> WeekDay in
|
||||
|
@ -1191,7 +1191,7 @@ public extension TelegramEngine {
|
||||
}
|
||||
|
||||
var selectedMedia: EngineMedia
|
||||
if let alternativeMedia = itemAndPeer.item.alternativeMedia.flatMap(EngineMedia.init), !preferHighQuality {
|
||||
if let alternativeMedia = itemAndPeer.item.alternativeMedia.flatMap(EngineMedia.init), (!preferHighQuality && !itemAndPeer.item.isMy) {
|
||||
selectedMedia = alternativeMedia
|
||||
} else {
|
||||
selectedMedia = EngineMedia(media)
|
||||
|
@ -66,6 +66,7 @@ public final class ListActionItemComponent: Component {
|
||||
|
||||
public let theme: PresentationTheme
|
||||
public let title: AnyComponent<Empty>
|
||||
public let contentInsets: UIEdgeInsets
|
||||
public let leftIcon: AnyComponentWithIdentity<Empty>?
|
||||
public let icon: Icon?
|
||||
public let accessory: Accessory?
|
||||
@ -74,6 +75,7 @@ public final class ListActionItemComponent: Component {
|
||||
public init(
|
||||
theme: PresentationTheme,
|
||||
title: AnyComponent<Empty>,
|
||||
contentInsets: UIEdgeInsets = UIEdgeInsets(top: 12.0, left: 0.0, bottom: 12.0, right: 0.0),
|
||||
leftIcon: AnyComponentWithIdentity<Empty>? = nil,
|
||||
icon: Icon? = nil,
|
||||
accessory: Accessory? = .arrow,
|
||||
@ -81,6 +83,7 @@ public final class ListActionItemComponent: Component {
|
||||
) {
|
||||
self.theme = theme
|
||||
self.title = title
|
||||
self.contentInsets = contentInsets
|
||||
self.leftIcon = leftIcon
|
||||
self.icon = icon
|
||||
self.accessory = accessory
|
||||
@ -94,6 +97,9 @@ public final class ListActionItemComponent: Component {
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.contentInsets != rhs.contentInsets {
|
||||
return false
|
||||
}
|
||||
if lhs.leftIcon != rhs.leftIcon {
|
||||
return false
|
||||
}
|
||||
@ -172,8 +178,6 @@ public final class ListActionItemComponent: Component {
|
||||
|
||||
let themeUpdated = component.theme !== previousComponent?.theme
|
||||
|
||||
let verticalInset: CGFloat = 12.0
|
||||
|
||||
var contentLeftInset: CGFloat = 16.0
|
||||
let contentRightInset: CGFloat
|
||||
switch component.accessory {
|
||||
@ -186,7 +190,7 @@ public final class ListActionItemComponent: Component {
|
||||
}
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
contentHeight += verticalInset
|
||||
contentHeight += component.contentInsets.top
|
||||
|
||||
if component.leftIcon != nil {
|
||||
contentLeftInset += 46.0
|
||||
@ -198,7 +202,7 @@ public final class ListActionItemComponent: Component {
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - contentLeftInset - contentRightInset, height: availableSize.height)
|
||||
)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: contentLeftInset, y: verticalInset), size: titleSize)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: contentLeftInset, y: contentHeight), size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
titleView.isUserInteractionEnabled = false
|
||||
@ -208,7 +212,7 @@ public final class ListActionItemComponent: Component {
|
||||
}
|
||||
contentHeight += titleSize.height
|
||||
|
||||
contentHeight += verticalInset
|
||||
contentHeight += component.contentInsets.bottom
|
||||
|
||||
if let iconValue = component.icon {
|
||||
if previousComponent?.icon?.component.id != iconValue.component.id, let icon = self.icon {
|
||||
|
@ -208,7 +208,7 @@ public final class ListMultilineTextFieldItemComponent: Component {
|
||||
containerSize: availableSize
|
||||
)
|
||||
|
||||
let size = textFieldSize
|
||||
let size = CGSize(width: textFieldSize.width, height: textFieldSize.height - 1.0)
|
||||
let textFieldFrame = CGRect(origin: CGPoint(), size: textFieldSize)
|
||||
|
||||
if let textFieldView = self.textField.view {
|
||||
|
@ -139,6 +139,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/Settings/BoostLevelIconComponent",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
"//submodules/TelegramUI/Components/Settings/PeerNameColorItem",
|
||||
"//submodules/TelegramUI/Components/PlainButtonComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -11,8 +11,9 @@ import TelegramCore
|
||||
import ComponentFlow
|
||||
import MultilineTextComponent
|
||||
import BundleIconComponent
|
||||
import PlainButtonComponent
|
||||
|
||||
private func dayBusinessHoursText(_ day: TelegramBusinessHours.WeekDay) -> String {
|
||||
private func dayBusinessHoursText(_ day: TelegramBusinessHours.WeekDay, offsetMinutes: Int) -> String {
|
||||
var businessHoursText: String = ""
|
||||
switch day {
|
||||
case .open:
|
||||
@ -26,6 +27,8 @@ private func dayBusinessHoursText(_ day: TelegramBusinessHours.WeekDay) -> Strin
|
||||
|
||||
var resultText: String = ""
|
||||
for range in intervals {
|
||||
let range = TelegramBusinessHours.WorkingTimeInterval(startMinute: range.startMinute + offsetMinutes, endMinute: range.endMinute + offsetMinutes)
|
||||
|
||||
if !resultText.isEmpty {
|
||||
resultText.append("\n")
|
||||
}
|
||||
@ -47,13 +50,13 @@ final class PeerInfoScreenBusinessHoursItem: PeerInfoScreenItem {
|
||||
let id: AnyHashable
|
||||
let label: String
|
||||
let businessHours: TelegramBusinessHours
|
||||
let requestLayout: () -> Void
|
||||
let requestLayout: (Bool) -> Void
|
||||
|
||||
init(
|
||||
id: AnyHashable,
|
||||
label: String,
|
||||
businessHours: TelegramBusinessHours,
|
||||
requestLayout: @escaping () -> Void
|
||||
requestLayout: @escaping (Bool) -> Void
|
||||
) {
|
||||
self.id = id
|
||||
self.label = label
|
||||
@ -79,6 +82,7 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode
|
||||
private let labelNode: ImmediateTextNode
|
||||
private let currentStatusText = ComponentView<Empty>()
|
||||
private let currentDayText = ComponentView<Empty>()
|
||||
private var timezoneSwitchButton: ComponentView<Empty>?
|
||||
private var dayTitles: [ComponentView<Empty>] = []
|
||||
private var dayValues: [ComponentView<Empty>] = []
|
||||
private let arrowIcon = ComponentView<Empty>()
|
||||
@ -90,6 +94,8 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode
|
||||
private var item: PeerInfoScreenBusinessHoursItem?
|
||||
private var theme: PresentationTheme?
|
||||
|
||||
private var currentTimezone: TimeZone
|
||||
private var displayLocalTimezone: Bool = false
|
||||
private var cachedDays: [TelegramBusinessHours.WeekDay] = []
|
||||
private var cachedWeekMinuteSet = IndexSet()
|
||||
|
||||
@ -115,6 +121,8 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
|
||||
self.currentTimezone = TimeZone.current
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.bottomSeparatorNode)
|
||||
@ -179,7 +187,7 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode
|
||||
switch gesture {
|
||||
case .tap, .longTap:
|
||||
self.isExpanded = !self.isExpanded
|
||||
self.item?.requestLayout()
|
||||
self.item?.requestLayout(true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -255,11 +263,16 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode
|
||||
let currentHour = currentCalendar.component(.hour, from: currentDate)
|
||||
let currentWeekMinute = currentDayIndex * 24 * 60 + currentHour * 60 + currentMinute
|
||||
|
||||
var timezoneOffsetMinutes: Int = 0
|
||||
if self.displayLocalTimezone {
|
||||
timezoneOffsetMinutes = (self.currentTimezone.secondsFromGMT() - currentCalendar.timeZone.secondsFromGMT()) / 60
|
||||
}
|
||||
|
||||
let isOpen = self.cachedWeekMinuteSet.contains(currentWeekMinute)
|
||||
//TODO:localize
|
||||
let openStatusText = isOpen ? "Open" : "Closed"
|
||||
|
||||
var currentDayStatusText = currentDayIndex >= 0 && currentDayIndex < businessDays.count ? dayBusinessHoursText(businessDays[currentDayIndex]) : " "
|
||||
var currentDayStatusText = currentDayIndex >= 0 && currentDayIndex < businessDays.count ? dayBusinessHoursText(businessDays[currentDayIndex], offsetMinutes: timezoneOffsetMinutes) : " "
|
||||
|
||||
if !isOpen {
|
||||
for range in self.cachedWeekMinuteSet.rangeView {
|
||||
@ -325,6 +338,61 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode
|
||||
environment: {},
|
||||
containerSize: CGSize(width: width - sideInset - dayRightInset, height: 100.0)
|
||||
)
|
||||
|
||||
var timezoneSwitchButtonSize: CGSize?
|
||||
if item.businessHours.timezoneId != self.currentTimezone.identifier {
|
||||
let timezoneSwitchButton: ComponentView<Empty>
|
||||
if let current = self.timezoneSwitchButton {
|
||||
timezoneSwitchButton = current
|
||||
} else {
|
||||
timezoneSwitchButton = ComponentView()
|
||||
self.timezoneSwitchButton = timezoneSwitchButton
|
||||
}
|
||||
let timezoneSwitchTitle: String
|
||||
//TODO:localize
|
||||
if self.displayLocalTimezone {
|
||||
timezoneSwitchTitle = "my time"
|
||||
} else {
|
||||
timezoneSwitchTitle = "local time"
|
||||
}
|
||||
timezoneSwitchButtonSize = timezoneSwitchButton.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: timezoneSwitchTitle, font: Font.regular(12.0), textColor: presentationData.theme.list.itemAccentColor))
|
||||
)),
|
||||
background: AnyComponent(RoundedRectangle(
|
||||
color: presentationData.theme.list.itemAccentColor.withMultipliedAlpha(0.1),
|
||||
cornerRadius: nil
|
||||
)),
|
||||
effectAlignment: .center,
|
||||
contentInsets: UIEdgeInsets(top: 1.0, left: 7.0, bottom: 2.0, right: 7.0),
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.displayLocalTimezone = !self.displayLocalTimezone
|
||||
self.item?.requestLayout(false)
|
||||
},
|
||||
animateAlpha: true,
|
||||
animateScale: false,
|
||||
animateContents: false
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
} else {
|
||||
if let timezoneSwitchButton = self.timezoneSwitchButton {
|
||||
self.timezoneSwitchButton = nil
|
||||
timezoneSwitchButton.view?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
let timezoneSwitchButtonSpacing: CGFloat = 3.0
|
||||
if timezoneSwitchButtonSize != nil {
|
||||
topOffset -= 20.0
|
||||
}
|
||||
|
||||
let currentDayTextFrame = CGRect(origin: CGPoint(x: width - dayRightInset - currentDayTextSize.width, y: topOffset), size: currentDayTextSize)
|
||||
if let currentDayTextView = self.currentDayText.view {
|
||||
if currentDayTextView.superview == nil {
|
||||
@ -337,6 +405,20 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode
|
||||
|
||||
topOffset += max(currentStatusTextSize.height, currentDayTextSize.height)
|
||||
|
||||
if let timezoneSwitchButtonView = self.timezoneSwitchButton?.view, let timezoneSwitchButtonSize {
|
||||
topOffset += timezoneSwitchButtonSpacing
|
||||
|
||||
var timezoneSwitchButtonTransition = transition
|
||||
if timezoneSwitchButtonView.superview == nil {
|
||||
timezoneSwitchButtonTransition = .immediate
|
||||
self.contextSourceNode.contentNode.view.addSubview(timezoneSwitchButtonView)
|
||||
}
|
||||
let timezoneSwitchButtonFrame = CGRect(origin: CGPoint(x: width - dayRightInset - timezoneSwitchButtonSize.width, y: topOffset), size: timezoneSwitchButtonSize)
|
||||
timezoneSwitchButtonTransition.updateFrame(view: timezoneSwitchButtonView, frame: timezoneSwitchButtonFrame)
|
||||
|
||||
topOffset += timezoneSwitchButtonSize.height
|
||||
}
|
||||
|
||||
let daySpacing: CGFloat = 15.0
|
||||
|
||||
var dayHeights: CGFloat = 0.0
|
||||
@ -383,7 +465,7 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode
|
||||
dayTitleValue = " "
|
||||
}
|
||||
|
||||
let businessHoursText = dayBusinessHoursText(businessDays[i])
|
||||
let businessHoursText = dayBusinessHoursText(businessDays[i], offsetMinutes: timezoneOffsetMinutes)
|
||||
|
||||
let dayTitleSize = dayTitle.update(
|
||||
transition: .immediate,
|
||||
|
@ -1166,8 +1166,8 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
|
||||
if let businessHours = cachedData.businessHours {
|
||||
//TODO:localize
|
||||
items[.peerInfo]!.append(PeerInfoScreenBusinessHoursItem(id: 300, label: "business hours", businessHours: businessHours, requestLayout: {
|
||||
interaction.requestLayout(true)
|
||||
items[.peerInfo]!.append(PeerInfoScreenBusinessHoursItem(id: 300, label: "business hours", businessHours: businessHours, requestLayout: { animated in
|
||||
interaction.requestLayout(animated)
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,7 @@ final class BusinessDaySetupScreenComponent: Component {
|
||||
|
||||
private(set) var isOpen: Bool = false
|
||||
private(set) var ranges: [BusinessHoursSetupScreenComponent.WorkingHourRange] = []
|
||||
private var intersectingRanges = Set<Int>()
|
||||
private var nextRangeId: Int = 0
|
||||
|
||||
override init(frame: CGRect) {
|
||||
@ -116,7 +117,25 @@ final class BusinessDaySetupScreenComponent: Component {
|
||||
}
|
||||
|
||||
func attemptNavigation(complete: @escaping () -> Void) -> Bool {
|
||||
return true
|
||||
guard let component = self.component else {
|
||||
return true
|
||||
}
|
||||
|
||||
if self.intersectingRanges.isEmpty {
|
||||
return true
|
||||
}
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
//TODO:localize
|
||||
self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: "Business hours are intersecting. Reset?", actions: [
|
||||
TextAlertAction(type: .genericAction, title: "Cancel", action: {
|
||||
}),
|
||||
TextAlertAction(type: .defaultAction, title: "Reset", action: {
|
||||
complete()
|
||||
})
|
||||
]), in: .window(.root))
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
@ -191,6 +210,26 @@ final class BusinessDaySetupScreenComponent: Component {
|
||||
|
||||
private func validateRanges() {
|
||||
self.ranges.sort(by: { $0.startMinute < $1.startMinute })
|
||||
|
||||
self.intersectingRanges.removeAll()
|
||||
for i in 0 ..< self.ranges.count {
|
||||
var minuteSet = IndexSet()
|
||||
inner: for j in 0 ..< self.ranges.count {
|
||||
if i == j {
|
||||
continue inner
|
||||
}
|
||||
let range = self.ranges[j]
|
||||
let rangeMinutes = range.startMinute ..< range.endMinute
|
||||
minuteSet.insert(integersIn: rangeMinutes)
|
||||
}
|
||||
|
||||
let range = self.ranges[i]
|
||||
let rangeMinutes = range.startMinute ..< range.endMinute
|
||||
|
||||
if minuteSet.intersects(integersIn: rangeMinutes) {
|
||||
self.intersectingRanges.insert(range.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: BusinessDaySetupScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
|
||||
@ -260,7 +299,7 @@ final class BusinessDaySetupScreenComponent: Component {
|
||||
|
||||
let bottomContentInset: CGFloat = 24.0
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
let sectionSpacing: CGFloat = 32.0
|
||||
let sectionSpacing: CGFloat = 24.0
|
||||
|
||||
let _ = bottomContentInset
|
||||
let _ = sectionSpacing
|
||||
@ -335,80 +374,47 @@ final class BusinessDaySetupScreenComponent: Component {
|
||||
let endText = stringForShortTimestamp(hours: Int32(endHours), minutes: Int32(endMinutes), dateTimeFormat: PresentationDateTimeFormat())
|
||||
|
||||
var rangeSectionItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
rangeSectionItems.append(AnyComponentWithIdentity(id: rangeSectionItems.count, component: AnyComponent(ListActionItemComponent(
|
||||
theme: environment.theme,
|
||||
title: AnyComponent(VStack([
|
||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "Opening time",
|
||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||
textColor: environment.theme.list.itemPrimaryTextColor
|
||||
for i in 0 ..< 2 {
|
||||
let isOpenTime = i == 0
|
||||
rangeSectionItems.append(AnyComponentWithIdentity(id: rangeSectionItems.count, component: AnyComponent(ListActionItemComponent(
|
||||
theme: environment.theme,
|
||||
title: AnyComponent(VStack([
|
||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: isOpenTime ? "Opening time" : "Closing Time",
|
||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||
textColor: environment.theme.list.itemPrimaryTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 1
|
||||
))),
|
||||
], alignment: .left, spacing: 2.0)),
|
||||
icon: ListActionItemComponent.Icon(component: AnyComponentWithIdentity(id: 0, component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: isOpenTime ? startText : endText, font: Font.regular(17.0), textColor: self.intersectingRanges.contains(range.id) ? environment.theme.list.itemDestructiveColor : environment.theme.list.itemPrimaryTextColor))
|
||||
)),
|
||||
maximumNumberOfLines: 1
|
||||
))),
|
||||
], alignment: .left, spacing: 2.0)),
|
||||
icon: ListActionItemComponent.Icon(component: AnyComponentWithIdentity(id: 0, component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: startText, font: Font.regular(17.0), textColor: environment.theme.list.itemPrimaryTextColor))
|
||||
)),
|
||||
background: AnyComponent(RoundedRectangle(color: environment.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.1), cornerRadius: 6.0)),
|
||||
effectAlignment: .center,
|
||||
minSize: nil,
|
||||
contentInsets: UIEdgeInsets(top: 7.0, left: 8.0, bottom: 7.0, right: 8.0),
|
||||
action: { [weak self] in
|
||||
background: AnyComponent(RoundedRectangle(color: environment.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.1), cornerRadius: 6.0)),
|
||||
effectAlignment: .center,
|
||||
minSize: nil,
|
||||
contentInsets: UIEdgeInsets(top: 7.0, left: 8.0, bottom: 7.0, right: 8.0),
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openRangeDateSetup(rangeId: rangeId, isStartTime: isOpenTime)
|
||||
},
|
||||
animateAlpha: true,
|
||||
animateScale: false
|
||||
))), insets: .custom(UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0)), allowUserInteraction: true),
|
||||
accessory: nil,
|
||||
action: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openRangeDateSetup(rangeId: rangeId, isStartTime: true)
|
||||
},
|
||||
animateAlpha: true,
|
||||
animateScale: false
|
||||
))), insets: .custom(UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0)), allowUserInteraction: true),
|
||||
accessory: nil,
|
||||
action: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
self.openRangeDateSetup(rangeId: rangeId, isStartTime: isOpenTime)
|
||||
}
|
||||
self.openRangeDateSetup(rangeId: rangeId, isStartTime: true)
|
||||
}
|
||||
))))
|
||||
rangeSectionItems.append(AnyComponentWithIdentity(id: rangeSectionItems.count, component: AnyComponent(ListActionItemComponent(
|
||||
theme: environment.theme,
|
||||
title: AnyComponent(VStack([
|
||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "Closing time",
|
||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||
textColor: environment.theme.list.itemPrimaryTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 1
|
||||
))),
|
||||
], alignment: .left, spacing: 2.0)),
|
||||
icon: ListActionItemComponent.Icon(component: AnyComponentWithIdentity(id: 0, component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: endText, font: Font.regular(17.0), textColor: environment.theme.list.itemPrimaryTextColor))
|
||||
)),
|
||||
background: AnyComponent(RoundedRectangle(color: environment.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.1), cornerRadius: 6.0)),
|
||||
effectAlignment: .center,
|
||||
minSize: nil,
|
||||
contentInsets: UIEdgeInsets(top: 7.0, left: 8.0, bottom: 7.0, right: 8.0),
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openRangeDateSetup(rangeId: rangeId, isStartTime: false)
|
||||
},
|
||||
animateAlpha: true,
|
||||
animateScale: false
|
||||
))), insets: .custom(UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0)), allowUserInteraction: true),
|
||||
accessory: nil,
|
||||
action: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openRangeDateSetup(rangeId: rangeId, isStartTime: false)
|
||||
}
|
||||
))))
|
||||
))))
|
||||
}
|
||||
|
||||
rangeSectionItems.append(AnyComponentWithIdentity(id: rangeSectionItems.count, component: AnyComponent(ListActionItemComponent(
|
||||
theme: environment.theme,
|
||||
title: AnyComponent(VStack([
|
||||
@ -519,13 +525,13 @@ final class BusinessDaySetupScreenComponent: Component {
|
||||
text: .plain(NSAttributedString(
|
||||
string: "Add a Set of Hours",
|
||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||
textColor: environment.theme.list.itemPrimaryTextColor
|
||||
textColor: environment.theme.list.itemAccentColor
|
||||
)),
|
||||
maximumNumberOfLines: 1
|
||||
))),
|
||||
], alignment: .left, spacing: 2.0)),
|
||||
leftIcon: AnyComponentWithIdentity(id: 0, component: AnyComponent(BundleIconComponent(
|
||||
name: "Chat List/AddIcon",
|
||||
name: "Item List/AddTimeIcon",
|
||||
tintColor: environment.theme.list.itemAccentColor
|
||||
))),
|
||||
accessory: nil,
|
||||
@ -619,7 +625,7 @@ final class BusinessDaySetupScreenComponent: Component {
|
||||
|
||||
final class BusinessDaySetupScreen: ViewControllerComponentContainer {
|
||||
private let context: AccountContext
|
||||
private let updateDay: (BusinessHoursSetupScreenComponent.Day) -> Void
|
||||
fileprivate let updateDay: (BusinessHoursSetupScreenComponent.Day) -> Void
|
||||
|
||||
init(context: AccountContext, dayIndex: Int, day: BusinessHoursSetupScreenComponent.Day, updateDay: @escaping (BusinessHoursSetupScreenComponent.Day) -> Void) {
|
||||
self.context = context
|
||||
@ -647,9 +653,12 @@ final class BusinessDaySetupScreen: ViewControllerComponentContainer {
|
||||
return true
|
||||
}
|
||||
|
||||
self.updateDay(BusinessHoursSetupScreenComponent.Day(ranges: componentView.isOpen ? componentView.ranges : nil))
|
||||
|
||||
return componentView.attemptNavigation(complete: complete)
|
||||
if componentView.attemptNavigation(complete: complete) {
|
||||
self.updateDay(BusinessHoursSetupScreenComponent.Day(ranges: componentView.isOpen ? componentView.ranges : nil))
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,32 @@ import LocationUI
|
||||
import TelegramStringFormatting
|
||||
import TimezoneSelectionScreen
|
||||
|
||||
private func wrappedMinuteRange(range: Range<Int>, dayIndexOffset: Int = 0) -> IndexSet {
|
||||
let mappedRange = (range.lowerBound + dayIndexOffset * 24 * 60) ..< (range.upperBound + dayIndexOffset * 24 * 60)
|
||||
|
||||
var result = IndexSet()
|
||||
if mappedRange.upperBound > 7 * 24 * 60 {
|
||||
result.insert(integersIn: mappedRange.lowerBound ..< 7 * 24 * 60)
|
||||
result.insert(integersIn: 0 ..< (mappedRange.upperBound - 7 * 24 * 60))
|
||||
} else {
|
||||
result.insert(integersIn: mappedRange)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private func getDayRanges(days: [BusinessHoursSetupScreenComponent.Day], index: Int) -> [BusinessHoursSetupScreenComponent.WorkingHourRange] {
|
||||
let day = days[index]
|
||||
if let ranges = day.ranges {
|
||||
if ranges.isEmpty {
|
||||
return [BusinessHoursSetupScreenComponent.WorkingHourRange(id: 0, startMinute: 0, endMinute: 24 * 60)]
|
||||
} else {
|
||||
return ranges
|
||||
}
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
final class BusinessHoursSetupScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
@ -68,6 +94,16 @@ final class BusinessHoursSetupScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
struct DayRangeIndex: Hashable {
|
||||
var day: Int
|
||||
var id: Int
|
||||
|
||||
init(day: Int, id: Int) {
|
||||
self.day = day
|
||||
self.id = id
|
||||
}
|
||||
}
|
||||
|
||||
struct Day: Equatable {
|
||||
var ranges: [WorkingHourRange]?
|
||||
|
||||
@ -83,7 +119,7 @@ final class BusinessHoursSetupScreenComponent: Component {
|
||||
|
||||
var timezoneId: String
|
||||
private(set) var days: [Day]
|
||||
private(set) var intersectingDays = Set<Int>()
|
||||
private(set) var intersectingRanges = Set<DayRangeIndex>()
|
||||
|
||||
init(timezoneId: String, days: [Day]) {
|
||||
self.timezoneId = timezoneId
|
||||
@ -111,13 +147,45 @@ final class BusinessHoursSetupScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
if let value = try? self.asBusinessHours() {
|
||||
if value != businessHours {
|
||||
assertionFailure("Inconsistent representation")
|
||||
}
|
||||
}
|
||||
|
||||
self.validate()
|
||||
}
|
||||
|
||||
mutating func validate() {
|
||||
self.intersectingDays.removeAll()
|
||||
self.intersectingRanges.removeAll()
|
||||
|
||||
for dayIndex in 0 ..< self.days.count {
|
||||
var otherDaysMinutes = IndexSet()
|
||||
inner: for otherDayIndex in 0 ..< self.days.count {
|
||||
if dayIndex == otherDayIndex {
|
||||
continue inner
|
||||
}
|
||||
for range in getDayRanges(days: self.days, index: otherDayIndex) {
|
||||
otherDaysMinutes.formUnion(wrappedMinuteRange(range: range.startMinute ..< range.endMinute, dayIndexOffset: otherDayIndex))
|
||||
}
|
||||
}
|
||||
|
||||
let dayRanges = getDayRanges(days: self.days, index: dayIndex)
|
||||
for i in 0 ..< dayRanges.count {
|
||||
var currentDayOtherMinutes = IndexSet()
|
||||
inner: for j in 0 ..< dayRanges.count {
|
||||
if i == j {
|
||||
continue inner
|
||||
}
|
||||
currentDayOtherMinutes.formUnion(wrappedMinuteRange(range: dayRanges[j].startMinute ..< dayRanges[j].endMinute, dayIndexOffset: dayIndex))
|
||||
}
|
||||
|
||||
let currentDayIndices = wrappedMinuteRange(range: dayRanges[i].startMinute ..< dayRanges[i].endMinute, dayIndexOffset: dayIndex)
|
||||
if !otherDaysMinutes.intersection(currentDayIndices).isEmpty || !currentDayOtherMinutes.intersection(currentDayIndices).isEmpty {
|
||||
self.intersectingRanges.insert(DayRangeIndex(day: dayIndex, id: dayRanges[i].id))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutating func update(days: [Day]) {
|
||||
@ -140,13 +208,7 @@ final class BusinessHoursSetupScreenComponent: Component {
|
||||
for range in effectiveRanges {
|
||||
let minuteRange: Range<Int> = (dayStartMinute + range.startMinute) ..< (dayStartMinute + range.endMinute)
|
||||
|
||||
var wrappedMinutes = IndexSet()
|
||||
if minuteRange.upperBound > 7 * 24 * 60 {
|
||||
wrappedMinutes.insert(integersIn: minuteRange.lowerBound ..< 7 * 24 * 60)
|
||||
wrappedMinutes.insert(integersIn: 0 ..< (7 * 24 * 60 - minuteRange.upperBound))
|
||||
} else {
|
||||
wrappedMinutes.insert(integersIn: minuteRange)
|
||||
}
|
||||
let wrappedMinutes = wrappedMinuteRange(range: minuteRange)
|
||||
|
||||
if !filledMinutes.intersection(wrappedMinutes).isEmpty {
|
||||
throw ValidationError.intersectingRanges
|
||||
@ -156,7 +218,20 @@ final class BusinessHoursSetupScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
return TelegramBusinessHours(timezoneId: self.timezoneId, weeklyTimeIntervals: mappedIntervals)
|
||||
var mergedIntervals: [TelegramBusinessHours.WorkingTimeInterval] = []
|
||||
for interval in mappedIntervals {
|
||||
if mergedIntervals.isEmpty {
|
||||
mergedIntervals.append(interval)
|
||||
} else {
|
||||
if mergedIntervals[mergedIntervals.count - 1].endMinute >= interval.startMinute {
|
||||
mergedIntervals[mergedIntervals.count - 1] = TelegramBusinessHours.WorkingTimeInterval(startMinute: mergedIntervals[mergedIntervals.count - 1].startMinute, endMinute: interval.endMinute)
|
||||
} else {
|
||||
mergedIntervals.append(interval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TelegramBusinessHours(timezoneId: self.timezoneId, weeklyTimeIntervals: mergedIntervals)
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,7 +306,6 @@ final class BusinessHoursSetupScreenComponent: Component {
|
||||
return true
|
||||
} catch let error {
|
||||
let _ = error
|
||||
//TODO:localize
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
//TODO:localize
|
||||
@ -359,7 +433,7 @@ final class BusinessHoursSetupScreenComponent: Component {
|
||||
|
||||
let bottomContentInset: CGFloat = 24.0
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
let sectionSpacing: CGFloat = 32.0
|
||||
let sectionSpacing: CGFloat = 30.0
|
||||
|
||||
let _ = bottomContentInset
|
||||
let _ = sectionSpacing
|
||||
@ -370,14 +444,14 @@ final class BusinessHoursSetupScreenComponent: Component {
|
||||
|
||||
let iconSize = self.icon.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: "⏰", font: Font.semibold(90.0), textColor: environment.theme.rootController.navigationBar.primaryTextColor)),
|
||||
horizontalAlignment: .center
|
||||
component: AnyComponent(LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(name: "BusinessHoursEmoji"),
|
||||
loop: true
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: contentHeight + 2.0), size: iconSize)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: contentHeight + 10.0), size: iconSize)
|
||||
if let iconView = self.icon.view {
|
||||
if iconView.superview == nil {
|
||||
self.scrollView.addSubview(iconView)
|
||||
@ -386,7 +460,7 @@ final class BusinessHoursSetupScreenComponent: Component {
|
||||
iconView.bounds = CGRect(origin: CGPoint(), size: iconFrame.size)
|
||||
}
|
||||
|
||||
contentHeight += 129.0
|
||||
contentHeight += 126.0
|
||||
|
||||
//TODO:localize
|
||||
let subtitleString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString("Turn this on to show your opening hours schedule to your customers.", attributes: MarkdownAttributes(
|
||||
@ -506,28 +580,42 @@ final class BusinessHoursSetupScreenComponent: Component {
|
||||
title = " "
|
||||
}
|
||||
|
||||
let subtitle: String
|
||||
let subtitle = NSMutableAttributedString()
|
||||
|
||||
var invalidIndices: [Int] = []
|
||||
let effectiveDayRanges = getDayRanges(days: self.daysState.days, index: dayIndex)
|
||||
for range in effectiveDayRanges {
|
||||
if self.daysState.intersectingRanges.contains(DayRangeIndex(day: dayIndex, id: range.id)) {
|
||||
invalidIndices.append(range.id)
|
||||
}
|
||||
}
|
||||
|
||||
let subtitleFont = Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 15.0 / 17.0))
|
||||
|
||||
if let ranges = self.daysState.days[dayIndex].ranges {
|
||||
if ranges.isEmpty {
|
||||
subtitle = "Open 24 Hours"
|
||||
subtitle.append(NSAttributedString(string: "Open 24 Hours", font: subtitleFont, textColor: invalidIndices.contains(0) ? environment.theme.list.itemDestructiveColor : environment.theme.list.itemAccentColor))
|
||||
} else {
|
||||
var resultText: String = ""
|
||||
for range in ranges {
|
||||
if !resultText.isEmpty {
|
||||
resultText.append(", ")
|
||||
}
|
||||
for i in 0 ..< ranges.count {
|
||||
let range = ranges[i]
|
||||
|
||||
let startHours = clipMinutes(range.startMinute) / 60
|
||||
let startMinutes = clipMinutes(range.startMinute) % 60
|
||||
let startText = stringForShortTimestamp(hours: Int32(startHours), minutes: Int32(startMinutes), dateTimeFormat: PresentationDateTimeFormat())
|
||||
let endHours = clipMinutes(range.endMinute) / 60
|
||||
let endMinutes = clipMinutes(range.endMinute) % 60
|
||||
let endText = stringForShortTimestamp(hours: Int32(endHours), minutes: Int32(endMinutes), dateTimeFormat: PresentationDateTimeFormat())
|
||||
resultText.append("\(startText)\u{00a0}- \(endText)")
|
||||
|
||||
var rangeString = "\(startText)\u{00a0}- \(endText)"
|
||||
if i != ranges.count - 1 {
|
||||
rangeString.append(", ")
|
||||
}
|
||||
|
||||
subtitle.append(NSAttributedString(string: rangeString, font: subtitleFont, textColor: invalidIndices.contains(range.id) ? environment.theme.list.itemDestructiveColor : environment.theme.list.itemAccentColor))
|
||||
}
|
||||
subtitle = resultText
|
||||
}
|
||||
} else {
|
||||
subtitle = "Closed"
|
||||
subtitle.append(NSAttributedString(string: "Closed", font: subtitleFont, textColor: environment.theme.list.itemAccentColor))
|
||||
}
|
||||
|
||||
daysSectionItems.append(AnyComponentWithIdentity(id: dayIndex, component: AnyComponent(ListActionItemComponent(
|
||||
@ -542,14 +630,11 @@ final class BusinessHoursSetupScreenComponent: Component {
|
||||
maximumNumberOfLines: 1
|
||||
))),
|
||||
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: subtitle,
|
||||
font: Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 15.0 / 17.0)),
|
||||
textColor: environment.theme.list.itemAccentColor
|
||||
)),
|
||||
maximumNumberOfLines: 5
|
||||
text: .plain(subtitle),
|
||||
maximumNumberOfLines: 20
|
||||
)))
|
||||
], alignment: .left, spacing: 2.0)),
|
||||
], alignment: .left, spacing: 3.0)),
|
||||
contentInsets: UIEdgeInsets(top: 9.0, left: 0.0, bottom: 10.0, right: 0.0),
|
||||
accessory: .toggle(ListActionItemComponent.Toggle(style: .regular, isOn: day.ranges != nil, action: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
|
@ -109,20 +109,38 @@ final class BusinessLocationSetupScreenComponent: Component {
|
||||
}
|
||||
|
||||
func attemptNavigation(complete: @escaping () -> Void) -> Bool {
|
||||
if let component = self.component {
|
||||
var address = ""
|
||||
if let textView = self.addressSection.findTaggedView(tag: self.textFieldTag) as? ListMultilineTextFieldItemComponent.View {
|
||||
address = textView.currentText
|
||||
}
|
||||
|
||||
var businessLocation: TelegramBusinessLocation?
|
||||
if !address.isEmpty || self.mapCoordinates != nil {
|
||||
businessLocation = TelegramBusinessLocation(address: address, coordinates: self.mapCoordinates)
|
||||
}
|
||||
|
||||
let _ = component.context.engine.accountData.updateAccountBusinessLocation(businessLocation: businessLocation).startStandalone()
|
||||
guard let component = self.component else {
|
||||
return true
|
||||
}
|
||||
|
||||
var address = ""
|
||||
if let textView = self.addressSection.findTaggedView(tag: self.textFieldTag) as? ListMultilineTextFieldItemComponent.View {
|
||||
address = textView.currentText
|
||||
}
|
||||
|
||||
var businessLocation: TelegramBusinessLocation?
|
||||
if !address.isEmpty || self.mapCoordinates != nil {
|
||||
businessLocation = TelegramBusinessLocation(address: address, coordinates: self.mapCoordinates)
|
||||
}
|
||||
|
||||
if businessLocation != nil && address.isEmpty {
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
//TODO:localize
|
||||
self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: "Address can't be empty.", actions: [
|
||||
TextAlertAction(type: .genericAction, title: "Cancel", action: {
|
||||
}),
|
||||
TextAlertAction(type: .destructiveAction, title: "Delete", action: {
|
||||
let _ = component.context.engine.accountData.updateAccountBusinessLocation(businessLocation: nil).startStandalone()
|
||||
|
||||
complete()
|
||||
})
|
||||
]), in: .window(.root))
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
let _ = component.context.engine.accountData.updateAccountBusinessLocation(businessLocation: businessLocation).startStandalone()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -228,10 +246,7 @@ final class BusinessLocationSetupScreenComponent: Component {
|
||||
|
||||
let bottomContentInset: CGFloat = 24.0
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
let sectionSpacing: CGFloat = 32.0
|
||||
|
||||
let _ = bottomContentInset
|
||||
let _ = sectionSpacing
|
||||
let sectionSpacing: CGFloat = 24.0
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
|
||||
@ -246,7 +261,7 @@ final class BusinessLocationSetupScreenComponent: Component {
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: contentHeight + 2.0), size: iconSize)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: contentHeight + 11.0), size: iconSize)
|
||||
if let iconView = self.icon.view {
|
||||
if iconView.superview == nil {
|
||||
self.scrollView.addSubview(iconView)
|
||||
@ -304,6 +319,7 @@ final class BusinessLocationSetupScreenComponent: Component {
|
||||
contentHeight += subtitleSize.height
|
||||
contentHeight += 27.0
|
||||
|
||||
//TODO:localize
|
||||
var addressSectionItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
addressSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(ListMultilineTextFieldItemComponent(
|
||||
context: component.context,
|
||||
|
@ -34,7 +34,22 @@ private struct TimezoneListEntry: Comparable, Identifiable {
|
||||
}
|
||||
|
||||
func item(presentationData: PresentationData, searchMode: Bool, action: @escaping (String) -> Void) -> ListViewItem {
|
||||
return ItemListActionItem(presentationData: ItemListPresentationData(presentationData), title: self.title, kind: .neutral, alignment: .natural, sectionId: 0, style: .plain, action: {
|
||||
let hours = abs(self.offset / (60 * 60))
|
||||
let minutes = abs(self.offset % (60 * 60)) / 60
|
||||
let offsetString: String
|
||||
if minutes == 0 {
|
||||
offsetString = "UTC \(self.offset >= 0 ? "+" : "-")\(hours)"
|
||||
} else {
|
||||
let minutesString: String
|
||||
if minutes < 10 {
|
||||
minutesString = "0\(minutes)"
|
||||
} else {
|
||||
minutesString = "\(minutes)"
|
||||
}
|
||||
offsetString = "UTC \(self.offset >= 0 ? "+" : "-")\(hours):\(minutesString)"
|
||||
}
|
||||
|
||||
return ItemListDisclosureItem(presentationData: ItemListPresentationData(presentationData), title: self.title, label: offsetString, sectionId: 0, style: .plain, disclosureStyle: .none, action: {
|
||||
action(self.id)
|
||||
})
|
||||
}
|
||||
|
@ -990,7 +990,7 @@ public final class StoryContentContextImpl: StoryContentContext {
|
||||
}
|
||||
|
||||
var selectedMedia: EngineMedia
|
||||
if let slice = stateValue.slice, let alternativeMedia = item.alternativeMedia, !slice.additionalPeerData.preferHighQualityStories {
|
||||
if let slice = stateValue.slice, let alternativeMedia = item.alternativeMedia, (!slice.additionalPeerData.preferHighQualityStories && !item.isMy) {
|
||||
selectedMedia = alternativeMedia
|
||||
} else {
|
||||
selectedMedia = item.media
|
||||
@ -1642,7 +1642,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext {
|
||||
}
|
||||
|
||||
var selectedMedia: EngineMedia
|
||||
if let alternativeMedia = item.alternativeMedia, !preferHighQualityStories {
|
||||
if let alternativeMedia = item.alternativeMedia, (!preferHighQualityStories && !item.isMy) {
|
||||
selectedMedia = alternativeMedia
|
||||
} else {
|
||||
selectedMedia = item.media
|
||||
@ -2880,7 +2880,7 @@ public final class RepostStoriesContentContextImpl: StoryContentContext {
|
||||
}
|
||||
|
||||
var selectedMedia: EngineMedia
|
||||
if let slice = stateValue.slice, let alternativeMedia = item.alternativeMedia, !slice.additionalPeerData.preferHighQualityStories {
|
||||
if let slice = stateValue.slice, let alternativeMedia = item.alternativeMedia, (!slice.additionalPeerData.preferHighQualityStories && !item.isMy) {
|
||||
selectedMedia = alternativeMedia
|
||||
} else {
|
||||
selectedMedia = item.media
|
||||
|
@ -593,7 +593,7 @@ final class StoryItemContentComponent: Component {
|
||||
|
||||
let selectedMedia: EngineMedia
|
||||
var messageMedia: EngineMedia?
|
||||
if !component.preferHighQuality, let alternativeMedia = component.item.alternativeMedia {
|
||||
if !component.preferHighQuality, !component.item.isMy, let alternativeMedia = component.item.alternativeMedia {
|
||||
selectedMedia = alternativeMedia
|
||||
|
||||
switch alternativeMedia {
|
||||
|
@ -6857,7 +6857,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
})))
|
||||
}
|
||||
|
||||
if case let .file(file) = component.slice.item.storyItem.media, file.isVideo {
|
||||
if !component.slice.item.storyItem.isMy, case let .file(file) = component.slice.item.storyItem.media, file.isVideo {
|
||||
let isHq = component.slice.additionalPeerData.preferHighQualityStories
|
||||
items.append(.action(ContextMenuActionItem(text: isHq ? component.strings.Story_ContextMenuSD : component.strings.Story_ContextMenuHD, icon: { theme in
|
||||
if isHq {
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Item List/AddTimeIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Item List/AddTimeIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "addclock_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
117
submodules/TelegramUI/Images.xcassets/Item List/AddTimeIcon.imageset/addclock_30.pdf
vendored
Normal file
117
submodules/TelegramUI/Images.xcassets/Item List/AddTimeIcon.imageset/addclock_30.pdf
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 4.334961 4.334961 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
1.330000 10.665078 m
|
||||
1.330000 15.820656 5.509422 20.000078 10.665000 20.000078 c
|
||||
15.820579 20.000078 20.000000 15.820656 20.000000 10.665078 c
|
||||
20.000000 5.509500 15.820579 1.330078 10.665000 1.330078 c
|
||||
10.024749 1.330078 9.400214 1.394436 8.797290 1.516823 c
|
||||
8.437361 1.589886 8.086353 1.357334 8.013291 0.997406 c
|
||||
7.940229 0.637478 8.172781 0.286469 8.532710 0.213406 c
|
||||
9.222226 0.073441 9.935388 0.000076 10.665000 0.000076 c
|
||||
16.555117 0.000076 21.330002 4.774961 21.330002 10.665078 c
|
||||
21.330002 16.555195 16.555117 21.330078 10.665000 21.330078 c
|
||||
4.774883 21.330078 0.000000 16.555195 0.000000 10.665078 c
|
||||
0.000000 9.935467 0.073363 9.222304 0.213327 8.532788 c
|
||||
0.286389 8.172859 0.637397 7.940308 0.997326 8.013370 c
|
||||
1.357255 8.086432 1.589807 8.437439 1.516745 8.797368 c
|
||||
1.394358 9.400291 1.330000 10.024826 1.330000 10.665078 c
|
||||
h
|
||||
11.330000 17.165077 m
|
||||
11.330000 17.532347 11.032269 17.830078 10.665000 17.830078 c
|
||||
10.297730 17.830078 10.000000 17.532347 10.000000 17.165077 c
|
||||
10.000000 11.114144 l
|
||||
10.000000 10.638557 10.203376 10.185671 10.558834 9.869707 c
|
||||
14.723198 6.168051 l
|
||||
14.997698 5.924050 15.418027 5.948775 15.662027 6.223276 c
|
||||
15.906028 6.497777 15.881302 6.918105 15.606802 7.162106 c
|
||||
11.442438 10.863762 l
|
||||
11.370919 10.927334 11.330000 11.018456 11.330000 11.114144 c
|
||||
11.330000 17.165077 l
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 3.339844 3.334961 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
5.330000 8.665077 m
|
||||
5.330000 9.032347 5.032269 9.330078 4.665000 9.330078 c
|
||||
4.297730 9.330078 4.000000 9.032347 4.000000 8.665077 c
|
||||
4.000000 5.330077 l
|
||||
0.665000 5.330077 l
|
||||
0.297731 5.330077 0.000000 5.032347 0.000000 4.665077 c
|
||||
0.000000 4.297808 0.297731 4.000077 0.665000 4.000077 c
|
||||
4.000000 4.000077 l
|
||||
4.000000 0.665077 l
|
||||
4.000000 0.297808 4.297730 0.000077 4.665000 0.000077 c
|
||||
5.032269 0.000077 5.330000 0.297808 5.330000 0.665077 c
|
||||
5.330000 4.000077 l
|
||||
8.665000 4.000077 l
|
||||
9.032269 4.000077 9.330000 4.297808 9.330000 4.665077 c
|
||||
9.330000 5.032347 9.032269 5.330077 8.665000 5.330077 c
|
||||
5.330000 5.330077 l
|
||||
5.330000 8.665077 l
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
2167
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000002257 00000 n
|
||||
0000002280 00000 n
|
||||
0000002453 00000 n
|
||||
0000002527 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
2586
|
||||
%%EOF
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user