mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Experimental widget settings
This commit is contained in:
parent
70f5732f5f
commit
08040c1598
@ -5888,3 +5888,5 @@ Sorry for the inconvenience.";
|
|||||||
"Stats.Message.Views" = "Views";
|
"Stats.Message.Views" = "Views";
|
||||||
"Stats.Message.PublicShares" = "Public Shares";
|
"Stats.Message.PublicShares" = "Public Shares";
|
||||||
"Stats.Message.PrivateShares" = "Private Shares";
|
"Stats.Message.PrivateShares" = "Private Shares";
|
||||||
|
|
||||||
|
"ChatSettings.WidgetSettings" = "Widget";
|
||||||
|
@ -208,11 +208,13 @@ private final class LoadingShimmerNode: ASDisplayNode {
|
|||||||
public struct ItemListPeerItemEditing: Equatable {
|
public struct ItemListPeerItemEditing: Equatable {
|
||||||
public var editable: Bool
|
public var editable: Bool
|
||||||
public var editing: Bool
|
public var editing: Bool
|
||||||
|
public var canBeReordered: Bool
|
||||||
public var revealed: Bool?
|
public var revealed: Bool?
|
||||||
|
|
||||||
public init(editable: Bool, editing: Bool, revealed: Bool?) {
|
public init(editable: Bool, editing: Bool, canBeReordered: Bool = false, revealed: Bool?) {
|
||||||
self.editable = editable
|
self.editable = editable
|
||||||
self.editing = editing
|
self.editing = editing
|
||||||
|
self.canBeReordered = canBeReordered
|
||||||
self.revealed = revealed
|
self.revealed = revealed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -460,6 +462,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
private var layoutParams: (ItemListPeerItem, ListViewItemLayoutParams, ItemListNeighbors, Bool)?
|
private var layoutParams: (ItemListPeerItem, ListViewItemLayoutParams, ItemListNeighbors, Bool)?
|
||||||
|
|
||||||
private var editableControlNode: ItemListEditableControlNode?
|
private var editableControlNode: ItemListEditableControlNode?
|
||||||
|
private var reorderControlNode: ItemListEditableReorderControlNode?
|
||||||
|
|
||||||
override public var canBeSelected: Bool {
|
override public var canBeSelected: Bool {
|
||||||
if self.editableControlNode != nil || self.disabledOverlayNode != nil {
|
if self.editableControlNode != nil || self.disabledOverlayNode != nil {
|
||||||
@ -560,6 +563,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
let makeStatusLayout = TextNode.asyncLayout(self.statusNode)
|
let makeStatusLayout = TextNode.asyncLayout(self.statusNode)
|
||||||
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
||||||
let editableControlLayout = ItemListEditableControlNode.asyncLayout(self.editableControlNode)
|
let editableControlLayout = ItemListEditableControlNode.asyncLayout(self.editableControlNode)
|
||||||
|
let reorderControlLayout = ItemListEditableReorderControlNode.asyncLayout(self.reorderControlNode)
|
||||||
|
|
||||||
var currentDisabledOverlayNode = self.disabledOverlayNode
|
var currentDisabledOverlayNode = self.disabledOverlayNode
|
||||||
|
|
||||||
@ -761,12 +765,20 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
}
|
}
|
||||||
|
|
||||||
var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)?
|
var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)?
|
||||||
|
var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool, ContainedViewLayoutTransition) -> ItemListEditableReorderControlNode)?
|
||||||
|
|
||||||
let editingOffset: CGFloat
|
let editingOffset: CGFloat
|
||||||
|
var reorderInset: CGFloat = 0.0
|
||||||
if item.editing.editing {
|
if item.editing.editing {
|
||||||
let sizeAndApply = editableControlLayout(item.presentationData.theme, false)
|
let sizeAndApply = editableControlLayout(item.presentationData.theme, false)
|
||||||
editableControlSizeAndApply = sizeAndApply
|
editableControlSizeAndApply = sizeAndApply
|
||||||
editingOffset = sizeAndApply.0
|
editingOffset = sizeAndApply.0
|
||||||
|
|
||||||
|
if item.editing.canBeReordered {
|
||||||
|
let reorderSizeAndApply = reorderControlLayout(item.presentationData.theme)
|
||||||
|
reorderControlSizeAndApply = reorderSizeAndApply
|
||||||
|
reorderInset = reorderSizeAndApply.0
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
editingOffset = 0.0
|
editingOffset = 0.0
|
||||||
}
|
}
|
||||||
@ -804,6 +816,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
labelInset += 15.0
|
labelInset += 15.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
labelInset += reorderInset
|
||||||
|
|
||||||
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: labelAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 16.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: labelAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 16.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 - 12.0 - editingOffset - rightInset - labelLayout.size.width - labelInset, 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 - 12.0 - editingOffset - rightInset - labelLayout.size.width - labelInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
@ -931,6 +945,23 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let reorderControlSizeAndApply = reorderControlSizeAndApply {
|
||||||
|
if strongSelf.reorderControlNode == nil {
|
||||||
|
let reorderControlNode = reorderControlSizeAndApply.1(layout.contentSize.height, false, .immediate)
|
||||||
|
strongSelf.reorderControlNode = reorderControlNode
|
||||||
|
strongSelf.addSubnode(reorderControlNode)
|
||||||
|
reorderControlNode.alpha = 0.0
|
||||||
|
transition.updateAlpha(node: reorderControlNode, alpha: 1.0)
|
||||||
|
}
|
||||||
|
let reorderControlFrame = CGRect(origin: CGPoint(x: params.width + revealOffset - params.rightInset - reorderControlSizeAndApply.0, y: 0.0), size: CGSize(width: reorderControlSizeAndApply.0, height: layout.contentSize.height))
|
||||||
|
strongSelf.reorderControlNode?.frame = reorderControlFrame
|
||||||
|
} else if let reorderControlNode = strongSelf.reorderControlNode {
|
||||||
|
strongSelf.reorderControlNode = nil
|
||||||
|
transition.updateAlpha(node: reorderControlNode, alpha: 0.0, completion: { [weak reorderControlNode] _ in
|
||||||
|
reorderControlNode?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
let _ = titleApply()
|
let _ = titleApply()
|
||||||
let _ = statusApply()
|
let _ = statusApply()
|
||||||
let _ = labelApply()
|
let _ = labelApply()
|
||||||
@ -1293,6 +1324,13 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
shimmerNode.updateAbsoluteRect(rect, within: containerSize)
|
shimmerNode.updateAbsoluteRect(rect, within: containerSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override public func isReorderable(at point: CGPoint) -> Bool {
|
||||||
|
if let reorderControlNode = self.reorderControlNode, reorderControlNode.frame.contains(point), !self.isDisplayingRevealedOptions {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class ItemListPeerItemHeader: ListViewItemHeader {
|
public final class ItemListPeerItemHeader: ListViewItemHeader {
|
||||||
|
@ -226,7 +226,7 @@ public func mergeListsStableWithUpdates<T>(leftList: [T], rightList: [T], isLess
|
|||||||
}
|
}
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < rightList.count {
|
while i < rightList.count {
|
||||||
if updatedItems[i].1 != getId(rightList[i]) {
|
if updatedItems.count <= i || updatedItems[i].1 != getId(rightList[i]) {
|
||||||
updatedItems.insert((rightList[i], getId(rightList[i])), at: i)
|
updatedItems.insert((rightList[i], getId(rightList[i])), at: i)
|
||||||
var previousIndex: Int?
|
var previousIndex: Int?
|
||||||
for k in 0 ..< leftList.count {
|
for k in 0 ..< leftList.count {
|
||||||
|
@ -86,6 +86,7 @@ swift_library(
|
|||||||
"//submodules/OpenInExternalAppUI:OpenInExternalAppUI",
|
"//submodules/OpenInExternalAppUI:OpenInExternalAppUI",
|
||||||
"//submodules/AccountUtils:AccountUtils",
|
"//submodules/AccountUtils:AccountUtils",
|
||||||
"//submodules/AuthTransferUI:AuthTransferUI",
|
"//submodules/AuthTransferUI:AuthTransferUI",
|
||||||
|
"//submodules/WidgetSetupScreen:WidgetSetupScreen",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -12,6 +12,7 @@ import ItemListUI
|
|||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import OpenInExternalAppUI
|
import OpenInExternalAppUI
|
||||||
|
import WidgetSetupScreen
|
||||||
|
|
||||||
private final class DataAndStorageControllerArguments {
|
private final class DataAndStorageControllerArguments {
|
||||||
let openStorageUsage: () -> Void
|
let openStorageUsage: () -> Void
|
||||||
@ -27,9 +28,10 @@ private final class DataAndStorageControllerArguments {
|
|||||||
let toggleDownloadInBackground: (Bool) -> Void
|
let toggleDownloadInBackground: (Bool) -> Void
|
||||||
let openBrowserSelection: () -> Void
|
let openBrowserSelection: () -> Void
|
||||||
let openIntents: () -> Void
|
let openIntents: () -> Void
|
||||||
|
let openWidgetSettings: () -> 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, 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, openWidgetSettings: @escaping () -> Void, toggleEnableSensitiveContent: @escaping (Bool) -> Void) {
|
||||||
self.openStorageUsage = openStorageUsage
|
self.openStorageUsage = openStorageUsage
|
||||||
self.openNetworkUsage = openNetworkUsage
|
self.openNetworkUsage = openNetworkUsage
|
||||||
self.openProxy = openProxy
|
self.openProxy = openProxy
|
||||||
@ -43,6 +45,7 @@ private final class DataAndStorageControllerArguments {
|
|||||||
self.toggleDownloadInBackground = toggleDownloadInBackground
|
self.toggleDownloadInBackground = toggleDownloadInBackground
|
||||||
self.openBrowserSelection = openBrowserSelection
|
self.openBrowserSelection = openBrowserSelection
|
||||||
self.openIntents = openIntents
|
self.openIntents = openIntents
|
||||||
|
self.openWidgetSettings = openWidgetSettings
|
||||||
self.toggleEnableSensitiveContent = toggleEnableSensitiveContent
|
self.toggleEnableSensitiveContent = toggleEnableSensitiveContent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,6 +90,7 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||||||
case useLessVoiceData(PresentationTheme, String, String)
|
case useLessVoiceData(PresentationTheme, String, String)
|
||||||
case otherHeader(PresentationTheme, String)
|
case otherHeader(PresentationTheme, String)
|
||||||
case shareSheet(PresentationTheme, String)
|
case shareSheet(PresentationTheme, String)
|
||||||
|
case widgetSettings(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)
|
||||||
@ -106,7 +110,7 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||||||
return DataAndStorageSection.autoPlay.rawValue
|
return DataAndStorageSection.autoPlay.rawValue
|
||||||
case .voiceCallsHeader, .useLessVoiceData:
|
case .voiceCallsHeader, .useLessVoiceData:
|
||||||
return DataAndStorageSection.voiceCalls.rawValue
|
return DataAndStorageSection.voiceCalls.rawValue
|
||||||
case .otherHeader, .shareSheet, .saveIncomingPhotos, .saveEditedPhotos, .openLinksIn, .downloadInBackground, .downloadInBackgroundInfo:
|
case .otherHeader, .shareSheet, .widgetSettings, .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
|
||||||
@ -143,22 +147,24 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||||||
return 11
|
return 11
|
||||||
case .shareSheet:
|
case .shareSheet:
|
||||||
return 12
|
return 12
|
||||||
case .saveIncomingPhotos:
|
case .widgetSettings:
|
||||||
return 13
|
return 13
|
||||||
case .saveEditedPhotos:
|
case .saveIncomingPhotos:
|
||||||
return 14
|
return 14
|
||||||
case .openLinksIn:
|
case .saveEditedPhotos:
|
||||||
return 15
|
return 15
|
||||||
case .downloadInBackground:
|
case .openLinksIn:
|
||||||
return 16
|
return 16
|
||||||
case .downloadInBackgroundInfo:
|
case .downloadInBackground:
|
||||||
return 17
|
return 17
|
||||||
case .connectionHeader:
|
case .downloadInBackgroundInfo:
|
||||||
return 18
|
return 18
|
||||||
case .connectionProxy:
|
case .connectionHeader:
|
||||||
return 19
|
return 19
|
||||||
case .enableSensitiveContent:
|
case .connectionProxy:
|
||||||
return 20
|
return 20
|
||||||
|
case .enableSensitiveContent:
|
||||||
|
return 21
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,6 +248,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
case let .widgetSettings(text):
|
||||||
|
if case .widgetSettings(text) = rhs {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
case let .saveIncomingPhotos(lhsTheme, lhsText):
|
case let .saveIncomingPhotos(lhsTheme, lhsText):
|
||||||
if case let .saveIncomingPhotos(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .saveIncomingPhotos(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
@ -346,6 +358,10 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openIntents()
|
arguments.openIntents()
|
||||||
})
|
})
|
||||||
|
case let .widgetSettings(text):
|
||||||
|
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
||||||
|
arguments.openWidgetSettings()
|
||||||
|
})
|
||||||
case let .saveIncomingPhotos(theme, text):
|
case let .saveIncomingPhotos(theme, text):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openSaveIncomingPhotos()
|
arguments.openSaveIncomingPhotos()
|
||||||
@ -489,6 +505,9 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat
|
|||||||
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))
|
||||||
}
|
}
|
||||||
|
if #available(iOSApplicationExtension 14.0, iOS 14.0, *) {
|
||||||
|
entries.append(.widgetSettings(presentationData.strings.ChatSettings_WidgetSettings))
|
||||||
|
}
|
||||||
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))
|
||||||
@ -641,6 +660,9 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da
|
|||||||
}, openIntents: {
|
}, openIntents: {
|
||||||
let controller = intentsSettingsController(context: context)
|
let controller = intentsSettingsController(context: context)
|
||||||
pushControllerImpl?(controller)
|
pushControllerImpl?(controller)
|
||||||
|
}, openWidgetSettings: {
|
||||||
|
let controller = widgetSetupScreen(context: context)
|
||||||
|
pushControllerImpl?(controller)
|
||||||
}, toggleEnableSensitiveContent: { value in
|
}, toggleEnableSensitiveContent: { value in
|
||||||
let _ = (contentSettingsConfiguration.get()
|
let _ = (contentSettingsConfiguration.get()
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -57,6 +57,7 @@ private var telegramUIDeclaredEncodables: Void = {
|
|||||||
declareEncodable(IntentsSettings.self, f: { IntentsSettings(decoder: $0) })
|
declareEncodable(IntentsSettings.self, f: { IntentsSettings(decoder: $0) })
|
||||||
declareEncodable(CachedGeocode.self, f: { CachedGeocode(decoder: $0) })
|
declareEncodable(CachedGeocode.self, f: { CachedGeocode(decoder: $0) })
|
||||||
declareEncodable(ChatListFilterSettings.self, f: { ChatListFilterSettings(decoder: $0) })
|
declareEncodable(ChatListFilterSettings.self, f: { ChatListFilterSettings(decoder: $0) })
|
||||||
|
declareEncodable(WidgetSettings.self, f: { WidgetSettings(decoder: $0) })
|
||||||
return
|
return
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import WidgetItems
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import NotificationsPresentationData
|
import NotificationsPresentationData
|
||||||
import WidgetKit
|
import WidgetKit
|
||||||
|
import TelegramUIPreferences
|
||||||
|
|
||||||
final class WidgetDataContext {
|
final class WidgetDataContext {
|
||||||
private var currentAccount: Account?
|
private var currentAccount: Account?
|
||||||
@ -24,7 +25,7 @@ final class WidgetDataContext {
|
|||||||
return .single(.notAuthorized)
|
return .single(.notAuthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RecentPeers {
|
enum CombinedRecentPeers {
|
||||||
struct Unread {
|
struct Unread {
|
||||||
var count: Int32
|
var count: Int32
|
||||||
var isMuted: Bool
|
var isMuted: Bool
|
||||||
@ -34,19 +35,47 @@ final class WidgetDataContext {
|
|||||||
case peers(peers: [Peer], unread: [PeerId: Unread])
|
case peers(peers: [Peer], unread: [PeerId: Unread])
|
||||||
}
|
}
|
||||||
|
|
||||||
let recent: Signal<RecentPeers, NoError> = recentPeers(account: account)
|
let preferencesKey: PostboxViewKey = .preferences(keys: Set([
|
||||||
|> mapToSignal { recent -> Signal<RecentPeers, NoError> in
|
ApplicationSpecificPreferencesKeys.widgetSettings
|
||||||
|
]))
|
||||||
|
let sourcePeers: Signal<RecentPeers, NoError> = account.postbox.combinedView(keys: [
|
||||||
|
preferencesKey
|
||||||
|
])
|
||||||
|
|> mapToSignal { views -> Signal<RecentPeers, NoError> in
|
||||||
|
let widgetSettings: WidgetSettings
|
||||||
|
if let view = views.views[preferencesKey] as? PreferencesView, let value = view.values[ApplicationSpecificPreferencesKeys.widgetSettings] as? WidgetSettings {
|
||||||
|
widgetSettings = value
|
||||||
|
} else {
|
||||||
|
widgetSettings = .default
|
||||||
|
}
|
||||||
|
|
||||||
|
if widgetSettings.useHints {
|
||||||
|
return recentPeers(account: account)
|
||||||
|
} else {
|
||||||
|
return account.postbox.transaction { transaction -> RecentPeers in
|
||||||
|
return .peers(widgetSettings.peers.compactMap { peerId -> Peer? in
|
||||||
|
guard let peer = transaction.getPeer(peerId) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return peer
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let recent: Signal<CombinedRecentPeers, NoError> = sourcePeers
|
||||||
|
|> mapToSignal { recent -> Signal<CombinedRecentPeers, NoError> in
|
||||||
switch recent {
|
switch recent {
|
||||||
case .disabled:
|
case .disabled:
|
||||||
return .single(.disabled)
|
return .single(.disabled)
|
||||||
case let .peers(peers):
|
case let .peers(peers):
|
||||||
return combineLatest(queue: .mainQueue(), peers.filter { !$0.isDeleted }.map { account.postbox.peerView(id: $0.id)}) |> mapToSignal { peerViews -> Signal<RecentPeers, NoError> in
|
return combineLatest(queue: .mainQueue(), peers.filter { !$0.isDeleted }.map { account.postbox.peerView(id: $0.id)}) |> mapToSignal { peerViews -> Signal<CombinedRecentPeers, NoError> in
|
||||||
return account.postbox.unreadMessageCountsView(items: peerViews.map {
|
return account.postbox.unreadMessageCountsView(items: peerViews.map {
|
||||||
.peer($0.peerId)
|
.peer($0.peerId)
|
||||||
})
|
})
|
||||||
|> map { values -> RecentPeers in
|
|> map { values -> CombinedRecentPeers in
|
||||||
var peers: [Peer] = []
|
var peers: [Peer] = []
|
||||||
var unread: [PeerId: RecentPeers.Unread] = [:]
|
var unread: [PeerId: CombinedRecentPeers.Unread] = [:]
|
||||||
for peerView in peerViews {
|
for peerView in peerViews {
|
||||||
if let peer = peerViewMainPeer(peerView) {
|
if let peer = peerViewMainPeer(peerView) {
|
||||||
var isMuted: Bool = false
|
var isMuted: Bool = false
|
||||||
@ -61,7 +90,7 @@ final class WidgetDataContext {
|
|||||||
|
|
||||||
let unreadCount = values.count(for: .peer(peerView.peerId))
|
let unreadCount = values.count(for: .peer(peerView.peerId))
|
||||||
if let unreadCount = unreadCount, unreadCount > 0 {
|
if let unreadCount = unreadCount, unreadCount > 0 {
|
||||||
unread[peerView.peerId] = RecentPeers.Unread(count: Int32(unreadCount), isMuted: isMuted)
|
unread[peerView.peerId] = CombinedRecentPeers.Unread(count: Int32(unreadCount), isMuted: isMuted)
|
||||||
}
|
}
|
||||||
|
|
||||||
peers.append(peer)
|
peers.append(peer)
|
||||||
@ -80,13 +109,10 @@ final class WidgetDataContext {
|
|||||||
return .disabled
|
return .disabled
|
||||||
case let .peers(peers, unread):
|
case let .peers(peers, unread):
|
||||||
return .peers(WidgetDataPeers(accountPeerId: account.peerId.toInt64(), peers: peers.compactMap { peer -> WidgetDataPeer? in
|
return .peers(WidgetDataPeers(accountPeerId: account.peerId.toInt64(), peers: peers.compactMap { peer -> WidgetDataPeer? in
|
||||||
guard let user = peer as? TelegramUser else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var name: String = ""
|
var name: String = ""
|
||||||
var lastName: String?
|
var lastName: String?
|
||||||
|
|
||||||
|
if let user = peer as? TelegramUser {
|
||||||
if let firstName = user.firstName {
|
if let firstName = user.firstName {
|
||||||
name = firstName
|
name = firstName
|
||||||
lastName = user.lastName
|
lastName = user.lastName
|
||||||
@ -95,6 +121,9 @@ final class WidgetDataContext {
|
|||||||
} else if let phone = user.phone, !phone.isEmpty {
|
} else if let phone = user.phone, !phone.isEmpty {
|
||||||
name = phone
|
name = phone
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
name = peer.debugDisplayTitle
|
||||||
|
}
|
||||||
|
|
||||||
var badge: WidgetDataPeer.Badge?
|
var badge: WidgetDataPeer.Badge?
|
||||||
if let unreadValue = unread[peer.id], unreadValue.count > 0 {
|
if let unreadValue = unread[peer.id], unreadValue.count > 0 {
|
||||||
@ -104,7 +133,7 @@ final class WidgetDataContext {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return WidgetDataPeer(id: user.id.toInt64(), name: name, lastName: lastName, letters: user.displayLetters, avatarPath: smallestImageRepresentation(user.photo).flatMap { representation in
|
return WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in
|
||||||
return account.postbox.mediaBox.resourcePath(representation.resource)
|
return account.postbox.mediaBox.resourcePath(representation.resource)
|
||||||
}, badge: badge)
|
}, badge: badge)
|
||||||
}))
|
}))
|
||||||
|
@ -7,12 +7,14 @@ private enum ApplicationSpecificPreferencesKeyValues: Int32 {
|
|||||||
case voipDerivedState = 16
|
case voipDerivedState = 16
|
||||||
case chatArchiveSettings = 17
|
case chatArchiveSettings = 17
|
||||||
case chatListFilterSettings = 18
|
case chatListFilterSettings = 18
|
||||||
|
case widgetSettings = 19
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ApplicationSpecificPreferencesKeys {
|
public struct ApplicationSpecificPreferencesKeys {
|
||||||
public static let voipDerivedState = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.voipDerivedState.rawValue)
|
public static let voipDerivedState = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.voipDerivedState.rawValue)
|
||||||
public static let chatArchiveSettings = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.chatArchiveSettings.rawValue)
|
public static let chatArchiveSettings = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.chatArchiveSettings.rawValue)
|
||||||
public static let chatListFilterSettings = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.chatListFilterSettings.rawValue)
|
public static let chatListFilterSettings = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.chatListFilterSettings.rawValue)
|
||||||
|
public static let widgetSettings = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.widgetSettings.rawValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ApplicationSpecificSharedDataKeyValues: Int32 {
|
private enum ApplicationSpecificSharedDataKeyValues: Int32 {
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Postbox
|
||||||
|
import SwiftSignalKit
|
||||||
|
|
||||||
|
public struct WidgetSettings: PreferencesEntry, Equatable {
|
||||||
|
public var useHints: Bool
|
||||||
|
public var peers: [PeerId]
|
||||||
|
|
||||||
|
public static var `default`: WidgetSettings {
|
||||||
|
return WidgetSettings(
|
||||||
|
useHints: true,
|
||||||
|
peers: []
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(
|
||||||
|
useHints: Bool,
|
||||||
|
peers: [PeerId]
|
||||||
|
) {
|
||||||
|
self.useHints = useHints
|
||||||
|
self.peers = peers
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(decoder: PostboxDecoder) {
|
||||||
|
self.useHints = decoder.decodeBoolForKey("useHints", orElse: true)
|
||||||
|
self.peers = decoder.decodeInt64ArrayForKey("peers").map { PeerId($0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
|
encoder.encodeBool(self.useHints, forKey: "useHints")
|
||||||
|
encoder.encodeInt64Array(self.peers.map { $0.toInt64() }, forKey: "peers")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func isEqual(to: PreferencesEntry) -> Bool {
|
||||||
|
if let to = to as? WidgetSettings {
|
||||||
|
return self == to
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateWidgetSettingsInteractively(postbox: Postbox, _ f: @escaping (WidgetSettings) -> WidgetSettings) -> Signal<Never, NoError> {
|
||||||
|
return postbox.transaction { transaction -> Void in
|
||||||
|
updateWidgetSettingsInteractively(transaction: transaction, f)
|
||||||
|
}
|
||||||
|
|> ignoreValues
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateWidgetSettingsInteractively(transaction: Transaction, _ f: @escaping (WidgetSettings) -> WidgetSettings) {
|
||||||
|
transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.widgetSettings, { entry in
|
||||||
|
let currentSettings: WidgetSettings
|
||||||
|
if let entry = entry as? WidgetSettings {
|
||||||
|
currentSettings = entry
|
||||||
|
} else {
|
||||||
|
currentSettings = .default
|
||||||
|
}
|
||||||
|
return f(currentSettings)
|
||||||
|
})
|
||||||
|
}
|
25
submodules/WidgetSetupScreen/BUILD
Normal file
25
submodules/WidgetSetupScreen/BUILD
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "WidgetSetupScreen",
|
||||||
|
module_name = "WidgetSetupScreen",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.swift",
|
||||||
|
]),
|
||||||
|
deps = [
|
||||||
|
"//submodules/Display:Display",
|
||||||
|
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||||
|
"//submodules/Postbox:Postbox",
|
||||||
|
"//submodules/SyncCore:SyncCore",
|
||||||
|
"//submodules/TelegramCore:TelegramCore",
|
||||||
|
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||||
|
"//submodules/ItemListUI:ItemListUI",
|
||||||
|
"//submodules/ItemListPeerItem:ItemListPeerItem",
|
||||||
|
"//submodules/ItemListPeerActionItem:ItemListPeerActionItem",
|
||||||
|
"//submodules/PresentationDataUtils:PresentationDataUtils",
|
||||||
|
"//submodules/AccountContext:AccountContext",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
453
submodules/WidgetSetupScreen/Sources/WidgetSetupScreen.swift
Normal file
453
submodules/WidgetSetupScreen/Sources/WidgetSetupScreen.swift
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import SyncCore
|
||||||
|
import TelegramPresentationData
|
||||||
|
import TelegramUIPreferences
|
||||||
|
import ItemListUI
|
||||||
|
import PresentationDataUtils
|
||||||
|
import AccountContext
|
||||||
|
import ItemListPeerItem
|
||||||
|
import ItemListPeerActionItem
|
||||||
|
|
||||||
|
private final class Arguments {
|
||||||
|
let context: AccountContext
|
||||||
|
|
||||||
|
let updateUseHints: (Bool) -> Void
|
||||||
|
let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
|
||||||
|
let removePeer: (PeerId) -> Void
|
||||||
|
let addPeer: () -> Void
|
||||||
|
let openPeer: (PeerId) -> Void
|
||||||
|
|
||||||
|
init(context: AccountContext, updateUseHints: @escaping (Bool) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, addPeer: @escaping () -> Void, openPeer: @escaping (PeerId) -> Void) {
|
||||||
|
self.context = context
|
||||||
|
self.updateUseHints = updateUseHints
|
||||||
|
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
|
||||||
|
self.removePeer = removePeer
|
||||||
|
self.addPeer = addPeer
|
||||||
|
self.openPeer = openPeer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum WidgetSetupScreenEntry: ItemListNodeEntry {
|
||||||
|
enum Section: Int32 {
|
||||||
|
case mode
|
||||||
|
case peers
|
||||||
|
}
|
||||||
|
|
||||||
|
enum StableId: Hashable {
|
||||||
|
case useHints
|
||||||
|
case peersHeaderItem
|
||||||
|
case add
|
||||||
|
case peer(PeerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
case useHints(String, Bool)
|
||||||
|
case peersHeaderItem(String)
|
||||||
|
case peerItem(Int32, PresentationDateTimeFormat, PresentationPersonNameOrder, SelectivePrivacyPeer, ItemListPeerItemEditing, Bool)
|
||||||
|
case addItem(String, Bool)
|
||||||
|
|
||||||
|
var section: ItemListSectionId {
|
||||||
|
switch self {
|
||||||
|
case .useHints:
|
||||||
|
return Section.mode.rawValue
|
||||||
|
case .peersHeaderItem, .peerItem:
|
||||||
|
return Section.peers.rawValue
|
||||||
|
case .addItem:
|
||||||
|
return Section.peers.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stableId: StableId {
|
||||||
|
switch self {
|
||||||
|
case .useHints:
|
||||||
|
return .useHints
|
||||||
|
case .peersHeaderItem:
|
||||||
|
return .peersHeaderItem
|
||||||
|
case let .peerItem(_, _, _, peer, _, _):
|
||||||
|
return .peer(peer.peer.id)
|
||||||
|
case .addItem:
|
||||||
|
return .add
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sortIndex: Int32 {
|
||||||
|
switch self {
|
||||||
|
case .useHints:
|
||||||
|
return 0
|
||||||
|
case .peersHeaderItem:
|
||||||
|
return 1
|
||||||
|
case .addItem:
|
||||||
|
return 2
|
||||||
|
case let .peerItem(index, _, _, _, _, _):
|
||||||
|
return 10 + index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: WidgetSetupScreenEntry, rhs: WidgetSetupScreenEntry) -> Bool {
|
||||||
|
switch lhs {
|
||||||
|
case let .useHints(text, value):
|
||||||
|
if case .useHints(text, value) = rhs {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .peersHeaderItem(text):
|
||||||
|
if case .peersHeaderItem(text) = rhs {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .peerItem(lhsIndex, lhsDateTimeFormat, lhsNameOrder, lhsPeer, lhsEditing, lhsEnabled):
|
||||||
|
if case let .peerItem(rhsIndex, rhsDateTimeFormat, rhsNameOrder, rhsPeer, rhsEditing, rhsEnabled) = rhs {
|
||||||
|
if lhsIndex != rhsIndex {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhsDateTimeFormat != rhsDateTimeFormat {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhsNameOrder != rhsNameOrder {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhsPeer != rhsPeer {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhsEditing != rhsEditing {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhsEnabled != rhsEnabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .addItem(lhsText, lhsEditing):
|
||||||
|
if case let .addItem(rhsText, rhsEditing) = rhs, lhsText == rhsText, lhsEditing == rhsEditing {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func <(lhs: WidgetSetupScreenEntry, rhs: WidgetSetupScreenEntry) -> Bool {
|
||||||
|
return lhs.sortIndex < rhs.sortIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||||
|
let arguments = arguments as! Arguments
|
||||||
|
switch self {
|
||||||
|
case let .useHints(text, value):
|
||||||
|
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
|
arguments.updateUseHints(value)
|
||||||
|
})
|
||||||
|
case let .peersHeaderItem(text):
|
||||||
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
|
case let .peerItem(_, dateTimeFormat, nameOrder, peer, editing, enabled):
|
||||||
|
var text: ItemListPeerItemText = .none
|
||||||
|
if let group = peer.peer as? TelegramGroup {
|
||||||
|
text = .text(presentationData.strings.Conversation_StatusMembers(Int32(group.participantCount)))
|
||||||
|
} else if let channel = peer.peer as? TelegramChannel {
|
||||||
|
if let participantCount = peer.participantCount {
|
||||||
|
text = .text(presentationData.strings.Conversation_StatusMembers(Int32(participantCount)))
|
||||||
|
} else {
|
||||||
|
switch channel.info {
|
||||||
|
case .group:
|
||||||
|
text = .text(presentationData.strings.Group_Status)
|
||||||
|
case .broadcast:
|
||||||
|
text = .text(presentationData.strings.Channel_Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, context: arguments.context, peer: peer.peer, presence: nil, text: text, label: .none, editing: editing, switchValue: nil, enabled: enabled, selectable: true, sectionId: self.section, action: {
|
||||||
|
arguments.openPeer(peer.peer.id)
|
||||||
|
}, setPeerIdWithRevealedOptions: { previousId, id in
|
||||||
|
arguments.setPeerIdWithRevealedOptions(previousId, id)
|
||||||
|
}, removePeer: { peerId in
|
||||||
|
arguments.removePeer(peerId)
|
||||||
|
})
|
||||||
|
case let .addItem(text, editing):
|
||||||
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.plusIconImage(presentationData.theme), title: text, sectionId: self.section, editing: editing, action: {
|
||||||
|
arguments.addPeer()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct WidgetSetupScreenControllerState: Equatable {
|
||||||
|
var editing: Bool = false
|
||||||
|
var peerIdWithRevealedOptions: PeerId? = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private func selectivePrivacyPeersControllerEntries(presentationData: PresentationData, state: WidgetSetupScreenControllerState, useHints: Bool, peers: [SelectivePrivacyPeer]) -> [WidgetSetupScreenEntry] {
|
||||||
|
var entries: [WidgetSetupScreenEntry] = []
|
||||||
|
|
||||||
|
entries.append(.useHints("Show Recent Chats", useHints))
|
||||||
|
|
||||||
|
if !useHints {
|
||||||
|
entries.append(.peersHeaderItem(presentationData.strings.Privacy_ChatsTitle))
|
||||||
|
entries.append(.addItem(presentationData.strings.Privacy_AddNewPeer, state.editing))
|
||||||
|
var index: Int32 = 0
|
||||||
|
for peer in peers {
|
||||||
|
entries.append(.peerItem(index, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: state.editing, canBeReordered: state.editing, revealed: peer.peer.id == state.peerIdWithRevealedOptions), true))
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
public func widgetSetupScreen(context: AccountContext) -> ViewController {
|
||||||
|
let statePromise = ValuePromise(WidgetSetupScreenControllerState(), ignoreRepeated: true)
|
||||||
|
let stateValue = Atomic(value: WidgetSetupScreenControllerState())
|
||||||
|
let updateState: ((WidgetSetupScreenControllerState) -> WidgetSetupScreenControllerState) -> Void = { f in
|
||||||
|
statePromise.set(stateValue.modify { f($0) })
|
||||||
|
}
|
||||||
|
|
||||||
|
var dismissImpl: (() -> Void)?
|
||||||
|
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||||
|
var pushControllerImpl: ((ViewController) -> Void)?
|
||||||
|
|
||||||
|
let actionsDisposable = DisposableSet()
|
||||||
|
let addPeerDisposable = MetaDisposable()
|
||||||
|
actionsDisposable.add(addPeerDisposable)
|
||||||
|
|
||||||
|
let arguments = Arguments(context: context, updateUseHints: { value in
|
||||||
|
let _ = (updateWidgetSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||||
|
var settings = settings
|
||||||
|
settings.useHints = value
|
||||||
|
return settings
|
||||||
|
})
|
||||||
|
|> deliverOnMainQueue).start()
|
||||||
|
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||||
|
updateState { state in
|
||||||
|
var state = state
|
||||||
|
if (peerId == nil && fromPeerId == state.peerIdWithRevealedOptions) || (peerId != nil && fromPeerId == nil) {
|
||||||
|
state.peerIdWithRevealedOptions = peerId
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}, removePeer: { memberId in
|
||||||
|
|
||||||
|
}, addPeer: {
|
||||||
|
let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .peerSelection(searchChatList: true, searchGroups: true, searchChannels: false), options: []))
|
||||||
|
addPeerDisposable.set((controller.result
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak controller] result in
|
||||||
|
var peerIds: [ContactListPeerId] = []
|
||||||
|
if case let .result(peerIdsValue, _) = result {
|
||||||
|
peerIds = peerIdsValue
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = (updateWidgetSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||||
|
var settings = settings
|
||||||
|
for peerId in peerIds {
|
||||||
|
switch peerId {
|
||||||
|
case let .peer(peerId):
|
||||||
|
settings.peers.removeAll(where: { $0 == peerId })
|
||||||
|
settings.peers.insert(peerId, at: 0)
|
||||||
|
case .deviceContact:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return settings
|
||||||
|
})
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
controller?.dismiss()
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
|
}, openPeer: { peerId in
|
||||||
|
let _ = (context.account.postbox.transaction { transaction -> Peer? in
|
||||||
|
return transaction.getPeer(peerId)
|
||||||
|
}
|
||||||
|
|> deliverOnMainQueue).start(next: { peer in
|
||||||
|
guard let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pushControllerImpl?(controller)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
var previousPeers: [SelectivePrivacyPeer]?
|
||||||
|
var previousState: WidgetSetupScreenControllerState?
|
||||||
|
|
||||||
|
struct InputData {
|
||||||
|
var settings: WidgetSettings
|
||||||
|
var peers: [SelectivePrivacyPeer]
|
||||||
|
}
|
||||||
|
|
||||||
|
let preferencesKey: PostboxViewKey = .preferences(keys: Set([
|
||||||
|
ApplicationSpecificPreferencesKeys.widgetSettings
|
||||||
|
]))
|
||||||
|
|
||||||
|
let inputData: Signal<InputData, NoError> = context.account.postbox.combinedView(keys: [
|
||||||
|
preferencesKey
|
||||||
|
])
|
||||||
|
|> mapToSignal { views -> Signal<InputData, NoError> in
|
||||||
|
let widgetSettings: WidgetSettings
|
||||||
|
if let view = views.views[preferencesKey] as? PreferencesView, let value = view.values[ApplicationSpecificPreferencesKeys.widgetSettings] as? WidgetSettings {
|
||||||
|
widgetSettings = value
|
||||||
|
} else {
|
||||||
|
widgetSettings = .default
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.account.postbox.transaction { transaction -> InputData in
|
||||||
|
return InputData(
|
||||||
|
settings: widgetSettings,
|
||||||
|
peers: widgetSettings.peers.compactMap { peerId -> SelectivePrivacyPeer? in
|
||||||
|
guard let peer = transaction.getPeer(peerId) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return SelectivePrivacyPeer(peer: peer, participantCount: nil)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), inputData)
|
||||||
|
|> deliverOnMainQueue
|
||||||
|
|> map { presentationData, state, inputData -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
|
var rightNavigationButton: ItemListNavigationButton?
|
||||||
|
if !inputData.peers.isEmpty {
|
||||||
|
if state.editing {
|
||||||
|
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
|
||||||
|
updateState { state in
|
||||||
|
var state = state
|
||||||
|
state.editing = false
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: {
|
||||||
|
updateState { state in
|
||||||
|
var state = state
|
||||||
|
state.editing = true
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let previous = previousPeers
|
||||||
|
previousPeers = inputData.peers
|
||||||
|
|
||||||
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Widget"), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||||
|
|
||||||
|
var animated = true
|
||||||
|
if let previous = previous {
|
||||||
|
if previous.count <= inputData.peers.count {
|
||||||
|
if Set(previous.map { $0.peer.id }) == Set(inputData.peers.map { $0.peer.id }) && previous.map({ $0.peer.id }) != inputData.peers.map({ $0.peer.id }) {
|
||||||
|
} else {
|
||||||
|
animated = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
animated = false
|
||||||
|
}
|
||||||
|
if let previousState = previousState {
|
||||||
|
if previousState.editing != state.editing {
|
||||||
|
animated = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
animated = false
|
||||||
|
}
|
||||||
|
|
||||||
|
previousState = state
|
||||||
|
|
||||||
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: selectivePrivacyPeersControllerEntries(presentationData: presentationData, state: state, useHints: inputData.settings.useHints, peers: inputData.peers), style: .blocks, emptyStateItem: nil, animateChanges: animated)
|
||||||
|
|
||||||
|
return (controllerState, (listState, arguments))
|
||||||
|
}
|
||||||
|
|> afterDisposed {
|
||||||
|
actionsDisposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = ItemListController(context: context, state: signal)
|
||||||
|
dismissImpl = { [weak controller] in
|
||||||
|
if let controller = controller, let navigationController = controller.navigationController as? NavigationController {
|
||||||
|
navigationController.filterController(controller, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
presentControllerImpl = { [weak controller] c, p in
|
||||||
|
if let controller = controller {
|
||||||
|
controller.present(c, in: .window(.root), with: p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pushControllerImpl = { [weak controller] c in
|
||||||
|
if let navigationController = controller?.navigationController as? NavigationController {
|
||||||
|
navigationController.pushViewController(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [WidgetSetupScreenEntry]) -> Signal<Bool, NoError> in
|
||||||
|
let fromEntry = entries[fromIndex]
|
||||||
|
guard case let .peerItem(_, _, _, fromPeer, _, _) = fromEntry else {
|
||||||
|
return .single(false)
|
||||||
|
}
|
||||||
|
var referencePeerId: PeerId?
|
||||||
|
var beforeAll = false
|
||||||
|
var afterAll = false
|
||||||
|
if toIndex < entries.count {
|
||||||
|
switch entries[toIndex] {
|
||||||
|
case let .peerItem(_, _, _, peer, _, _):
|
||||||
|
referencePeerId = peer.peer.id
|
||||||
|
default:
|
||||||
|
if entries[toIndex] < fromEntry {
|
||||||
|
beforeAll = true
|
||||||
|
} else {
|
||||||
|
afterAll = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
afterAll = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.account.postbox.transaction { transaction -> Bool in
|
||||||
|
var updatedOrder = false
|
||||||
|
|
||||||
|
updateWidgetSettingsInteractively(transaction: transaction, { settings in
|
||||||
|
let initialPeers = settings.peers
|
||||||
|
var settings = settings
|
||||||
|
|
||||||
|
if let index = settings.peers.firstIndex(of: fromPeer.peer.id) {
|
||||||
|
settings.peers.remove(at: index)
|
||||||
|
}
|
||||||
|
if let referencePeerId = referencePeerId {
|
||||||
|
var inserted = false
|
||||||
|
for i in 0 ..< settings.peers.count {
|
||||||
|
if settings.peers[i] == referencePeerId {
|
||||||
|
if fromIndex < toIndex {
|
||||||
|
settings.peers.insert(fromPeer.peer.id, at: i + 1)
|
||||||
|
} else {
|
||||||
|
settings.peers.insert(fromPeer.peer.id, at: i)
|
||||||
|
}
|
||||||
|
inserted = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !inserted {
|
||||||
|
settings.peers.append(fromPeer.peer.id)
|
||||||
|
}
|
||||||
|
} else if beforeAll {
|
||||||
|
settings.peers.insert(fromPeer.peer.id, at: 0)
|
||||||
|
} else if afterAll {
|
||||||
|
settings.peers.append(fromPeer.peer.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if initialPeers != settings.peers {
|
||||||
|
updatedOrder = true
|
||||||
|
}
|
||||||
|
return settings
|
||||||
|
})
|
||||||
|
|
||||||
|
return updatedOrder
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return controller
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user