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)
|> map { resolvedUrl -> [SettingsSearchableItem] in
var results: [SettingsSearchableItem] = []
var nextIndex: Int = 1
if case let .instantView(webPage, _) = resolvedUrl {
if case let .Loaded(content) = webPage.content, let instantPage = content.instantPage {
var processingQuestions = false
@ -83,14 +84,15 @@ func faqSearchableItems(context: AccountContext) -> Signal<[SettingsSearchableIt
if results.isEmpty {
processingQuestions = true
}
case let .anchor(anchor):
currentAnchor = anchor
case let .header(text):
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
present(.push, InstantPageController(context: context, webPage: webPage, sourcePeerType: .channel, anchor: anchor))
}))
}
// case let .anchor(anchor):
// currentAnchor = anchor
// case let .header(text):
// if let anchor = currentAnchor {
// 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))
// }))
// nextIndex += 1
// }
default:
break
}
@ -107,9 +109,20 @@ func faqSearchableItems(context: AccountContext) -> Signal<[SettingsSearchableIt
for item in items {
if case let .text(itemText, _) = item, case let .url(text, url, _) = itemText {
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))
}))
})
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)
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 {
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
@ -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
if case let .fetchStatus(fetchStatus) = status.mediaStatus {
if case .Local = fetchStatus {
@ -511,6 +520,8 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
} else {
progressRequired = true
}
} else if isBuffering ?? false {
progressRequired = true
}
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
switch status.mediaStatus {
case var .fetchStatus(fetchStatus):
@ -550,7 +552,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
case let .Fetching(_, progress):
if let isBuffering = 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 {
state = .none
}
@ -573,7 +575,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
}
default:
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 {
state = .none
}

View File

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

View File

@ -400,22 +400,22 @@ func editSettingsController(context: AccountContext, currentName: ItemListAvatar
let peerView = context.account.viewTracker.peerView(context.account.peerId)
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), peerView)
|> map { presentationData, state, view -> (ItemListControllerState, (ItemListNodeState<SettingsEntry>, SettingsEntry.ItemGenerationArguments)) in
let rightNavigationButton: ItemListNavigationButton
if state.updatingName != nil || state.updatingBioText {
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
} else {
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
arguments.saveEditingState()
})
}
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.EditProfile_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(entries: editSettingsEntries(presentationData: presentationData, state: state, view: view, canAddAccounts: canAddAccounts), style: .blocks, ensureVisibleItemTag: focusOnItemTag)
return (controllerState, (listState, arguments))
} |> afterDisposed {
actionsDisposable.dispose()
|> map { presentationData, state, view -> (ItemListControllerState, (ItemListNodeState<SettingsEntry>, SettingsEntry.ItemGenerationArguments)) in
let rightNavigationButton: ItemListNavigationButton
if state.updatingName != nil || state.updatingBioText {
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
} else {
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
arguments.saveEditingState()
})
}
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.EditProfile_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(entries: editSettingsEntries(presentationData: presentationData, state: state, view: view, canAddAccounts: canAddAccounts), style: .blocks, ensureVisibleItemTag: focusOnItemTag)
return (controllerState, (listState, arguments))
} |> afterDisposed {
actionsDisposable.dispose()
}
let controller = ItemListController(context: context, state: signal, tabBarItem: nil)
@ -527,12 +527,12 @@ func editSettingsController(context: AccountContext, currentName: ItemListAvatar
return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations)
}) |> deliverOnMainQueue).start(next: { result in
switch result {
case .complete:
updateState {
$0.withUpdatedUpdatingAvatar(nil)
}
case .progress:
break
case .complete:
updateState {
$0.withUpdatedUpdatingAvatar(nil)
}
case .progress:
break
}
}))
}

View File

