Various settings UI improvements
@ -4097,6 +4097,7 @@ Unused sets are archived when you add more.";
|
|||||||
|
|
||||||
"ChatSettings.AutoDownloadSettings.TypePhoto" = "Photos";
|
"ChatSettings.AutoDownloadSettings.TypePhoto" = "Photos";
|
||||||
"ChatSettings.AutoDownloadSettings.TypeVideo" = "Videos (%@)";
|
"ChatSettings.AutoDownloadSettings.TypeVideo" = "Videos (%@)";
|
||||||
|
"ChatSettings.AutoDownloadSettings.TypeMedia" = "Media (%@)";
|
||||||
"ChatSettings.AutoDownloadSettings.TypeFile" = "Files (%@)";
|
"ChatSettings.AutoDownloadSettings.TypeFile" = "Files (%@)";
|
||||||
"ChatSettings.AutoDownloadSettings.OffForAll" = "Disabled";
|
"ChatSettings.AutoDownloadSettings.OffForAll" = "Disabled";
|
||||||
"ChatSettings.AutoDownloadSettings.Delimeter" = ", ";
|
"ChatSettings.AutoDownloadSettings.Delimeter" = ", ";
|
||||||
@ -6938,3 +6939,21 @@ Sorry for the inconvenience.";
|
|||||||
"Map.ETADays_3_10" = "%@ days";
|
"Map.ETADays_3_10" = "%@ days";
|
||||||
"Map.ETADays_many" = "%@ days";
|
"Map.ETADays_many" = "%@ days";
|
||||||
"Map.ETADays_any" = "%@ days";
|
"Map.ETADays_any" = "%@ days";
|
||||||
|
|
||||||
|
"ChatSettings.UseLessDataForCalls" = "Use Less Data for Calls";
|
||||||
|
|
||||||
|
"Time.JustNow" = "just now";
|
||||||
|
"Time.MinutesAgo_0" = "%@ minutes ago"; //three to ten
|
||||||
|
"Time.MinutesAgo_1" = "%@ minute ago"; //one
|
||||||
|
"Time.MinutesAgo_2" = "%@ minutes ago"; //two
|
||||||
|
"Time.MinutesAgo_3_10" = "%@ minutes ago"; //three to ten
|
||||||
|
"Time.MinutesAgo_many" = "%@ minutes ago"; // more than ten
|
||||||
|
"Time.MinutesAgo_any" = "%@ minutes ago"; // more than ten
|
||||||
|
"Time.HoursAgo_0" = "%@ hours ago";
|
||||||
|
"Time.HoursAgo_1" = "%@ hour ago";
|
||||||
|
"Time.HoursAgo_2" = "%@ hours ago";
|
||||||
|
"Time.HoursAgo_3_10" = "%@ hours ago";
|
||||||
|
"Time.HoursAgo_any" = "%@ hours ago";
|
||||||
|
"Time.HoursAgo_many" = "%@ hours ago";
|
||||||
|
"Time.HoursAgo_0" = "%@ hours ago";
|
||||||
|
"Time.AtDate" = "last seen %@";
|
||||||
|
@ -343,7 +343,7 @@ public final class AvatarNode: ASDisplayNode {
|
|||||||
|
|
||||||
let parameters: AvatarNodeParameters
|
let parameters: AvatarNodeParameters
|
||||||
|
|
||||||
if let peer = peer, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded) {
|
if let peer = peer, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, round: clipStyle == .round, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded) {
|
||||||
self.contents = nil
|
self.contents = nil
|
||||||
self.displaySuspended = true
|
self.displaySuspended = true
|
||||||
self.imageReady.set(self.imageNode.contentReady)
|
self.imageReady.set(self.imageNode.contentReady)
|
||||||
|
@ -188,6 +188,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
|||||||
private let topStripeNode: ASDisplayNode
|
private let topStripeNode: ASDisplayNode
|
||||||
private let bottomStripeNode: ASDisplayNode
|
private let bottomStripeNode: ASDisplayNode
|
||||||
private let highlightedBackgroundNode: ASDisplayNode
|
private let highlightedBackgroundNode: ASDisplayNode
|
||||||
|
private let maskNode: ASImageNode
|
||||||
|
|
||||||
private let avatarNode: AvatarNode
|
private let avatarNode: AvatarNode
|
||||||
private let titleNode: TextNode
|
private let titleNode: TextNode
|
||||||
@ -206,6 +207,8 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
|||||||
self.backgroundNode = ASDisplayNode()
|
self.backgroundNode = ASDisplayNode()
|
||||||
self.backgroundNode.isLayerBacked = true
|
self.backgroundNode.isLayerBacked = true
|
||||||
|
|
||||||
|
self.maskNode = ASImageNode()
|
||||||
|
|
||||||
self.topStripeNode = ASDisplayNode()
|
self.topStripeNode = ASDisplayNode()
|
||||||
self.topStripeNode.isLayerBacked = true
|
self.topStripeNode.isLayerBacked = true
|
||||||
|
|
||||||
@ -523,7 +526,9 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
|||||||
} else if last && strongSelf.bottomStripeNode.supernode != nil {
|
} else if last && strongSelf.bottomStripeNode.supernode != nil {
|
||||||
strongSelf.bottomStripeNode.removeFromSupernode()
|
strongSelf.bottomStripeNode.removeFromSupernode()
|
||||||
}
|
}
|
||||||
|
if strongSelf.maskNode.supernode != nil {
|
||||||
|
strongSelf.maskNode.removeFromSupernode()
|
||||||
|
}
|
||||||
transition.updateFrameAdditive(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight)))
|
transition.updateFrameAdditive(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight)))
|
||||||
case .blocks:
|
case .blocks:
|
||||||
if strongSelf.backgroundNode.supernode == nil {
|
if strongSelf.backgroundNode.supernode == nil {
|
||||||
@ -535,11 +540,18 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
|||||||
if strongSelf.bottomStripeNode.supernode == nil {
|
if strongSelf.bottomStripeNode.supernode == nil {
|
||||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
|
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
|
||||||
}
|
}
|
||||||
|
if strongSelf.maskNode.supernode == nil {
|
||||||
|
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||||
|
}
|
||||||
|
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||||
|
var hasTopCorners = false
|
||||||
|
var hasBottomCorners = false
|
||||||
switch neighbors.top {
|
switch neighbors.top {
|
||||||
case .sameSection(false):
|
case .sameSection(false):
|
||||||
strongSelf.topStripeNode.isHidden = true
|
strongSelf.topStripeNode.isHidden = true
|
||||||
default:
|
default:
|
||||||
strongSelf.topStripeNode.isHidden = false
|
hasTopCorners = true
|
||||||
|
strongSelf.topStripeNode.isHidden = hasCorners
|
||||||
}
|
}
|
||||||
let bottomStripeInset: CGFloat
|
let bottomStripeInset: CGFloat
|
||||||
switch neighbors.bottom {
|
switch neighbors.bottom {
|
||||||
@ -547,9 +559,14 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
|||||||
bottomStripeInset = leftInset
|
bottomStripeInset = leftInset
|
||||||
default:
|
default:
|
||||||
bottomStripeInset = 0.0
|
bottomStripeInset = 0.0
|
||||||
|
hasBottomCorners = true
|
||||||
|
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||||
|
|
||||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||||
|
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: nodeLayout.size.width, height: separatorHeight))
|
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: nodeLayout.size.width, height: separatorHeight))
|
||||||
transition.updateFrameAdditive(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: nodeLayout.size.width - bottomStripeInset, height: separatorHeight)))
|
transition.updateFrameAdditive(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: nodeLayout.size.width - bottomStripeInset, height: separatorHeight)))
|
||||||
}
|
}
|
||||||
|
@ -262,6 +262,12 @@ public final class CallListController: TelegramBaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if case .navigation = self.mode {
|
||||||
|
self.controllerNode.navigationBar = self.navigationBar
|
||||||
|
self.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate)
|
||||||
|
}
|
||||||
|
|
||||||
self.controllerNode.startNewCall = { [weak self] in
|
self.controllerNode.startNewCall = { [weak self] in
|
||||||
self?.beginCallImpl()
|
self?.beginCallImpl()
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ private func mappedInsertEntries(context: AccountContext, presentationData: Item
|
|||||||
return entries.map { entry -> ListViewInsertItem in
|
return entries.map { entry -> ListViewInsertItem in
|
||||||
switch entry.entry {
|
switch entry.entry {
|
||||||
case let .displayTab(_, text, value):
|
case let .displayTab(_, text, value):
|
||||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: true, noCorners: true, sectionId: 0, style: .blocks, updated: { value in
|
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: true, noCorners: false, sectionId: 0, style: .blocks, updated: { value in
|
||||||
nodeInteraction.updateShowCallsTab(value)
|
nodeInteraction.updateShowCallsTab(value)
|
||||||
}), directionHint: entry.directionHint)
|
}), directionHint: entry.directionHint)
|
||||||
case let .displayTabInfo(_, text):
|
case let .displayTabInfo(_, text):
|
||||||
@ -136,7 +136,7 @@ private func mappedUpdateEntries(context: AccountContext, presentationData: Item
|
|||||||
return entries.map { entry -> ListViewUpdateItem in
|
return entries.map { entry -> ListViewUpdateItem in
|
||||||
switch entry.entry {
|
switch entry.entry {
|
||||||
case let .displayTab(_, text, value):
|
case let .displayTab(_, text, value):
|
||||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: true, noCorners: true, sectionId: 0, style: .blocks, updated: { value in
|
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: true, noCorners: false, sectionId: 0, style: .blocks, updated: { value in
|
||||||
nodeInteraction.updateShowCallsTab(value)
|
nodeInteraction.updateShowCallsTab(value)
|
||||||
}), directionHint: entry.directionHint)
|
}), directionHint: entry.directionHint)
|
||||||
case let .displayTabInfo(_, text):
|
case let .displayTabInfo(_, text):
|
||||||
@ -177,6 +177,8 @@ final class CallListControllerNode: ASDisplayNode {
|
|||||||
return _ready.get()
|
return _ready.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
weak var navigationBar: NavigationBar?
|
||||||
|
|
||||||
var peerSelected: ((EnginePeer.Id) -> Void)?
|
var peerSelected: ((EnginePeer.Id) -> Void)?
|
||||||
var activateSearch: (() -> Void)?
|
var activateSearch: (() -> Void)?
|
||||||
var deletePeerChat: ((EnginePeer.Id) -> Void)?
|
var deletePeerChat: ((EnginePeer.Id) -> Void)?
|
||||||
@ -215,6 +217,8 @@ final class CallListControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
private let openGroupCallDisposable = MetaDisposable()
|
private let openGroupCallDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
private var previousContentOffset: ListViewVisibleContentOffset?
|
||||||
|
|
||||||
init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EnginePeer.Id, Bool) -> Void, joinGroupCall: @escaping (EnginePeer.Id, EngineGroupCallDescription) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void) {
|
init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EnginePeer.Id, Bool) -> Void, joinGroupCall: @escaping (EnginePeer.Id, EngineGroupCallDescription) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void) {
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -272,7 +276,7 @@ final class CallListControllerNode: ASDisplayNode {
|
|||||||
self.addSubnode(self.emptyButtonTextNode)
|
self.addSubnode(self.emptyButtonTextNode)
|
||||||
self.addSubnode(self.emptyButtonIconNode)
|
self.addSubnode(self.emptyButtonIconNode)
|
||||||
self.addSubnode(self.emptyButtonNode)
|
self.addSubnode(self.emptyButtonNode)
|
||||||
|
|
||||||
switch self.mode {
|
switch self.mode {
|
||||||
case .tab:
|
case .tab:
|
||||||
self.backgroundColor = presentationData.theme.chatList.backgroundColor
|
self.backgroundColor = presentationData.theme.chatList.backgroundColor
|
||||||
@ -607,6 +611,39 @@ final class CallListControllerNode: ASDisplayNode {
|
|||||||
strongSelf.updateEmptyPlaceholder(theme: state.presentationData.theme, strings: state.presentationData.strings, type: type, isHidden: !isEmpty)
|
strongSelf.updateEmptyPlaceholder(theme: state.presentationData.theme, strings: state.presentationData.strings, type: type, isHidden: !isEmpty)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
if case .navigation = mode {
|
||||||
|
self.listNode.itemNodeHitTest = { [weak self] point in
|
||||||
|
if let strongSelf = self {
|
||||||
|
return point.x > strongSelf.leftOverlayNode.frame.maxX && point.x < strongSelf.rightOverlayNode.frame.minX
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.listNode.visibleContentOffsetChanged = { [weak self] offset in
|
||||||
|
if let strongSelf = self {
|
||||||
|
var previousContentOffsetValue: CGFloat?
|
||||||
|
if let previousContentOffset = strongSelf.previousContentOffset, case let .known(value) = previousContentOffset {
|
||||||
|
previousContentOffsetValue = value
|
||||||
|
}
|
||||||
|
switch offset {
|
||||||
|
case let .known(value):
|
||||||
|
let transition: ContainedViewLayoutTransition
|
||||||
|
if let previousContentOffsetValue = previousContentOffsetValue, value <= 0.0, previousContentOffsetValue > 30.0 {
|
||||||
|
transition = .animated(duration: 0.2, curve: .easeInOut)
|
||||||
|
} else {
|
||||||
|
transition = .immediate
|
||||||
|
}
|
||||||
|
strongSelf.navigationBar?.updateBackgroundAlpha(min(30.0, value) / 30.0, transition: transition)
|
||||||
|
case .unknown, .none:
|
||||||
|
strongSelf.navigationBar?.updateBackgroundAlpha(1.0, transition: .immediate)
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.previousContentOffset = offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -838,8 +875,25 @@ final class CallListControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
var insets = layout.insets(options: [.input])
|
var insets = layout.insets(options: [.input])
|
||||||
insets.top += max(navigationBarHeight, layout.insets(options: [.statusBar]).top)
|
insets.top += max(navigationBarHeight, layout.insets(options: [.statusBar]).top)
|
||||||
insets.left += layout.safeInsets.left
|
|
||||||
insets.right += layout.safeInsets.right
|
let inset = max(16.0, floor((layout.size.width - 674.0) / 2.0))
|
||||||
|
if case .navigation = self.mode {
|
||||||
|
insets.left += inset
|
||||||
|
insets.right += inset
|
||||||
|
|
||||||
|
self.leftOverlayNode.frame = CGRect(x: 0.0, y: 0.0, width: insets.left, height: layout.size.height)
|
||||||
|
self.rightOverlayNode.frame = CGRect(x: layout.size.width - insets.right, y: 0.0, width: insets.right, height: layout.size.height)
|
||||||
|
|
||||||
|
if self.leftOverlayNode.supernode == nil {
|
||||||
|
self.insertSubnode(self.leftOverlayNode, aboveSubnode: self.listNode)
|
||||||
|
}
|
||||||
|
if self.rightOverlayNode.supernode == nil {
|
||||||
|
self.insertSubnode(self.rightOverlayNode, aboveSubnode: self.listNode)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
insets.left += layout.safeInsets.left
|
||||||
|
insets.right += layout.safeInsets.right
|
||||||
|
}
|
||||||
|
|
||||||
self.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
|
self.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
|
||||||
self.listNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)
|
self.listNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)
|
||||||
|
@ -922,8 +922,8 @@ open class NavigationBar: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) {
|
public func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
transition.updateAlpha(node: self.backgroundNode, alpha: alpha)
|
transition.updateAlpha(node: self.backgroundNode, alpha: alpha, delay: 0.15)
|
||||||
transition.updateAlpha(node: self.stripeNode, alpha: alpha)
|
transition.updateAlpha(node: self.stripeNode, alpha: alpha, delay: 0.15)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updatePresentationData(_ presentationData: NavigationBarPresentationData) {
|
public func updatePresentationData(_ presentationData: NavigationBarPresentationData) {
|
||||||
|
@ -357,13 +357,13 @@ open class ItemListControllerNode: ASDisplayNode {
|
|||||||
case let .known(value):
|
case let .known(value):
|
||||||
let transition: ContainedViewLayoutTransition
|
let transition: ContainedViewLayoutTransition
|
||||||
if let previousContentOffsetValue = previousContentOffsetValue, value <= 0.0, previousContentOffsetValue > 30.0 {
|
if let previousContentOffsetValue = previousContentOffsetValue, value <= 0.0, previousContentOffsetValue > 30.0 {
|
||||||
transition = .animated(duration: 0.3, curve: .linear)
|
transition = .animated(duration: 0.2, curve: .easeInOut)
|
||||||
} else {
|
} else {
|
||||||
transition = .immediate
|
transition = .immediate
|
||||||
}
|
}
|
||||||
strongSelf.navigationBar.updateBackgroundAlpha(min(30.0, value) / 30.0, transition: transition)
|
strongSelf.navigationBar.updateBackgroundAlpha(min(30.0, value) / 30.0, transition: transition)
|
||||||
case .unknown, .none:
|
case .unknown, .none:
|
||||||
strongSelf.navigationBar.updateBackgroundAlpha(0.0, transition: .immediate)
|
strongSelf.navigationBar.updateBackgroundAlpha(1.0, transition: .immediate)
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.previousContentOffset = offset
|
strongSelf.previousContentOffset = offset
|
||||||
|
@ -157,15 +157,15 @@ private enum AutodownloadMediaCategoryEntry: ItemListNodeEntry {
|
|||||||
case let .typesHeader(_, text):
|
case let .typesHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .photos(_, text, value, enabled):
|
case let .photos(_, text, value, enabled):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/MenuIcons/Photos")?.precomposed(), title: text, enabled: enabled, label: value, labelStyle: .detailText, sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Photos")?.precomposed(), title: text, enabled: enabled, label: value, labelStyle: .detailText, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.customize(.photo)
|
arguments.customize(.photo)
|
||||||
})
|
})
|
||||||
case let .videos(_, text, value, enabled):
|
case let .videos(_, text, value, enabled):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/MenuIcons/Videos")?.precomposed(), title: text, enabled: enabled, label: value, labelStyle: .detailText, sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Videos")?.precomposed(), title: text, enabled: enabled, label: value, labelStyle: .detailText, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.customize(.video)
|
arguments.customize(.video)
|
||||||
})
|
})
|
||||||
case let .files(_, text, value, enabled):
|
case let .files(_, text, value, enabled):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/MenuIcons/Files")?.precomposed(), title: text, enabled: enabled, label: value, labelStyle: .detailText, sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Files")?.precomposed(), title: text, enabled: enabled, label: value, labelStyle: .detailText, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.customize(.file)
|
arguments.customize(.file)
|
||||||
})
|
})
|
||||||
case let .voiceMessagesInfo(_, text):
|
case let .voiceMessagesInfo(_, text):
|
||||||
|
@ -18,7 +18,7 @@ private final class DataAndStorageControllerArguments {
|
|||||||
let openProxy: () -> Void
|
let openProxy: () -> Void
|
||||||
let openAutomaticDownloadConnectionType: (AutomaticDownloadConnectionType) -> Void
|
let openAutomaticDownloadConnectionType: (AutomaticDownloadConnectionType) -> Void
|
||||||
let resetAutomaticDownload: () -> Void
|
let resetAutomaticDownload: () -> Void
|
||||||
let openVoiceUseLessData: () -> Void
|
let toggleVoiceUseLessData: (Bool) -> Void
|
||||||
let openSaveIncomingPhotos: () -> Void
|
let openSaveIncomingPhotos: () -> Void
|
||||||
let toggleSaveEditedPhotos: (Bool) -> Void
|
let toggleSaveEditedPhotos: (Bool) -> Void
|
||||||
let toggleAutoplayGifs: (Bool) -> Void
|
let toggleAutoplayGifs: (Bool) -> Void
|
||||||
@ -28,13 +28,13 @@ private final class DataAndStorageControllerArguments {
|
|||||||
let openIntents: () -> Void
|
let openIntents: () -> Void
|
||||||
let toggleEnableSensitiveContent: (Bool) -> Void
|
let toggleEnableSensitiveContent: (Bool) -> Void
|
||||||
|
|
||||||
init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, openVoiceUseLessData: @escaping () -> Void, openSaveIncomingPhotos: @escaping () -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, toggleAutoplayGifs: @escaping (Bool) -> Void, toggleAutoplayVideos: @escaping (Bool) -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void, openBrowserSelection: @escaping () -> Void, openIntents: @escaping () -> Void, toggleEnableSensitiveContent: @escaping (Bool) -> Void) {
|
init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, toggleVoiceUseLessData: @escaping (Bool) -> Void, openSaveIncomingPhotos: @escaping () -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, toggleAutoplayGifs: @escaping (Bool) -> Void, toggleAutoplayVideos: @escaping (Bool) -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void, openBrowserSelection: @escaping () -> Void, openIntents: @escaping () -> Void, toggleEnableSensitiveContent: @escaping (Bool) -> Void) {
|
||||||
self.openStorageUsage = openStorageUsage
|
self.openStorageUsage = openStorageUsage
|
||||||
self.openNetworkUsage = openNetworkUsage
|
self.openNetworkUsage = openNetworkUsage
|
||||||
self.openProxy = openProxy
|
self.openProxy = openProxy
|
||||||
self.openAutomaticDownloadConnectionType = openAutomaticDownloadConnectionType
|
self.openAutomaticDownloadConnectionType = openAutomaticDownloadConnectionType
|
||||||
self.resetAutomaticDownload = resetAutomaticDownload
|
self.resetAutomaticDownload = resetAutomaticDownload
|
||||||
self.openVoiceUseLessData = openVoiceUseLessData
|
self.toggleVoiceUseLessData = toggleVoiceUseLessData
|
||||||
self.openSaveIncomingPhotos = openSaveIncomingPhotos
|
self.openSaveIncomingPhotos = openSaveIncomingPhotos
|
||||||
self.toggleSaveEditedPhotos = toggleSaveEditedPhotos
|
self.toggleSaveEditedPhotos = toggleSaveEditedPhotos
|
||||||
self.toggleAutoplayGifs = toggleAutoplayGifs
|
self.toggleAutoplayGifs = toggleAutoplayGifs
|
||||||
@ -49,6 +49,7 @@ private final class DataAndStorageControllerArguments {
|
|||||||
private enum DataAndStorageSection: Int32 {
|
private enum DataAndStorageSection: Int32 {
|
||||||
case usage
|
case usage
|
||||||
case autoDownload
|
case autoDownload
|
||||||
|
case backgroundDownload
|
||||||
case autoPlay
|
case autoPlay
|
||||||
case voiceCalls
|
case voiceCalls
|
||||||
case other
|
case other
|
||||||
@ -79,18 +80,20 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||||||
case automaticDownloadCellular(PresentationTheme, String, String)
|
case automaticDownloadCellular(PresentationTheme, String, String)
|
||||||
case automaticDownloadWifi(PresentationTheme, String, String)
|
case automaticDownloadWifi(PresentationTheme, String, String)
|
||||||
case automaticDownloadReset(PresentationTheme, String, Bool)
|
case automaticDownloadReset(PresentationTheme, String, Bool)
|
||||||
|
case downloadInBackground(PresentationTheme, String, Bool)
|
||||||
|
case downloadInBackgroundInfo(PresentationTheme, String)
|
||||||
|
|
||||||
case autoplayHeader(PresentationTheme, String)
|
case autoplayHeader(PresentationTheme, String)
|
||||||
case autoplayGifs(PresentationTheme, String, Bool)
|
case autoplayGifs(PresentationTheme, String, Bool)
|
||||||
case autoplayVideos(PresentationTheme, String, Bool)
|
case autoplayVideos(PresentationTheme, String, Bool)
|
||||||
case voiceCallsHeader(PresentationTheme, String)
|
case useLessVoiceData(PresentationTheme, String, Bool)
|
||||||
case useLessVoiceData(PresentationTheme, String, String)
|
case useLessVoiceDataInfo(PresentationTheme, String)
|
||||||
case otherHeader(PresentationTheme, String)
|
case otherHeader(PresentationTheme, String)
|
||||||
case shareSheet(PresentationTheme, String)
|
case shareSheet(PresentationTheme, String)
|
||||||
case saveIncomingPhotos(PresentationTheme, String)
|
case saveIncomingPhotos(PresentationTheme, String)
|
||||||
case saveEditedPhotos(PresentationTheme, String, Bool)
|
case saveEditedPhotos(PresentationTheme, String, Bool)
|
||||||
case openLinksIn(PresentationTheme, String, String)
|
case openLinksIn(PresentationTheme, String, String)
|
||||||
case downloadInBackground(PresentationTheme, String, Bool)
|
|
||||||
case downloadInBackgroundInfo(PresentationTheme, String)
|
|
||||||
case connectionHeader(PresentationTheme, String)
|
case connectionHeader(PresentationTheme, String)
|
||||||
case connectionProxy(PresentationTheme, String, String)
|
case connectionProxy(PresentationTheme, String, String)
|
||||||
case enableSensitiveContent(String, Bool)
|
case enableSensitiveContent(String, Bool)
|
||||||
@ -101,11 +104,13 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||||||
return DataAndStorageSection.usage.rawValue
|
return DataAndStorageSection.usage.rawValue
|
||||||
case .automaticDownloadHeader, .automaticDownloadCellular, .automaticDownloadWifi, .automaticDownloadReset:
|
case .automaticDownloadHeader, .automaticDownloadCellular, .automaticDownloadWifi, .automaticDownloadReset:
|
||||||
return DataAndStorageSection.autoDownload.rawValue
|
return DataAndStorageSection.autoDownload.rawValue
|
||||||
|
case .downloadInBackground, .downloadInBackgroundInfo:
|
||||||
|
return DataAndStorageSection.backgroundDownload.rawValue
|
||||||
|
case .useLessVoiceData, .useLessVoiceDataInfo:
|
||||||
|
return DataAndStorageSection.voiceCalls.rawValue
|
||||||
case .autoplayHeader, .autoplayGifs, .autoplayVideos:
|
case .autoplayHeader, .autoplayGifs, .autoplayVideos:
|
||||||
return DataAndStorageSection.autoPlay.rawValue
|
return DataAndStorageSection.autoPlay.rawValue
|
||||||
case .voiceCallsHeader, .useLessVoiceData:
|
case .otherHeader, .shareSheet, .saveIncomingPhotos, .saveEditedPhotos, .openLinksIn:
|
||||||
return DataAndStorageSection.voiceCalls.rawValue
|
|
||||||
case .otherHeader, .shareSheet, .saveIncomingPhotos, .saveEditedPhotos, .openLinksIn, .downloadInBackground, .downloadInBackgroundInfo:
|
|
||||||
return DataAndStorageSection.other.rawValue
|
return DataAndStorageSection.other.rawValue
|
||||||
case .connectionHeader, .connectionProxy:
|
case .connectionHeader, .connectionProxy:
|
||||||
return DataAndStorageSection.connection.rawValue
|
return DataAndStorageSection.connection.rawValue
|
||||||
@ -128,36 +133,36 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||||||
return 4
|
return 4
|
||||||
case .automaticDownloadReset:
|
case .automaticDownloadReset:
|
||||||
return 5
|
return 5
|
||||||
case .autoplayHeader:
|
|
||||||
return 6
|
|
||||||
case .autoplayGifs:
|
|
||||||
return 7
|
|
||||||
case .autoplayVideos:
|
|
||||||
return 8
|
|
||||||
case .voiceCallsHeader:
|
|
||||||
return 9
|
|
||||||
case .useLessVoiceData:
|
|
||||||
return 10
|
|
||||||
case .otherHeader:
|
|
||||||
return 11
|
|
||||||
case .shareSheet:
|
|
||||||
return 12
|
|
||||||
case .saveIncomingPhotos:
|
|
||||||
return 14
|
|
||||||
case .saveEditedPhotos:
|
|
||||||
return 15
|
|
||||||
case .openLinksIn:
|
|
||||||
return 16
|
|
||||||
case .downloadInBackground:
|
case .downloadInBackground:
|
||||||
return 17
|
return 6
|
||||||
case .downloadInBackgroundInfo:
|
case .downloadInBackgroundInfo:
|
||||||
return 18
|
return 7
|
||||||
|
case .useLessVoiceData:
|
||||||
|
return 8
|
||||||
|
case .useLessVoiceDataInfo:
|
||||||
|
return 9
|
||||||
|
case .autoplayHeader:
|
||||||
|
return 10
|
||||||
|
case .autoplayGifs:
|
||||||
|
return 11
|
||||||
|
case .autoplayVideos:
|
||||||
|
return 12
|
||||||
|
case .otherHeader:
|
||||||
|
return 13
|
||||||
|
case .shareSheet:
|
||||||
|
return 14
|
||||||
|
case .saveIncomingPhotos:
|
||||||
|
return 15
|
||||||
|
case .saveEditedPhotos:
|
||||||
|
return 16
|
||||||
|
case .openLinksIn:
|
||||||
|
return 17
|
||||||
case .connectionHeader:
|
case .connectionHeader:
|
||||||
return 19
|
return 18
|
||||||
case .connectionProxy:
|
case .connectionProxy:
|
||||||
return 20
|
return 19
|
||||||
case .enableSensitiveContent:
|
case .enableSensitiveContent:
|
||||||
return 21
|
return 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,14 +222,14 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .voiceCallsHeader(lhsTheme, lhsText):
|
case let .useLessVoiceData(lhsTheme, lhsText, lhsValue):
|
||||||
if case let .voiceCallsHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .useLessVoiceData(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .useLessVoiceData(lhsTheme, lhsText, lhsValue):
|
case let .useLessVoiceDataInfo(lhsTheme, lhsText):
|
||||||
if case let .useLessVoiceData(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
if case let .useLessVoiceDataInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -300,21 +305,21 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||||||
let arguments = arguments as! DataAndStorageControllerArguments
|
let arguments = arguments as! DataAndStorageControllerArguments
|
||||||
switch self {
|
switch self {
|
||||||
case let .storageUsage(_, text):
|
case let .storageUsage(_, text):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/MenuIcons/Storage")?.precomposed(), title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Storage")?.precomposed(), title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openStorageUsage()
|
arguments.openStorageUsage()
|
||||||
})
|
})
|
||||||
case let .networkUsage(_, text):
|
case let .networkUsage(_, text):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/MenuIcons/Network")?.precomposed(), title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Network")?.precomposed(), title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openNetworkUsage()
|
arguments.openNetworkUsage()
|
||||||
})
|
})
|
||||||
case let .automaticDownloadHeader(_, text):
|
case let .automaticDownloadHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .automaticDownloadCellular(_, text, value):
|
case let .automaticDownloadCellular(_, text, value):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/MenuIcons/Cellular")?.precomposed(), title: text, label: value, labelStyle: .detailText, sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Cellular")?.precomposed(), title: text, label: value, labelStyle: .detailText, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openAutomaticDownloadConnectionType(.cellular)
|
arguments.openAutomaticDownloadConnectionType(.cellular)
|
||||||
})
|
})
|
||||||
case let .automaticDownloadWifi(_, text, value):
|
case let .automaticDownloadWifi(_, text, value):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/MenuIcons/WiFi")?.precomposed(), title: text, label: value, labelStyle: .detailText, sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/WiFi")?.precomposed(), title: text, label: value, labelStyle: .detailText, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openAutomaticDownloadConnectionType(.wifi)
|
arguments.openAutomaticDownloadConnectionType(.wifi)
|
||||||
})
|
})
|
||||||
case let .automaticDownloadReset(_, text, enabled):
|
case let .automaticDownloadReset(_, text, enabled):
|
||||||
@ -333,12 +338,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
arguments.toggleAutoplayVideos(value)
|
arguments.toggleAutoplayVideos(value)
|
||||||
}, tag: DataAndStorageEntryTag.autoplayVideos)
|
}, tag: DataAndStorageEntryTag.autoplayVideos)
|
||||||
case let .voiceCallsHeader(_, text):
|
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
|
||||||
case let .useLessVoiceData(_, text, value):
|
case let .useLessVoiceData(_, text, value):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
arguments.openVoiceUseLessData()
|
arguments.toggleVoiceUseLessData(value)
|
||||||
})
|
}, tag: DataAndStorageEntryTag.autoplayVideos)
|
||||||
|
case let .useLessVoiceDataInfo(_, text):
|
||||||
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
case let .otherHeader(_, text):
|
case let .otherHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .shareSheet(_, text):
|
case let .shareSheet(_, text):
|
||||||
@ -418,11 +423,15 @@ private func stringForUseLessDataSetting(_ dataSaving: VoiceCallDataSaving, stri
|
|||||||
|
|
||||||
private func stringForAutoDownloadTypes(strings: PresentationStrings, decimalSeparator: String, photo: Bool, videoSize: Int32?, fileSize: Int32?) -> String {
|
private func stringForAutoDownloadTypes(strings: PresentationStrings, decimalSeparator: String, photo: Bool, videoSize: Int32?, fileSize: Int32?) -> String {
|
||||||
var types: [String] = []
|
var types: [String] = []
|
||||||
if photo {
|
if photo && videoSize == nil {
|
||||||
types.append(strings.ChatSettings_AutoDownloadSettings_TypePhoto)
|
types.append(strings.ChatSettings_AutoDownloadSettings_TypePhoto)
|
||||||
}
|
}
|
||||||
if let videoSize = videoSize {
|
if let videoSize = videoSize {
|
||||||
types.append(strings.ChatSettings_AutoDownloadSettings_TypeVideo(autodownloadDataSizeString(Int64(videoSize), decimalSeparator: decimalSeparator)).string)
|
if photo {
|
||||||
|
types.append(strings.ChatSettings_AutoDownloadSettings_TypeMedia(autodownloadDataSizeString(Int64(videoSize), decimalSeparator: decimalSeparator)).string)
|
||||||
|
} else {
|
||||||
|
types.append(strings.ChatSettings_AutoDownloadSettings_TypeVideo(autodownloadDataSizeString(Int64(videoSize), decimalSeparator: decimalSeparator)).string)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let fileSize = fileSize {
|
if let fileSize = fileSize {
|
||||||
types.append(strings.ChatSettings_AutoDownloadSettings_TypeFile(autodownloadDataSizeString(Int64(fileSize), decimalSeparator: decimalSeparator)).string)
|
types.append(strings.ChatSettings_AutoDownloadSettings_TypeFile(autodownloadDataSizeString(Int64(fileSize), decimalSeparator: decimalSeparator)).string)
|
||||||
@ -476,14 +485,17 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat
|
|||||||
let defaultSettings = MediaAutoDownloadSettings.defaultSettings
|
let defaultSettings = MediaAutoDownloadSettings.defaultSettings
|
||||||
entries.append(.automaticDownloadReset(presentationData.theme, presentationData.strings.ChatSettings_AutoDownloadReset, data.automaticMediaDownloadSettings.cellular != defaultSettings.cellular || data.automaticMediaDownloadSettings.wifi != defaultSettings.wifi))
|
entries.append(.automaticDownloadReset(presentationData.theme, presentationData.strings.ChatSettings_AutoDownloadReset, data.automaticMediaDownloadSettings.cellular != defaultSettings.cellular || data.automaticMediaDownloadSettings.wifi != defaultSettings.wifi))
|
||||||
|
|
||||||
|
entries.append(.downloadInBackground(presentationData.theme, presentationData.strings.ChatSettings_DownloadInBackground, data.automaticMediaDownloadSettings.downloadInBackground))
|
||||||
|
entries.append(.downloadInBackgroundInfo(presentationData.theme, presentationData.strings.ChatSettings_DownloadInBackgroundInfo))
|
||||||
|
|
||||||
|
let dataSaving = effectiveDataSaving(for: data.voiceCallSettings, autodownloadSettings: data.autodownloadSettings)
|
||||||
|
entries.append(.useLessVoiceData(presentationData.theme, presentationData.strings.ChatSettings_UseLessDataForCalls, dataSaving != .never))
|
||||||
|
entries.append(.useLessVoiceDataInfo(presentationData.theme, presentationData.strings.CallSettings_UseLessDataLongDescription))
|
||||||
|
|
||||||
entries.append(.autoplayHeader(presentationData.theme, presentationData.strings.ChatSettings_AutoPlayTitle))
|
entries.append(.autoplayHeader(presentationData.theme, presentationData.strings.ChatSettings_AutoPlayTitle))
|
||||||
entries.append(.autoplayGifs(presentationData.theme, presentationData.strings.ChatSettings_AutoPlayGifs, data.automaticMediaDownloadSettings.autoplayGifs))
|
entries.append(.autoplayGifs(presentationData.theme, presentationData.strings.ChatSettings_AutoPlayGifs, data.automaticMediaDownloadSettings.autoplayGifs))
|
||||||
entries.append(.autoplayVideos(presentationData.theme, presentationData.strings.ChatSettings_AutoPlayVideos, data.automaticMediaDownloadSettings.autoplayVideos))
|
entries.append(.autoplayVideos(presentationData.theme, presentationData.strings.ChatSettings_AutoPlayVideos, data.automaticMediaDownloadSettings.autoplayVideos))
|
||||||
|
|
||||||
entries.append(.voiceCallsHeader(presentationData.theme, presentationData.strings.Settings_CallSettings.uppercased()))
|
|
||||||
let dataSaving = effectiveDataSaving(for: data.voiceCallSettings, autodownloadSettings: data.autodownloadSettings)
|
|
||||||
entries.append(.useLessVoiceData(presentationData.theme, presentationData.strings.CallSettings_UseLessData, stringForUseLessDataSetting(dataSaving, strings: presentationData.strings)))
|
|
||||||
|
|
||||||
entries.append(.otherHeader(presentationData.theme, presentationData.strings.ChatSettings_Other))
|
entries.append(.otherHeader(presentationData.theme, presentationData.strings.ChatSettings_Other))
|
||||||
if #available(iOSApplicationExtension 13.2, iOS 13.2, *) {
|
if #available(iOSApplicationExtension 13.2, iOS 13.2, *) {
|
||||||
entries.append(.shareSheet(presentationData.theme, presentationData.strings.ChatSettings_IntentsSettings))
|
entries.append(.shareSheet(presentationData.theme, presentationData.strings.ChatSettings_IntentsSettings))
|
||||||
@ -491,9 +503,7 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat
|
|||||||
entries.append(.saveIncomingPhotos(presentationData.theme, presentationData.strings.Settings_SaveIncomingPhotos))
|
entries.append(.saveIncomingPhotos(presentationData.theme, presentationData.strings.Settings_SaveIncomingPhotos))
|
||||||
entries.append(.saveEditedPhotos(presentationData.theme, presentationData.strings.Settings_SaveEditedPhotos, data.generatedMediaStoreSettings.storeEditedPhotos))
|
entries.append(.saveEditedPhotos(presentationData.theme, presentationData.strings.Settings_SaveEditedPhotos, data.generatedMediaStoreSettings.storeEditedPhotos))
|
||||||
entries.append(.openLinksIn(presentationData.theme, presentationData.strings.ChatSettings_OpenLinksIn, defaultWebBrowser))
|
entries.append(.openLinksIn(presentationData.theme, presentationData.strings.ChatSettings_OpenLinksIn, defaultWebBrowser))
|
||||||
entries.append(.downloadInBackground(presentationData.theme, presentationData.strings.ChatSettings_DownloadInBackground, data.automaticMediaDownloadSettings.downloadInBackground))
|
|
||||||
entries.append(.downloadInBackgroundInfo(presentationData.theme, presentationData.strings.ChatSettings_DownloadInBackgroundInfo))
|
|
||||||
|
|
||||||
let proxyValue: String
|
let proxyValue: String
|
||||||
if let proxySettings = data.proxySettings, let activeServer = proxySettings.activeServer, proxySettings.enabled {
|
if let proxySettings = data.proxySettings, let activeServer = proxySettings.activeServer, proxySettings.enabled {
|
||||||
switch activeServer.connection {
|
switch activeServer.connection {
|
||||||
@ -608,8 +618,12 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da
|
|||||||
})
|
})
|
||||||
])])
|
])])
|
||||||
presentControllerImpl?(actionSheet, nil)
|
presentControllerImpl?(actionSheet, nil)
|
||||||
}, openVoiceUseLessData: {
|
}, toggleVoiceUseLessData: { value in
|
||||||
pushControllerImpl?(voiceCallDataSavingController(context: context))
|
let _ = updateVoiceCallSettingsSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||||
|
var current = current
|
||||||
|
current.dataSaving = value ? .always : .never
|
||||||
|
return current
|
||||||
|
}).start()
|
||||||
}, openSaveIncomingPhotos: {
|
}, openSaveIncomingPhotos: {
|
||||||
pushControllerImpl?(saveIncomingMediaController(context: context))
|
pushControllerImpl?(saveIncomingMediaController(context: context))
|
||||||
}, toggleSaveEditedPhotos: { value in
|
}, toggleSaveEditedPhotos: { value in
|
||||||
|
@ -0,0 +1,433 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import TelegramPresentationData
|
||||||
|
import TelegramUIPreferences
|
||||||
|
import DeviceAccess
|
||||||
|
import ItemListUI
|
||||||
|
import PresentationDataUtils
|
||||||
|
import AccountContext
|
||||||
|
import AlertUI
|
||||||
|
import PresentationDataUtils
|
||||||
|
import TelegramNotices
|
||||||
|
import NotificationSoundSelectionUI
|
||||||
|
import TelegramStringFormatting
|
||||||
|
import ItemListPeerItem
|
||||||
|
import ItemListPeerActionItem
|
||||||
|
|
||||||
|
private final class NotificationsPeerCategoryControllerArguments {
|
||||||
|
let context: AccountContext
|
||||||
|
let presentController: (ViewController, ViewControllerPresentationArguments?) -> Void
|
||||||
|
let pushController: (ViewController) -> Void
|
||||||
|
let soundSelectionDisposable: MetaDisposable
|
||||||
|
|
||||||
|
let updateEnabled: (Bool) -> Void
|
||||||
|
let updatePreviews: (Bool) -> Void
|
||||||
|
let updateSound: (PeerMessageSound) -> Void
|
||||||
|
|
||||||
|
let addException: () -> Void
|
||||||
|
let openException: (Peer) -> Void
|
||||||
|
let removeAllExceptions: () -> Void
|
||||||
|
let updateRevealedPeerId: (PeerId?) -> Void
|
||||||
|
let removePeer: (Peer) -> Void
|
||||||
|
|
||||||
|
let updatedExceptionMode: (NotificationExceptionMode) -> Void
|
||||||
|
|
||||||
|
init(context: AccountContext, presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, pushController: @escaping (ViewController) -> Void, soundSelectionDisposable: MetaDisposable, updateEnabled: @escaping (Bool) -> Void, updatePreviews: @escaping (Bool) -> Void, updateSound: @escaping (PeerMessageSound) -> Void, addException: @escaping () -> Void, openException: @escaping (Peer) -> Void, removeAllExceptions: @escaping () -> Void, updateRevealedPeerId: @escaping (PeerId?) -> Void, removePeer: @escaping (Peer) -> Void, updatedExceptionMode: @escaping (NotificationExceptionMode) -> Void) {
|
||||||
|
self.context = context
|
||||||
|
self.presentController = presentController
|
||||||
|
self.pushController = pushController
|
||||||
|
self.soundSelectionDisposable = soundSelectionDisposable
|
||||||
|
|
||||||
|
self.updateEnabled = updateEnabled
|
||||||
|
self.updatePreviews = updatePreviews
|
||||||
|
self.updateSound = updateSound
|
||||||
|
|
||||||
|
self.addException = addException
|
||||||
|
self.openException = openException
|
||||||
|
self.removeAllExceptions = removeAllExceptions
|
||||||
|
|
||||||
|
self.updateRevealedPeerId = updateRevealedPeerId
|
||||||
|
self.removePeer = removePeer
|
||||||
|
|
||||||
|
self.updatedExceptionMode = updatedExceptionMode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum NotificationsPeerCategorySection: Int32 {
|
||||||
|
case enable
|
||||||
|
case options
|
||||||
|
case exceptions
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum NotificationsPeerCategoryEntryTag: ItemListItemTag {
|
||||||
|
case enable
|
||||||
|
case previews
|
||||||
|
case sound
|
||||||
|
|
||||||
|
public func isEqual(to other: ItemListItemTag) -> Bool {
|
||||||
|
if let other = other as? NotificationsPeerCategoryEntryTag, self == other {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
||||||
|
case enable(PresentationTheme, String, Bool)
|
||||||
|
case optionsHeader(PresentationTheme, String)
|
||||||
|
case previews(PresentationTheme, String, Bool)
|
||||||
|
case sound(PresentationTheme, String, String, PeerMessageSound)
|
||||||
|
|
||||||
|
case exceptionsHeader(PresentationTheme, String)
|
||||||
|
case addException(PresentationTheme, String)
|
||||||
|
case exception(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, TelegramPeerNotificationSettings, Bool, Bool)
|
||||||
|
case removeAllExceptions(PresentationTheme, String)
|
||||||
|
|
||||||
|
var section: ItemListSectionId {
|
||||||
|
switch self {
|
||||||
|
case .enable:
|
||||||
|
return NotificationsPeerCategorySection.enable.rawValue
|
||||||
|
case .optionsHeader, .previews, .sound:
|
||||||
|
return NotificationsPeerCategorySection.options.rawValue
|
||||||
|
case .exceptionsHeader, .addException, .exception, .removeAllExceptions:
|
||||||
|
return NotificationsPeerCategorySection.exceptions.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stableId: Int32 {
|
||||||
|
switch self {
|
||||||
|
case .enable:
|
||||||
|
return 0
|
||||||
|
case .optionsHeader:
|
||||||
|
return 1
|
||||||
|
case .previews:
|
||||||
|
return 2
|
||||||
|
case .sound:
|
||||||
|
return 3
|
||||||
|
case .exceptionsHeader:
|
||||||
|
return 4
|
||||||
|
case .addException:
|
||||||
|
return 5
|
||||||
|
case let .exception(index, _, _, _, _, _, _, _, _):
|
||||||
|
return 6 + index
|
||||||
|
case .removeAllExceptions:
|
||||||
|
return 100000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tag: ItemListItemTag? {
|
||||||
|
switch self {
|
||||||
|
case .enable:
|
||||||
|
return NotificationsPeerCategoryEntryTag.enable
|
||||||
|
case .previews:
|
||||||
|
return NotificationsPeerCategoryEntryTag.previews
|
||||||
|
case .sound:
|
||||||
|
return NotificationsPeerCategoryEntryTag.sound
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: NotificationsPeerCategoryEntry, rhs: NotificationsPeerCategoryEntry) -> Bool {
|
||||||
|
switch lhs {
|
||||||
|
case let .enable(lhsTheme, lhsText, lhsValue):
|
||||||
|
if case let .enable(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .optionsHeader(lhsTheme, lhsText):
|
||||||
|
if case let .optionsHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .previews(lhsTheme, lhsText, lhsValue):
|
||||||
|
if case let .previews(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .sound(lhsTheme, lhsText, lhsValue, lhsSound):
|
||||||
|
if case let .sound(rhsTheme, rhsText, rhsValue, rhsSound) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsSound == rhsSound {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .exceptionsHeader(lhsTheme, lhsText):
|
||||||
|
if case let .exceptionsHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .addException(lhsTheme, lhsText):
|
||||||
|
if case let .addException(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .exception(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsDisplayNameOrder, lhsPeer, lhsSettings, lhsEditing, lhsRevealed):
|
||||||
|
if case let .exception(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsDisplayNameOrder, rhsPeer, rhsSettings, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsDisplayNameOrder == rhsDisplayNameOrder, arePeersEqual(lhsPeer, rhsPeer), lhsSettings == rhsSettings, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .removeAllExceptions(lhsTheme, lhsText):
|
||||||
|
if case let .removeAllExceptions(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func <(lhs: NotificationsPeerCategoryEntry, rhs: NotificationsPeerCategoryEntry) -> Bool {
|
||||||
|
return lhs.stableId < rhs.stableId
|
||||||
|
}
|
||||||
|
|
||||||
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||||
|
let arguments = arguments as! NotificationsPeerCategoryControllerArguments
|
||||||
|
switch self {
|
||||||
|
case let .enable(_, text, value):
|
||||||
|
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
||||||
|
arguments.updateEnabled(updatedValue)
|
||||||
|
}, tag: self.tag)
|
||||||
|
case let .optionsHeader(_, text):
|
||||||
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
|
case let .previews(_, text, value):
|
||||||
|
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
|
arguments.updatePreviews(value)
|
||||||
|
})
|
||||||
|
case let .sound(_, text, value, _):
|
||||||
|
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||||
|
|
||||||
|
}, tag: self.tag)
|
||||||
|
case let .exceptionsHeader(_, text):
|
||||||
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
|
case let .addException(theme, text):
|
||||||
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addPersonIcon(theme), title: text, sectionId: self.section, height: .generic, color: .accent, editing: false, action: {
|
||||||
|
arguments.addException()
|
||||||
|
})
|
||||||
|
case let .removeAllExceptions(theme, text):
|
||||||
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addPersonIcon(theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: {
|
||||||
|
arguments.removeAllExceptions()
|
||||||
|
})
|
||||||
|
case let .exception(_, _, _, dateTimeFormat, nameDisplayOrder, peer, _, editing, revealed):
|
||||||
|
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: EnginePeer(peer), presence: nil, text: .text("", .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: editing, revealed: revealed), switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: {
|
||||||
|
arguments.openException(peer)
|
||||||
|
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||||
|
arguments.updateRevealedPeerId(peerId)
|
||||||
|
}, removePeer: { peerId in
|
||||||
|
arguments.removePeer(peer)
|
||||||
|
}, hasTopStripe: false, hasTopGroupInset: false, noInsets: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func filteredGlobalSound(_ sound: PeerMessageSound) -> PeerMessageSound {
|
||||||
|
if case .default = sound {
|
||||||
|
return .bundledModern(id: 0)
|
||||||
|
} else {
|
||||||
|
return sound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func notificationsPeerCategoryEntries(category: NotificationsPeerCategory, globalSettings: GlobalNotificationSettingsSet, exceptions: (users: NotificationExceptionMode, groups: NotificationExceptionMode, channels: NotificationExceptionMode), presentationData: PresentationData) -> [NotificationsPeerCategoryEntry] {
|
||||||
|
var entries: [NotificationsPeerCategoryEntry] = []
|
||||||
|
|
||||||
|
let notificationSettings: MessageNotificationSettings
|
||||||
|
switch category {
|
||||||
|
case .privateChat:
|
||||||
|
notificationSettings = globalSettings.privateChats
|
||||||
|
case .group:
|
||||||
|
notificationSettings = globalSettings.groupChats
|
||||||
|
case .channel:
|
||||||
|
notificationSettings = globalSettings.channels
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.append(.enable(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsAlert, notificationSettings.enabled))
|
||||||
|
|
||||||
|
if notificationSettings.enabled || !exceptions.users.isEmpty {
|
||||||
|
entries.append(.optionsHeader(presentationData.theme, presentationData.strings.Notifications_MessageNotifications.uppercased()))
|
||||||
|
entries.append(.previews(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsPreview, notificationSettings.displayPreviews))
|
||||||
|
entries.append(.sound(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsSound, localizedPeerNotificationSoundString(strings: presentationData.strings, sound: filteredGlobalSound(notificationSettings.sound)), filteredGlobalSound(notificationSettings.sound)))
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.append(.exceptionsHeader(presentationData.theme, presentationData.strings.Notifications_MessageNotifications.uppercased()))
|
||||||
|
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum NotificationsPeerCategory {
|
||||||
|
case privateChat
|
||||||
|
case group
|
||||||
|
case channel
|
||||||
|
}
|
||||||
|
|
||||||
|
public func notificationsPeerCategoryController(context: AccountContext, category: NotificationsPeerCategory, exceptionsList: NotificationExceptionsList?, focusOnItemTag: NotificationsPeerCategoryEntryTag? = nil) -> ViewController {
|
||||||
|
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||||
|
var pushControllerImpl: ((ViewController) -> Void)?
|
||||||
|
|
||||||
|
let notificationExceptions: Promise<(users: NotificationExceptionMode, groups: NotificationExceptionMode, channels: NotificationExceptionMode)> = Promise()
|
||||||
|
|
||||||
|
let updateNotificationExceptions:((users: NotificationExceptionMode, groups: NotificationExceptionMode, channels: NotificationExceptionMode)) -> Void = { value in
|
||||||
|
notificationExceptions.set(.single(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
let arguments = NotificationsPeerCategoryControllerArguments(context: context, presentController: { controller, arguments in
|
||||||
|
presentControllerImpl?(controller, arguments)
|
||||||
|
}, pushController: { controller in
|
||||||
|
pushControllerImpl?(controller)
|
||||||
|
}, soundSelectionDisposable: MetaDisposable(), updateEnabled: { value in
|
||||||
|
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||||
|
var settings = settings
|
||||||
|
switch category {
|
||||||
|
case .privateChat:
|
||||||
|
settings.privateChats.enabled = value
|
||||||
|
case .group:
|
||||||
|
settings.groupChats.enabled = value
|
||||||
|
case .channel:
|
||||||
|
settings.channels.enabled = value
|
||||||
|
}
|
||||||
|
return settings
|
||||||
|
}).start()
|
||||||
|
}, updatePreviews: { value in
|
||||||
|
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||||
|
var settings = settings
|
||||||
|
switch category {
|
||||||
|
case .privateChat:
|
||||||
|
settings.privateChats.displayPreviews = value
|
||||||
|
case .group:
|
||||||
|
settings.groupChats.displayPreviews = value
|
||||||
|
case .channel:
|
||||||
|
settings.channels.displayPreviews = value
|
||||||
|
}
|
||||||
|
return settings
|
||||||
|
}).start()
|
||||||
|
}, updateSound: { value in
|
||||||
|
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||||
|
var settings = settings
|
||||||
|
switch category {
|
||||||
|
case .privateChat:
|
||||||
|
settings.privateChats.sound = value
|
||||||
|
case .group:
|
||||||
|
settings.groupChats.sound = value
|
||||||
|
case .channel:
|
||||||
|
settings.channels.sound = value
|
||||||
|
}
|
||||||
|
return settings
|
||||||
|
}).start()
|
||||||
|
}, addException: {
|
||||||
|
|
||||||
|
}, openException: { peer in
|
||||||
|
|
||||||
|
}, removeAllExceptions: {
|
||||||
|
|
||||||
|
}, updateRevealedPeerId: { peerId in
|
||||||
|
|
||||||
|
}, removePeer: { peer in
|
||||||
|
|
||||||
|
}, updatedExceptionMode: { mode in
|
||||||
|
_ = (notificationExceptions.get() |> take(1) |> deliverOnMainQueue).start(next: { (users, groups, channels) in
|
||||||
|
switch mode {
|
||||||
|
case .users:
|
||||||
|
updateNotificationExceptions((mode, groups, channels))
|
||||||
|
case .groups:
|
||||||
|
updateNotificationExceptions((users, mode, channels))
|
||||||
|
case .channels:
|
||||||
|
updateNotificationExceptions((users, groups, mode))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
let sharedData = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings])
|
||||||
|
let preferences = context.account.postbox.preferencesView(keys: [PreferencesKeys.globalNotifications])
|
||||||
|
|
||||||
|
let exceptionsSignal = Signal<NotificationExceptionsList?, NoError>.single(exceptionsList) |> then(context.engine.peers.notificationExceptionsList() |> map(Optional.init))
|
||||||
|
|
||||||
|
notificationExceptions.set(exceptionsSignal |> map { list -> (NotificationExceptionMode, NotificationExceptionMode, NotificationExceptionMode) in
|
||||||
|
var users:[PeerId : NotificationExceptionWrapper] = [:]
|
||||||
|
var groups: [PeerId : NotificationExceptionWrapper] = [:]
|
||||||
|
var channels:[PeerId : NotificationExceptionWrapper] = [:]
|
||||||
|
if let list = list {
|
||||||
|
for (key, value) in list.settings {
|
||||||
|
if let peer = list.peers[key], !peer.debugDisplayTitle.isEmpty, peer.id != context.account.peerId {
|
||||||
|
switch value.muteState {
|
||||||
|
case .default:
|
||||||
|
switch value.messageSound {
|
||||||
|
case .default:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
switch key.namespace {
|
||||||
|
case Namespaces.Peer.CloudUser:
|
||||||
|
users[key] = NotificationExceptionWrapper(settings: value, peer: peer)
|
||||||
|
default:
|
||||||
|
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||||
|
channels[key] = NotificationExceptionWrapper(settings: value, peer: peer)
|
||||||
|
} else {
|
||||||
|
groups[key] = NotificationExceptionWrapper(settings: value, peer: peer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
switch key.namespace {
|
||||||
|
case Namespaces.Peer.CloudUser:
|
||||||
|
users[key] = NotificationExceptionWrapper(settings: value, peer: peer)
|
||||||
|
default:
|
||||||
|
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||||
|
channels[key] = NotificationExceptionWrapper(settings: value, peer: peer)
|
||||||
|
} else {
|
||||||
|
groups[key] = NotificationExceptionWrapper(settings: value, peer: peer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (.users(users), .groups(groups), .channels(channels))
|
||||||
|
})
|
||||||
|
|
||||||
|
let signal = combineLatest(context.sharedContext.presentationData, sharedData, preferences, notificationExceptions.get())
|
||||||
|
|> map { presentationData, sharedData, view, exceptions -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
|
let viewSettings: GlobalNotificationSettingsSet
|
||||||
|
if let settings = view.values[PreferencesKeys.globalNotifications]?.get(GlobalNotificationSettings.self) {
|
||||||
|
viewSettings = settings.effective
|
||||||
|
} else {
|
||||||
|
viewSettings = GlobalNotificationSettingsSet.defaultSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
let entries = notificationsPeerCategoryEntries(category: category, globalSettings: viewSettings, exceptions: exceptions, presentationData: presentationData)
|
||||||
|
|
||||||
|
var index = 0
|
||||||
|
var scrollToItem: ListViewScrollToItem?
|
||||||
|
if let focusOnItemTag = focusOnItemTag {
|
||||||
|
for entry in entries {
|
||||||
|
if entry.tag?.isEqual(to: focusOnItemTag) ?? false {
|
||||||
|
scrollToItem = ListViewScrollToItem(index: index, position: .top(0.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Up)
|
||||||
|
}
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Notifications_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||||
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: entries, style: .blocks, ensureVisibleItemTag: focusOnItemTag, initialScrollToItem: scrollToItem)
|
||||||
|
|
||||||
|
return (controllerState, (listState, arguments))
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = ItemListController(context: context, state: signal)
|
||||||
|
presentControllerImpl = { [weak controller] c, a in
|
||||||
|
controller?.present(c, in: .window(.root), with: a)
|
||||||
|
}
|
||||||
|
pushControllerImpl = { [weak controller] c in
|
||||||
|
(controller?.navigationController as? NavigationController)?.pushViewController(c)
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
@ -287,7 +287,7 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
|||||||
case let .privacyHeader(_, text):
|
case let .privacyHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .blockedPeers(_, text, value):
|
case let .blockedPeers(_, text, value):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/MenuIcons/Blocked")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Blocked")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openBlockedUsers()
|
arguments.openBlockedUsers()
|
||||||
})
|
})
|
||||||
case let .phoneNumberPrivacy(_, text, value):
|
case let .phoneNumberPrivacy(_, text, value):
|
||||||
@ -317,15 +317,15 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
|||||||
arguments.openVoiceCallPrivacy()
|
arguments.openVoiceCallPrivacy()
|
||||||
})
|
})
|
||||||
case let .passcode(_, text, hasFaceId, value):
|
case let .passcode(_, text, hasFaceId, value):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: hasFaceId ? "Settings/MenuIcons/FaceId" : "Settings/MenuIcons/TouchId")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: hasFaceId ? "Settings/Menu/FaceId" : "Settings/Menu/TouchId")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openPasscode()
|
arguments.openPasscode()
|
||||||
})
|
})
|
||||||
case let .twoStepVerification(_, text, value, data):
|
case let .twoStepVerification(_, text, value, data):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/MenuIcons/TwoStepAuth")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/TwoStepAuth")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openTwoStepVerification(data)
|
arguments.openTwoStepVerification(data)
|
||||||
})
|
})
|
||||||
case let .activeSessions(_, text, value):
|
case let .activeSessions(_, text, value):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/MenuIcons/Websites")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Websites")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openActiveSessions()
|
arguments.openActiveSessions()
|
||||||
})
|
})
|
||||||
case let .autoArchiveHeader(text):
|
case let .autoArchiveHeader(text):
|
||||||
|
@ -107,10 +107,10 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
|||||||
private var disabledOverlayNode: ASDisplayNode?
|
private var disabledOverlayNode: ASDisplayNode?
|
||||||
private let maskNode: ASImageNode
|
private let maskNode: ASImageNode
|
||||||
|
|
||||||
|
let iconNode: ASImageNode
|
||||||
private let titleNode: TextNode
|
private let titleNode: TextNode
|
||||||
private let appNode: TextNode
|
private let appNode: TextNode
|
||||||
private let locationNode: TextNode
|
private let locationNode: TextNode
|
||||||
private let labelNode: TextNode
|
|
||||||
|
|
||||||
private let activateArea: AccessibilityAreaNode
|
private let activateArea: AccessibilityAreaNode
|
||||||
|
|
||||||
@ -130,6 +130,10 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
self.maskNode = ASImageNode()
|
self.maskNode = ASImageNode()
|
||||||
|
|
||||||
|
self.iconNode = ASImageNode()
|
||||||
|
self.iconNode.cornerRadius = 7.0
|
||||||
|
self.iconNode.clipsToBounds = true
|
||||||
|
|
||||||
self.titleNode = TextNode()
|
self.titleNode = TextNode()
|
||||||
self.titleNode.isUserInteractionEnabled = false
|
self.titleNode.isUserInteractionEnabled = false
|
||||||
self.titleNode.contentMode = .left
|
self.titleNode.contentMode = .left
|
||||||
@ -144,12 +148,7 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
|||||||
self.locationNode.isUserInteractionEnabled = false
|
self.locationNode.isUserInteractionEnabled = false
|
||||||
self.locationNode.contentMode = .left
|
self.locationNode.contentMode = .left
|
||||||
self.locationNode.contentsScale = UIScreen.main.scale
|
self.locationNode.contentsScale = UIScreen.main.scale
|
||||||
|
|
||||||
self.labelNode = TextNode()
|
|
||||||
self.labelNode.isUserInteractionEnabled = false
|
|
||||||
self.labelNode.contentMode = .left
|
|
||||||
self.labelNode.contentsScale = UIScreen.main.scale
|
|
||||||
|
|
||||||
self.highlightedBackgroundNode = ASDisplayNode()
|
self.highlightedBackgroundNode = ASDisplayNode()
|
||||||
self.highlightedBackgroundNode.isLayerBacked = true
|
self.highlightedBackgroundNode.isLayerBacked = true
|
||||||
|
|
||||||
@ -157,10 +156,10 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||||
|
|
||||||
|
self.addSubnode(self.iconNode)
|
||||||
self.addSubnode(self.titleNode)
|
self.addSubnode(self.titleNode)
|
||||||
self.addSubnode(self.appNode)
|
self.addSubnode(self.appNode)
|
||||||
self.addSubnode(self.locationNode)
|
self.addSubnode(self.locationNode)
|
||||||
self.addSubnode(self.labelNode)
|
|
||||||
|
|
||||||
self.addSubnode(self.activateArea)
|
self.addSubnode(self.activateArea)
|
||||||
}
|
}
|
||||||
@ -169,7 +168,6 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
|||||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||||
let makeAppLayout = TextNode.asyncLayout(self.appNode)
|
let makeAppLayout = TextNode.asyncLayout(self.appNode)
|
||||||
let makeLocationLayout = TextNode.asyncLayout(self.locationNode)
|
let makeLocationLayout = TextNode.asyncLayout(self.locationNode)
|
||||||
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
|
||||||
let editableControlLayout = ItemListEditableControlNode.asyncLayout(self.editableControlNode)
|
let editableControlLayout = ItemListEditableControlNode.asyncLayout(self.editableControlNode)
|
||||||
|
|
||||||
var currentDisabledOverlayNode = self.disabledOverlayNode
|
var currentDisabledOverlayNode = self.disabledOverlayNode
|
||||||
@ -179,8 +177,8 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
|||||||
return { item, params, neighbors in
|
return { item, params, neighbors in
|
||||||
var updatedTheme: PresentationTheme?
|
var updatedTheme: PresentationTheme?
|
||||||
|
|
||||||
let titleFont = Font.medium(floor(item.presentationData.fontSize.itemListBaseFontSize * 15.0 / 17.0))
|
let titleFont = Font.medium(floor(item.presentationData.fontSize.itemListBaseFontSize * 16.0 / 17.0))
|
||||||
let textFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 13.0 / 17.0))
|
let textFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0))
|
||||||
|
|
||||||
let verticalInset: CGFloat = 10.0
|
let verticalInset: CGFloat = 10.0
|
||||||
let titleSpacing: CGFloat = 1.0
|
let titleSpacing: CGFloat = 1.0
|
||||||
@ -193,7 +191,6 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
|||||||
var titleAttributedString: NSAttributedString?
|
var titleAttributedString: NSAttributedString?
|
||||||
var appAttributedString: NSAttributedString?
|
var appAttributedString: NSAttributedString?
|
||||||
var locationAttributedString: NSAttributedString?
|
var locationAttributedString: NSAttributedString?
|
||||||
var labelAttributedString: NSAttributedString?
|
|
||||||
|
|
||||||
let peerRevealOptions: [ItemListRevealOption]
|
let peerRevealOptions: [ItemListRevealOption]
|
||||||
if item.editable && item.enabled {
|
if item.editable && item.enabled {
|
||||||
@ -226,16 +223,18 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
appAttributedString = NSAttributedString(string: appString, font: textFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
|
appAttributedString = NSAttributedString(string: appString, font: textFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||||
locationAttributedString = NSAttributedString(string: "\(item.session.ip) — \(item.session.country)", font: textFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
|
||||||
|
let label: String
|
||||||
if item.session.isCurrent {
|
if item.session.isCurrent {
|
||||||
labelAttributedString = NSAttributedString(string: item.presentationData.strings.Presence_online, font: textFont, textColor: item.presentationData.theme.list.itemAccentColor)
|
label = item.presentationData.strings.Presence_online
|
||||||
} else {
|
} else {
|
||||||
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||||
let dateText = stringForRelativeTimestamp(strings: item.presentationData.strings, relativeTimestamp: item.session.activityDate, relativeTo: timestamp, dateTimeFormat: item.dateTimeFormat)
|
label = stringForRelativeActivityTimestamp(strings: item.presentationData.strings, dateTimeFormat: item.dateTimeFormat, relativeTimestamp: item.session.activityDate, relativeTo: timestamp)
|
||||||
labelAttributedString = NSAttributedString(string: dateText, font: textFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let leftInset: CGFloat = 15.0 + params.leftInset
|
locationAttributedString = NSAttributedString(string: "\(item.session.country) • \(label)", font: textFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
||||||
|
|
||||||
|
let leftInset: CGFloat = 59.0 + params.leftInset
|
||||||
|
|
||||||
var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)?
|
var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)?
|
||||||
|
|
||||||
@ -248,8 +247,7 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
|||||||
editingOffset = 0.0
|
editingOffset = 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: labelAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 16.0 - editingOffset - rightInset - 5.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 16.0 - editingOffset - rightInset - labelLayout.size.width - 5.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
|
||||||
let (appLayout, appApply) = makeAppLayout(TextNodeLayoutArguments(attributedString: appAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (appLayout, appApply) = makeAppLayout(TextNodeLayoutArguments(attributedString: appAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
let (locationLayout, locationApply) = makeLocationLayout(TextNodeLayoutArguments(attributedString: locationAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (locationLayout, locationApply) = makeLocationLayout(TextNodeLayoutArguments(attributedString: locationAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
@ -364,7 +362,6 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = labelApply()
|
|
||||||
let _ = titleApply()
|
let _ = titleApply()
|
||||||
let _ = appApply()
|
let _ = appApply()
|
||||||
let _ = locationApply()
|
let _ = locationApply()
|
||||||
@ -412,7 +409,7 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
|||||||
transition.updateFrame(node: strongSelf.topStripeNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)))
|
transition.updateFrame(node: strongSelf.topStripeNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)))
|
||||||
transition.updateFrame(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)))
|
transition.updateFrame(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)))
|
||||||
|
|
||||||
transition.updateFrame(node: strongSelf.labelNode, frame: CGRect(origin: CGPoint(x: revealOffset + params.width - labelLayout.size.width - 15.0 - rightInset, y: verticalInset), size: labelLayout.size))
|
transition.updateFrame(node: strongSelf.iconNode, frame: CGRect(origin: CGPoint(x: params.leftInset + revealOffset + editingOffset + 16.0, y: 12.0), size: CGSize(width: 30.0, height: 30.0)))
|
||||||
transition.updateFrame(node: strongSelf.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: verticalInset), size: titleLayout.size))
|
transition.updateFrame(node: strongSelf.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: verticalInset), size: titleLayout.size))
|
||||||
transition.updateFrame(node: strongSelf.appNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: strongSelf.titleNode.frame.maxY + titleSpacing), size: appLayout.size))
|
transition.updateFrame(node: strongSelf.appNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: strongSelf.titleNode.frame.maxY + titleSpacing), size: appLayout.size))
|
||||||
transition.updateFrame(node: strongSelf.locationNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: strongSelf.appNode.frame.maxY + textSpacing), size: locationLayout.size))
|
transition.updateFrame(node: strongSelf.locationNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: strongSelf.appNode.frame.maxY + textSpacing), size: locationLayout.size))
|
||||||
@ -455,7 +452,7 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
|||||||
editingOffset = 0.0
|
editingOffset = 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
transition.updateFrame(node: self.labelNode, frame: CGRect(origin: CGPoint(x: revealOffset + params.width - params.rightInset - self.labelNode.bounds.size.width - 15.0, y: self.labelNode.frame.minY), size: self.labelNode.bounds.size))
|
transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(x: params.leftInset + self.revealOffset + editingOffset + 16.0, y: self.iconNode.frame.minY), size: self.iconNode.bounds.size))
|
||||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: self.titleNode.frame.minY), size: self.titleNode.bounds.size))
|
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: self.titleNode.frame.minY), size: self.titleNode.bounds.size))
|
||||||
transition.updateFrame(node: self.appNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: self.appNode.frame.minY), size: self.appNode.bounds.size))
|
transition.updateFrame(node: self.appNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: self.appNode.frame.minY), size: self.appNode.bounds.size))
|
||||||
transition.updateFrame(node: self.locationNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: self.locationNode.frame.minY), size: self.locationNode.bounds.size))
|
transition.updateFrame(node: self.locationNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: self.locationNode.frame.minY), size: self.locationNode.bounds.size))
|
||||||
|
@ -31,8 +31,7 @@ struct ItemListWebsiteItemEditing: Equatable {
|
|||||||
|
|
||||||
final class ItemListWebsiteItem: ListViewItem, ItemListItem {
|
final class ItemListWebsiteItem: ListViewItem, ItemListItem {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let theme: PresentationTheme
|
let presentationData: ItemListPresentationData
|
||||||
let strings: PresentationStrings
|
|
||||||
let dateTimeFormat: PresentationDateTimeFormat
|
let dateTimeFormat: PresentationDateTimeFormat
|
||||||
let nameDisplayOrder: PresentationPersonNameOrder
|
let nameDisplayOrder: PresentationPersonNameOrder
|
||||||
let website: WebAuthorization
|
let website: WebAuthorization
|
||||||
@ -44,10 +43,9 @@ final class ItemListWebsiteItem: ListViewItem, ItemListItem {
|
|||||||
let setSessionIdWithRevealedOptions: (Int64?, Int64?) -> Void
|
let setSessionIdWithRevealedOptions: (Int64?, Int64?) -> Void
|
||||||
let removeSession: (Int64) -> Void
|
let removeSession: (Int64) -> Void
|
||||||
|
|
||||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool, sectionId: ItemListSectionId, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void) {
|
init(context: AccountContext, presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool, sectionId: ItemListSectionId, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.presentationData = presentationData
|
||||||
self.strings = strings
|
|
||||||
self.dateTimeFormat = dateTimeFormat
|
self.dateTimeFormat = dateTimeFormat
|
||||||
self.nameDisplayOrder = nameDisplayOrder
|
self.nameDisplayOrder = nameDisplayOrder
|
||||||
self.website = website
|
self.website = website
|
||||||
@ -99,9 +97,7 @@ final class ItemListWebsiteItem: ListViewItem, ItemListItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let avatarFont = avatarPlaceholderFont(size: 9.0)
|
private let avatarFont = avatarPlaceholderFont(size: 11.0)
|
||||||
private let titleFont = Font.medium(15.0)
|
|
||||||
private let textFont = Font.regular(13.0)
|
|
||||||
|
|
||||||
class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode {
|
class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode {
|
||||||
private let backgroundNode: ASDisplayNode
|
private let backgroundNode: ASDisplayNode
|
||||||
@ -134,6 +130,8 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode {
|
|||||||
self.maskNode = ASImageNode()
|
self.maskNode = ASImageNode()
|
||||||
|
|
||||||
self.avatarNode = AvatarNode(font: avatarFont)
|
self.avatarNode = AvatarNode(font: avatarFont)
|
||||||
|
self.avatarNode.cornerRadius = 7.0
|
||||||
|
self.avatarNode.clipsToBounds = true
|
||||||
|
|
||||||
self.titleNode = TextNode()
|
self.titleNode = TextNode()
|
||||||
self.titleNode.isUserInteractionEnabled = false
|
self.titleNode.isUserInteractionEnabled = false
|
||||||
@ -171,7 +169,6 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode {
|
|||||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||||
let makeAppLayout = TextNode.asyncLayout(self.appNode)
|
let makeAppLayout = TextNode.asyncLayout(self.appNode)
|
||||||
let makeLocationLayout = TextNode.asyncLayout(self.locationNode)
|
let makeLocationLayout = TextNode.asyncLayout(self.locationNode)
|
||||||
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
|
||||||
let editableControlLayout = ItemListEditableControlNode.asyncLayout(self.editableControlNode)
|
let editableControlLayout = ItemListEditableControlNode.asyncLayout(self.editableControlNode)
|
||||||
|
|
||||||
var currentDisabledOverlayNode = self.disabledOverlayNode
|
var currentDisabledOverlayNode = self.disabledOverlayNode
|
||||||
@ -181,21 +178,23 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode {
|
|||||||
return { item, params, neighbors in
|
return { item, params, neighbors in
|
||||||
var updatedTheme: PresentationTheme?
|
var updatedTheme: PresentationTheme?
|
||||||
|
|
||||||
if currentItem?.theme !== item.theme {
|
let titleFont = Font.medium(floor(item.presentationData.fontSize.itemListBaseFontSize * 16.0 / 17.0))
|
||||||
updatedTheme = item.theme
|
let textFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0))
|
||||||
|
|
||||||
|
if currentItem?.presentationData !== item.presentationData.theme {
|
||||||
|
updatedTheme = item.presentationData.theme
|
||||||
}
|
}
|
||||||
|
|
||||||
var titleAttributedString: NSAttributedString?
|
var titleAttributedString: NSAttributedString?
|
||||||
var appAttributedString: NSAttributedString?
|
var appAttributedString: NSAttributedString?
|
||||||
var locationAttributedString: NSAttributedString?
|
var locationAttributedString: NSAttributedString?
|
||||||
var labelAttributedString: NSAttributedString?
|
|
||||||
|
|
||||||
let peerRevealOptions = [ItemListRevealOption(key: 0, title: item.strings.AuthSessions_LogOut, icon: .none, color: item.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.theme.list.itemDisclosureActions.destructive.foregroundColor)]
|
let peerRevealOptions = [ItemListRevealOption(key: 0, title: item.presentationData.strings.AuthSessions_LogOut, icon: .none, color: item.presentationData.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.presentationData.theme.list.itemDisclosureActions.destructive.foregroundColor)]
|
||||||
|
|
||||||
let rightInset: CGFloat = params.rightInset
|
let rightInset: CGFloat = params.rightInset
|
||||||
|
|
||||||
if let user = item.peer as? TelegramUser {
|
if let user = item.peer as? TelegramUser {
|
||||||
titleAttributedString = NSAttributedString(string: EnginePeer(user).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), font: titleFont, textColor: item.theme.list.itemPrimaryTextColor)
|
titleAttributedString = NSAttributedString(string: EnginePeer(user).displayTitle(strings: item.presentationData.strings, displayOrder: item.nameDisplayOrder), font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
var appString = ""
|
var appString = ""
|
||||||
@ -217,28 +216,27 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode {
|
|||||||
appString += item.website.platform
|
appString += item.website.platform
|
||||||
}
|
}
|
||||||
|
|
||||||
appAttributedString = NSAttributedString(string: appString, font: textFont, textColor: item.theme.list.itemPrimaryTextColor)
|
appAttributedString = NSAttributedString(string: appString, font: textFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||||
locationAttributedString = NSAttributedString(string: "\(item.website.ip) — \(item.website.region)", font: textFont, textColor: item.theme.list.itemSecondaryTextColor)
|
|
||||||
|
|
||||||
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
|
||||||
let dateText = stringForRelativeTimestamp(strings: item.strings, relativeTimestamp: item.website.dateActive, relativeTo: timestamp, dateTimeFormat: item.dateTimeFormat)
|
|
||||||
labelAttributedString = NSAttributedString(string: dateText, font: textFont, textColor: item.theme.list.itemSecondaryTextColor)
|
|
||||||
|
|
||||||
let leftInset: CGFloat = 15.0 + params.leftInset
|
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||||
|
let label = stringForRelativeActivityTimestamp(strings: item.presentationData.strings, dateTimeFormat: item.dateTimeFormat, relativeTimestamp: item.website.dateActive, relativeTo: timestamp)
|
||||||
|
|
||||||
|
locationAttributedString = NSAttributedString(string: "\(item.website.region) • \(label)", font: textFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
||||||
|
|
||||||
|
let leftInset: CGFloat = 59.0 + params.leftInset
|
||||||
|
|
||||||
var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)?
|
var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)?
|
||||||
|
|
||||||
let editingOffset: CGFloat
|
let editingOffset: CGFloat
|
||||||
if item.editing {
|
if item.editing {
|
||||||
let sizeAndApply = editableControlLayout(item.theme, false)
|
let sizeAndApply = editableControlLayout(item.presentationData.theme, false)
|
||||||
editableControlSizeAndApply = sizeAndApply
|
editableControlSizeAndApply = sizeAndApply
|
||||||
editingOffset = sizeAndApply.0
|
editingOffset = sizeAndApply.0
|
||||||
} else {
|
} else {
|
||||||
editingOffset = 0.0
|
editingOffset = 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: labelAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - editingOffset - rightInset - 5.0 - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - editingOffset - rightInset - labelLayout.size.width - 5.0 - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
|
||||||
let (appLayout, appApply) = makeAppLayout(TextNodeLayoutArguments(attributedString: appAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (appLayout, appApply) = makeAppLayout(TextNodeLayoutArguments(attributedString: appAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
let (locationLayout, locationApply) = makeLocationLayout(TextNodeLayoutArguments(attributedString: locationAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (locationLayout, locationApply) = makeLocationLayout(TextNodeLayoutArguments(attributedString: locationAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
@ -263,14 +261,14 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode {
|
|||||||
strongSelf.layoutParams = (item, params, neighbors)
|
strongSelf.layoutParams = (item, params, neighbors)
|
||||||
|
|
||||||
if let _ = updatedTheme {
|
if let _ = updatedTheme {
|
||||||
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
strongSelf.topStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor
|
||||||
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
strongSelf.bottomStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor
|
||||||
strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor
|
||||||
strongSelf.highlightedBackgroundNode.backgroundColor = item.theme.list.itemHighlightedBackgroundColor
|
strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor
|
||||||
}
|
}
|
||||||
|
|
||||||
if let peer = item.peer {
|
if let peer = item.peer {
|
||||||
strongSelf.avatarNode.setPeer(context: item.context, theme: item.theme, peer: EnginePeer(peer), authorOfMessage: nil, overrideImage: nil, emptyColor: nil, clipStyle: .none, synchronousLoad: false)
|
strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(peer), authorOfMessage: nil, overrideImage: nil, emptyColor: nil, clipStyle: .none, synchronousLoad: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
let revealOffset = strongSelf.revealOffset
|
let revealOffset = strongSelf.revealOffset
|
||||||
@ -329,7 +327,6 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = labelApply()
|
|
||||||
let _ = titleApply()
|
let _ = titleApply()
|
||||||
let _ = appApply()
|
let _ = appApply()
|
||||||
let _ = locationApply()
|
let _ = locationApply()
|
||||||
@ -370,7 +367,7 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode {
|
|||||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||||
|
|
||||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
@ -378,9 +375,9 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode {
|
|||||||
transition.updateFrame(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)))
|
transition.updateFrame(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)))
|
||||||
|
|
||||||
|
|
||||||
transition.updateFrame(node: strongSelf.avatarNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset + 1.0, y: 12.0), size: CGSize(width: 13.0, height: 13.0)))
|
transition.updateFrame(node: strongSelf.avatarNode, frame: CGRect(origin: CGPoint(x: params.leftInset + revealOffset + editingOffset + 16.0, y: 12.0), size: CGSize(width: 30.0, height: 30.0)))
|
||||||
transition.updateFrame(node: strongSelf.labelNode, frame: CGRect(origin: CGPoint(x: revealOffset + params.width - labelLayout.size.width - 15.0 - rightInset, y: 10.0), size: labelLayout.size))
|
|
||||||
transition.updateFrame(node: strongSelf.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset + 20.0, y: 10.0), size: titleLayout.size))
|
transition.updateFrame(node: strongSelf.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: 10.0), size: titleLayout.size))
|
||||||
transition.updateFrame(node: strongSelf.appNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: 30.0), size: appLayout.size))
|
transition.updateFrame(node: strongSelf.appNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: 30.0), size: appLayout.size))
|
||||||
transition.updateFrame(node: strongSelf.locationNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: 50.0), size: locationLayout.size))
|
transition.updateFrame(node: strongSelf.locationNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: 50.0), size: locationLayout.size))
|
||||||
|
|
||||||
@ -422,7 +419,7 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode {
|
|||||||
editingOffset = 0.0
|
editingOffset = 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: leftInset + self.revealOffset + editingOffset + 1.0, y: self.avatarNode.frame.minY), size: self.avatarNode.bounds.size))
|
transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: params.leftInset + self.revealOffset + editingOffset + 16.0, y: self.avatarNode.frame.minY), size: self.avatarNode.bounds.size))
|
||||||
transition.updateFrame(node: self.labelNode, frame: CGRect(origin: CGPoint(x: self.revealOffset + params.width - params.rightInset - self.labelNode.bounds.size.width - 15.0, y: self.labelNode.frame.minY), size: self.labelNode.bounds.size))
|
transition.updateFrame(node: self.labelNode, frame: CGRect(origin: CGPoint(x: self.revealOffset + params.width - params.rightInset - self.labelNode.bounds.size.width - 15.0, y: self.labelNode.frame.minY), size: self.labelNode.bounds.size))
|
||||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + self.revealOffset + editingOffset + 20.0, y: self.titleNode.frame.minY), size: self.titleNode.bounds.size))
|
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + self.revealOffset + editingOffset + 20.0, y: self.titleNode.frame.minY), size: self.titleNode.bounds.size))
|
||||||
transition.updateFrame(node: self.appNode, frame: CGRect(origin: CGPoint(x: leftInset + self.revealOffset + editingOffset, y: self.appNode.frame.minY), size: self.appNode.bounds.size))
|
transition.updateFrame(node: self.appNode, frame: CGRect(origin: CGPoint(x: leftInset + self.revealOffset + editingOffset, y: self.appNode.frame.minY), size: self.appNode.bounds.size))
|
||||||
|
@ -280,8 +280,8 @@ private enum RecentSessionsEntry: ItemListNodeEntry {
|
|||||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.blockDestructiveIcon(theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: {
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.blockDestructiveIcon(theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: {
|
||||||
arguments.terminateAllWebSessions()
|
arguments.terminateAllWebSessions()
|
||||||
})
|
})
|
||||||
case let .currentAddDevice(_, text):
|
case let .currentAddDevice(theme, text):
|
||||||
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addDeviceIcon(theme), title: text, sectionId: self.section, height: .generic, color: .accent, editing: false, action: {
|
||||||
arguments.addDevice()
|
arguments.addDevice()
|
||||||
})
|
})
|
||||||
case let .currentSessionInfo(_, text):
|
case let .currentSessionInfo(_, text):
|
||||||
@ -303,8 +303,8 @@ private enum RecentSessionsEntry: ItemListNodeEntry {
|
|||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
case let .otherSessionsHeader(_, text):
|
case let .otherSessionsHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .addDevice(_, text):
|
case let .addDevice(theme, text):
|
||||||
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addDeviceIcon(theme), title: text, sectionId: self.section, height: .generic, color: .accent, editing: false, action: {
|
||||||
arguments.addDevice()
|
arguments.addDevice()
|
||||||
})
|
})
|
||||||
case let .session(_, _, _, dateTimeFormat, session, enabled, editing, revealed):
|
case let .session(_, _, _, dateTimeFormat, session, enabled, editing, revealed):
|
||||||
@ -313,8 +313,8 @@ private enum RecentSessionsEntry: ItemListNodeEntry {
|
|||||||
}, removeSession: { id in
|
}, removeSession: { id in
|
||||||
arguments.removeSession(id)
|
arguments.removeSession(id)
|
||||||
})
|
})
|
||||||
case let .website(_, theme, strings, dateTimeFormat, nameDisplayOrder, website, peer, enabled, editing, revealed):
|
case let .website(_, _, _, dateTimeFormat, nameDisplayOrder, website, peer, enabled, editing, revealed):
|
||||||
return ItemListWebsiteItem(context: arguments.context, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, website: website, peer: peer, enabled: enabled, editing: editing, revealed: revealed, sectionId: self.section, setSessionIdWithRevealedOptions: { previousId, id in
|
return ItemListWebsiteItem(context: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, website: website, peer: peer, enabled: enabled, editing: editing, revealed: revealed, sectionId: self.section, setSessionIdWithRevealedOptions: { previousId, id in
|
||||||
arguments.setSessionIdWithRevealedOptions(previousId, id)
|
arguments.setSessionIdWithRevealedOptions(previousId, id)
|
||||||
}, removeSession: { id in
|
}, removeSession: { id in
|
||||||
arguments.removeWebSession(id)
|
arguments.removeWebSession(id)
|
||||||
|
@ -126,7 +126,7 @@ class BubbleSettingsRadiusItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
sliderView.enablePanHandling = true
|
sliderView.enablePanHandling = true
|
||||||
sliderView.trackCornerRadius = 2.0
|
sliderView.trackCornerRadius = 2.0
|
||||||
sliderView.lineSize = 4.0
|
sliderView.lineSize = 4.0
|
||||||
sliderView.dotSize = 5.0
|
sliderView.dotSize = 8.0
|
||||||
sliderView.minimumValue = 0.0
|
sliderView.minimumValue = 0.0
|
||||||
sliderView.maximumValue = 4.0
|
sliderView.maximumValue = 4.0
|
||||||
sliderView.startValue = 0.0
|
sliderView.startValue = 0.0
|
||||||
|
@ -123,8 +123,8 @@ class ThemeSettingsFontSizeItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
sliderView.enablePanHandling = true
|
sliderView.enablePanHandling = true
|
||||||
sliderView.enablePanHandling = true
|
sliderView.enablePanHandling = true
|
||||||
sliderView.trackCornerRadius = 1.0
|
sliderView.trackCornerRadius = 1.0
|
||||||
sliderView.lineSize = 2.0
|
sliderView.lineSize = 4.0
|
||||||
sliderView.dotSize = 5.0
|
sliderView.dotSize = 8.0
|
||||||
sliderView.minimumValue = 0.0
|
sliderView.minimumValue = 0.0
|
||||||
sliderView.maximumValue = 6.0
|
sliderView.maximumValue = 6.0
|
||||||
sliderView.startValue = 0.0
|
sliderView.startValue = 0.0
|
||||||
|
@ -62,6 +62,7 @@ public enum PresentationResourceKey: Int32 {
|
|||||||
case itemListKnob
|
case itemListKnob
|
||||||
case itemListBlockAccentIcon
|
case itemListBlockAccentIcon
|
||||||
case itemListBlockDestructiveIcon
|
case itemListBlockDestructiveIcon
|
||||||
|
case itemListAddDeviceIcon
|
||||||
|
|
||||||
case itemListVoiceCallIcon
|
case itemListVoiceCallIcon
|
||||||
case itemListVideoCallIcon
|
case itemListVideoCallIcon
|
||||||
|
@ -210,6 +210,12 @@ public struct PresentationResourcesItemList {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func addDeviceIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||||
|
return theme.image(PresentationResourceKey.itemListAddDeviceIcon.rawValue, { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.list.itemAccentColor)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
public static func cornersImage(_ theme: PresentationTheme, top: Bool, bottom: Bool) -> UIImage? {
|
public static func cornersImage(_ theme: PresentationTheme, top: Bool, bottom: Bool) -> UIImage? {
|
||||||
if !top && !bottom {
|
if !top && !bottom {
|
||||||
return nil
|
return nil
|
||||||
|
@ -23,19 +23,19 @@ private func renderIcon(name: String) -> UIImage? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public struct PresentationResourcesSettings {
|
public struct PresentationResourcesSettings {
|
||||||
public static let editProfile = renderIcon(name: "Settings/MenuIcons/EditProfile")
|
public static let editProfile = renderIcon(name: "Settings/Menu/EditProfile")
|
||||||
public static let proxy = renderIcon(name: "Settings/MenuIcons/Proxy")
|
public static let proxy = renderIcon(name: "Settings/Menu/Proxy")
|
||||||
public static let savedMessages = renderIcon(name: "Settings/MenuIcons/SavedMessages")
|
public static let savedMessages = renderIcon(name: "Settings/Menu/SavedMessages")
|
||||||
public static let recentCalls = renderIcon(name: "Settings/MenuIcons/RecentCalls")
|
public static let recentCalls = renderIcon(name: "Settings/Menu/RecentCalls")
|
||||||
public static let devices = renderIcon(name: "Settings/MenuIcons/Sessions")
|
public static let devices = renderIcon(name: "Settings/Menu/Sessions")
|
||||||
public static let chatFolders = renderIcon(name: "Settings/MenuIcons/ChatListFilters")
|
public static let chatFolders = renderIcon(name: "Settings/Menu/ChatListFilters")
|
||||||
public static let stickers = renderIcon(name: "Settings/MenuIcons/Stickers")
|
public static let stickers = renderIcon(name: "Settings/Menu/Stickers")
|
||||||
|
|
||||||
public static let notifications = renderIcon(name: "Settings/MenuIcons/Notifications")
|
public static let notifications = renderIcon(name: "Settings/Menu/Notifications")
|
||||||
public static let security = renderIcon(name: "Settings/MenuIcons/Security")
|
public static let security = renderIcon(name: "Settings/Menu/Security")
|
||||||
public static let dataAndStorage = renderIcon(name: "Settings/MenuIcons/DataAndStorage")
|
public static let dataAndStorage = renderIcon(name: "Settings/Menu/DataAndStorage")
|
||||||
public static let appearance = renderIcon(name: "Settings/MenuIcons/Appearance")
|
public static let appearance = renderIcon(name: "Settings/Menu/Appearance")
|
||||||
public static let language = renderIcon(name: "Settings/MenuIcons/Language")
|
public static let language = renderIcon(name: "Settings/Menu/Language")
|
||||||
|
|
||||||
public static let wallet = generateImage(CGSize(width: 29.0, height: 29.0), contextGenerator: { size, context in
|
public static let wallet = generateImage(CGSize(width: 29.0, height: 29.0), contextGenerator: { size, context in
|
||||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
@ -44,24 +44,24 @@ public struct PresentationResourcesSettings {
|
|||||||
context.setFillColor(UIColor.white.cgColor)
|
context.setFillColor(UIColor.white.cgColor)
|
||||||
context.fill(bounds.insetBy(dx: 5.0, dy: 5.0))
|
context.fill(bounds.insetBy(dx: 5.0, dy: 5.0))
|
||||||
|
|
||||||
if let image = generateTintedImage(image: UIImage(bundleImageName: "Settings/MenuIcons/Wallet"), color: UIColor(rgb: 0x1b1b1c))?.cgImage {
|
if let image = generateTintedImage(image: UIImage(bundleImageName: "Settings/Menu/Wallet"), color: UIColor(rgb: 0x1b1b1c))?.cgImage {
|
||||||
context.draw(image, in: bounds)
|
context.draw(image, in: bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
drawBorder(context: context, rect: bounds)
|
drawBorder(context: context, rect: bounds)
|
||||||
})
|
})
|
||||||
|
|
||||||
public static let passport = renderIcon(name: "Settings/MenuIcons/Passport")
|
public static let passport = renderIcon(name: "Settings/Menu/Passport")
|
||||||
public static let watch = renderIcon(name: "Settings/MenuIcons/Watch")
|
public static let watch = renderIcon(name: "Settings/Menu/Watch")
|
||||||
|
|
||||||
public static let support = renderIcon(name: "Settings/MenuIcons/Support")
|
public static let support = renderIcon(name: "Settings/Menu/Support")
|
||||||
public static let faq = renderIcon(name: "Settings/MenuIcons/Faq")
|
public static let faq = renderIcon(name: "Settings/Menu/Faq")
|
||||||
public static let tips = renderIcon(name: "Settings/MenuIcons/Tips")
|
public static let tips = renderIcon(name: "Settings/Menu/Tips")
|
||||||
|
|
||||||
public static let addAccount = renderIcon(name: "Settings/MenuIcons/AddAccount")
|
public static let addAccount = renderIcon(name: "Settings/Menu/AddAccount")
|
||||||
public static let setPasscode = renderIcon(name: "Settings/MenuIcons/SetPasscode")
|
public static let setPasscode = renderIcon(name: "Settings/Menu/SetPasscode")
|
||||||
public static let clearCache = renderIcon(name: "Settings/MenuIcons/ClearCache")
|
public static let clearCache = renderIcon(name: "Settings/Menu/ClearCache")
|
||||||
public static let changePhoneNumber = renderIcon(name: "Settings/MenuIcons/ChangePhoneNumber")
|
public static let changePhoneNumber = renderIcon(name: "Settings/Menu/ChangePhoneNumber")
|
||||||
|
|
||||||
public static let websites = renderIcon(name: "Settings/MenuIcons/Websites")
|
public static let websites = renderIcon(name: "Settings/Menu/Websites")
|
||||||
}
|
}
|
||||||
|
@ -345,6 +345,42 @@ public func stringForRelativeLiveLocationUpdateTimestamp(strings: PresentationSt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func stringForRelativeActivityTimestamp(strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, relativeTimestamp: Int32, relativeTo timestamp: Int32) -> String {
|
||||||
|
let difference = timestamp - relativeTimestamp
|
||||||
|
if difference < 60 {
|
||||||
|
return strings.Time_JustNow
|
||||||
|
} else if difference < 60 * 60 {
|
||||||
|
let minutes = difference / 60
|
||||||
|
return strings.Time_MinutesAgo(minutes)
|
||||||
|
} else {
|
||||||
|
var t: time_t = time_t(relativeTimestamp)
|
||||||
|
var timeinfo: tm = tm()
|
||||||
|
localtime_r(&t, &timeinfo)
|
||||||
|
|
||||||
|
var now: time_t = time_t(timestamp)
|
||||||
|
var timeinfoNow: tm = tm()
|
||||||
|
localtime_r(&now, &timeinfoNow)
|
||||||
|
|
||||||
|
if timeinfo.tm_year != timeinfoNow.tm_year {
|
||||||
|
return strings.Time_AtDate(stringForTimestamp(day: timeinfo.tm_mday, month: timeinfo.tm_mon + 1, year: timeinfo.tm_year, dateTimeFormat: dateTimeFormat)).string
|
||||||
|
}
|
||||||
|
|
||||||
|
let dayDifference = timeinfo.tm_yday - timeinfoNow.tm_yday
|
||||||
|
if dayDifference == 0 || dayDifference == -1 {
|
||||||
|
let day: RelativeTimestampFormatDay
|
||||||
|
if dayDifference == 0 {
|
||||||
|
let minutes = difference / (60 * 60)
|
||||||
|
return strings.Time_HoursAgo(minutes)
|
||||||
|
} else {
|
||||||
|
day = .yesterday
|
||||||
|
}
|
||||||
|
return humanReadableStringForTimestamp(strings: strings, day: day, dateTimeFormat: dateTimeFormat, hours: timeinfo.tm_hour, minutes: timeinfo.tm_min).string
|
||||||
|
} else {
|
||||||
|
return strings.Time_AtDate(stringForTimestamp(day: timeinfo.tm_mday, month: timeinfo.tm_mon + 1, year: timeinfo.tm_year, dateTimeFormat: dateTimeFormat)).string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func stringAndActivityForUserPresence(strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, presence: EnginePeer.Presence, relativeTo timestamp: Int32, expanded: Bool = false) -> (String, Bool) {
|
public func stringAndActivityForUserPresence(strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, presence: EnginePeer.Presence, relativeTo timestamp: Int32, expanded: Bool = false) -> (String, Bool) {
|
||||||
switch presence.status {
|
switch presence.status {
|
||||||
case let .present(statusTimestamp):
|
case let .present(statusTimestamp):
|
||||||
|
12
submodules/TelegramUI/Images.xcassets/Settings/Devices/Android.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Icon-31.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
122
submodules/TelegramUI/Images.xcassets/Settings/Devices/Android.imageset/Icon-31.pdf
vendored
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
%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 0.000000 0.000000 cm
|
||||||
|
0.203922 0.780392 0.349020 scn
|
||||||
|
0.000000 18.799999 m
|
||||||
|
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
|
||||||
|
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
|
||||||
|
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
|
||||||
|
18.799999 30.000000 l
|
||||||
|
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
|
||||||
|
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
|
||||||
|
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
|
||||||
|
30.000000 11.200001 l
|
||||||
|
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
|
||||||
|
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
|
||||||
|
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
|
||||||
|
11.200000 0.000000 l
|
||||||
|
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||||
|
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||||
|
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
|
||||||
|
0.000000 18.799999 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 4.123901 8.382080 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
4.601601 14.520930 m
|
||||||
|
4.378984 14.921641 3.873677 15.066014 3.472967 14.843397 c
|
||||||
|
3.072257 14.620780 2.927883 14.115474 3.150500 13.714764 c
|
||||||
|
5.004922 10.376803 l
|
||||||
|
2.789933 8.917612 1.109708 6.612994 0.339578 3.738848 c
|
||||||
|
0.004541 2.488480 -0.162977 1.863297 0.219400 1.161238 c
|
||||||
|
0.304677 1.004665 0.468011 0.791803 0.597177 0.668907 c
|
||||||
|
1.176347 0.117853 1.962918 0.117853 3.536060 0.117853 c
|
||||||
|
18.216061 0.117853 l
|
||||||
|
19.789202 0.117853 20.575773 0.117853 21.154943 0.668907 c
|
||||||
|
21.284109 0.791803 21.447443 1.004665 21.532721 1.161236 c
|
||||||
|
21.915098 1.863297 21.747580 2.488480 21.412542 3.738846 c
|
||||||
|
20.642410 6.613000 18.962179 8.917621 16.747183 10.376812 c
|
||||||
|
18.601601 13.714764 l
|
||||||
|
18.824217 14.115474 18.679844 14.620780 18.279133 14.843397 c
|
||||||
|
17.878422 15.066014 17.373116 14.921641 17.150499 14.520930 c
|
||||||
|
15.293184 11.177763 l
|
||||||
|
13.941256 11.788494 12.447121 12.117853 10.876060 12.117853 c
|
||||||
|
9.304994 12.117853 7.810853 11.788492 6.458920 11.177755 c
|
||||||
|
4.601601 14.520930 l
|
||||||
|
h
|
||||||
|
7.876053 5.617853 m
|
||||||
|
7.876053 4.789426 7.204480 4.117853 6.376053 4.117853 c
|
||||||
|
5.547626 4.117853 4.876053 4.789426 4.876053 5.617853 c
|
||||||
|
4.876053 6.446280 5.547626 7.117853 6.376053 7.117853 c
|
||||||
|
7.204480 7.117853 7.876053 6.446280 7.876053 5.617853 c
|
||||||
|
h
|
||||||
|
16.876053 5.617853 m
|
||||||
|
16.876053 4.789426 16.204479 4.117853 15.376053 4.117853 c
|
||||||
|
14.547626 4.117853 13.876053 4.789426 13.876053 5.617853 c
|
||||||
|
13.876053 6.446280 14.547626 7.117853 15.376053 7.117853 c
|
||||||
|
16.204479 7.117853 16.876053 6.446280 16.876053 5.617853 c
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
2502
|
||||||
|
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
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000002592 00000 n
|
||||||
|
0000002615 00000 n
|
||||||
|
0000002788 00000 n
|
||||||
|
0000002862 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
2921
|
||||||
|
%%EOF
|
12
submodules/TelegramUI/Images.xcassets/Settings/Devices/Opera.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Icon-32.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
101
submodules/TelegramUI/Images.xcassets/Settings/Devices/Opera.imageset/Icon-32.pdf
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
%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 0.000000 0.000000 cm
|
||||||
|
1.000000 0.231373 0.188235 scn
|
||||||
|
0.000000 18.799999 m
|
||||||
|
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
|
||||||
|
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
|
||||||
|
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
|
||||||
|
18.799999 30.000000 l
|
||||||
|
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
|
||||||
|
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
|
||||||
|
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
|
||||||
|
30.000000 11.200001 l
|
||||||
|
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
|
||||||
|
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
|
||||||
|
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
|
||||||
|
11.200000 0.000000 l
|
||||||
|
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||||
|
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||||
|
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
|
||||||
|
0.000000 18.799999 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 5.000000 5.000000 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
10.000000 0.000000 m
|
||||||
|
15.522847 0.000000 20.000000 4.477153 20.000000 10.000000 c
|
||||||
|
20.000000 15.522848 15.522847 20.000000 10.000000 20.000000 c
|
||||||
|
4.477152 20.000000 0.000000 15.522848 0.000000 10.000000 c
|
||||||
|
0.000000 4.477153 4.477152 0.000000 10.000000 0.000000 c
|
||||||
|
h
|
||||||
|
10.000000 2.000000 m
|
||||||
|
12.761424 2.000000 15.000000 5.581722 15.000000 10.000000 c
|
||||||
|
15.000000 14.418278 12.761424 18.000000 10.000000 18.000000 c
|
||||||
|
7.238576 18.000000 5.000000 14.418278 5.000000 10.000000 c
|
||||||
|
5.000000 5.581722 7.238576 2.000000 10.000000 2.000000 c
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
1564
|
||||||
|
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
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000001654 00000 n
|
||||||
|
0000001677 00000 n
|
||||||
|
0000001850 00000 n
|
||||||
|
0000001924 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
1983
|
||||||
|
%%EOF
|
12
submodules/TelegramUI/Images.xcassets/Settings/Devices/iOS.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Icon-30.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
123
submodules/TelegramUI/Images.xcassets/Settings/Devices/iOS.imageset/Icon-30.pdf
vendored
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
%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 0.000000 0.000000 cm
|
||||||
|
0.000000 0.478431 1.000000 scn
|
||||||
|
0.000000 18.799999 m
|
||||||
|
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
|
||||||
|
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
|
||||||
|
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
|
||||||
|
18.799999 30.000000 l
|
||||||
|
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
|
||||||
|
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
|
||||||
|
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
|
||||||
|
30.000000 11.200001 l
|
||||||
|
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
|
||||||
|
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
|
||||||
|
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
|
||||||
|
11.200000 0.000000 l
|
||||||
|
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||||
|
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||||
|
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
|
||||||
|
0.000000 18.799999 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 5.709961 5.475098 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
17.318722 4.661448 m
|
||||||
|
17.012165 3.946257 16.632772 3.264536 16.186548 2.627071 c
|
||||||
|
15.593926 1.777941 15.107445 1.194162 14.735950 0.866894 c
|
||||||
|
14.152173 0.336187 13.533016 0.061989 12.869633 0.044298 c
|
||||||
|
12.391996 0.044298 11.817065 0.176975 11.144837 0.460018 c
|
||||||
|
10.557850 0.714588 9.927019 0.852770 9.287363 0.866894 c
|
||||||
|
8.630317 0.855038 7.981685 0.716904 7.376821 0.460018 c
|
||||||
|
6.695747 0.185820 6.147350 0.044300 5.722785 0.026609 c
|
||||||
|
5.085937 0.000074 4.449090 0.283115 3.812242 0.875738 c
|
||||||
|
3.405367 1.229542 2.901196 1.839857 2.290883 2.697832 c
|
||||||
|
1.606449 3.680990 1.064481 4.755993 0.681073 5.890916 c
|
||||||
|
0.229973 7.199992 0.000000 8.464844 0.000000 9.694314 c
|
||||||
|
0.000000 11.100687 0.300734 12.312468 0.911046 13.329655 c
|
||||||
|
1.372815 14.127305 2.030385 14.794008 2.821589 15.266735 c
|
||||||
|
3.601674 15.737316 4.493386 15.990782 5.404361 16.000879 c
|
||||||
|
5.908533 16.000879 6.580760 15.841667 7.403356 15.532089 c
|
||||||
|
8.225950 15.222509 8.756658 15.063297 8.986630 15.063297 c
|
||||||
|
9.163532 15.063297 9.747309 15.249044 10.746806 15.611694 c
|
||||||
|
11.690285 15.947809 12.486344 16.089331 13.134986 16.036259 c
|
||||||
|
14.904008 15.894737 16.230774 15.195974 17.115284 13.939968 c
|
||||||
|
15.532009 12.984696 14.753640 11.640240 14.771331 9.924288 c
|
||||||
|
14.789021 8.579831 15.275502 7.465346 16.230774 6.580835 c
|
||||||
|
16.655304 6.180765 17.146141 5.857531 17.681372 5.625564 c
|
||||||
|
17.575230 5.289450 17.451399 4.971027 17.318722 4.661448 c
|
||||||
|
h
|
||||||
|
13.267662 20.600336 m
|
||||||
|
13.267662 19.547770 12.887323 18.565962 12.117799 17.663759 c
|
||||||
|
11.197907 16.584656 10.074579 15.965499 8.871644 16.062796 c
|
||||||
|
8.853408 16.194679 8.844540 16.327688 8.845108 16.460825 c
|
||||||
|
8.845108 17.469168 9.287364 18.548271 10.065734 19.432783 c
|
||||||
|
10.479326 19.902321 10.985150 20.281689 11.551712 20.547266 c
|
||||||
|
12.079977 20.818914 12.657554 20.981358 13.249973 21.024902 c
|
||||||
|
13.258819 20.883381 13.267662 20.741859 13.267662 20.600336 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
2862
|
||||||
|
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
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000002952 00000 n
|
||||||
|
0000002975 00000 n
|
||||||
|
0000003148 00000 n
|
||||||
|
0000003222 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
3281
|
||||||
|
%%EOF
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"provides-namespace" : true
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 666 B After Width: | Height: | Size: 666 B |
Before Width: | Height: | Size: 954 B After Width: | Height: | Size: 954 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 854 B After Width: | Height: | Size: 854 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 925 B After Width: | Height: | Size: 925 B |
Before Width: | Height: | Size: 760 B After Width: | Height: | Size: 760 B |