Various UI fixes

This commit is contained in:
Ilya Laktyushin 2019-03-18 13:31:26 +03:00
parent 9bf4297c60
commit ebc595a62f
18 changed files with 248 additions and 175 deletions

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "EditProfile@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "EditProfile@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 B

View File

@ -71,6 +71,7 @@ func faqSearchableItems(context: AccountContext) -> Signal<[SettingsSearchableIt
return cachedFaqInstantPage(context: context) return cachedFaqInstantPage(context: context)
|> map { resolvedUrl -> [SettingsSearchableItem] in |> map { resolvedUrl -> [SettingsSearchableItem] in
var results: [SettingsSearchableItem] = [] var results: [SettingsSearchableItem] = []
var nextIndex: Int = 1
if case let .instantView(webPage, _) = resolvedUrl { if case let .instantView(webPage, _) = resolvedUrl {
if case let .Loaded(content) = webPage.content, let instantPage = content.instantPage { if case let .Loaded(content) = webPage.content, let instantPage = content.instantPage {
var processingQuestions = false var processingQuestions = false
@ -83,14 +84,15 @@ func faqSearchableItems(context: AccountContext) -> Signal<[SettingsSearchableIt
if results.isEmpty { if results.isEmpty {
processingQuestions = true processingQuestions = true
} }
case let .anchor(anchor): // case let .anchor(anchor):
currentAnchor = anchor // currentAnchor = anchor
case let .header(text): // case let .header(text):
if let anchor = currentAnchor { // if let anchor = currentAnchor {
results.append(SettingsSearchableItem(id: .faq(results.count + 1), title: text.plainText, alternate: [], icon: .faq, breadcrumbs: [strings.SettingsSearch_FAQ], present: { context, _, present in // results.append(SettingsSearchableItem(id: .faq(nextIndex), title: text.plainText, alternate: [], icon: .faq, breadcrumbs: [strings.SettingsSearch_FAQ], present: { context, _, present in
present(.push, InstantPageController(context: context, webPage: webPage, sourcePeerType: .channel, anchor: anchor)) // present(.push, InstantPageController(context: context, webPage: webPage, sourcePeerType: .channel, anchor: anchor))
})) // }))
} // nextIndex += 1
// }
default: default:
break break
} }
@ -107,9 +109,20 @@ func faqSearchableItems(context: AccountContext) -> Signal<[SettingsSearchableIt
for item in items { for item in items {
if case let .text(itemText, _) = item, case let .url(text, url, _) = itemText { if case let .text(itemText, _) = item, case let .url(text, url, _) = itemText {
let (_, anchor) = extractAnchor(string: url) let (_, anchor) = extractAnchor(string: url)
results.append(SettingsSearchableItem(id: .faq(results.count + 1), title: text.plainText, alternate: [], icon: .faq, breadcrumbs: [strings.SettingsSearch_FAQ, currentSection], present: { context, _, present in var index = nextIndex
if anchor?.contains("delete-my-account") ?? false {
index = 0
} else {
nextIndex += 1
}
let item = SettingsSearchableItem(id: .faq(index), title: text.plainText, alternate: [], icon: .faq, breadcrumbs: [strings.SettingsSearch_FAQ, currentSection], present: { context, _, present in
present(.push, InstantPageController(context: context, webPage: webPage, sourcePeerType: .channel, anchor: anchor)) present(.push, InstantPageController(context: context, webPage: webPage, sourcePeerType: .channel, anchor: anchor))
})) })
if index == 0 {
results.insert(item, at: 0)
} else {
results.append(item)
}
} }
} }
} }

View File

@ -352,7 +352,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
} }
durationNode.defaultDuration = telegramFile.duration.flatMap(Double.init) durationNode.defaultDuration = telegramFile.duration.flatMap(Double.init)
let streamVideo = automaticDownload && isMediaStreamable(message: item.message, media: telegramFile) let streamVideo = automaticDownload && isMediaStreamable(message: item.message, media: telegramFile) && telegramFile.id?.namespace != Namespaces.Media.LocalFile
if let videoNode = strongSelf.videoNode { if let videoNode = strongSelf.videoNode {
videoNode.layer.allowsGroupOpacity = true videoNode.layer.allowsGroupOpacity = true
videoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.5, delay: 0.2, removeOnCompletion: false, completion: { [weak videoNode] _ in videoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.5, delay: 0.2, removeOnCompletion: false, completion: { [weak videoNode] _ in
@ -500,6 +500,15 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
} }
} }
var isBuffering: Bool?
if let message = self.item?.message, let media = self.media, let size = media.size, (isMediaStreamable(message: message, media: media) || size <= 256 * 1024) && (self.automaticDownload ?? false) {
if let playerStatus = self.playerStatus, case .buffering = playerStatus.status {
isBuffering = true
} else {
isBuffering = false
}
}
var progressRequired = false var progressRequired = false
if case let .fetchStatus(fetchStatus) = status.mediaStatus { if case let .fetchStatus(fetchStatus) = status.mediaStatus {
if case .Local = fetchStatus { if case .Local = fetchStatus {
@ -511,6 +520,8 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
} else { } else {
progressRequired = true progressRequired = true
} }
} else if isBuffering ?? false {
progressRequired = true
} }
if progressRequired { if progressRequired {
@ -530,15 +541,6 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
} }
} }
var isBuffering: Bool?
if let message = self.item?.message, let media = self.media, let size = media.size, (isMediaStreamable(message: message, media: media) || size <= 256 * 1024) && (self.automaticDownload ?? false) {
if let playerStatus = self.playerStatus, case .buffering = playerStatus.status {
isBuffering = true
} else {
isBuffering = false
}
}
var state: RadialStatusNodeState var state: RadialStatusNodeState
switch status.mediaStatus { switch status.mediaStatus {
case var .fetchStatus(fetchStatus): case var .fetchStatus(fetchStatus):
@ -550,7 +552,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
case let .Fetching(_, progress): case let .Fetching(_, progress):
if let isBuffering = isBuffering { if let isBuffering = isBuffering {
if isBuffering { if isBuffering {
state = .progress(color: bubbleTheme.mediaOverlayControlForegroundColor, lineWidth: nil, value: nil, cancelEnabled: false) state = .progress(color: bubbleTheme.mediaOverlayControlForegroundColor, lineWidth: nil, value: nil, cancelEnabled: true)
} else { } else {
state = .none state = .none
} }
@ -573,7 +575,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
} }
default: default:
if isBuffering ?? false { if isBuffering ?? false {
state = .progress(color: bubbleTheme.mediaOverlayControlForegroundColor, lineWidth: nil, value: nil, cancelEnabled: false) state = .progress(color: bubbleTheme.mediaOverlayControlForegroundColor, lineWidth: nil, value: nil, cancelEnabled: true)
} else { } else {
state = .none state = .none
} }

View File

@ -178,8 +178,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
let point = recognizer.location(in: self.imageNode.view) let point = recognizer.location(in: self.imageNode.view)
if let fetchStatus = self.fetchStatus, case .Local = fetchStatus { if let fetchStatus = self.fetchStatus, case .Local = fetchStatus {
var videoContentMatch = true var videoContentMatch = true
if let content = self.videoContent, case let .message(id, _, _) = content.nativeId { if let content = self.videoContent, case let .message(id, _, mediaId) = content.nativeId {
videoContentMatch = self.message?.id == id videoContentMatch = self.message?.id == id && self.media?.id == mediaId
} }
self.activateLocalContent((self.automaticPlayback ?? false) && videoContentMatch ? .automaticPlayback : .default) self.activateLocalContent((self.automaticPlayback ?? false) && videoContentMatch ? .automaticPlayback : .default)
} else { } else {
@ -698,9 +698,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
} else if let image = media as? TelegramMediaWebFile { } else if let image = media as? TelegramMediaWebFile {
strongSelf.fetchDisposable.set(chatMessageWebFileInteractiveFetched(account: context.account, image: image).start()) strongSelf.fetchDisposable.set(chatMessageWebFileInteractiveFetched(account: context.account, image: image).start())
} else if let file = media as? TelegramMediaFile { } else if let file = media as? TelegramMediaFile {
if automaticPlayback || !file.isAnimated {
let fetchSignal = messageMediaFileInteractiveFetched(context: context, message: message, file: file, userInitiated: false) let fetchSignal = messageMediaFileInteractiveFetched(context: context, message: message, file: file, userInitiated: false)
if !file.isAnimated {
let visibilityAwareFetchSignal = strongSelf.visibilityPromise.get() let visibilityAwareFetchSignal = strongSelf.visibilityPromise.get()
|> mapToSignal { visibility -> Signal<Void, NoError> in |> mapToSignal { visibility -> Signal<Void, NoError> in
switch visibility { switch visibility {
@ -714,10 +712,6 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
} }
} }
strongSelf.fetchDisposable.set(visibilityAwareFetchSignal.start()) strongSelf.fetchDisposable.set(visibilityAwareFetchSignal.start())
} else {
strongSelf.fetchDisposable.set(fetchSignal.start())
}
}
} }
} else if case .prefetch = automaticDownload, message.id.namespace != Namespaces.Message.SecretIncoming { } else if case .prefetch = automaticDownload, message.id.namespace != Namespaces.Message.SecretIncoming {
if let file = media as? TelegramMediaFile { if let file = media as? TelegramMediaFile {
@ -927,7 +921,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
if wideLayout { if wideLayout {
if let size = file.size { if let size = file.size {
let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, decimalSeparator: decimalSeparator)) / \(dataSizeString(size, forceDecimal: true, decimalSeparator: decimalSeparator))" let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, decimalSeparator: decimalSeparator)) / \(dataSizeString(size, forceDecimal: true, decimalSeparator: decimalSeparator))"
if file.isAnimated && !automaticDownload { if file.isAnimated && (!automaticDownload || !automaticPlayback) {
badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: "GIF " + sizeString, size: nil, muted: false, active: false) badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: "GIF " + sizeString, size: nil, muted: false, active: false)
} }
else if let duration = file.duration, !message.flags.contains(.Unsent) { else if let duration = file.duration, !message.flags.contains(.Unsent) {
@ -1031,7 +1025,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
case .Remote: case .Remote:
state = .download(bubbleTheme.mediaOverlayControlForegroundColor) state = .download(bubbleTheme.mediaOverlayControlForegroundColor)
if let file = self.media as? TelegramMediaFile { if let file = self.media as? TelegramMediaFile {
if file.isAnimated && !automaticDownload { if file.isAnimated && (!automaticDownload || !automaticPlayback) {
let string = "GIF " + dataSizeString(file.size ?? 0, decimalSeparator: decimalSeparator) let string = "GIF " + dataSizeString(file.size ?? 0, decimalSeparator: decimalSeparator)
badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: string, size: nil, muted: false, active: false) badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: string, size: nil, muted: false, active: false)
} else { } else {

View File

@ -267,7 +267,7 @@ final class GridMessageItemNode: GridItemNode {
badgeContent = .text(inset: 0.0, backgroundColor: mediaBadgeBackgroundColor, foregroundColor: mediaBadgeTextColor, text: NSAttributedString(string: durationString)) badgeContent = .text(inset: 0.0, backgroundColor: mediaBadgeBackgroundColor, foregroundColor: mediaBadgeTextColor, text: NSAttributedString(string: durationString))
} }
strongSelf.mediaBadgeNode.update(theme: item.theme, content: badgeContent, mediaDownloadState: mediaDownloadState, alignment: .right, animated: false) strongSelf.mediaBadgeNode.update(theme: item.theme, content: badgeContent, mediaDownloadState: mediaDownloadState, alignment: .right, animated: false, badgeAnimated: false)
} }
} }
})) }))

