mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge commit '3adf49cde0'
This commit is contained in:
commit
21520d226f
18 changed files with 3085 additions and 2881 deletions
|
|
@ -4416,11 +4416,13 @@ Any member of this group will be able to see messages in the channel.";
|
|||
|
||||
"Common.ActionNotAllowedError" = "Sorry, you are not allowed to do this.";
|
||||
|
||||
"Group.Location.Title" = "Location (Optional)";
|
||||
"Group.Location.Title" = "Location";
|
||||
"Group.Location.SetLocation" = "Set Location";
|
||||
"Group.Location.ChangeLocation" = "Change Location";
|
||||
"Group.Location.RemoveLocation" = "Remove Location";
|
||||
"Group.Location.Info" = "People will be able to find your group using People Nearby section";
|
||||
"Group.Location.Info" = "People will be able to find your group in the Groups Nearby section (Contacts > Add People Nearby).";
|
||||
|
||||
"Group.Username.Title" = "Username";
|
||||
|
||||
"Channel.AdminLog.MessageTransferedName" = "transferred ownership to %1$@";
|
||||
"Channel.AdminLog.MessageTransferedNameUsername" = "transferred ownership to %1$@ (%2$@)";
|
||||
|
|
@ -4439,3 +4441,5 @@ Any member of this group will be able to see messages in the channel.";
|
|||
"ReportGroupLocation.Title" = "Report Unrelated Group";
|
||||
"ReportGroupLocation.Text" = "Please tell us if this group is not related to this location.";
|
||||
"ReportGroupLocation.Report" = "Report";
|
||||
|
||||
"Group.Setup.TypePublicWithLocationHelp" = "Public groups can be found in search, chat history is available to everyone and anyone can join.\n\nTo make your group public, set a username or add a location.";
|
||||
|
|
|
|||
|
|
@ -278,6 +278,25 @@ struct AccountMutableState {
|
|||
|
||||
mutating func mergeChats(_ chats: [Api.Chat]) {
|
||||
self.addOperation(.MergeApiChats(chats))
|
||||
|
||||
for chat in chats {
|
||||
switch chat {
|
||||
case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount):
|
||||
if let participantsCount = participantsCount {
|
||||
self.addOperation(.UpdateCachedPeerData(chat.peerId, { current in
|
||||
var previous: CachedChannelData
|
||||
if let current = current as? CachedChannelData {
|
||||
previous = current
|
||||
} else {
|
||||
previous = CachedChannelData()
|
||||
}
|
||||
return previous.withUpdatedParticipantsSummary(previous.participantsSummary.withUpdatedMemberCount(participantsCount))
|
||||
}))
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutating func updatePeer(_ id: PeerId, _ f: @escaping (Peer?) -> Peer?) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ public enum ChannelOwnershipTransferError {
|
|||
case twoStepAuthMissing
|
||||
case twoStepAuthTooFresh(Int32)
|
||||
case authSessionTooFresh(Int32)
|
||||
case limitExceeded
|
||||
case requestPassword
|
||||
case invalidPassword
|
||||
case adminsTooMuch
|
||||
|
|
@ -105,7 +106,9 @@ public func updateChannelOwnership(postbox: Postbox, network: Network, accountSt
|
|||
|> mapToSignal { password -> Signal<Never, ChannelOwnershipTransferError> in
|
||||
return network.request(Api.functions.channels.editCreator(channel: apiChannel, userId: apiUser, password: password))
|
||||
|> mapError { error -> ChannelOwnershipTransferError in
|
||||
if error.errorDescription == "PASSWORD_HASH_INVALID" {
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||
return .limitExceeded
|
||||
} else if error.errorDescription == "PASSWORD_HASH_INVALID" {
|
||||
return .invalidPassword
|
||||
} else if error.errorDescription == "PASSWORD_MISSING" {
|
||||
return .twoStepAuthMissing
|
||||
|
|
|
|||
|
|
@ -112,7 +112,26 @@ public func addGroupAdmin(account: Account, peerId: PeerId, adminId: PeerId) ->
|
|||
if let peer = transaction.getPeer(peerId), let adminPeer = transaction.getPeer(adminId), let inputUser = apiInputUser(adminPeer) {
|
||||
if let group = peer as? TelegramGroup {
|
||||
return account.network.request(Api.functions.messages.editChatAdmin(chatId: group.id.id, userId: inputUser, isAdmin: .boolTrue))
|
||||
|> mapError { _ -> AddGroupAdminError in return .generic }
|
||||
|> `catch` { error -> Signal<Api.Bool, AddGroupAdminError> in
|
||||
if error.errorDescription == "USER_NOT_PARTICIPANT" {
|
||||
return addGroupMember(account: account, peerId: peerId, memberId: adminId)
|
||||
|> mapError { error -> AddGroupAdminError in
|
||||
return .addMemberError(error)
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Api.Bool, AddGroupAdminError> in
|
||||
return .complete()
|
||||
}
|
||||
|> then(
|
||||
account.network.request(Api.functions.messages.editChatAdmin(chatId: group.id.id, userId: inputUser, isAdmin: .boolTrue))
|
||||
|> mapError { error -> AddGroupAdminError in
|
||||
return .generic
|
||||
}
|
||||
)
|
||||
} else if error.errorDescription == "USER_PRIVACY_RESTRICTED" {
|
||||
return .fail(.addMemberError(.privacy))
|
||||
}
|
||||
return .fail(.generic)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Void, AddGroupAdminError> in
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||
|
|
@ -205,67 +224,69 @@ public func updateChannelAdminRights(account: Account, peerId: PeerId, adminId:
|
|||
updatedParticipant = ChannelParticipant.member(id: adminId, invitedAt: Int32(Date().timeIntervalSince1970), adminInfo: adminInfo, banInfo: nil)
|
||||
}
|
||||
return account.network.request(Api.functions.channels.editAdmin(channel: inputChannel, userId: inputUser, adminRights: rights.apiAdminRights))
|
||||
|> map { [$0] }
|
||||
|> `catch` { error -> Signal<[Api.Updates], UpdateChannelAdminRightsError> in
|
||||
if error.errorDescription == "USER_NOT_PARTICIPANT" {
|
||||
return addChannelMember(account: account, peerId: peerId, memberId: adminId)
|
||||
|> map { _ -> [Api.Updates] in
|
||||
return []
|
||||
}
|
||||
|> map { [$0] }
|
||||
|> `catch` { error -> Signal<[Api.Updates], UpdateChannelAdminRightsError> in
|
||||
if error.errorDescription == "USER_NOT_PARTICIPANT" {
|
||||
return addChannelMember(account: account, peerId: peerId, memberId: adminId)
|
||||
|> map { _ -> [Api.Updates] in
|
||||
return []
|
||||
}
|
||||
|> mapError { error -> UpdateChannelAdminRightsError in
|
||||
return .addMemberError(error)
|
||||
}
|
||||
|> then(account.network.request(Api.functions.channels.editAdmin(channel: inputChannel, userId: inputUser, adminRights: rights.apiAdminRights))
|
||||
|> mapError { error -> UpdateChannelAdminRightsError in
|
||||
return .addMemberError(error)
|
||||
return .generic
|
||||
}
|
||||
|> then(account.network.request(Api.functions.channels.editAdmin(channel: inputChannel, userId: inputUser, adminRights: rights.apiAdminRights))
|
||||
|> mapError { error -> UpdateChannelAdminRightsError in
|
||||
return .generic
|
||||
}
|
||||
|> map { [$0] })
|
||||
}
|
||||
return .fail(.generic)
|
||||
|> map { [$0] })
|
||||
} else if error.errorDescription == "USER_PRIVACY_RESTRICTED" {
|
||||
return .fail(.addMemberError(.restricted))
|
||||
}
|
||||
|> mapToSignal { result -> Signal<(ChannelParticipant?, RenderedChannelParticipant), UpdateChannelAdminRightsError> in
|
||||
for updates in result {
|
||||
account.stateManager.addUpdates(updates)
|
||||
}
|
||||
return account.postbox.transaction { transaction -> (ChannelParticipant?, RenderedChannelParticipant) in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
if let cachedData = cachedData as? CachedChannelData, let adminCount = cachedData.participantsSummary.adminCount {
|
||||
var updatedAdminCount = adminCount
|
||||
var wasAdmin = false
|
||||
if let currentParticipant = currentParticipant {
|
||||
switch currentParticipant {
|
||||
case .creator:
|
||||
return .fail(.generic)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<(ChannelParticipant?, RenderedChannelParticipant), UpdateChannelAdminRightsError> in
|
||||
for updates in result {
|
||||
account.stateManager.addUpdates(updates)
|
||||
}
|
||||
return account.postbox.transaction { transaction -> (ChannelParticipant?, RenderedChannelParticipant) in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
if let cachedData = cachedData as? CachedChannelData, let adminCount = cachedData.participantsSummary.adminCount {
|
||||
var updatedAdminCount = adminCount
|
||||
var wasAdmin = false
|
||||
if let currentParticipant = currentParticipant {
|
||||
switch currentParticipant {
|
||||
case .creator:
|
||||
wasAdmin = true
|
||||
case let .member(_, _, adminInfo, _):
|
||||
if let adminInfo = adminInfo, !adminInfo.rights.isEmpty {
|
||||
wasAdmin = true
|
||||
case let .member(_, _, adminInfo, _):
|
||||
if let adminInfo = adminInfo, !adminInfo.rights.isEmpty {
|
||||
wasAdmin = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if wasAdmin && rights.isEmpty {
|
||||
updatedAdminCount = max(1, adminCount - 1)
|
||||
} else if !wasAdmin && !rights.isEmpty {
|
||||
updatedAdminCount = adminCount + 1
|
||||
}
|
||||
|
||||
return cachedData.withUpdatedParticipantsSummary(cachedData.participantsSummary.withUpdatedAdminCount(updatedAdminCount))
|
||||
} else {
|
||||
return cachedData
|
||||
}
|
||||
})
|
||||
var peers: [PeerId: Peer] = [:]
|
||||
var presences: [PeerId: PeerPresence] = [:]
|
||||
peers[adminPeer.id] = adminPeer
|
||||
if let presence = transaction.getPeerPresence(peerId: adminPeer.id) {
|
||||
presences[adminPeer.id] = presence
|
||||
}
|
||||
if case let .member(_, _, maybeAdminInfo, _) = updatedParticipant, let adminInfo = maybeAdminInfo {
|
||||
if let peer = transaction.getPeer(adminInfo.promotedBy) {
|
||||
peers[peer.id] = peer
|
||||
if wasAdmin && rights.isEmpty {
|
||||
updatedAdminCount = max(1, adminCount - 1)
|
||||
} else if !wasAdmin && !rights.isEmpty {
|
||||
updatedAdminCount = adminCount + 1
|
||||
}
|
||||
|
||||
return cachedData.withUpdatedParticipantsSummary(cachedData.participantsSummary.withUpdatedAdminCount(updatedAdminCount))
|
||||
} else {
|
||||
return cachedData
|
||||
}
|
||||
return (currentParticipant, RenderedChannelParticipant(participant: updatedParticipant, peer: adminPeer, peers: peers, presences: presences))
|
||||
} |> mapError { _ -> UpdateChannelAdminRightsError in return .generic }
|
||||
})
|
||||
var peers: [PeerId: Peer] = [:]
|
||||
var presences: [PeerId: PeerPresence] = [:]
|
||||
peers[adminPeer.id] = adminPeer
|
||||
if let presence = transaction.getPeerPresence(peerId: adminPeer.id) {
|
||||
presences[adminPeer.id] = presence
|
||||
}
|
||||
if case let .member(_, _, maybeAdminInfo, _) = updatedParticipant, let adminInfo = maybeAdminInfo {
|
||||
if let peer = transaction.getPeer(adminInfo.promotedBy) {
|
||||
peers[peer.id] = peer
|
||||
}
|
||||
}
|
||||
return (currentParticipant, RenderedChannelParticipant(participant: updatedParticipant, peer: adminPeer, peers: peers, presences: presences))
|
||||
} |> mapError { _ -> UpdateChannelAdminRightsError in return .generic }
|
||||
}
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -752,6 +752,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
|
|||
}
|
||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
}
|
||||
dismissImpl?()
|
||||
}, completed: {
|
||||
updated(TelegramChatAdminRights(flags: updateFlags))
|
||||
dismissImpl?()
|
||||
|
|
@ -778,7 +779,13 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
|
|||
return current.withUpdatedUpdating(true)
|
||||
}
|
||||
updateRightsDisposable.set((addGroupAdmin(account: context.account, peerId: peerId, adminId: adminId)
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
|> deliverOnMainQueue).start(error: { error in
|
||||
if case let .addMemberError(error) = error, case .privacy = error, let admin = adminView.peers[adminView.peerId] {
|
||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(admin.compactDisplayTitle, admin.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
}
|
||||
|
||||
dismissImpl?()
|
||||
}, completed: {
|
||||
dismissImpl?()
|
||||
}))
|
||||
} else if updateFlags != defaultFlags {
|
||||
|
|
@ -813,9 +820,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
|
|||
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(admin.compactDisplayTitle, admin.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
}
|
||||
|
||||
updateState { current in
|
||||
return current.withUpdatedUpdating(false)
|
||||
}
|
||||
dismissImpl?()
|
||||
}))
|
||||
} else {
|
||||
dismissImpl?()
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ public func channelDiscussionGroupSetupController(context: AccountContext, peerI
|
|||
guard let peer = peer else {
|
||||
return
|
||||
}
|
||||
pushControllerImpl?(createGroupController(context: context, peerIds: [], initialTitle: peer.displayTitle + " Chat", supergroup: true, completion: { groupId, dismiss in
|
||||
pushControllerImpl?(createGroupController(context: context, peerIds: [], initialTitle: peer.displayTitle + " Chat", type: .supergroup, completion: { groupId, dismiss in
|
||||
var applySignal = updateGroupDiscussionForChannel(network: context.account.network, postbox: context.account.postbox, channelId: peerId, groupId: groupId)
|
||||
var cancelImpl: (() -> Void)?
|
||||
let progressSignal = Signal<Never, NoError> { subscriber in
|
||||
|
|
|
|||
|
|
@ -397,7 +397,7 @@ private final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
|
|||
}
|
||||
}
|
||||
|
||||
private func commitChannelOwnershipTransferController(context: AccountContext, peer: Peer, member: TelegramUser, completion: @escaping (PeerId?) -> Void) -> ViewController {
|
||||
private func commitChannelOwnershipTransferController(context: AccountContext, peer: Peer, member: TelegramUser, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (PeerId?) -> Void) -> ViewController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
var dismissImpl: (() -> Void)?
|
||||
|
|
@ -462,8 +462,32 @@ private func commitChannelOwnershipTransferController(context: AccountContext, p
|
|||
dismissImpl?()
|
||||
completion(upgradedPeerId)
|
||||
}, error: { [weak contentNode] error in
|
||||
var isGroup = true
|
||||
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
isGroup = false
|
||||
}
|
||||
|
||||
var errorTextAndActions: (String, [TextAlertAction])?
|
||||
switch error {
|
||||
case .invalidPassword:
|
||||
contentNode?.animateError()
|
||||
case .limitExceeded:
|
||||
errorTextAndActions = (presentationData.strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||
case .adminsTooMuch:
|
||||
errorTextAndActions = (isGroup ? presentationData.strings.Group_OwnershipTransfer_ErrorAdminsTooMuch : presentationData.strings.Channel_OwnershipTransfer_ErrorAdminsTooMuch, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||
case .userPublicChannelsTooMuch:
|
||||
errorTextAndActions = (presentationData.strings.Channel_OwnershipTransfer_ErrorPublicChannelsTooMuch, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||
case .userBlocked, .restricted:
|
||||
errorTextAndActions = (isGroup ? presentationData.strings.Group_OwnershipTransfer_ErrorPrivacyRestricted : presentationData.strings.Channel_OwnershipTransfer_ErrorPrivacyRestricted, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||
default:
|
||||
errorTextAndActions = (presentationData.strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||
}
|
||||
contentNode?.updateIsChecking(false)
|
||||
contentNode?.animateError()
|
||||
|
||||
if let (text, actions) = errorTextAndActions {
|
||||
dismissImpl?()
|
||||
present(textAlertController(context: context, title: nil, text: text, actions: actions), nil)
|
||||
}
|
||||
}))
|
||||
}
|
||||
return controller
|
||||
|
|
@ -497,7 +521,7 @@ private func confirmChannelOwnershipTransferController(context: AccountContext,
|
|||
|
||||
let controller = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Channel_OwnershipTransfer_ChangeOwner, action: {
|
||||
dismissImpl?()
|
||||
present(commitChannelOwnershipTransferController(context: context, peer: peer, member: member, completion: completion), nil)
|
||||
present(commitChannelOwnershipTransferController(context: context, peer: peer, member: member, present: present, completion: completion), nil)
|
||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
dismissImpl?()
|
||||
})], actionLayout: .vertical)
|
||||
|
|
@ -519,7 +543,6 @@ func channelOwnershipTransferController(context: AccountContext, peer: Peer, mem
|
|||
isGroup = false
|
||||
}
|
||||
|
||||
var dismissImpl: (() -> Void)?
|
||||
var actions: [TextAlertAction] = []
|
||||
|
||||
switch initialError {
|
||||
|
|
@ -559,9 +582,5 @@ func channelOwnershipTransferController(context: AccountContext, peer: Peer, mem
|
|||
let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor)
|
||||
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center)
|
||||
|
||||
let controller = richTextAlertController(context: context, title: title, text: attributedText, actions: actions)
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
return controller
|
||||
return richTextAlertController(context: context, title: title, text: attributedText, actions: actions)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
|||
case typePrivate(PresentationTheme, String, Bool)
|
||||
case typeInfo(PresentationTheme, String)
|
||||
|
||||
case publicLinkHeader(PresentationTheme, String)
|
||||
case publicLinkAvailability(PresentationTheme, String, Bool)
|
||||
case privateLink(PresentationTheme, String, String?)
|
||||
case editablePublicLink(PresentationTheme, String)
|
||||
|
|
@ -87,7 +88,7 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
|||
switch self {
|
||||
case .typeHeader, .typePublic, .typePrivate, .typeInfo:
|
||||
return ChannelVisibilitySection.type.rawValue
|
||||
case .publicLinkAvailability, .privateLink, .editablePublicLink, .privateLinkInfo, .publicLinkInfo, .publicLinkStatus:
|
||||
case .publicLinkHeader, .publicLinkAvailability, .privateLink, .editablePublicLink, .privateLinkInfo, .publicLinkInfo, .publicLinkStatus:
|
||||
return ChannelVisibilitySection.link.rawValue
|
||||
case .privateLinkCopy, .privateLinkRevoke, .privateLinkShare:
|
||||
return ChannelVisibilitySection.linkActions.rawValue
|
||||
|
|
@ -108,28 +109,30 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
|||
return 2
|
||||
case .typeInfo:
|
||||
return 3
|
||||
case .publicLinkAvailability:
|
||||
case .publicLinkHeader:
|
||||
return 4
|
||||
case .privateLink:
|
||||
case .publicLinkAvailability:
|
||||
return 5
|
||||
case .editablePublicLink:
|
||||
case .privateLink:
|
||||
return 6
|
||||
case .privateLinkInfo:
|
||||
case .editablePublicLink:
|
||||
return 7
|
||||
case .privateLinkCopy:
|
||||
case .privateLinkInfo:
|
||||
return 8
|
||||
case .privateLinkRevoke:
|
||||
case .privateLinkCopy:
|
||||
return 9
|
||||
case .privateLinkShare:
|
||||
case .privateLinkRevoke:
|
||||
return 10
|
||||
case .publicLinkStatus:
|
||||
case .privateLinkShare:
|
||||
return 11
|
||||
case .publicLinkInfo:
|
||||
case .publicLinkStatus:
|
||||
return 12
|
||||
case .existingLinksInfo:
|
||||
case .publicLinkInfo:
|
||||
return 13
|
||||
case .existingLinksInfo:
|
||||
return 14
|
||||
case let .existingLinkPeerItem(index, _, _, _, _, _, _, _):
|
||||
return 14 + index
|
||||
return 15 + index
|
||||
case .locationHeader:
|
||||
return 1000
|
||||
case .location:
|
||||
|
|
@ -169,6 +172,12 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case let .publicLinkHeader(lhsTheme, lhsTitle):
|
||||
if case let .publicLinkHeader(rhsTheme, rhsTitle) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .publicLinkAvailability(lhsTheme, lhsText, lhsValue):
|
||||
if case let .publicLinkAvailability(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
|
|
@ -310,6 +319,8 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
|||
})
|
||||
case let .typeInfo(theme, text):
|
||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||
case let .publicLinkHeader(theme, title):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: title, sectionId: self.section)
|
||||
case let .publicLinkAvailability(theme, text, value):
|
||||
let attr = NSMutableAttributedString(string: text, textColor: value ? theme.list.freeTextColor : theme.list.freeTextErrorColor)
|
||||
attr.addAttribute(.font, value: Font.regular(13), range: NSMakeRange(0, attr.length))
|
||||
|
|
@ -379,7 +390,7 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
|||
return ItemListSectionHeaderItem(theme: theme, text: title, sectionId: self.section)
|
||||
case let .location(theme, location):
|
||||
let imageSignal = chatMapSnapshotImage(account: arguments.account, resource: MapSnapshotMediaResource(latitude: location.latitude, longitude: location.longitude, width: 90, height: 90))
|
||||
return ItemListAddressItem(theme: theme, label: "", text: location.address, imageSignal: imageSignal, selected: nil, sectionId: self.section, style: .blocks, action: nil)
|
||||
return ItemListAddressItem(theme: theme, label: "", text: location.address.replacingOccurrences(of: ", ", with: "\n"), imageSignal: imageSignal, selected: nil, sectionId: self.section, style: .blocks, action: nil)
|
||||
case let .locationSetup(theme, text):
|
||||
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.setLocation()
|
||||
|
|
@ -543,7 +554,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||
switch selectedType {
|
||||
case .publicChannel:
|
||||
if isGroup {
|
||||
entries.append(.typeInfo(presentationData.theme, presentationData.strings.Group_Setup_TypePublicHelp))
|
||||
entries.append(.typeInfo(presentationData.theme, presentationData.strings.Group_Setup_TypePublicWithLocationHelp))
|
||||
} else {
|
||||
entries.append(.typeInfo(presentationData.theme, presentationData.strings.Channel_Setup_TypePublicHelp))
|
||||
}
|
||||
|
|
@ -585,6 +596,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||
entries.append(.publicLinkAvailability(presentationData.theme, presentationData.strings.Group_Username_CreatePublicLinkHelp, true))
|
||||
}
|
||||
} else {
|
||||
entries.append(.publicLinkHeader(presentationData.theme, presentationData.strings.Group_Username_Title.uppercased()))
|
||||
entries.append(.editablePublicLink(presentationData.theme, currentAddressName))
|
||||
if let status = state.addressNameValidationStatus {
|
||||
let text: String
|
||||
|
|
@ -629,8 +641,6 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||
entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePublicLinkHelp))
|
||||
|
||||
entries.append(.locationHeader(presentationData.theme, presentationData.strings.Group_Location_Title.uppercased()))
|
||||
|
||||
|
||||
if let currentEditingLocation = state.editingLocation {
|
||||
if case .removed = currentEditingLocation {
|
||||
entries.append(.locationSetup(presentationData.theme, presentationData.strings.Group_Location_SetLocation))
|
||||
|
|
@ -717,7 +727,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||
entries.append(.typePublic(presentationData.theme, presentationData.strings.Channel_Setup_TypePublic, selectedType == .publicChannel))
|
||||
entries.append(.typePrivate(presentationData.theme, presentationData.strings.Channel_Setup_TypePrivate, selectedType == .privateChannel))
|
||||
|
||||
entries.append(.typeInfo(presentationData.theme, presentationData.strings.Group_Setup_TypePublicHelp))
|
||||
entries.append(.typeInfo(presentationData.theme, presentationData.strings.Group_Setup_TypePublicWithLocationHelp))
|
||||
|
||||
switch selectedType {
|
||||
case .publicChannel:
|
||||
|
|
@ -745,6 +755,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||
entries.append(.publicLinkAvailability(presentationData.theme, presentationData.strings.Group_Username_CreatePublicLinkHelp, true))
|
||||
}
|
||||
} else {
|
||||
entries.append(.publicLinkHeader(presentationData.theme, presentationData.strings.Group_Username_Title.uppercased()))
|
||||
entries.append(.editablePublicLink(presentationData.theme, currentAddressName))
|
||||
if let status = state.addressNameValidationStatus {
|
||||
let text: String
|
||||
|
|
@ -778,6 +789,19 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||
entries.append(.publicLinkStatus(presentationData.theme, text, status))
|
||||
}
|
||||
entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePublicLinkHelp))
|
||||
|
||||
entries.append(.locationHeader(presentationData.theme, presentationData.strings.Group_Location_Title.uppercased()))
|
||||
if let currentEditingLocation = state.editingLocation {
|
||||
if case .removed = currentEditingLocation {
|
||||
entries.append(.locationSetup(presentationData.theme, presentationData.strings.Group_Location_SetLocation))
|
||||
} else if case let .location(location) = currentEditingLocation {
|
||||
entries.append(.location(presentationData.theme, location))
|
||||
entries.append(.locationSetup(presentationData.theme, presentationData.strings.Group_Location_ChangeLocation))
|
||||
entries.append(.locationRemove(presentationData.theme, presentationData.strings.Group_Location_RemoveLocation))
|
||||
}
|
||||
} else {
|
||||
entries.append(.locationSetup(presentationData.theme, presentationData.strings.Group_Location_SetLocation))
|
||||
}
|
||||
}
|
||||
case .privateChannel:
|
||||
let link = (view.cachedData as? CachedGroupData)?.exportedInvitation?.link
|
||||
|
|
@ -1056,7 +1080,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
|
|||
updateState { state in
|
||||
let address: String
|
||||
if let placemark = placemark {
|
||||
address = placemark.fullAddress.replacingOccurrences(of: ", ", with: "\n")
|
||||
address = placemark.fullAddress
|
||||
} else {
|
||||
address = "\(coordinate.latitude), \(coordinate.longitude)"
|
||||
}
|
||||
|
|
@ -1109,6 +1133,18 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
|
|||
case .privateChannel:
|
||||
break
|
||||
case .publicChannel:
|
||||
var hasLocation = false
|
||||
if let editingLocation = state.editingLocation {
|
||||
switch editingLocation {
|
||||
case .location:
|
||||
hasLocation = true
|
||||
case .removed:
|
||||
hasLocation = false
|
||||
}
|
||||
} else if let cachedChannelData = view.cachedData as? CachedChannelData, cachedChannelData.peerGeoLocation != nil {
|
||||
hasLocation = true
|
||||
}
|
||||
|
||||
if let addressNameValidationStatus = state.addressNameValidationStatus {
|
||||
switch addressNameValidationStatus {
|
||||
case .availability(.available):
|
||||
|
|
@ -1117,7 +1153,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
|
|||
doneEnabled = false
|
||||
}
|
||||
} else {
|
||||
doneEnabled = !(peer.addressName?.isEmpty ?? true)
|
||||
doneEnabled = !(peer.addressName?.isEmpty ?? true) || hasLocation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1211,6 +1247,16 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
|
|||
case .privateChannel:
|
||||
break
|
||||
case .publicChannel:
|
||||
var hasLocation = false
|
||||
if let editingLocation = state.editingLocation {
|
||||
switch editingLocation {
|
||||
case .location:
|
||||
hasLocation = true
|
||||
case .removed:
|
||||
hasLocation = false
|
||||
}
|
||||
}
|
||||
|
||||
if let addressNameValidationStatus = state.addressNameValidationStatus {
|
||||
switch addressNameValidationStatus {
|
||||
case .availability(.available):
|
||||
|
|
@ -1219,7 +1265,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
|
|||
doneEnabled = false
|
||||
}
|
||||
} else {
|
||||
doneEnabled = !(peer.addressName?.isEmpty ?? true)
|
||||
doneEnabled = !(peer.addressName?.isEmpty ?? true) || hasLocation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1329,6 +1375,8 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
|
|||
} else {
|
||||
if let addressName = peer.addressName, !addressName.isEmpty {
|
||||
selectedType = .publicChannel
|
||||
} else if let cachedChannelData = view.cachedData as? CachedChannelData, cachedChannelData.peerGeoLocation != nil {
|
||||
selectedType = .publicChannel
|
||||
} else {
|
||||
selectedType = .privateChannel
|
||||
}
|
||||
|
|
@ -1360,7 +1408,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
|
|||
controller?.dismiss()
|
||||
}
|
||||
dismissInputImpl = { [weak controller] in
|
||||
controller?.dismiss()
|
||||
controller?.view.endEditing(true)
|
||||
}
|
||||
nextImpl = { [weak controller] in
|
||||
if let controller = controller {
|
||||
|
|
|
|||
|
|
@ -3445,7 +3445,9 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
|
|||
strongSelf.chatDisplayNode.dismissInput()
|
||||
|
||||
let actions = [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.ReportGroupLocation_Report, action: {})]
|
||||
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.ReportGroupLocation_Report, action: {
|
||||
|
||||
})]
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.ReportGroupLocation_Title, text: strongSelf.presentationData.strings.ReportGroupLocation_Text, actions: actions), in: .window(.root))
|
||||
}
|
||||
}, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get()))
|
||||
|
|
@ -5431,7 +5433,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
|
|||
case .upperBound:
|
||||
searchLocation = .index(MessageIndex.upperBound(peerId: peerId))
|
||||
}
|
||||
let historyView = preloadedShatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(location: searchLocation, count: 50), id: 0), account: self.context.account, chatLocation: self.chatLocation, fixedCombinedReadStates: nil, tagMask: nil, additionalData: [])
|
||||
let historyView = preloadedChatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(location: searchLocation, count: 50), id: 0), account: self.context.account, chatLocation: self.chatLocation, fixedCombinedReadStates: nil, tagMask: nil, additionalData: [])
|
||||
let signal = historyView
|
||||
|> mapToSignal { historyView -> Signal<(MessageIndex?, Bool), NoError> in
|
||||
switch historyView {
|
||||
|
|
@ -5523,7 +5525,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
|
|||
self.historyNavigationStack.add(fromIndex)
|
||||
}
|
||||
self.loadingMessage.set(true)
|
||||
let historyView = preloadedShatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(location: searchLocation, count: 50), id: 0), account: self.context.account, chatLocation: self.chatLocation, fixedCombinedReadStates: nil, tagMask: nil, additionalData: [])
|
||||
let historyView = preloadedChatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(location: searchLocation, count: 50), id: 0), account: self.context.account, chatLocation: self.chatLocation, fixedCombinedReadStates: nil, tagMask: nil, additionalData: [])
|
||||
let signal = historyView
|
||||
|> mapToSignal { historyView -> Signal<MessageIndex?, NoError> in
|
||||
switch historyView {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import TelegramCore
|
|||
import SwiftSignalKit
|
||||
import Display
|
||||
|
||||
func preloadedShatHistoryViewForLocation(_ location: ChatHistoryLocationInput, account: Account, chatLocation: ChatLocation, fixedCombinedReadStates: MessageHistoryViewReadState?, tagMask: MessageTags?, additionalData: [AdditionalMessageHistoryViewData], orderStatistics: MessageHistoryViewOrderStatistics = []) -> Signal<ChatHistoryViewUpdate, NoError> {
|
||||
func preloadedChatHistoryViewForLocation(_ location: ChatHistoryLocationInput, account: Account, chatLocation: ChatLocation, fixedCombinedReadStates: MessageHistoryViewReadState?, tagMask: MessageTags?, additionalData: [AdditionalMessageHistoryViewData], orderStatistics: MessageHistoryViewOrderStatistics = []) -> Signal<ChatHistoryViewUpdate, NoError> {
|
||||
return chatHistoryViewForLocation(location, account: account, chatLocation: chatLocation, fixedCombinedReadStates: fixedCombinedReadStates, tagMask: tagMask, additionalData: additionalData, orderStatistics: orderStatistics)
|
||||
|> introduceError(Bool.self)
|
||||
|> mapToSignal { update -> Signal<ChatHistoryViewUpdate, Bool> in
|
||||
|
|
|
|||
|
|
@ -60,15 +60,17 @@ class ContactListActionItem: ListViewItem {
|
|||
let title: String
|
||||
let icon: ContactListActionItemIcon
|
||||
let highlight: ContactListActionItemHighlight
|
||||
let clearHighlightAutomatically: Bool
|
||||
let action: () -> Void
|
||||
let header: ListViewItemHeader?
|
||||
|
||||
init(theme: PresentationTheme, title: String, icon: ContactListActionItemIcon, highlight: ContactListActionItemHighlight = .cell, header: ListViewItemHeader?, action: @escaping () -> Void) {
|
||||
init(theme: PresentationTheme, title: String, icon: ContactListActionItemIcon, highlight: ContactListActionItemHighlight = .cell, clearHighlightAutomatically: Bool = true, header: ListViewItemHeader?, action: @escaping () -> Void) {
|
||||
self.theme = theme
|
||||
self.title = title
|
||||
self.icon = icon
|
||||
self.highlight = highlight
|
||||
self.header = header
|
||||
self.clearHighlightAutomatically = clearHighlightAutomatically
|
||||
self.action = action
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +113,7 @@ class ContactListActionItem: ListViewItem {
|
|||
|
||||
func selected(listView: ListView){
|
||||
self.action()
|
||||
if case .alpha = self.highlight {
|
||||
if self.clearHighlightAutomatically {
|
||||
listView.clearHighlightAnimated(true)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
|||
interaction.authorize()
|
||||
})
|
||||
case let .option(_, option, header, theme, _):
|
||||
return ContactListActionItem(theme: theme, title: option.title, icon: option.icon, header: header, action: option.action)
|
||||
return ContactListActionItem(theme: theme, title: option.title, icon: option.icon, clearHighlightAutomatically: false, header: header, action: option.action)
|
||||
case let .peer(_, peer, presence, header, selection, theme, strings, dateTimeFormat, nameSortOrder, nameDisplayOrder, enabled):
|
||||
let status: ContactsPeerItemStatus
|
||||
let itemPeer: ContactsPeerItemPeer
|
||||
|
|
|
|||
|
|
@ -257,16 +257,12 @@ public class ContactsController: ViewController {
|
|||
}
|
||||
|
||||
self.contactsNode.openPeopleNearby = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let _ = (DeviceAccess.authorizationStatus(subject: .location(.tracking))
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let presentPeersNearby = {
|
||||
let controller = peersNearbyController(context: strongSelf.context)
|
||||
(strongSelf.navigationController as? NavigationController)?.replaceAllButRootController(controller, animated: true, completion: { [weak self] in
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ private struct CreateGroupArguments {
|
|||
private enum CreateGroupSection: Int32 {
|
||||
case info
|
||||
case members
|
||||
case location
|
||||
}
|
||||
|
||||
private enum CreateGroupEntryTag: ItemListItemTag {
|
||||
|
|
@ -43,8 +44,9 @@ private enum CreateGroupEntryTag: ItemListItemTag {
|
|||
private enum CreateGroupEntry: ItemListNodeEntry {
|
||||
case groupInfo(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer?, ItemListAvatarAndNameInfoItemState, ItemListAvatarAndNameInfoItemUpdatingAvatar?)
|
||||
case setProfilePhoto(PresentationTheme, String)
|
||||
|
||||
case member(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, PeerPresence?)
|
||||
case locationHeader(PresentationTheme, String)
|
||||
case location(PresentationTheme, PeerGeoLocation)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
|
|
@ -52,6 +54,8 @@ private enum CreateGroupEntry: ItemListNodeEntry {
|
|||
return CreateGroupSection.info.rawValue
|
||||
case .member:
|
||||
return CreateGroupSection.members.rawValue
|
||||
case .locationHeader, .location:
|
||||
return CreateGroupSection.location.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -63,6 +67,10 @@ private enum CreateGroupEntry: ItemListNodeEntry {
|
|||
return 1
|
||||
case let .member(index, _, _, _, _, _, _):
|
||||
return 2 + index
|
||||
case .locationHeader:
|
||||
return 10000
|
||||
case .location:
|
||||
return 10001
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,6 +141,18 @@ private enum CreateGroupEntry: ItemListNodeEntry {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case let .locationHeader(lhsTheme, lhsTitle):
|
||||
if case let .locationHeader(rhsTheme, rhsTitle) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .location(lhsTheme, lhsLocation):
|
||||
if case let .location(rhsTheme, rhsLocation) = rhs, lhsTheme === rhsTheme, lhsLocation == rhsLocation {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -153,6 +173,11 @@ private enum CreateGroupEntry: ItemListNodeEntry {
|
|||
})
|
||||
case let .member(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, presence):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: presence, 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(theme, title):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: title, sectionId: self.section)
|
||||
case let .location(theme, location):
|
||||
let imageSignal = chatMapSnapshotImage(account: arguments.account, resource: MapSnapshotMediaResource(latitude: location.latitude, longitude: location.longitude, width: 90, height: 90))
|
||||
return ItemListAddressItem(theme: theme, label: "", text: location.address.replacingOccurrences(of: ", ", with: "\n"), imageSignal: imageSignal, selected: nil, sectionId: self.section, style: .blocks, action: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +202,7 @@ private struct CreateGroupState: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
private func createGroupEntries(presentationData: PresentationData, state: CreateGroupState, peerIds: [PeerId], view: MultiplePeersView) -> [CreateGroupEntry] {
|
||||
private func createGroupEntries(presentationData: PresentationData, state: CreateGroupState, peerIds: [PeerId], view: MultiplePeersView, geoLocation: PeerGeoLocation?) -> [CreateGroupEntry] {
|
||||
var entries: [CreateGroupEntry] = []
|
||||
|
||||
let groupInfoState = ItemListAvatarAndNameInfoItemState(editingName: state.editingName, updatingName: nil)
|
||||
|
|
@ -218,10 +243,21 @@ private func createGroupEntries(presentationData: PresentationData, state: Creat
|
|||
entries.append(.member(Int32(i), presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peers[i], view.presences[peers[i].id]))
|
||||
}
|
||||
|
||||
if let geoLocation = geoLocation {
|
||||
entries.append(.locationHeader(presentationData.theme, presentationData.strings.Group_Location_Title.uppercased()))
|
||||
entries.append(.location(presentationData.theme, geoLocation))
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
public func createGroupController(context: AccountContext, peerIds: [PeerId], initialTitle: String? = nil, supergroup: Bool = false, completion: ((PeerId, @escaping () -> Void) -> Void)? = nil) -> ViewController {
|
||||
public enum CreateGroupType {
|
||||
case generic
|
||||
case supergroup
|
||||
case locatedGroup(latitude: Double, longitude: Double)
|
||||
}
|
||||
|
||||
public func createGroupController(context: AccountContext, peerIds: [PeerId], initialTitle: String? = nil, type: CreateGroupType = .generic, completion: ((PeerId, @escaping () -> Void) -> Void)? = nil) -> ViewController {
|
||||
let initialState = CreateGroupState(creating: false, editingName: .title(title: initialTitle ?? "", type: .group), avatar: nil)
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
|
|
@ -240,6 +276,11 @@ public func createGroupController(context: AccountContext, peerIds: [PeerId], in
|
|||
|
||||
let uploadedAvatar = Promise<UploadedPeerPhotoData>()
|
||||
|
||||
let placemarkPromise = Promise<ReverseGeocodedPlacemark?>()
|
||||
if case let .locatedGroup(latitude, longitude) = type {
|
||||
placemarkPromise.set(reverseGeocodeLocation(latitude: latitude, longitude: longitude))
|
||||
}
|
||||
|
||||
let arguments = CreateGroupArguments(account: context.account, updateEditingName: { editingName in
|
||||
updateState { current in
|
||||
var current = current
|
||||
|
|
@ -260,19 +301,45 @@ public func createGroupController(context: AccountContext, peerIds: [PeerId], in
|
|||
endEditingImpl?()
|
||||
|
||||
let createSignal: Signal<PeerId?, CreateGroupError>
|
||||
if supergroup {
|
||||
createSignal = createSupergroup(account: context.account, title: title, description: nil)
|
||||
|> map(Optional.init)
|
||||
|> mapError { error -> CreateGroupError in
|
||||
switch error {
|
||||
case .generic:
|
||||
return .generic
|
||||
case .restricted:
|
||||
return .restricted
|
||||
switch type {
|
||||
case .generic:
|
||||
createSignal = createGroup(account: context.account, title: title, peerIds: peerIds)
|
||||
case .supergroup:
|
||||
createSignal = createSupergroup(account: context.account, title: title, description: nil)
|
||||
|> map(Optional.init)
|
||||
|> mapError { error -> CreateGroupError in
|
||||
switch error {
|
||||
case .generic:
|
||||
return .generic
|
||||
case .restricted:
|
||||
return .restricted
|
||||
}
|
||||
}
|
||||
case let .locatedGroup(latitude, longitude):
|
||||
createSignal = createSupergroup(account: context.account, title: title, description: nil)
|
||||
|> map(Optional.init)
|
||||
|> mapError { error -> CreateGroupError in
|
||||
switch error {
|
||||
case .generic:
|
||||
return .generic
|
||||
case .restricted:
|
||||
return .restricted
|
||||
}
|
||||
}
|
||||
|> mapToSignal { peerId in
|
||||
guard let peerId = peerId else {
|
||||
return .single(nil)
|
||||
}
|
||||
return placemarkPromise.get()
|
||||
|> introduceError(CreateGroupError.self)
|
||||
|> mapToSignal { placemark in
|
||||
return updateChannelGeoLocation(postbox: context.account.postbox, network: context.account.network, channelId: peerId, coordinate: (latitude, longitude), address: placemark?.fullAddress ?? "\(latitude), \(longitude)")
|
||||
|> introduceError(CreateGroupError.self)
|
||||
|> map { _ in
|
||||
return peerId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
createSignal = createGroup(account: context.account, title: title, peerIds: peerIds)
|
||||
}
|
||||
|
||||
actionsDisposable.add((createSignal
|
||||
|
|
@ -410,8 +477,8 @@ public func createGroupController(context: AccountContext, peerIds: [PeerId], in
|
|||
})
|
||||
})
|
||||
|
||||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), context.account.postbox.multiplePeersView(peerIds))
|
||||
|> map { presentationData, state, view -> (ItemListControllerState, (ItemListNodeState<CreateGroupEntry>, CreateGroupEntry.ItemGenerationArguments)) in
|
||||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), context.account.postbox.multiplePeersView(peerIds), .single(nil) |> then(placemarkPromise.get()))
|
||||
|> map { presentationData, state, view, placemark -> (ItemListControllerState, (ItemListNodeState<CreateGroupEntry>, CreateGroupEntry.ItemGenerationArguments)) in
|
||||
|
||||
let rightNavigationButton: ItemListNavigationButton
|
||||
if state.creating {
|
||||
|
|
@ -422,8 +489,17 @@ public func createGroupController(context: AccountContext, peerIds: [PeerId], in
|
|||
})
|
||||
}
|
||||
|
||||
var geoLocation: PeerGeoLocation?
|
||||
if case let .locatedGroup(latitude, longitude) = type {
|
||||
if let placemark = placemark {
|
||||
geoLocation = PeerGeoLocation(latitude: latitude, longitude: longitude, address: placemark.fullAddress)
|
||||
} else {
|
||||
geoLocation = PeerGeoLocation(latitude: latitude, longitude: longitude, address: "")
|
||||
}
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Compose_NewGroupTitle), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
let listState = ItemListNodeState(entries: createGroupEntries(presentationData: presentationData, state: state, peerIds: peerIds, view: view), style: .blocks, focusItemTag: CreateGroupEntryTag.info)
|
||||
let listState = ItemListNodeState(entries: createGroupEntries(presentationData: presentationData, state: state, peerIds: peerIds, view: view, geoLocation: geoLocation), style: .blocks, focusItemTag: CreateGroupEntryTag.info)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@ private func arePeerNearbyArraysEqual(_ lhs: [PeerNearbyEntry], _ rhs: [PeerNear
|
|||
private final class PeersNearbyControllerArguments {
|
||||
let context: AccountContext
|
||||
let openChat: (Peer) -> Void
|
||||
let openCreateGroup: () -> Void
|
||||
let openCreateGroup: (Double, Double) -> Void
|
||||
|
||||
init(context: AccountContext, openChat: @escaping (Peer) -> Void, openCreateGroup: @escaping () -> Void) {
|
||||
init(context: AccountContext, openChat: @escaping (Peer) -> Void, openCreateGroup: @escaping (Double, Double) -> Void) {
|
||||
self.context = context
|
||||
self.openChat = openChat
|
||||
self.openCreateGroup = openCreateGroup
|
||||
|
|
@ -61,7 +61,7 @@ private enum PeersNearbyEntry: ItemListNodeEntry {
|
|||
case user(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PeerNearbyEntry)
|
||||
|
||||
case groupsHeader(PresentationTheme, String)
|
||||
case createGroup(PresentationTheme, String)
|
||||
case createGroup(PresentationTheme, String, Double?, Double?)
|
||||
case group(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PeerNearbyEntry)
|
||||
|
||||
case channelsHeader(PresentationTheme, String)
|
||||
|
|
@ -135,8 +135,8 @@ private enum PeersNearbyEntry: ItemListNodeEntry {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case let .createGroup(lhsTheme, lhsText):
|
||||
if case let .createGroup(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
case let .createGroup(lhsTheme, lhsText, lhsLatitude, lhsLongitude):
|
||||
if case let .createGroup(rhsTheme, rhsText, rhsLatitude, rhsLongitude) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsLatitude == rhsLatitude && lhsLongitude == rhsLongitude {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
|
|
@ -186,21 +186,23 @@ private enum PeersNearbyEntry: ItemListNodeEntry {
|
|||
case let .empty(theme, text, loading):
|
||||
return ItemListPlaceholderItem(theme: theme, text: text, sectionId: self.section, style: .blocks)
|
||||
case let .user(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.context.account, peer: peer.peer.0, aliasHandling: .standard, nameColor: .primary, nameStyle: .distinctBold, presence: nil, text: .text(stringForDistance(peer.distance)), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: {
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.context.account, peer: peer.peer.0, aliasHandling: .standard, nameColor: .primary, nameStyle: .distinctBold, presence: nil, text: .text(strings.Map_DistanceAway(stringForDistance(peer.distance)).0), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: {
|
||||
arguments.openChat(peer.peer.0)
|
||||
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: nil, hasTopGroupInset: false, tag: nil)
|
||||
case let .groupsHeader(theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .createGroup(theme, title):
|
||||
case let .createGroup(theme, title, latitude, longitude):
|
||||
return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.createGroupIcon(theme), title: title, alwaysPlain: false, sectionId: self.section, editing: false, action: {
|
||||
arguments.openCreateGroup()
|
||||
if let latitude = latitude, let longitude = longitude {
|
||||
arguments.openCreateGroup(latitude, longitude)
|
||||
}
|
||||
})
|
||||
case let .group(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer):
|
||||
var text: ItemListPeerItemText
|
||||
if let cachedData = peer.peer.1 as? CachedChannelData, let memberCount = cachedData.participantsSummary.memberCount {
|
||||
text = .text("\(stringForDistance(peer.distance)), \(strings.Conversation_StatusMembers(memberCount))")
|
||||
text = .text("\(strings.Map_DistanceAway(stringForDistance(peer.distance)).0), \(strings.Conversation_StatusMembers(memberCount))")
|
||||
} else {
|
||||
text = .text(stringForDistance(peer.distance))
|
||||
text = .text(strings.Map_DistanceAway(stringForDistance(peer.distance)).0)
|
||||
}
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.context.account, peer: peer.peer.0, aliasHandling: .standard, nameColor: .primary, nameStyle: .distinctBold, presence: nil, text: text, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: {
|
||||
arguments.openChat(peer.peer.0)
|
||||
|
|
@ -210,9 +212,9 @@ private enum PeersNearbyEntry: ItemListNodeEntry {
|
|||
case let .channel(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer):
|
||||
var text: ItemListPeerItemText
|
||||
if let cachedData = peer.peer.1 as? CachedChannelData, let memberCount = cachedData.participantsSummary.memberCount {
|
||||
text = .text("\(stringForDistance(peer.distance)), \(strings.Conversation_StatusSubscribers(memberCount))")
|
||||
text = .text("\(strings.Map_DistanceAway(stringForDistance(peer.distance)).0), \(strings.Conversation_StatusSubscribers(memberCount))")
|
||||
} else {
|
||||
text = .text(stringForDistance(peer.distance))
|
||||
text = .text(strings.Map_DistanceAway(stringForDistance(peer.distance)).0)
|
||||
}
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.context.account, peer: peer.peer.0, aliasHandling: .standard, nameColor: .primary, nameStyle: .distinctBold, presence: nil, text: text, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: {
|
||||
arguments.openChat(peer.peer.0)
|
||||
|
|
@ -221,29 +223,27 @@ private enum PeersNearbyEntry: ItemListNodeEntry {
|
|||
}
|
||||
}
|
||||
|
||||
private struct PeersNearbyControllerState: Equatable {
|
||||
static func ==(lhs: PeersNearbyControllerState, rhs: PeersNearbyControllerState) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private struct PeersNearbyData: Equatable {
|
||||
let latitude: Double
|
||||
let longitude: Double
|
||||
let users: [PeerNearbyEntry]
|
||||
let groups: [PeerNearbyEntry]
|
||||
let channels: [PeerNearbyEntry]
|
||||
|
||||
init(users: [PeerNearbyEntry], groups: [PeerNearbyEntry], channels: [PeerNearbyEntry]) {
|
||||
init(latitude: Double, longitude: Double, users: [PeerNearbyEntry], groups: [PeerNearbyEntry], channels: [PeerNearbyEntry]) {
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.users = users
|
||||
self.groups = groups
|
||||
self.channels = channels
|
||||
}
|
||||
|
||||
static func ==(lhs: PeersNearbyData, rhs: PeersNearbyData) -> Bool {
|
||||
return arePeerNearbyArraysEqual(lhs.users, rhs.users) && arePeerNearbyArraysEqual(lhs.groups, rhs.groups) && arePeerNearbyArraysEqual(lhs.channels, rhs.channels)
|
||||
return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude && arePeerNearbyArraysEqual(lhs.users, rhs.users) && arePeerNearbyArraysEqual(lhs.groups, rhs.groups) && arePeerNearbyArraysEqual(lhs.channels, rhs.channels)
|
||||
}
|
||||
}
|
||||
|
||||
private func peersNearbyControllerEntries(state: PeersNearbyControllerState, data: PeersNearbyData?, presentationData: PresentationData) -> [PeersNearbyEntry] {
|
||||
private func peersNearbyControllerEntries(data: PeersNearbyData?, presentationData: PresentationData) -> [PeersNearbyEntry] {
|
||||
var entries: [PeersNearbyEntry] = []
|
||||
|
||||
entries.append(.header(presentationData.theme, presentationData.strings.PeopleNearby_Description))
|
||||
|
|
@ -259,7 +259,7 @@ private func peersNearbyControllerEntries(state: PeersNearbyControllerState, dat
|
|||
}
|
||||
|
||||
entries.append(.groupsHeader(presentationData.theme, presentationData.strings.PeopleNearby_Groups.uppercased()))
|
||||
entries.append(.createGroup(presentationData.theme, presentationData.strings.PeopleNearby_CreateGroup))
|
||||
entries.append(.createGroup(presentationData.theme, presentationData.strings.PeopleNearby_CreateGroup, data?.latitude, data?.longitude))
|
||||
if let data = data, !data.groups.isEmpty {
|
||||
var i: Int32 = 0
|
||||
for group in data.groups {
|
||||
|
|
@ -281,12 +281,6 @@ private func peersNearbyControllerEntries(state: PeersNearbyControllerState, dat
|
|||
}
|
||||
|
||||
public func peersNearbyController(context: AccountContext) -> ViewController {
|
||||
let statePromise = ValuePromise(PeersNearbyControllerState(), ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: PeersNearbyControllerState())
|
||||
let updateState: ((PeersNearbyControllerState) -> PeersNearbyControllerState) -> Void = { f in
|
||||
statePromise.set(stateValue.modify { f($0) })
|
||||
}
|
||||
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var replaceTopControllerImpl: ((ViewController, Bool) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||
|
|
@ -298,13 +292,14 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
|
|||
|
||||
let arguments = PeersNearbyControllerArguments(context: context, openChat: { peer in
|
||||
navigateToChatImpl?(peer)
|
||||
}, openCreateGroup: {
|
||||
let controller = createGroupController(context: context, peerIds: [], supergroup: true)
|
||||
replaceTopControllerImpl?(controller, true)
|
||||
}, openCreateGroup: { latitude, longitude in
|
||||
let controller = createGroupController(context: context, peerIds: [], type: .locatedGroup(latitude: latitude, longitude: longitude))
|
||||
pushControllerImpl?(controller)
|
||||
})
|
||||
|
||||
let dataSignal: Signal<PeersNearbyData?, NoError> = currentLocationManagerCoordinate(manager: context.sharedContext.locationManager!, timeout: 5.0)
|
||||
|> mapToSignal { coordinate -> Signal<PeersNearbyData?, NoError> in
|
||||
let dataSignal: Signal<PeersNearbyData?, Void> = currentLocationManagerCoordinate(manager: context.sharedContext.locationManager!, timeout: 5.0)
|
||||
|> introduceError(Void.self)
|
||||
|> mapToSignal { coordinate -> Signal<PeersNearbyData?, Void> in
|
||||
guard let coordinate = coordinate else {
|
||||
return .single(nil)
|
||||
}
|
||||
|
|
@ -312,8 +307,9 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
|
|||
return Signal { subscriber in
|
||||
let peersNearbyContext = PeersNearbyContext(network: context.account.network, accountStateManager: context.account.stateManager, coordinate: (latitude: coordinate.latitude, longitude: coordinate.longitude))
|
||||
|
||||
let disposable = (peersNearbyContext.get()
|
||||
|> mapToSignal { peersNearby -> Signal<PeersNearbyData?, NoError> in
|
||||
let peersNearby: Signal<PeersNearbyData?, Void> = peersNearbyContext.get()
|
||||
|> introduceError(Void.self)
|
||||
|> mapToSignal { peersNearby -> Signal<PeersNearbyData?, Void> in
|
||||
return context.account.postbox.transaction { transaction -> PeersNearbyData? in
|
||||
var users: [PeerNearbyEntry] = []
|
||||
var groups: [PeerNearbyEntry] = []
|
||||
|
|
@ -327,9 +323,12 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
|
|||
}
|
||||
}
|
||||
}
|
||||
return PeersNearbyData(users: users, groups: groups, channels: [])
|
||||
return PeersNearbyData(latitude: coordinate.latitude, longitude: coordinate.longitude, users: users, groups: groups, channels: [])
|
||||
}
|
||||
}).start(next: { data in
|
||||
|> introduceError(Void.self)
|
||||
}
|
||||
|
||||
let disposable = peersNearby.start(next: { data in
|
||||
subscriber.putNext(data)
|
||||
})
|
||||
|
||||
|
|
@ -339,14 +338,22 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
dataPromise.set((dataSignal |> then(.complete() |> suspendAwareDelay(25.0, queue: Queue.concurrentDefaultQueue()))) |> restart)
|
||||
|
||||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), dataPromise.get())
|
||||
let errorSignal: Signal<Void, Void> = .single(Void()) |> then( Signal.fail(Void()) |> suspendAwareDelay(25.0, queue: Queue.concurrentDefaultQueue()) )
|
||||
let combinedSignal = combineLatest(dataSignal, errorSignal) |> map { data, _ -> PeersNearbyData? in
|
||||
return data
|
||||
}
|
||||
|> restartIfError
|
||||
|> `catch` { _ -> Signal<PeersNearbyData?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
dataPromise.set(combinedSignal)
|
||||
|
||||
let signal = combineLatest(context.sharedContext.presentationData, dataPromise.get())
|
||||
|> deliverOnMainQueue
|
||||
|> map { presentationData, state, data -> (ItemListControllerState, (ItemListNodeState<PeersNearbyEntry>, PeersNearbyEntry.ItemGenerationArguments)) in
|
||||
|> map { presentationData, data -> (ItemListControllerState, (ItemListNodeState<PeersNearbyEntry>, PeersNearbyEntry.ItemGenerationArguments)) in
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.PeopleNearby_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||
let listState = ItemListNodeState(entries: peersNearbyControllerEntries(state: state, data: data, presentationData: presentationData), style: .blocks, emptyStateItem: nil, crossfadeState: false, animateChanges: true, userInteractionEnabled: true)
|
||||
let listState = ItemListNodeState(entries: peersNearbyControllerEntries(data: data, presentationData: presentationData), style: .blocks, emptyStateItem: nil, crossfadeState: false, animateChanges: true, userInteractionEnabled: true)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -530,7 +530,7 @@ public class TelegramController: ViewController, KeyShortcutResponder {
|
|||
}
|
||||
if let id = state.id as? PeerMessagesMediaPlaylistItemId {
|
||||
if type == .music {
|
||||
let historyView = preloadedShatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(location: .id(id.messageId), count: 60), id: 0), account: account, chatLocation: .peer(id.messageId.peerId), fixedCombinedReadStates: nil, tagMask: MessageTags.music, additionalData: [])
|
||||
let historyView = preloadedChatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(location: .id(id.messageId), count: 60), id: 0), account: account, chatLocation: .peer(id.messageId.peerId), fixedCombinedReadStates: nil, tagMask: MessageTags.music, additionalData: [])
|
||||
let signal = historyView
|
||||
|> mapToSignal { historyView -> Signal<(MessageIndex?, Bool), NoError> in
|
||||
switch historyView {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue