Merge commit '0456336a34f789b8c3707552e6dfc9c3aa101d79'

This commit is contained in:
Ali 2021-02-14 20:45:20 +04:00
commit e5ce9ee0ce
18 changed files with 3029 additions and 2777 deletions

View File

@ -6077,3 +6077,5 @@ Sorry for the inconvenience.";
"Channel.AdminLog.MessageChangedAutoremoveTimeoutSet" = "%1$@ set auto-remove timer to %2$@"; "Channel.AdminLog.MessageChangedAutoremoveTimeoutSet" = "%1$@ set auto-remove timer to %2$@";
"Channel.AdminLog.MessageChangedAutoremoveTimeoutRemove" = "%1$@ disabled auto-remove timer"; "Channel.AdminLog.MessageChangedAutoremoveTimeoutRemove" = "%1$@ disabled auto-remove timer";
"ChannelInfo.InviteLink.RevokeAlert.Text" = "Are you sure you want to revoke this link? Once you do, no one will be able to join the channel using it.";

View File

@ -179,7 +179,7 @@ private final class InnerActionsContainerNode: ASDisplayNode {
if self.effectView == nil { if self.effectView == nil {
let effectView: UIVisualEffectView let effectView: UIVisualEffectView
if #available(iOS 13.0, *) { if #available(iOS 13.0, *) {
if self.presentationData.theme.overallDarkAppearance { if self.presentationData.theme.rootController.keyboardColor == .dark {
effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialDark)) effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialDark))
} else { } else {
effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialLight)) effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialLight))

View File

@ -578,7 +578,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
propertyAnimator?.stopAnimation(true) propertyAnimator?.stopAnimation(true)
} }
self.effectView.effect = makeCustomZoomBlurEffect(isLight: !self.presentationData.theme.overallDarkAppearance) self.effectView.effect = makeCustomZoomBlurEffect(isLight: presentationData.theme.rootController.keyboardColor == .light)
self.effectView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor) self.effectView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor)
self.propertyAnimator = UIViewPropertyAnimator(duration: 0.2 * animationDurationFactor * UIView.animationDurationFactor(), curve: .easeInOut, animations: { self.propertyAnimator = UIViewPropertyAnimator(duration: 0.2 * animationDurationFactor * UIView.animationDurationFactor(), curve: .easeInOut, animations: {
}) })
@ -596,7 +596,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
} }
} else { } else {
UIView.animate(withDuration: 0.2 * animationDurationFactor, animations: { UIView.animate(withDuration: 0.2 * animationDurationFactor, animations: {
self.effectView.effect = makeCustomZoomBlurEffect(isLight: !self.presentationData.theme.overallDarkAppearance) self.effectView.effect = makeCustomZoomBlurEffect(isLight: self.presentationData.theme.rootController.keyboardColor == .light)
}, completion: { [weak self] _ in }, completion: { [weak self] _ in
self?.didCompleteAnimationIn = true self?.didCompleteAnimationIn = true
self?.actionsContainerNode.animateIn() self?.actionsContainerNode.animateIn()
@ -1106,7 +1106,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
propertyAnimator?.stopAnimation(true) propertyAnimator?.stopAnimation(true)
} }
} }
self.effectView.effect = makeCustomZoomBlurEffect(isLight: !self.presentationData.theme.overallDarkAppearance) self.effectView.effect = makeCustomZoomBlurEffect(isLight: presentationData.theme.rootController.keyboardColor == .light)
self.dimNode.alpha = 1.0 self.dimNode.alpha = 1.0
} }
self.dimNode.isHidden = false self.dimNode.isHidden = false

View File