View File

@ -338,23 +338,29 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
shouldUpdateVisibleItems = true shouldUpdateVisibleItems = true
self.updateNavigationBar() self.updateNavigationBar()
} }
var didSetScrollOffset = false
if resetOffset { if resetOffset {
var contentOffset = CGPoint(x: 0.0, y: -self.scrollNode.view.contentInset.top) var contentOffset = CGPoint(x: 0.0, y: -self.scrollNode.view.contentInset.top)
if let state = self.initialState { if let state = self.initialState {
self.setupScrollOffsetOnLayout = false didSetScrollOffset = true
contentOffset = CGPoint(x: 0.0, y: CGFloat(state.contentOffset)) contentOffset = CGPoint(x: 0.0, y: CGFloat(state.contentOffset))
} }
else if let anchor = self.initialAnchor, !anchor.isEmpty { else if let anchor = self.initialAnchor, !anchor.isEmpty {
if let items = self.currentLayout?.items { if let items = self.currentLayout?.items {
self.setupScrollOffsetOnLayout = false didSetScrollOffset = true
if let (item, lineOffset, _, _) = self.findAnchorItem(anchor, items: items) { if let (item, lineOffset, _, _) = self.findAnchorItem(anchor, items: items) {
contentOffset = CGPoint(x: 0.0, y: item.frame.minY + lineOffset - self.scrollNode.view.contentInset.top) contentOffset = CGPoint(x: 0.0, y: item.frame.minY + lineOffset - self.scrollNode.view.contentInset.top)
} }
} }
} else { } else {
self.setupScrollOffsetOnLayout = false didSetScrollOffset = true
} }
self.scrollNode.view.contentOffset = contentOffset self.scrollNode.view.contentOffset = contentOffset
if didSetScrollOffset {
self.previousContentOffset = contentOffset
self.updateNavigationBar()
self.setupScrollOffsetOnLayout = false
}
} }
if shouldUpdateVisibleItems { if shouldUpdateVisibleItems {
self.updateVisibleItems(visibleBounds: self.scrollNode.view.bounds) self.updateVisibleItems(visibleBounds: self.scrollNode.view.bounds)
@ -668,7 +674,9 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
} }
let delta: CGFloat let delta: CGFloat
if let previousContentOffset = self.previousContentOffset { if self.setupScrollOffsetOnLayout {
delta = 0.0
} else if let previousContentOffset = self.previousContentOffset {
delta = contentOffset.y - previousContentOffset.y delta = contentOffset.y - previousContentOffset.y
} else { } else {
delta = 0.0 delta = 0.0
@ -700,6 +708,10 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
navigationBarFrame.size.height = max(minBarHeight, min(maxBarHeight, navigationBarFrame.size.height)) navigationBarFrame.size.height = max(minBarHeight, min(maxBarHeight, navigationBarFrame.size.height))
} }
if self.setupScrollOffsetOnLayout {
navigationBarFrame.size.height = maxBarHeight
}
let transitionFactor = (navigationBarFrame.size.height - minBarHeight) / (maxBarHeight - minBarHeight) let transitionFactor = (navigationBarFrame.size.height - minBarHeight) / (maxBarHeight - minBarHeight)
if containerLayout.safeInsets.top.isZero { if containerLayout.safeInsets.top.isZero {

View File

@ -92,7 +92,7 @@ class ItemListActionItemNode: ListViewItemNode, ItemListItemNode {
private var item: ItemListActionItem? private var item: ItemListActionItem?
var tag: ItemListItemTag? { var tag: ItemListItemTag? {
return self.item?.tag return self.item?.tag as? ItemListItemTag
} }
init() { init() {

View File

@ -5,14 +5,6 @@ import Postbox
import TelegramCore import TelegramCore
import LegacyComponents import LegacyComponents
private final class LogoutOptionsItemIcons {
static let addAccount = UIImage(bundleImageName: "Settings/MenuIcons/AddAccount")?.precomposed()
static let setPasscode = UIImage(bundleImageName: "Settings/MenuIcons/SetPasscode")?.precomposed()
static let clearCache = UIImage(bundleImageName: "Settings/MenuIcons/ClearCache")?.precomposed()
static let changePhoneNumber = UIImage(bundleImageName: "Settings/MenuIcons/ChangePhoneNumber")?.precomposed()
static let contactSupport = UIImage(bundleImageName: "Settings/MenuIcons/Support")?.precomposed()
}
private struct LogoutOptionsItemArguments { private struct LogoutOptionsItemArguments {
let addAccount: () -> Void let addAccount: () -> Void
let setPasscode: () -> Void let setPasscode: () -> Void
@ -76,23 +68,23 @@ private enum LogoutOptionsEntry: ItemListNodeEntry, Equatable {
case let .alternativeHeader(theme, title): case let .alternativeHeader(theme, title):
return ItemListSectionHeaderItem(theme: theme, text: title, sectionId: self.section) return ItemListSectionHeaderItem(theme: theme, text: title, sectionId: self.section)
case let .addAccount(theme, title, text): case let .addAccount(theme, title, text):
return ItemListDisclosureItem(theme: theme, icon: LogoutOptionsItemIcons.addAccount, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { return ItemListDisclosureItem(theme: theme, icon: PresentationResourcesSettings.addAccount, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.addAccount() arguments.addAccount()
}) })
case let .setPasscode(theme, title, text): case let .setPasscode(theme, title, text):
return ItemListDisclosureItem(theme: theme, icon: LogoutOptionsItemIcons.setPasscode, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { return ItemListDisclosureItem(theme: theme, icon: PresentationResourcesSettings.setPasscode, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.setPasscode() arguments.setPasscode()
}) })
case let .clearCache(theme, title, text): case let .clearCache(theme, title, text):
return ItemListDisclosureItem(theme: theme, icon: LogoutOptionsItemIcons.clearCache, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { return ItemListDisclosureItem(theme: theme, icon: PresentationResourcesSettings.clearCache, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.clearCache() arguments.clearCache()
}) })
case let .changePhoneNumber(theme, title, text): case let .changePhoneNumber(theme, title, text):
return ItemListDisclosureItem(theme: theme, icon: LogoutOptionsItemIcons.changePhoneNumber, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { return ItemListDisclosureItem(theme: theme, icon: PresentationResourcesSettings.changePhoneNumber, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.changePhoneNumber() arguments.changePhoneNumber()
}) })
case let .contactSupport(theme, title, text): case let .contactSupport(theme, title, text):
return ItemListDisclosureItem(theme: theme, icon: LogoutOptionsItemIcons.contactSupport, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { return ItemListDisclosureItem(theme: theme, icon: PresentationResourcesSettings.support, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.contactSupport() arguments.contactSupport()
}) })
case let .logout(theme, title): case let .logout(theme, title):

View File

@ -312,7 +312,6 @@ private func chatMessageVideoDatas(postbox: Postbox, fileReference: FileMediaRef
|> mapToSignal { maybeData -> Signal<(Data?, (Data, String)?, Bool), NoError> in |> mapToSignal { maybeData -> Signal<(Data?, (Data, String)?, Bool), NoError> in
if maybeData.complete { if maybeData.complete {
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: []) let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: [])
return .single((nil, loadedData == nil ? nil : (loadedData!, maybeData.path), true)) return .single((nil, loadedData == nil ? nil : (loadedData!, maybeData.path), true))
} else { } else {
let thumbnail: Signal<Data?, NoError> let thumbnail: Signal<Data?, NoError>

View File

@ -2,6 +2,7 @@ import Foundation
import Display import Display
struct PresentationResourcesSettings { struct PresentationResourcesSettings {
static let editProfile = UIImage(bundleImageName: "Settings/MenuIcons/EditProfile")?.precomposed()
static let proxy = UIImage(bundleImageName: "Settings/MenuIcons/Proxy")?.precomposed() static let proxy = UIImage(bundleImageName: "Settings/MenuIcons/Proxy")?.precomposed()
static let savedMessages = UIImage(bundleImageName: "Settings/MenuIcons/SavedMessages")?.precomposed() static let savedMessages = UIImage(bundleImageName: "Settings/MenuIcons/SavedMessages")?.precomposed()
static let recentCalls = UIImage(bundleImageName: "Settings/MenuIcons/RecentCalls")?.precomposed() static let recentCalls = UIImage(bundleImageName: "Settings/MenuIcons/RecentCalls")?.precomposed()
@ -18,4 +19,9 @@ struct PresentationResourcesSettings {
static let support = UIImage(bundleImageName: "Settings/MenuIcons/Support")?.precomposed() static let support = UIImage(bundleImageName: "Settings/MenuIcons/Support")?.precomposed()
static let faq = UIImage(bundleImageName: "Settings/MenuIcons/Faq")?.precomposed() static let faq = UIImage(bundleImageName: "Settings/MenuIcons/Faq")?.precomposed()
static let addAccount = UIImage(bundleImageName: "Settings/MenuIcons/AddAccount")?.precomposed()
static let setPasscode = UIImage(bundleImageName: "Settings/MenuIcons/SetPasscode")?.precomposed()
static let clearCache = UIImage(bundleImageName: "Settings/MenuIcons/ClearCache")?.precomposed()
static let changePhoneNumber = UIImage(bundleImageName: "Settings/MenuIcons/ChangePhoneNumber")?.precomposed()
} }

View File

@ -598,7 +598,6 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
let actionsDisposable = DisposableSet() let actionsDisposable = DisposableSet()
let updateSettingsDisposable = MetaDisposable() let updateSettingsDisposable = MetaDisposable()
actionsDisposable.add(updateSettingsDisposable)
let arguments = SelectivePrivacySettingsControllerArguments(context: context, updateType: { type in let arguments = SelectivePrivacySettingsControllerArguments(context: context, updateType: { type in
updateState { updateState {
@ -721,9 +720,8 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
actionsDisposable.dispose() actionsDisposable.dispose()
} }
let controller = ItemListController(context: context, state: signal)
controller.didDisappear = { [weak controller] _ in let update: (Bool) -> Void = { save in
if let controller = controller, controller.navigationController?.viewControllers.firstIndex(of: controller) == nil {
var wasSaving = false var wasSaving = false
var settings: SelectivePrivacySettings? var settings: SelectivePrivacySettings?
var callP2PSettings: SelectivePrivacySettings? var callP2PSettings: SelectivePrivacySettings?
@ -787,6 +785,17 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
} }
} }
} }
let controller = ItemListController(context: context, state: signal)
controller.willDisappear = { [weak controller] _ in
if let controller = controller, controller.navigationController?.viewControllers.firstIndex(of: controller) == nil {
update(false)
}
}
controller.didDisappear = { [weak controller] _ in
if let controller = controller, controller.navigationController?.viewControllers.firstIndex(of: controller) == nil {
update(true)
}
} }
pushControllerImpl = { [weak controller] c in pushControllerImpl = { [weak controller] c in

View File

@ -1078,7 +1078,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
presentControllerImpl?(v, a) presentControllerImpl?(v, a)
}, pushController: { v in }, pushController: { v in
pushControllerImpl?(v) pushControllerImpl?(v)
}, getNavigationController: getNavigationControllerImpl) }, getNavigationController: getNavigationControllerImpl, exceptionsList: notifyExceptions.get())
let (hasPassport, hasWatchApp) = hasPassportAndWatch let (hasPassport, hasWatchApp) = hasPassportAndWatch
let listState = ItemListNodeState(entries: settingsEntries(account: context.account, presentationData: presentationData, state: state, view: view, proxySettings: proxySettings, notifyExceptions: preferencesAndExceptions.1, notificationsAuthorizationStatus: preferencesAndExceptions.2, notificationsWarningSuppressed: preferencesAndExceptions.3, unreadTrendingStickerPacks: unreadTrendingStickerPacks, archivedPacks: featuredAndArchived.1, hasPassport: hasPassport, hasWatchApp: hasWatchApp, accountsAndPeers: accountsAndPeers.1, inAppNotificationSettings: inAppNotificationSettings), style: .blocks, searchItem: searchItem, initialScrollToItem: ListViewScrollToItem(index: 0, position: .top(-navigationBarSearchContentHeight), animated: false, curve: .Default(duration: 0.0), directionHint: .Up)) let listState = ItemListNodeState(entries: settingsEntries(account: context.account, presentationData: presentationData, state: state, view: view, proxySettings: proxySettings, notifyExceptions: preferencesAndExceptions.1, notificationsAuthorizationStatus: preferencesAndExceptions.2, notificationsWarningSuppressed: preferencesAndExceptions.3, unreadTrendingStickerPacks: unreadTrendingStickerPacks, archivedPacks: featuredAndArchived.1, hasPassport: hasPassport, hasWatchApp: hasWatchApp, accountsAndPeers: accountsAndPeers.1, inAppNotificationSettings: inAppNotificationSettings), style: .blocks, searchItem: searchItem, initialScrollToItem: ListViewScrollToItem(index: 0, position: .top(-navigationBarSearchContentHeight), animated: false, curve: .Default(duration: 0.0), directionHint: .Up))

View File

@ -19,6 +19,8 @@ extension NavigationBarSearchContentNode: ItemListControllerSearchNavigationCont
extension SettingsSearchableItemIcon { extension SettingsSearchableItemIcon {
func image() -> UIImage? { func image() -> UIImage? {
switch self { switch self {
case .profile:
return PresentationResourcesSettings.editProfile
case .proxy: case .proxy:
return PresentationResourcesSettings.proxy return PresentationResourcesSettings.proxy
case .savedMessages: case .savedMessages:
@ -58,12 +60,13 @@ final class SettingsSearchItem: ItemListControllerSearch {
let presentController: (ViewController, Any?) -> Void let presentController: (ViewController, Any?) -> Void
let pushController: (ViewController) -> Void let pushController: (ViewController) -> Void
let getNavigationController: (() -> NavigationController?)? let getNavigationController: (() -> NavigationController?)?
let exceptionsList: Signal<NotificationExceptionsList?, NoError>
private var updateActivity: ((Bool) -> Void)? private var updateActivity: ((Bool) -> Void)?
private var activity: ValuePromise<Bool> = ValuePromise(ignoreRepeated: false) private var activity: ValuePromise<Bool> = ValuePromise(ignoreRepeated: false)
private let activityDisposable = MetaDisposable() private let activityDisposable = MetaDisposable()
init(context: AccountContext, theme: PresentationTheme, placeholder: String, activated: Bool, updateActivated: @escaping (Bool) -> Void, presentController: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, getNavigationController: (() -> NavigationController?)?) { init(context: AccountContext, theme: PresentationTheme, placeholder: String, activated: Bool, updateActivated: @escaping (Bool) -> Void, presentController: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, getNavigationController: (() -> NavigationController?)?, exceptionsList: Signal<NotificationExceptionsList?, NoError>) {
self.context = context self.context = context
self.theme = theme self.theme = theme
self.placeholder = placeholder self.placeholder = placeholder
@ -72,6 +75,7 @@ final class SettingsSearchItem: ItemListControllerSearch {
self.presentController = presentController self.presentController = presentController
self.pushController = pushController self.pushController = pushController
self.getNavigationController = getNavigationController self.getNavigationController = getNavigationController
self.exceptionsList = exceptionsList
self.activityDisposable.set((activity.get() |> mapToSignal { value -> Signal<Bool, NoError> in self.activityDisposable.set((activity.get() |> mapToSignal { value -> Signal<Bool, NoError> in
if value { if value {
return .single(value) |> delay(0.2, queue: Queue.mainQueue()) return .single(value) |> delay(0.2, queue: Queue.mainQueue())
@ -135,7 +139,7 @@ final class SettingsSearchItem: ItemListControllerSearch {
pushController(c) pushController(c)
}, presentController: { c, a in }, presentController: { c, a in
presentController(c, a) presentController(c, a)
}, getNavigationController: self.getNavigationController) }, getNavigationController: self.getNavigationController, exceptionsList: self.exceptionsList)
} }
} }
} }
@ -217,7 +221,7 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings)> private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings)>
init(context: AccountContext, listState: LocalizationListState, openResult: @escaping (SettingsSearchableItem) -> Void) { init(context: AccountContext, openResult: @escaping (SettingsSearchableItem) -> Void, exceptionsList: Signal<NotificationExceptionsList?, NoError>) {
self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings)) self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings))
@ -235,14 +239,20 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
self.addSubnode(self.dimNode) self.addSubnode(self.dimNode)
self.addSubnode(self.listNode) self.addSubnode(self.listNode)
let queryAndFoundItems = combineLatest(settingsSearchableItems(context: context), faqSearchableItems(context: context)) let queryAndFoundItems = combineLatest(settingsSearchableItems(context: context, exceptionsList: exceptionsList), faqSearchableItems(context: context))
|> mapToSignal { searchableItems, faqSearchableItems -> Signal<(String, [SettingsSearchableItem])?, NoError> in |> mapToSignal { searchableItems, faqSearchableItems -> Signal<(String, [SettingsSearchableItem])?, NoError> in
return self.searchQuery.get() return self.searchQuery.get()
|> mapToSignal { query -> Signal<(String, [SettingsSearchableItem])?, NoError> in |> mapToSignal { query -> Signal<(String, [SettingsSearchableItem])?, NoError> in
if let query = query, !query.isEmpty { if let query = query, !query.isEmpty {
let result = searchSettingsItems(items: searchableItems, query: query) let results = searchSettingsItems(items: searchableItems, query: query)
let faqResults = searchSettingsItems(items: faqSearchableItems, query: query) let faqResults = searchSettingsItems(items: faqSearchableItems, query: query)
return .single((query, result + faqResults)) let finalResults: [SettingsSearchableItem]
if faqResults.first?.id == .faq(0) {
finalResults = faqResults + results
} else {
finalResults = results + faqResults
}
return .single((query, finalResults))
} else { } else {
return .single(nil) return .single(nil)
} }
@ -404,16 +414,18 @@ private final class SettingsSearchItemNode: ItemListControllerSearchNode {
let pushController: (ViewController) -> Void let pushController: (ViewController) -> Void
let presentController: (ViewController, Any?) -> Void let presentController: (ViewController, Any?) -> Void
let getNavigationController: (() -> NavigationController?)? let getNavigationController: (() -> NavigationController?)?
let exceptionsList: Signal<NotificationExceptionsList?, NoError>
var cancel: () -> Void var cancel: () -> Void
init(context: AccountContext, cancel: @escaping () -> Void, updateActivity: @escaping(Bool) -> Void, pushController: @escaping (ViewController) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: (() -> NavigationController?)?) { init(context: AccountContext, cancel: @escaping () -> Void, updateActivity: @escaping(Bool) -> Void, pushController: @escaping (ViewController) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: (() -> NavigationController?)?, exceptionsList: Signal<NotificationExceptionsList?, NoError>) {
self.context = context self.context = context
self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.cancel = cancel
self.pushController = pushController self.pushController = pushController
self.presentController = presentController self.presentController = presentController
self.getNavigationController = getNavigationController self.getNavigationController = getNavigationController
self.cancel = cancel self.exceptionsList = exceptionsList
super.init() super.init()
} }
@ -428,7 +440,7 @@ private final class SettingsSearchItemNode: ItemListControllerSearchNode {
return return
} }
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: SettingsSearchContainerNode(context: self.context, listState: LocalizationListState.defaultSettings, openResult: { [weak self] result in self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: SettingsSearchContainerNode(context: self.context, openResult: { [weak self] result in
if let strongSelf = self { if let strongSelf = self {
result.present(strongSelf.context, strongSelf.getNavigationController?(), { [weak self] mode, controller in result.present(strongSelf.context, strongSelf.getNavigationController?(), { [weak self] mode, controller in
if let strongSelf = self { if let strongSelf = self {
@ -445,7 +457,7 @@ private final class SettingsSearchItemNode: ItemListControllerSearchNode {
} }
}) })
} }
}), cancel: { [weak self] in }, exceptionsList: self.exceptionsList), cancel: { [weak self] in
self?.cancel() self?.cancel()
}) })

View File

@ -7,6 +7,7 @@ import TelegramCore
private let maximumNumberOfAccounts = 3 private let maximumNumberOfAccounts = 3
enum SettingsSearchableItemIcon { enum SettingsSearchableItemIcon {
case profile
case proxy case proxy
case savedMessages case savedMessages
case calls case calls
@ -55,7 +56,7 @@ struct SettingsSearchableItem {
} }
private func profileSearchableItems(context: AccountContext, canAddAccount: Bool) -> [SettingsSearchableItem] { private func profileSearchableItems(context: AccountContext, canAddAccount: Bool) -> [SettingsSearchableItem] {
let icon: SettingsSearchableItemIcon = .calls let icon: SettingsSearchableItemIcon = .profile
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
let presentProfileSettings: (AccountContext, @escaping (SettingsSearchableItemPresentation, ViewController) -> Void, EditSettingsEntryTag?) -> Void = { context, present, itemTag in let presentProfileSettings: (AccountContext, @escaping (SettingsSearchableItemPresentation, ViewController) -> Void, EditSettingsEntryTag?) -> Void = { context, present, itemTag in
@ -155,12 +156,12 @@ private func stickerSearchableItems(context: AccountContext) -> [SettingsSearcha
] ]
} }
private func notificationSearchableItems(context: AccountContext, notifyExceptions: Signal<NotificationExceptionsList?, NoError>) -> [SettingsSearchableItem] { private func notificationSearchableItems(context: AccountContext, exceptionsList: NotificationExceptionsList?) -> [SettingsSearchableItem] {
let icon: SettingsSearchableItemIcon = .notifications let icon: SettingsSearchableItemIcon = .notifications
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
let presentNotificationSettings: (AccountContext, (SettingsSearchableItemPresentation, ViewController) -> Void, NotificationsAndSoundsEntryTag?) -> Void = { context, present, itemTag in let presentNotificationSettings: (AccountContext, (SettingsSearchableItemPresentation, ViewController) -> Void, NotificationsAndSoundsEntryTag?) -> Void = { context, present, itemTag in
present(.push, notificationsAndSoundsController(context: context, exceptionsList: nil, focusOnItemTag: itemTag)) present(.push, notificationsAndSoundsController(context: context, exceptionsList: exceptionsList, focusOnItemTag: itemTag))
} }
return [ return [
@ -475,7 +476,7 @@ private func appearanceSearchableItems(context: AccountContext) -> [SettingsSear
] ]
} }
func settingsSearchableItems(context: AccountContext) -> Signal<[SettingsSearchableItem], NoError> { func settingsSearchableItems(context: AccountContext, exceptionsList: Signal<NotificationExceptionsList?, NoError>) -> Signal<[SettingsSearchableItem], NoError> {
let watchAppInstalled = (context.watchManager?.watchAppInstalled ?? .single(false)) let watchAppInstalled = (context.watchManager?.watchAppInstalled ?? .single(false))
|> take(1) |> take(1)
let canAddAccount = activeAccountsAndPeers(context: context) let canAddAccount = activeAccountsAndPeers(context: context)
@ -483,8 +484,8 @@ func settingsSearchableItems(context: AccountContext) -> Signal<[SettingsSearcha
|> map { accountsAndPeers -> Bool in |> map { accountsAndPeers -> Bool in
return accountsAndPeers.1.count + 1 < maximumNumberOfAccounts return accountsAndPeers.1.count + 1 < maximumNumberOfAccounts
} }
return combineLatest(watchAppInstalled, canAddAccount) return combineLatest(watchAppInstalled, canAddAccount, exceptionsList)
|> map { watchAppInstalled, canAddAccount in |> map { watchAppInstalled, canAddAccount, exceptionsList in
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
var allItems: [SettingsSearchableItem] = [] var allItems: [SettingsSearchableItem] = []
@ -503,7 +504,7 @@ func settingsSearchableItems(context: AccountContext) -> Signal<[SettingsSearcha
let stickerItems = stickerSearchableItems(context: context) let stickerItems = stickerSearchableItems(context: context)
allItems.append(contentsOf: stickerItems) allItems.append(contentsOf: stickerItems)
let notificationItems = notificationSearchableItems(context: context, notifyExceptions: .complete()) let notificationItems = notificationSearchableItems(context: context, exceptionsList: exceptionsList)
allItems.append(contentsOf: notificationItems) allItems.append(contentsOf: notificationItems)
let privacyItems = privacySearchableItems(context: context) let privacyItems = privacySearchableItems(context: context)
@ -535,13 +536,25 @@ func settingsSearchableItems(context: AccountContext) -> Signal<[SettingsSearcha
}) })
allItems.append(passport) allItems.append(passport)
let support = SettingsSearchableItem(id: .support(0), title: strings.Settings_Support, alternate: ["Support"], icon: .support, breadcrumbs: [], present: { context, _, present in let support = SettingsSearchableItem(id: .support(0), title: strings.Settings_Support, alternate: [], icon: .support, breadcrumbs: [], present: { context, _, present in
//return .push(ChatController(context: context, chatLocation: .peer(context.account.peerId))) let _ = (supportPeerId(account: context.account)
|> deliverOnMainQueue).start(next: { peerId in
if let peerId = peerId {
present(.push, ChatController(context: context, chatLocation: .peer(peerId)))
}
})
}) })
allItems.append(support) allItems.append(support)
let faq = SettingsSearchableItem(id: .faq(0), title: strings.Settings_FAQ, alternate: [], icon: .faq, breadcrumbs: [], present: { context, _, present in let faq = SettingsSearchableItem(id: .faq(0), title: strings.Settings_FAQ, alternate: [], icon: .faq, breadcrumbs: [], present: { context, navigationController, present in
//return .push(ChatController(context: context, chatLocation: .peer(context.account.peerId)))
let _ = (cachedFaqInstantPage(context: context)
|> deliverOnMainQueue).start(next: { resolvedUrl in
openResolvedUrl(resolvedUrl, context: context, navigationController: navigationController, openPeer: { peer, navigation in
}, present: { controller, arguments in
present(.push, controller)
}, dismissInput: {})
})
}) })
allItems.append(faq) allItems.append(faq)

View File

@ -10,7 +10,7 @@ private final class SoftwareVideoThumbnailLayerNullAction: NSObject, CAAction {
} }
final class SoftwareVideoThumbnailLayer: CALayer { final class SoftwareVideoThumbnailLayer: CALayer {
var disposable: Disposable? var disposable = MetaDisposable()
var ready: (() -> Void)? { var ready: (() -> Void)? {
didSet { didSet {
@ -28,8 +28,7 @@ final class SoftwareVideoThumbnailLayer: CALayer {
self.masksToBounds = true self.masksToBounds = true
if let dimensions = fileReference.media.dimensions { if let dimensions = fileReference.media.dimensions {
self.disposable = (mediaGridMessageVideo(postbox: account.postbox, videoReference: fileReference) self.disposable.set((mediaGridMessageVideo(postbox: account.postbox, videoReference: fileReference)).start(next: { [weak self] transform in
|> deliverOn(Queue.concurrentDefaultQueue())).start(next: { [weak self] transform in
var boundingSize = dimensions.aspectFilled(CGSize(width: 93.0, height: 93.0)) var boundingSize = dimensions.aspectFilled(CGSize(width: 93.0, height: 93.0))
let imageSize = boundingSize let imageSize = boundingSize
boundingSize.width = min(200.0, boundingSize.width) boundingSize.width = min(200.0, boundingSize.width)
@ -42,7 +41,7 @@ final class SoftwareVideoThumbnailLayer: CALayer {
} }
} }
} }
}) }))
} }
} }
@ -51,7 +50,7 @@ final class SoftwareVideoThumbnailLayer: CALayer {
} }
deinit { deinit {
self.disposable?.dispose() self.disposable.dispose()
} }
override func action(forKey event: String) -> CAAction? { override func action(forKey event: String) -> CAAction? {