@ -267,7 +267,7 @@ final class GridMessageItemNode: GridItemNode {
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
self.updateNavigationBar()
}
var didSetScrollOffset = false
if resetOffset {
var contentOffset = CGPoint(x: 0.0, y: -self.scrollNode.view.contentInset.top)
if let state = self.initialState {
self.setupScrollOffsetOnLayout = false
didSetScrollOffset = true
contentOffset = CGPoint(x: 0.0, y: CGFloat(state.contentOffset))
}
else if let anchor = self.initialAnchor, !anchor.isEmpty {
if let items = self.currentLayout?.items {
self.setupScrollOffsetOnLayout = false
didSetScrollOffset = true
if let (item, lineOffset, _, _) = self.findAnchorItem(anchor, items: items) {
contentOffset = CGPoint(x: 0.0, y: item.frame.minY + lineOffset - self.scrollNode.view.contentInset.top)
}
}
} else {
self.setupScrollOffsetOnLayout = false
didSetScrollOffset = true
}
self.scrollNode.view.contentOffset = contentOffset
if didSetScrollOffset {
self.previousContentOffset = contentOffset
self.updateNavigationBar()
self.setupScrollOffsetOnLayout = false
}
}
if shouldUpdateVisibleItems {
self.updateVisibleItems(visibleBounds: self.scrollNode.view.bounds)
@ -668,7 +674,9 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
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
} else {
delta = 0.0
@ -700,6 +708,10 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
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)
if containerLayout.safeInsets.top.isZero {

View File

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

View File

@ -5,14 +5,6 @@ import Postbox
import TelegramCore
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 {
let addAccount: () -> Void
let setPasscode: () -> Void
@ -76,23 +68,23 @@ private enum LogoutOptionsEntry: ItemListNodeEntry, Equatable {
case let .alternativeHeader(theme, title):
return ItemListSectionHeaderItem(theme: theme, text: title, sectionId: self.section)
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()
})
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()
})
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()
})
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()
})
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()
})
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
if maybeData.complete {
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: [])
return .single((nil, loadedData == nil ? nil : (loadedData!, maybeData.path), true))
} else {
let thumbnail: Signal<Data?, NoError>

View File

@ -2,6 +2,7 @@ import Foundation
import Display
struct PresentationResourcesSettings {
static let editProfile = UIImage(bundleImageName: "Settings/MenuIcons/EditProfile")?.precomposed()
static let proxy = UIImage(bundleImageName: "Settings/MenuIcons/Proxy")?.precomposed()
static let savedMessages = UIImage(bundleImageName: "Settings/MenuIcons/SavedMessages")?.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 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 updateSettingsDisposable = MetaDisposable()
actionsDisposable.add(updateSettingsDisposable)
let arguments = SelectivePrivacySettingsControllerArguments(context: context, updateType: { type in
updateState {
@ -721,71 +720,81 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
actionsDisposable.dispose()
}
let controller = ItemListController(context: context, state: signal)
controller.didDisappear = { [weak controller] _ in
if let controller = controller, controller.navigationController?.viewControllers.firstIndex(of: controller) == nil {
var wasSaving = false
var settings: SelectivePrivacySettings?
var callP2PSettings: SelectivePrivacySettings?
var callDataSaving: VoiceCallDataSaving?
var callIntegrationEnabled: Bool?
updateState { state in
wasSaving = state.saving
callDataSaving = state.callDataSaving
callIntegrationEnabled = state.callIntegrationEnabled
switch state.setting {
case .everybody:
settings = SelectivePrivacySettings.enableEveryone(disableFor: state.disableFor)
case .contacts:
settings = SelectivePrivacySettings.enableContacts(enableFor: state.enableFor, disableFor: state.disableFor)
case .nobody:
settings = SelectivePrivacySettings.disableEveryone(enableFor: state.enableFor)
}
if case .voiceCalls = kind, let callP2PMode = state.callP2PMode, let disableFor = state.callP2PDisableFor, let enableFor = state.callP2PEnableFor {
switch callP2PMode {
case .everybody:
callP2PSettings = SelectivePrivacySettings.enableEveryone(disableFor: disableFor)
case .contacts:
callP2PSettings = SelectivePrivacySettings.enableContacts(enableFor: enableFor, disableFor: disableFor)
case .nobody:
callP2PSettings = SelectivePrivacySettings.disableEveryone(enableFor: enableFor)
}
}
return state.withUpdatedSaving(true)
let update: (Bool) -> Void = { save in
var wasSaving = false
var settings: SelectivePrivacySettings?
var callP2PSettings: SelectivePrivacySettings?
var callDataSaving: VoiceCallDataSaving?
var callIntegrationEnabled: Bool?
updateState { state in
wasSaving = state.saving
callDataSaving = state.callDataSaving
callIntegrationEnabled = state.callIntegrationEnabled
switch state.setting {
case .everybody:
settings = SelectivePrivacySettings.enableEveryone(disableFor: state.disableFor)
case .contacts:
settings = SelectivePrivacySettings.enableContacts(enableFor: state.enableFor, disableFor: state.disableFor)
case .nobody:
settings = SelectivePrivacySettings.disableEveryone(enableFor: state.enableFor)
}
if let settings = settings, !wasSaving {
let type: UpdateSelectiveAccountPrivacySettingsType
switch kind {
case .presence:
type = .presence
case .groupInvitations:
type = .groupInvitations
case .voiceCalls:
type = .voiceCalls
case .profilePhoto:
type = .profilePhoto
case .forwards:
type = .forwards
}
let updateSettingsSignal = updateSelectiveAccountPrivacySettings(account: context.account, type: type, settings: settings)
var updateCallP2PSettingsSignal: Signal<Void, NoError> = Signal.complete()
if let callP2PSettings = callP2PSettings {
updateCallP2PSettingsSignal = updateSelectiveAccountPrivacySettings(account: context.account, type: .voiceCallsP2P, settings: callP2PSettings)
}
updateSettingsDisposable.set((combineLatest(updateSettingsSignal, updateCallP2PSettingsSignal) |> deliverOnMainQueue).start(completed: {
}))
if case .voiceCalls = kind, let dataSaving = callDataSaving, let callP2PSettings = callP2PSettings, let systemIntegrationEnabled = callIntegrationEnabled {
updated(settings, (callP2PSettings, VoiceCallSettings(dataSaving: dataSaving, enableSystemIntegration: systemIntegrationEnabled)))
} else {
updated(settings, nil)
if case .voiceCalls = kind, let callP2PMode = state.callP2PMode, let disableFor = state.callP2PDisableFor, let enableFor = state.callP2PEnableFor {
switch callP2PMode {
case .everybody:
callP2PSettings = SelectivePrivacySettings.enableEveryone(disableFor: disableFor)
case .contacts:
callP2PSettings = SelectivePrivacySettings.enableContacts(enableFor: enableFor, disableFor: disableFor)
case .nobody:
callP2PSettings = SelectivePrivacySettings.disableEveryone(enableFor: enableFor)
}
}
return state.withUpdatedSaving(true)
}
if let settings = settings, !wasSaving {
let type: UpdateSelectiveAccountPrivacySettingsType
switch kind {
case .presence:
type = .presence
case .groupInvitations:
type = .groupInvitations
case .voiceCalls:
type = .voiceCalls
case .profilePhoto:
type = .profilePhoto
case .forwards:
type = .forwards
}
let updateSettingsSignal = updateSelectiveAccountPrivacySettings(account: context.account, type: type, settings: settings)
var updateCallP2PSettingsSignal: Signal<Void, NoError> = Signal.complete()
if let callP2PSettings = callP2PSettings {
updateCallP2PSettingsSignal = updateSelectiveAccountPrivacySettings(account: context.account, type: .voiceCallsP2P, settings: callP2PSettings)
}
updateSettingsDisposable.set((combineLatest(updateSettingsSignal, updateCallP2PSettingsSignal) |> deliverOnMainQueue).start(completed: {
}))
if case .voiceCalls = kind, let dataSaving = callDataSaving, let callP2PSettings = callP2PSettings, let systemIntegrationEnabled = callIntegrationEnabled {
updated(settings, (callP2PSettings, VoiceCallSettings(dataSaving: dataSaving, enableSystemIntegration: systemIntegrationEnabled)))
} else {
updated(settings, nil)
}
}
}
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)
}
}

View File

@ -1078,7 +1078,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
presentControllerImpl?(v, a)
}, pushController: { v in
pushControllerImpl?(v)
}, getNavigationController: getNavigationControllerImpl)
}, getNavigationController: getNavigationControllerImpl, exceptionsList: notifyExceptions.get())
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))