@ -12,6 +12,7 @@ swift_library(
"//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/TelegramStringFormatting:TelegramStringFormatting", "//submodules/TelegramStringFormatting:TelegramStringFormatting",
"//submodules/SegmentedControlNode:SegmentedControlNode", "//submodules/SegmentedControlNode:SegmentedControlNode",
"//submodules/DirectionalPanGesture:DirectionalPanGesture",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -5,6 +5,7 @@ import AsyncDisplayKit
import TelegramPresentationData import TelegramPresentationData
import TelegramStringFormatting import TelegramStringFormatting
import SegmentedControlNode import SegmentedControlNode
import DirectionalPanGesture
public final class DatePickerTheme: Equatable { public final class DatePickerTheme: Equatable {
public let backgroundColor: UIColor public let backgroundColor: UIColor
@ -69,7 +70,11 @@ private let dayFont = Font.regular(13.0)
private let dateFont = Font.with(size: 17.0, design: .regular, traits: .monospacedNumbers) private let dateFont = Font.with(size: 17.0, design: .regular, traits: .monospacedNumbers)
private let selectedDateFont = Font.with(size: 17.0, design: .regular, traits: [.bold, .monospacedNumbers]) private let selectedDateFont = Font.with(size: 17.0, design: .regular, traits: [.bold, .monospacedNumbers])
private let calendar = Calendar(identifier: .gregorian) private var calendar: Calendar = {
var calendar = Calendar(identifier: .gregorian)
calendar.locale = Locale.current
return calendar
}()
private func monthForDate(_ date: Date) -> Date { private func monthForDate(_ date: Date) -> Date {
var components = calendar.dateComponents([.year, .month], from: date) var components = calendar.dateComponents([.year, .month], from: date)
@ -300,9 +305,6 @@ public final class DatePickerNode: ASDisplayNode {
private var transitionFraction: CGFloat = 0.0 private var transitionFraction: CGFloat = 0.0
private var gestureRecognizer: UIPanGestureRecognizer?
private var gestureSelectedIndex: Int?
private var validLayout: CGSize? private var validLayout: CGSize?
public var valueUpdated: ((Date) -> Void)? public var valueUpdated: ((Date) -> Void)?
@ -479,7 +481,10 @@ public final class DatePickerNode: ASDisplayNode {
self.view.disablesInteractiveTransitionGestureRecognizer = true self.view.disablesInteractiveTransitionGestureRecognizer = true
self.contentNode.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))) let panGesture = DirectionalPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
panGesture.direction = .horizontal
self.contentNode.view.addGestureRecognizer(panGesture)
self.contentNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) self.contentNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
} }
@ -499,6 +504,7 @@ public final class DatePickerNode: ASDisplayNode {
} }
} }
self.pickerNode.date = self.state.date self.pickerNode.date = self.state.date
self.timePickerNode.date = self.state.date
if let size = self.validLayout { if let size = self.validLayout {
self.updateLayout(size: size, transition: animated ? .animated(duration: 0.3, curve: .spring) : .immediate) self.updateLayout(size: size, transition: animated ? .animated(duration: 0.3, curve: .spring) : .immediate)
@ -714,15 +720,17 @@ public final class DatePickerNode: ASDisplayNode {
let daysSideInset: CGFloat = 12.0 let daysSideInset: CGFloat = 12.0
let cellSize: CGFloat = floor((size.width - daysSideInset * 2.0) / 7.0) let cellSize: CGFloat = floor((size.width - daysSideInset * 2.0) / 7.0)
var dayIndex: Int32 = Int32(calendar.firstWeekday) - 1
for i in 0 ..< self.dayNodes.count { for i in 0 ..< self.dayNodes.count {
let dayNode = self.dayNodes[i] let dayNode = self.dayNodes[i]
dayNode.attributedText = NSAttributedString(string: shortStringForDayOfWeek(strings: self.strings, day: Int32(i)).uppercased(), font: dayFont, textColor: theme.secondaryTextColor) dayNode.attributedText = NSAttributedString(string: shortStringForDayOfWeek(strings: self.strings, day: dayIndex % 7).uppercased(), font: dayFont, textColor: theme.secondaryTextColor)
let textSize = dayNode.updateLayout(size) let textSize = dayNode.updateLayout(size)
let cellFrame = CGRect(x: daysSideInset + CGFloat(i) * cellSize, y: topInset - 38.0, width: cellSize, height: cellSize) let cellFrame = CGRect(x: daysSideInset + CGFloat(i) * cellSize, y: topInset - 38.0, width: cellSize, height: cellSize)
let textFrame = CGRect(origin: CGPoint(x: cellFrame.minX + floor((cellFrame.width - textSize.width) / 2.0), y: cellFrame.minY + floor((cellFrame.height - textSize.height) / 2.0)), size: textSize) let textFrame = CGRect(origin: CGPoint(x: cellFrame.minX + floor((cellFrame.width - textSize.width) / 2.0), y: cellFrame.minY + floor((cellFrame.height - textSize.height) / 2.0)), size: textSize)
dayNode.frame = textFrame dayNode.frame = textFrame
dayIndex += 1
} }
let containerSize = CGSize(width: size.width, height: size.height - topInset) let containerSize = CGSize(width: size.width, height: size.height - topInset)
@ -887,20 +895,124 @@ private final class MonthPickerNode: ASDisplayNode, UIPickerViewDelegate, UIPick
} }
} }
private class TimeInputView: UIView, UIKeyInput {
override var canBecomeFirstResponder: Bool {
return true
}
var keyboardType: UIKeyboardType = .numberPad
var text: String = ""
var hasText: Bool {
return !self.text.isEmpty
}
var textUpdated: ((String) -> Void)?
private let nonDigits = CharacterSet.decimalDigits.inverted
func insertText(_ text: String) {
if text.rangeOfCharacter(from: nonDigits) != nil {
return
}
var updatedText = self.text
updatedText.append(updatedText)
updatedText = String(updatedText.suffix(4))
self.text = updatedText
self.textUpdated?(self.text)
}
func deleteBackward() {
var updatedText = self.text
updatedText.removeLast()
self.text = updatedText
self.textUpdated?(self.text)
}
}
private class TimeInputNode: ASDisplayNode {
var textUpdated: ((String) -> Void)? {
didSet {
if let view = self.view as? TimeInputView {
view.textUpdated = self.textUpdated
}
}
}
override init() {
super.init()
self.setViewBlock { () -> UIView in
return TimeInputView()
}
}
override func didLoad() {
super.didLoad()
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTap)))
if let view = self.view as? TimeInputView {
view.textUpdated = self.textUpdated
}
}
@objc private func handleTap() {
self.view.becomeFirstResponder()
}
}
public func stringTimestamp(hours: Int32, minutes: Int32, dateTimeFormat: PresentationDateTimeFormat) -> String {
switch dateTimeFormat.timeFormat {
case .regular:
let hourString: String
if hours == 0 {
hourString = "12"
} else if hours > 12 {
hourString = "\(hours - 12)"
} else {
hourString = "\(hours)"
}
let periodString: String
if hours >= 12 {
periodString = "PM"
} else {
periodString = "AM"
}
if minutes >= 10 {
return "\(hourString) \(minutes) \(periodString)"
} else {
return "\(hourString):0\(minutes) \(periodString)"
}
case .military:
return String(format: "%02d %02d", arguments: [Int(hours), Int(minutes)])
}
}
private final class TimePickerNode: ASDisplayNode { private final class TimePickerNode: ASDisplayNode {
private var theme: DatePickerTheme private var theme: DatePickerTheme
private let dateTimeFormat: PresentationDateTimeFormat private let dateTimeFormat: PresentationDateTimeFormat
private let backgroundNode: ASDisplayNode private let backgroundNode: ASDisplayNode
private let textNode: ImmediateTextNode private let textNode: ImmediateTextNode
private let colonNode: ImmediateTextNode
private let inputNode: TimeInputNode
private let amPMSelectorNode: SegmentedControlNode private let amPMSelectorNode: SegmentedControlNode
var date: Date var date: Date {
didSet {
if let size = self.validLayout {
self.updateLayout(size: size)
}
}
}
var minDate: Date? var minDate: Date?
var maxDate: Date? var maxDate: Date?
var valueChanged: ((Date) -> Void)? var valueChanged: ((Date) -> Void)?
private var validLayout: CGSize?
init(theme: DatePickerTheme, dateTimeFormat: PresentationDateTimeFormat, date: Date) { init(theme: DatePickerTheme, dateTimeFormat: PresentationDateTimeFormat, date: Date) {
self.theme = theme self.theme = theme
self.dateTimeFormat = dateTimeFormat self.dateTimeFormat = dateTimeFormat
@ -911,6 +1023,8 @@ private final class TimePickerNode: ASDisplayNode {
self.backgroundNode.cornerRadius = 9.0 self.backgroundNode.cornerRadius = 9.0
self.textNode = ImmediateTextNode() self.textNode = ImmediateTextNode()
self.colonNode = ImmediateTextNode()
self.inputNode = TimeInputNode()
let hours = calendar.component(.hour, from: date) let hours = calendar.component(.hour, from: date)
@ -920,6 +1034,8 @@ private final class TimePickerNode: ASDisplayNode {
self.addSubnode(self.backgroundNode) self.addSubnode(self.backgroundNode)
self.addSubnode(self.textNode) self.addSubnode(self.textNode)
// self.addSubnode(self.colonNode)
// self.addSubnode(self.inputNode)
self.addSubnode(self.amPMSelectorNode) self.addSubnode(self.amPMSelectorNode)
self.amPMSelectorNode.selectedIndexChanged = { index in self.amPMSelectorNode.selectedIndexChanged = { index in
@ -934,6 +1050,10 @@ private final class TimePickerNode: ASDisplayNode {
self.valueChanged?(newDate) self.valueChanged?(newDate)
} }
} }
self.inputNode.textUpdated = { text in
}
} }
func updateTheme(_ theme: DatePickerTheme) { func updateTheme(_ theme: DatePickerTheme) {
@ -952,9 +1072,17 @@ private final class TimePickerNode: ASDisplayNode {
let string = stringForShortTimestamp(hours: hours, minutes: minutes, dateTimeFormat: self.dateTimeFormat).replacingOccurrences(of: " AM", with: "").replacingOccurrences(of: " PM", with: "") let string = stringForShortTimestamp(hours: hours, minutes: minutes, dateTimeFormat: self.dateTimeFormat).replacingOccurrences(of: " AM", with: "").replacingOccurrences(of: " PM", with: "")
self.textNode.attributedText = NSAttributedString(string: string, font: Font.with(size: 21.0, design: .regular, weight: .regular, traits: [.monospacedNumbers]), textColor: self.theme.textColor) self.textNode.attributedText = NSAttributedString(string: string, font: Font.with(size: 21.0, design: .regular, weight: .regular, traits: [.monospacedNumbers]), textColor: self.theme.textColor)
self.colonNode.attributedText = NSAttributedString(string: ":", font: Font.with(size: 21.0, design: .regular, weight: .regular, traits: [.monospacedNumbers]), textColor: self.theme.textColor)
let textSize = self.textNode.updateLayout(size) let textSize = self.textNode.updateLayout(size)
self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.backgroundNode.frame.width - textSize.width) / 2.0), y: floorToScreenPixels((self.backgroundNode.frame.height - textSize.height) / 2.0)), size: textSize) self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.backgroundNode.frame.width - textSize.width) / 2.0), y: floorToScreenPixels((self.backgroundNode.frame.height - textSize.height) / 2.0)), size: textSize)
let colonSize = self.colonNode.updateLayout(size)
self.colonNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.backgroundNode.frame.width - colonSize.width) / 2.0) - 7.0, y: floorToScreenPixels((self.backgroundNode.frame.height - colonSize.height) / 2.0) - 2.0), size: colonSize)
self.inputNode.frame = self.backgroundNode.frame
if self.dateTimeFormat.timeFormat == .military { if self.dateTimeFormat.timeFormat == .military {
contentSize = self.backgroundNode.frame.size contentSize = self.backgroundNode.frame.size
self.amPMSelectorNode.isHidden = true self.amPMSelectorNode.isHidden = true

View File

@ -2,11 +2,18 @@ import Foundation
import UIKit import UIKit
public class DirectionalPanGestureRecognizer: UIPanGestureRecognizer { public class DirectionalPanGestureRecognizer: UIPanGestureRecognizer {
public enum Direction {
case horizontal
case vertical
}
private var validatedGesture = false private var validatedGesture = false
private var firstLocation: CGPoint = CGPoint() private var firstLocation: CGPoint = CGPoint()
public var shouldBegin: ((CGPoint) -> Bool)? public var shouldBegin: ((CGPoint) -> Bool)?
public var direction: Direction = .vertical
override public init(target: Any?, action: Selector?) { override public init(target: Any?, action: Selector?) {
super.init(target: target, action: action) super.init(target: target, action: action)
@ -46,12 +53,21 @@ public class DirectionalPanGestureRecognizer: UIPanGestureRecognizer {
let absTranslationY: CGFloat = abs(translation.y) let absTranslationY: CGFloat = abs(translation.y)
if !self.validatedGesture { if !self.validatedGesture {
switch self.direction {
case .horizontal:
if absTranslationY > 4.0 && absTranslationY > absTranslationX * 2.0 {
self.state = .failed
} else if absTranslationX > 2.0 && absTranslationY * 2.0 < absTranslationX {
self.validatedGesture = true
}
case .vertical:
if absTranslationX > 4.0 && absTranslationX > absTranslationY * 2.0 { if absTranslationX > 4.0 && absTranslationX > absTranslationY * 2.0 {
self.state = .failed self.state = .failed
} else if absTranslationY > 2.0 && absTranslationX * 2.0 < absTranslationY { } else if absTranslationY > 2.0 && absTranslationX * 2.0 < absTranslationY {
self.validatedGesture = true self.validatedGesture = true
} }
} }
}
if self.validatedGesture { if self.validatedGesture {
super.touchesMoved(touches, with: event) super.touchesMoved(touches, with: event)

View File

@ -44,7 +44,11 @@ func isValidNumberOfUsers(_ number: String) -> Bool {
if number.rangeOfCharacter(from: invalidAmountCharacters) != nil || number == "0" { if number.rangeOfCharacter(from: invalidAmountCharacters) != nil || number == "0" {
return false return false
} }
if let _ = Int32(number) {
return true return true
} else {
return false
}
} }
private enum InviteLinksEditEntry: ItemListNodeEntry { private enum InviteLinksEditEntry: ItemListNodeEntry {
@ -55,7 +59,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
case timeInfo(PresentationTheme, String) case timeInfo(PresentationTheme, String)
case usageHeader(PresentationTheme, String) case usageHeader(PresentationTheme, String)
case usagePicker(PresentationTheme, InviteLinkUsageLimit) case usagePicker(PresentationTheme, PresentationDateTimeFormat, InviteLinkUsageLimit)
case usageCustomPicker(PresentationTheme, Int32?, Bool, Bool) case usageCustomPicker(PresentationTheme, Int32?, Bool, Bool)
case usageInfo(PresentationTheme, String) case usageInfo(PresentationTheme, String)
@ -135,8 +139,8 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .usagePicker(lhsTheme, lhsValue): case let .usagePicker(lhsTheme, lhsDateTimeFormat, lhsValue):
if case let .usagePicker(rhsTheme, rhsValue) = rhs, lhsTheme === rhsTheme, lhsValue == rhsValue { if case let .usagePicker(rhsTheme, rhsDateTimeFormat, rhsValue) = rhs, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, lhsValue == rhsValue {
return true return true
} else { } else {
return false return false
@ -209,8 +213,8 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
case let .usageHeader(_, text): case let .usageHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .usagePicker(_, value): case let .usagePicker(_, dateTimeFormat, value):
return ItemListInviteLinkUsageLimitItem(theme: presentationData.theme, strings: presentationData.strings, value: value, enabled: true, sectionId: self.section, updated: { value in return ItemListInviteLinkUsageLimitItem(theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: dateTimeFormat, value: value, enabled: true, sectionId: self.section, updated: { value in
arguments.dismissInput() arguments.dismissInput()
arguments.updateState({ state in arguments.updateState({ state in
var updatedState = state var updatedState = state
@ -234,7 +238,9 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
} }
arguments.updateState { state in arguments.updateState { state in
var updatedState = state var updatedState = state
updatedState.usage = InviteLinkUsageLimit(value: Int32(updatedText)) if let value = Int32(updatedText) {
updatedState.usage = InviteLinkUsageLimit(value: value)
}
return updatedState return updatedState
} }
}, shouldUpdateText: { text in }, shouldUpdateText: { text in
@ -288,7 +294,7 @@ private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state:
entries.append(.timeInfo(presentationData.theme, presentationData.strings.InviteLink_Create_TimeLimitInfo)) entries.append(.timeInfo(presentationData.theme, presentationData.strings.InviteLink_Create_TimeLimitInfo))
entries.append(.usageHeader(presentationData.theme, presentationData.strings.InviteLink_Create_UsersLimit.uppercased())) entries.append(.usageHeader(presentationData.theme, presentationData.strings.InviteLink_Create_UsersLimit.uppercased()))
entries.append(.usagePicker(presentationData.theme, state.usage)) entries.append(.usagePicker(presentationData.theme, presentationData.dateTimeFormat, state.usage))
var customValue = false var customValue = false
if case .custom = state.usage { if case .custom = state.usage {
@ -358,6 +364,14 @@ public func inviteLinkEditController(context: AccountContext, peerId: PeerId, in
guard let invite = invite else { guard let invite = invite else {
return return
} }
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = ActionSheetController(presentationData: presentationData) let controller = ActionSheetController(presentationData: presentationData)
let dismissAction: () -> Void = { [weak controller] in let dismissAction: () -> Void = { [weak controller] in
@ -365,7 +379,7 @@ public func inviteLinkEditController(context: AccountContext, peerId: PeerId, in
} }
controller.setItemGroups([ controller.setItemGroups([
ActionSheetItemGroup(items: [ ActionSheetItemGroup(items: [
ActionSheetTextItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text), ActionSheetTextItem(title: isGroup ? presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text : presentationData.strings.ChannelInfo_InviteLink_RevokeAlert_Text),
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: { ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: {
dismissAction() dismissAction()
dismissImpl?() dismissImpl?()
@ -395,6 +409,7 @@ public func inviteLinkEditController(context: AccountContext, peerId: PeerId, in
]) ])
presentControllerImpl?(controller, nil) presentControllerImpl?(controller, nil)
}) })
})
let previousState = Atomic<InviteLinkEditControllerState?>(value: nil) let previousState = Atomic<InviteLinkEditControllerState?>(value: nil)
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get()) let signal = combineLatest(context.sharedContext.presentationData, statePromise.get())

View File

@ -366,13 +366,21 @@ public final class InviteLinkInviteController: ViewController {
}, action: { _, f in }, action: { _, f in
f(.dismissWithoutContent) f(.dismissWithoutContent)
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let controller = ActionSheetController(presentationData: presentationData) let controller = ActionSheetController(presentationData: presentationData)
let dismissAction: () -> Void = { [weak controller] in let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated() controller?.dismissAnimated()
} }
controller.setItemGroups([ controller.setItemGroups([
ActionSheetItemGroup(items: [ ActionSheetItemGroup(items: [
ActionSheetTextItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text), ActionSheetTextItem(title: isGroup ? presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text : presentationData.strings.ChannelInfo_InviteLink_RevokeAlert_Text),
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: { ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: {
dismissAction() dismissAction()
@ -385,6 +393,7 @@ public final class InviteLinkInviteController: ViewController {
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
]) ])
self?.controller?.present(controller, in: .window(.root)) self?.controller?.present(controller, in: .window(.root))
})
}))) })))
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, blurBackground: false)), items: .single(items), reactionItems: [], gesture: gesture) let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, blurBackground: false)), items: .single(items), reactionItems: [], gesture: gesture)

View File

@ -297,7 +297,7 @@ private func inviteLinkListControllerEntries(presentationData: PresentationData,
mainInvite = nil mainInvite = nil
} }
entries.append(.mainLinkHeader(presentationData.theme, isPublic ? presentationData.strings.InviteLink_PublicLink.uppercased() : presentationData.strings.InviteLink_PermanentLink.uppercased())) entries.append(.mainLinkHeader(presentationData.theme, isPublic ? presentationData.strings.InviteLink_PublicLink.uppercased() : presentationData.strings.InviteLink_InviteLink.uppercased()))
let importersCount: Int32 let importersCount: Int32
if let count = importers?.count { if let count = importers?.count {
@ -477,13 +477,22 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
}, action: { _, f in }, action: { _, f in
f(.dismissWithoutContent) f(.dismissWithoutContent)
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let controller = ActionSheetController(presentationData: presentationData) let controller = ActionSheetController(presentationData: presentationData)
let dismissAction: () -> Void = { [weak controller] in let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated() controller?.dismissAnimated()
} }
controller.setItemGroups([ controller.setItemGroups([
ActionSheetItemGroup(items: [ ActionSheetItemGroup(items: [
ActionSheetTextItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text), ActionSheetTextItem(title: isGroup ? presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text : presentationData.strings.ChannelInfo_InviteLink_RevokeAlert_Text),
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: { ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: {
dismissAction() dismissAction()
@ -523,6 +532,7 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
]) ])
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
})
}))) })))
} }
@ -646,13 +656,21 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
}, action: { _, f in }, action: { _, f in
f(.default) f(.default)
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let controller = ActionSheetController(presentationData: presentationData) let controller = ActionSheetController(presentationData: presentationData)
let dismissAction: () -> Void = { [weak controller] in let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated() controller?.dismissAnimated()
} }
controller.setItemGroups([ controller.setItemGroups([
ActionSheetItemGroup(items: [ ActionSheetItemGroup(items: [
ActionSheetTextItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text), ActionSheetTextItem(title: isGroup ? presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text : presentationData.strings.ChannelInfo_InviteLink_RevokeAlert_Text),
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: { ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: {
dismissAction() dismissAction()
@ -667,6 +685,7 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
]) ])
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
})
}))) })))
} }

