- Forum improvements

- Global autoremove settings
This commit is contained in:
Ali
2022-11-25 23:08:40 +04:00
parent b358391e1d
commit 1656ecff49
82 changed files with 2495 additions and 1059 deletions

View File

@@ -26,6 +26,9 @@ import MapResourceToAvatarSizes
import ItemListAddressItem
import ItemListVenueItem
import LegacyMediaPickerUI
import ContextUI
import ChatTimerScreen
import AsyncDisplayKit
private struct CreateGroupArguments {
let context: AccountContext
@@ -35,10 +38,12 @@ private struct CreateGroupArguments {
let changeProfilePhoto: () -> Void
let changeLocation: () -> Void
let updateWithVenue: (TelegramMediaMap) -> Void
let updateAutoDelete: () -> Void
}
private enum CreateGroupSection: Int32 {
case info
case autoDelete
case members
case location
case venues
@@ -46,17 +51,11 @@ private enum CreateGroupSection: Int32 {
private enum CreateGroupEntryTag: ItemListItemTag {
case info
case autoDelete
func isEqual(to other: ItemListItemTag) -> Bool {
if let other = other as? CreateGroupEntryTag {
switch self {
case .info:
if case .info = other {
return true
} else {
return false
}
}
return self == other
} else {
return false
}
@@ -66,6 +65,8 @@ private enum CreateGroupEntryTag: ItemListItemTag {
private enum CreateGroupEntry: ItemListNodeEntry {
case groupInfo(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer?, ItemListAvatarAndNameInfoItemState, ItemListAvatarAndNameInfoItemUpdatingAvatar?)
case setProfilePhoto(PresentationTheme, String)
case autoDelete(title: String, value: String)
case autoDeleteInfo(String)
case member(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, PeerPresence?)
case locationHeader(PresentationTheme, String)
case location(PresentationTheme, PeerGeoLocation)
@@ -78,6 +79,8 @@ private enum CreateGroupEntry: ItemListNodeEntry {
switch self {
case .groupInfo, .setProfilePhoto:
return CreateGroupSection.info.rawValue
case .autoDelete, .autoDeleteInfo:
return CreateGroupSection.autoDelete.rawValue
case .member:
return CreateGroupSection.members.rawValue
case .locationHeader, .location, .changeLocation, .locationInfo:
@@ -93,8 +96,12 @@ private enum CreateGroupEntry: ItemListNodeEntry {
return 0
case .setProfilePhoto:
return 1
case .autoDelete:
return 2
case .autoDeleteInfo:
return 3
case let .member(index, _, _, _, _, _, _):
return 2 + index
return 4 + index
case .locationHeader:
return 10000
case .location:
@@ -146,6 +153,18 @@ private enum CreateGroupEntry: ItemListNodeEntry {
} else {
return false
}
case let .autoDelete(title, value):
if case .autoDelete(title, value) = rhs {
return true
} else {
return false
}
case let .autoDeleteInfo(text):
if case .autoDeleteInfo(text) = rhs {
return true
} else {
return false
}
case let .member(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameDisplayOrder, lhsPeer, lhsPresence):
if case let .member(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameDisplayOrder, rhsPeer, rhsPresence) = rhs {
if lhsIndex != rhsIndex {
@@ -244,6 +263,12 @@ private enum CreateGroupEntry: ItemListNodeEntry {
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
arguments.changeProfilePhoto()
})
case let .autoDelete(text, value):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .optionArrows, action: {
arguments.updateAutoDelete()
}, tag: CreateGroupEntryTag.autoDelete)
case let .autoDeleteInfo(text):
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
case let .member(_, _, _, dateTimeFormat, nameDisplayOrder, peer, presence):
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: EnginePeer(peer), presence: presence.flatMap(EnginePeer.Presence.init), text: .presence, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in })
case let .locationHeader(_, title):
@@ -273,25 +298,7 @@ private struct CreateGroupState: Equatable {
var nameSetFromVenue: Bool
var avatar: ItemListAvatarAndNameInfoItemUpdatingAvatar?
var location: PeerGeoLocation?
static func ==(lhs: CreateGroupState, rhs: CreateGroupState) -> Bool {
if lhs.creating != rhs.creating {
return false
}
if lhs.editingName != rhs.editingName {
return false
}
if lhs.nameSetFromVenue != rhs.nameSetFromVenue {
return false
}
if lhs.avatar != rhs.avatar {
return false
}
if lhs.location != rhs.location {
return false
}
return true
}
var autoremoveTimeout: Int32
}
private func createGroupEntries(presentationData: PresentationData, state: CreateGroupState, peerIds: [PeerId], view: MultiplePeersView, venues: [TelegramMediaMap]?) -> [CreateGroupEntry] {
@@ -303,6 +310,16 @@ private func createGroupEntries(presentationData: PresentationData, state: Creat
entries.append(.groupInfo(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, groupInfoState, state.avatar))
//TODO:localize
let autoRemoveText: String
if state.autoremoveTimeout == 0 {
autoRemoveText = "Off"
} else {
autoRemoveText = timeIntervalString(strings: presentationData.strings, value: state.autoremoveTimeout)
}
entries.append(.autoDelete(title: "Auto-Delete Messages", value: autoRemoveText))
entries.append(.autoDeleteInfo("Automatically delete messages sent in this group for everyone after a period of time."))
var peers: [Peer] = []
for peerId in peerIds {
if let peer = view.peers[peerId] {
@@ -365,7 +382,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
location = PeerGeoLocation(latitude: latitude, longitude: longitude, address: address ?? "")
}
let initialState = CreateGroupState(creating: false, editingName: .title(title: initialTitle ?? "", type: .group), nameSetFromVenue: false, avatar: nil, location: location)
let initialState = CreateGroupState(creating: false, editingName: .title(title: initialTitle ?? "", type: .group), nameSetFromVenue: false, avatar: nil, location: location, autoremoveTimeout: 0)
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
let stateValue = Atomic(value: initialState)
let updateState: ((CreateGroupState) -> CreateGroupState) -> Void = { f in
@@ -375,9 +392,11 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
var replaceControllerImpl: ((ViewController) -> Void)?
var dismissImpl: (() -> Void)?
var presentControllerImpl: ((ViewController, Any?) -> Void)?
var presentInGlobalOverlay: ((ViewController) -> Void)?
var pushImpl: ((ViewController) -> Void)?
var endEditingImpl: (() -> Void)?
var ensureItemVisibleImpl: ((CreateGroupEntryTag, Bool) -> Void)?
var findAutoremoveReferenceNode: (() -> ItemListDisclosureItemNode?)?
let actionsDisposable = DisposableSet()
@@ -422,29 +441,13 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
}
endEditingImpl?()
let autoremoveTimeout = stateValue.with({ $0 }).autoremoveTimeout
let ttlPeriod: Int32? = autoremoveTimeout == 0 ? nil : autoremoveTimeout
let createSignal: Signal<PeerId?, CreateGroupError>
switch mode {
case .generic:
if title.contains("*forum") {
createSignal = context.engine.peers.createSupergroup(title: title, description: nil)
|> map(Optional.init)
|> mapError { error -> CreateGroupError in
switch error {
case .generic:
return .generic
case .restricted:
return .restricted
case .tooMuchJoined:
return .tooMuchJoined
case .tooMuchLocationBasedGroups:
return .tooMuchLocationBasedGroups
case let .serverProvided(error):
return .serverProvided(error)
}
}
} else {
createSignal = context.engine.peers.createGroup(title: title, peerIds: peerIds, ttlPeriod: nil)
}
createSignal = context.engine.peers.createGroup(title: title, peerIds: peerIds, ttlPeriod: ttlPeriod)
case .supergroup:
createSignal = context.engine.peers.createSupergroup(title: title, description: nil)
|> map(Optional.init)
@@ -821,6 +824,86 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
}
})
ensureItemVisibleImpl?(.info, true)
}, updateAutoDelete: {
var subItems: [ContextMenuItem] = []
let currentValue = stateValue.with({ $0 }).autoremoveTimeout
let applyValue: (Int32) -> Void = { value in
updateState { state in
var state = state
state.autoremoveTimeout = value
return state
}
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
//TODO:localize
subItems.append(.action(ContextMenuActionItem(text: "Off", icon: { theme in
if currentValue == 0 {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
}
}, action: { _, f in
applyValue(0)
f(.default)
})))
subItems.append(.separator)
var presetValues: [Int32] = [
1 * 24 * 60 * 60,
7 * 24 * 60 * 60,
31 * 24 * 60 * 60
]
if currentValue != 0 && !presetValues.contains(currentValue) {
presetValues.append(currentValue)
presetValues.sort()
}
for value in presetValues {
subItems.append(.action(ContextMenuActionItem(text: timeIntervalString(strings: presentationData.strings, value: value), icon: { theme in
if currentValue == value {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
}
}, action: { _, f in
applyValue(value)
f(.default)
})))
}
//TODO:localize
subItems.append(.action(ContextMenuActionItem(text: "Set Custom Time...", icon: { _ in
return nil
}, action: { _, f in
f(.default)
let controller = ChatTimerScreen(context: context, updatedPresentationData: nil, style: .default, mode: .autoremove, currentTime: currentValue == 0 ? nil : currentValue, dismissByTapOutside: true, completion: { value in
applyValue(value)
})
endEditingImpl?()
presentControllerImpl?(controller, nil)
})))
if let sourceNode = findAutoremoveReferenceNode?() {
let items: Signal<ContextController.Items, NoError> = .single(ContextController.Items(content: .list(subItems)))
let source: ContextContentSource = .reference(CreateGroupContextReferenceContentSource(sourceView: sourceNode.labelNode.view))
let contextController = ContextController(
account: context.account,
presentationData: presentationData,
source: source,
items: items,
gesture: nil
)
sourceNode.updateHasContextMenu(hasContextMenu: true)
contextController.dismissed = { [weak sourceNode] in
sourceNode?.updateHasContextMenu(hasContextMenu: false)
}
presentInGlobalOverlay?(contextController)
}
})
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), context.account.postbox.multiplePeersView(peerIds), .single(nil) |> then(addressPromise.get()), .single(nil) |> then(venuesPromise.get()))
@@ -856,6 +939,9 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
presentControllerImpl = { [weak controller] c, a in
controller?.present(c, in: .window(.root), with: a)
}
presentInGlobalOverlay = { [weak controller] c in
controller?.presentInGlobalOverlay(c, with: nil)
}
pushImpl = { [weak controller] c in
controller?.push(c)
}
@@ -888,5 +974,41 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
}
})
}
findAutoremoveReferenceNode = { [weak controller] in
guard let controller else {
return nil
}
let targetTag: CreateGroupEntryTag = .autoDelete
var resultItemNode: ItemListItemNode?
controller.forEachItemNode { itemNode in
if let itemNode = itemNode as? ItemListItemNode {
if let tag = itemNode.tag, tag.isEqual(to: targetTag) {
resultItemNode = itemNode
return
}
}
}
if let resultItemNode = resultItemNode as? ItemListDisclosureItemNode {
return resultItemNode
} else {
return nil
}
}
return controller
}
private final class CreateGroupContextReferenceContentSource: ContextReferenceContentSource {
private let sourceView: UIView
init(sourceView: UIView) {
self.sourceView = sourceView
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds, insets: UIEdgeInsets(top: -4.0, left: 0.0, bottom: -4.0, right: 0.0))
}
}