View File

@ -19,6 +19,8 @@ extension NavigationBarSearchContentNode: ItemListControllerSearchNavigationCont
extension SettingsSearchableItemIcon {
func image() -> UIImage? {
switch self {
case .profile:
return PresentationResourcesSettings.editProfile
case .proxy:
return PresentationResourcesSettings.proxy
case .savedMessages:
@ -58,12 +60,13 @@ final class SettingsSearchItem: ItemListControllerSearch {
let presentController: (ViewController, Any?) -> Void
let pushController: (ViewController) -> Void
let getNavigationController: (() -> NavigationController?)?
let exceptionsList: Signal<NotificationExceptionsList?, NoError>
private var updateActivity: ((Bool) -> Void)?
private var activity: ValuePromise<Bool> = ValuePromise(ignoreRepeated: false)
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.theme = theme
self.placeholder = placeholder
@ -72,6 +75,7 @@ final class SettingsSearchItem: ItemListControllerSearch {
self.presentController = presentController
self.pushController = pushController
self.getNavigationController = getNavigationController
self.exceptionsList = exceptionsList
self.activityDisposable.set((activity.get() |> mapToSignal { value -> Signal<Bool, NoError> in
if value {
return .single(value) |> delay(0.2, queue: Queue.mainQueue())
@ -135,7 +139,7 @@ final class SettingsSearchItem: ItemListControllerSearch {
pushController(c)
}, presentController: { c, a in
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)>
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.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings))
@ -235,14 +239,20 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
self.addSubnode(self.dimNode)
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
return self.searchQuery.get()
|> mapToSignal { query -> Signal<(String, [SettingsSearchableItem])?, NoError> in
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)
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 {
return .single(nil)
}
@ -404,16 +414,18 @@ private final class SettingsSearchItemNode: ItemListControllerSearchNode {
let pushController: (ViewController) -> Void
let presentController: (ViewController, Any?) -> Void
let getNavigationController: (() -> NavigationController?)?
let exceptionsList: Signal<NotificationExceptionsList?, NoError>
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.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.cancel = cancel
self.pushController = pushController
self.presentController = presentController
self.getNavigationController = getNavigationController
self.cancel = cancel
self.exceptionsList = exceptionsList
super.init()
}
@ -428,7 +440,7 @@ private final class SettingsSearchItemNode: ItemListControllerSearchNode {
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 {
result.present(strongSelf.context, strongSelf.getNavigationController?(), { [weak self] mode, controller in
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()
})

View File

@ -7,6 +7,7 @@ import TelegramCore
private let maximumNumberOfAccounts = 3
enum SettingsSearchableItemIcon {
case profile
case proxy
case savedMessages
case calls
@ -55,7 +56,7 @@ struct 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 presentProfileSettings: (AccountContext, @escaping (SettingsSearchableItemPresentation, ViewController) -> Void, EditSettingsEntryTag?) -> Void = { context, present, itemTag in
@ -87,7 +88,7 @@ private func profileSearchableItems(context: AccountContext, canAddAccount: Bool
}))
if canAddAccount {
items.append(SettingsSearchableItem(id: .profile(4), title: strings.Settings_AddAccount, alternate: [], icon: icon, breadcrumbs: [strings.EditProfile_Title], present: { context, _, present in
let isTestingEnvironment = context.account.testingEnvironment
let isTestingEnvironment = context.account.testingEnvironment
context.sharedContext.beginNewAuth(testingEnvironment: isTestingEnvironment)
}))
}
@ -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 strings = context.sharedContext.currentPresentationData.with { $0 }.strings
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 [
@ -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))
|> take(1)
let canAddAccount = activeAccountsAndPeers(context: context)
@ -483,8 +484,8 @@ func settingsSearchableItems(context: AccountContext) -> Signal<[SettingsSearcha
|> map { accountsAndPeers -> Bool in
return accountsAndPeers.1.count + 1 < maximumNumberOfAccounts
}
return combineLatest(watchAppInstalled, canAddAccount)
|> map { watchAppInstalled, canAddAccount in
return combineLatest(watchAppInstalled, canAddAccount, exceptionsList)
|> map { watchAppInstalled, canAddAccount, exceptionsList in
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
var allItems: [SettingsSearchableItem] = []
@ -503,7 +504,7 @@ func settingsSearchableItems(context: AccountContext) -> Signal<[SettingsSearcha
let stickerItems = stickerSearchableItems(context: context)
allItems.append(contentsOf: stickerItems)
let notificationItems = notificationSearchableItems(context: context, notifyExceptions: .complete())
let notificationItems = notificationSearchableItems(context: context, exceptionsList: exceptionsList)
allItems.append(contentsOf: notificationItems)
let privacyItems = privacySearchableItems(context: context)
@ -535,13 +536,25 @@ func settingsSearchableItems(context: AccountContext) -> Signal<[SettingsSearcha
})
allItems.append(passport)
let support = SettingsSearchableItem(id: .support(0), title: strings.Settings_Support, alternate: ["Support"], icon: .support, breadcrumbs: [], present: { context, _, present in
//return .push(ChatController(context: context, chatLocation: .peer(context.account.peerId)))
let support = SettingsSearchableItem(id: .support(0), title: strings.Settings_Support, alternate: [], icon: .support, breadcrumbs: [], present: { context, _, present in
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)
let faq = SettingsSearchableItem(id: .faq(0), title: strings.Settings_FAQ, alternate: [], icon: .faq, breadcrumbs: [], present: { context, _, present in
//return .push(ChatController(context: context, chatLocation: .peer(context.account.peerId)))
let faq = SettingsSearchableItem(id: .faq(0), title: strings.Settings_FAQ, alternate: [], icon: .faq, breadcrumbs: [], present: { context, navigationController, present in
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)

View File

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