View File

@ -41,6 +41,11 @@ public final class InviteLinkQRCodeController: ViewController {
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
private var initialBrightness: CGFloat?
private var brightnessArguments: (Double, Double, CGFloat, CGFloat)?
private var animator: ConstantDisplayLinkAnimator?
private let idleTimerExtensionDisposable = MetaDisposable() private let idleTimerExtensionDisposable = MetaDisposable()
public init(context: AccountContext, invite: ExportedInvitation, isGroup: Bool) { public init(context: AccountContext, invite: ExportedInvitation, isGroup: Bool) {
@ -64,6 +69,11 @@ public final class InviteLinkQRCodeController: ViewController {
self.idleTimerExtensionDisposable.set(self.context.sharedContext.applicationBindings.pushIdleTimerExtension()) self.idleTimerExtensionDisposable.set(self.context.sharedContext.applicationBindings.pushIdleTimerExtension())
self.statusBar.statusBarStyle = .Ignore self.statusBar.statusBarStyle = .Ignore
self.animator = ConstantDisplayLinkAnimator(update: { [weak self] in
self?.updateBrightness()
})
self.animator?.isPaused = true
} }
required init(coder aDecoder: NSCoder) { required init(coder aDecoder: NSCoder) {
@ -73,6 +83,7 @@ public final class InviteLinkQRCodeController: ViewController {
deinit { deinit {
self.presentationDataDisposable?.dispose() self.presentationDataDisposable?.dispose()
self.idleTimerExtensionDisposable.dispose() self.idleTimerExtensionDisposable.dispose()
self.animator?.invalidate()
} }
override public func loadDisplayNode() { override public func loadDisplayNode() {
@ -85,20 +96,43 @@ public final class InviteLinkQRCodeController: ViewController {
} }
} }
override public func loadView() {
super.loadView()
}
override public func viewDidAppear(_ animated: Bool) { override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) super.viewDidAppear(animated)
if !self.animatedIn { if !self.animatedIn {
self.animatedIn = true self.animatedIn = true
self.controllerNode.animateIn() self.controllerNode.animateIn()
self.initialBrightness = UIScreen.main.brightness
self.brightnessArguments = (CACurrentMediaTime(), 0.3, UIScreen.main.brightness, 1.0)
self.updateBrightness()
}
}
private func updateBrightness() {
if let (startTime, duration, initial, target) = self.brightnessArguments {
self.animator?.isPaused = false
let t = CGFloat(max(0.0, min(1.0, (CACurrentMediaTime() - startTime) / duration)))
let value = initial + (target - initial) * t
UIScreen.main.brightness = value
if t >= 1.0 {
self.brightnessArguments = nil
self.animator?.isPaused = true
}
} else {
self.animator?.isPaused = true
} }
} }
override public func dismiss(completion: (() -> Void)? = nil) { override public func dismiss(completion: (() -> Void)? = nil) {
if UIScreen.main.brightness > 0.99, let initialBrightness = self.initialBrightness {
self.brightnessArguments = (CACurrentMediaTime(), 0.3, UIScreen.main.brightness, initialBrightness)
self.updateBrightness()
}
self.controllerNode.animateOut(completion: completion) self.controllerNode.animateOut(completion: completion)
} }
@ -344,7 +378,7 @@ public final class InviteLinkQRCodeController: ViewController {
self.containerLayout = (layout, navigationBarHeight) self.containerLayout = (layout, navigationBarHeight)
var insets = layout.insets(options: [.statusBar, .input]) var insets = layout.insets(options: [.statusBar, .input])
insets.top = max(10.0, insets.top) insets.top = 48.0
let makeImageLayout = self.qrImageNode.asyncLayout() let makeImageLayout = self.qrImageNode.asyncLayout()
let imageSide: CGFloat = 240.0 let imageSide: CGFloat = 240.0

View File

@ -511,13 +511,21 @@ public final class InviteLinkViewController: ViewController {
}, action: { [weak self] _, f in }, action: { [weak self] _, f in
f(.dismissWithoutContent) f(.dismissWithoutContent)
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let controller = ActionSheetController(presentationData: presentationData) let controller = ActionSheetController(presentationData: presentationData)
let dismissAction: () -> Void = { [weak controller] in let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated() controller?.dismissAnimated()
} }
controller.setItemGroups([ controller.setItemGroups([
ActionSheetItemGroup(items: [ ActionSheetItemGroup(items: [
ActionSheetTextItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text), ActionSheetTextItem(title: isGroup ? presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text : presentationData.strings.ChannelInfo_InviteLink_RevokeAlert_Text),
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: { ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: {
dismissAction() dismissAction()
self?.controller?.dismiss() self?.controller?.dismiss()
@ -531,6 +539,7 @@ public final class InviteLinkViewController: ViewController {
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
]) ])
self?.controller?.present(controller, in: .window(.root)) self?.controller?.present(controller, in: .window(.root))
})
}))) })))
} }

View File

@ -89,14 +89,16 @@ enum InviteLinkUsageLimit: Equatable {
final class ItemListInviteLinkUsageLimitItem: ListViewItem, ItemListItem { final class ItemListInviteLinkUsageLimitItem: ListViewItem, ItemListItem {
let theme: PresentationTheme let theme: PresentationTheme
let strings: PresentationStrings let strings: PresentationStrings
let dateTimeFormat: PresentationDateTimeFormat
let value: InviteLinkUsageLimit let value: InviteLinkUsageLimit
let enabled: Bool let enabled: Bool
let sectionId: ItemListSectionId let sectionId: ItemListSectionId
let updated: (InviteLinkUsageLimit) -> Void let updated: (InviteLinkUsageLimit) -> Void
init(theme: PresentationTheme, strings: PresentationStrings, value: InviteLinkUsageLimit, enabled: Bool, sectionId: ItemListSectionId, updated: @escaping (InviteLinkUsageLimit) -> Void) { init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, value: InviteLinkUsageLimit, enabled: Bool, sectionId: ItemListSectionId, updated: @escaping (InviteLinkUsageLimit) -> Void) {
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
self.dateTimeFormat = dateTimeFormat
self.value = value self.value = value
self.enabled = enabled self.enabled = enabled
self.sectionId = sectionId self.sectionId = sectionId
@ -285,7 +287,7 @@ private final class ItemListInviteLinkUsageLimitItemNode: ListViewItemNode {
let customTextString: String let customTextString: String
if case let .custom(value) = item.value { if case let .custom(value) = item.value {
customTextString = "\(value)" customTextString = compactNumericCountString(Int(value), decimalSeparator: item.dateTimeFormat.decimalSeparator)
} else { } else {
customTextString = "" customTextString = ""
} }

View File

@ -988,13 +988,22 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
}, action: { _, f in }, action: { _, f in
f(.dismissWithoutContent) f(.dismissWithoutContent)
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let controller = ActionSheetController(presentationData: presentationData) let controller = ActionSheetController(presentationData: presentationData)
let dismissAction: () -> Void = { [weak controller] in let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated() controller?.dismissAnimated()
} }
controller.setItemGroups([ controller.setItemGroups([
ActionSheetItemGroup(items: [ ActionSheetItemGroup(items: [
ActionSheetTextItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text), ActionSheetTextItem(title: isGroup ? presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text : presentationData.strings.ChannelInfo_InviteLink_RevokeAlert_Text),
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: { ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: {
dismissAction() dismissAction()
@ -1032,6 +1041,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
]) ])
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
})
}))) })))
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node)), items: .single(items), reactionItems: [], gesture: nil) let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node)), items: .single(items), reactionItems: [], gesture: nil)

View File

@ -71,7 +71,7 @@ public final class PermissionContentNode: ASDisplayNode {
if case let .animation(animation) = icon { if case let .animation(animation) = icon {
self.animationNode = AnimatedStickerNode() self.animationNode = AnimatedStickerNode()
if let path = getAppBundle().path(forResource: animation, ofType: "tgs") { if let path = getAppBundle().path(forResource: animation, ofType: "tgs") {
self.animationNode?.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 320, height: 320, playbackMode: .loop, mode: .direct(cachePathPrefix: nil)) self.animationNode?.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 320, height: 320, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
self.animationNode?.visibility = true self.animationNode?.visibility = true
} }
self.nearbyIconNode = nil self.nearbyIconNode = nil

View File

@ -873,6 +873,12 @@ extension PresentationThemeDecoding {
} else if number === kCFBooleanFalse as NSNumber { } else if number === kCFBooleanFalse as NSNumber {
return false return false
} }
} else if let string = value as? String {
if string.lowercased() == "true" {
return true
} else if string.lowercased() == "false" {
return false
}
} }
throw PresentationThemeDecodingError.typeMismatch throw PresentationThemeDecodingError.typeMismatch

View File

@ -413,7 +413,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
let titleString = strings.Notification_VoiceChatEnded(callDurationString(strings: strings, value: duration)).0 let titleString = strings.Notification_VoiceChatEnded(callDurationString(strings: strings, value: duration)).0
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor) attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
} else { } else {
var attributePeerIds: [(Int, PeerId?)] = [(0, message.author?.id)] let attributePeerIds: [(Int, PeerId?)] = [(0, message.author?.id)]
let titleString = strings.Notification_VoiceChatStarted(authorName) let titleString = strings.Notification_VoiceChatStarted(authorName)
attributedString = addAttributesToStringWithRanges(titleString, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds)) attributedString = addAttributesToStringWithRanges(titleString, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
} }