Merge branch 'master' of gitlab.com:peter-iakovlev/TelegramUI

This commit is contained in:
Ilya Laktyushin 2019-01-14 01:56:49 +04:00
commit c9aa4a85da
17 changed files with 2942 additions and 2957 deletions

View File

@ -272,7 +272,7 @@ private struct ChannelAdminControllerState: Equatable {
}
}
private func stringForRight(strings: PresentationStrings, right: TelegramChatAdminRightsFlags, isGroup: Bool) -> String {
private func stringForRight(strings: PresentationStrings, right: TelegramChatAdminRightsFlags, isGroup: Bool, defaultBannedRights: TelegramChatBannedRights?) -> String {
if right.contains(.canChangeInfo) {
return isGroup ? strings.Group_EditAdmin_PermissionChangeInfo : strings.Channel_EditAdmin_PermissionChangeInfo
} else if right.contains(.canPostMessages) {
@ -284,7 +284,15 @@ private func stringForRight(strings: PresentationStrings, right: TelegramChatAdm
} else if right.contains(.canBanUsers) {
return strings.Channel_EditAdmin_PermissionBanUsers
} else if right.contains(.canInviteUsers) {
return strings.Channel_EditAdmin_PermissionInviteUsers
if isGroup {
if let defaultBannedRights = defaultBannedRights, defaultBannedRights.flags.contains(.banAddMembers) {
return strings.Channel_EditAdmin_PermissionInviteMembers
} else {
return strings.Channel_EditAdmin_PermissionInviteViaLink
}
} else {
return strings.Channel_EditAdmin_PermissionInviteSubscribers
}
} else if right.contains(.canChangeInviteLink) {
return ""
} else if right.contains(.canPinMessages) {
@ -408,7 +416,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
var index = 0
for right in rightsOrder {
if accountUserRightsFlags.contains(right) {
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup), right, currentRightsFlags, currentRightsFlags.contains(right), !state.updating))
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, defaultBannedRights: channel.defaultBannedRights), right, currentRightsFlags, currentRightsFlags.contains(right), !state.updating))
index += 1
}
}
@ -440,7 +448,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
} else if let initialParticipant = initialParticipant, case let .member(_, _, maybeAdminInfo, _) = initialParticipant, let adminInfo = maybeAdminInfo {
var index = 0
for right in rightsOrder {
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup), right, adminInfo.rights.flags, adminInfo.rights.flags.contains(right), false))
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, defaultBannedRights: channel.defaultBannedRights), right, adminInfo.rights.flags, adminInfo.rights.flags.contains(right), false))
index += 1
}
}
@ -474,7 +482,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
var index = 0
for right in rightsOrder {
if accountUserRightsFlags.contains(right) {
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup), right, currentRightsFlags, currentRightsFlags.contains(right), !state.updating))
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, defaultBannedRights: group.defaultBannedRights), right, currentRightsFlags, currentRightsFlags.contains(right), !state.updating))
index += 1
}
}

View File

@ -769,7 +769,7 @@ public func channelVisibilityController(account: Account, peerId: PeerId, mode:
}
let peersDisablingAddressNameAssignment = Promise<[Peer]?>()
peersDisablingAddressNameAssignment.set(.single(nil) |> then(channelAddressNameAssignmentAvailability(account: account, peerId: peerId) |> mapToSignal { result -> Signal<[Peer]?, NoError> in
peersDisablingAddressNameAssignment.set(.single(nil) |> then(channelAddressNameAssignmentAvailability(account: account, peerId: peerId.namespace == Namespaces.Peer.CloudChannel ? peerId : nil) |> mapToSignal { result -> Signal<[Peer]?, NoError> in
if case .addressNameLimitReached = result {
return adminedPublicChannels(account: account)
|> map(Optional.init)

View File

@ -3263,22 +3263,36 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
}
if self.chatDisplayNode.frameForInputActionButton() != nil, self.presentationInterfaceState.interfaceState.mediaRecordingMode == .audio {
let _ = (ApplicationSpecificNotice.getChatMediaMediaRecordingTips(postbox: self.account.postbox)
|> deliverOnMainQueue).start(next: { [weak self] counter in
guard let strongSelf = self else {
return
var canSendMedia = false
if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
if channel.hasBannedPermission(.banSendMedia) == nil {
canSendMedia = true
}
var displayTip = false
if counter == 0 {
displayTip = true
} else if counter < 3 && arc4random_uniform(4) == 1 {
displayTip = true
} else if let group = self.presentationInterfaceState.renderedPeer?.peer as? TelegramGroup {
if !group.hasBannedPermission(.banSendMedia) {
canSendMedia = true
}
if displayTip {
let _ = ApplicationSpecificNotice.incrementChatMediaMediaRecordingTips(postbox: strongSelf.account.postbox).start()
strongSelf.displayMediaRecordingTip()
}
})
} else {
canSendMedia = true
}
if canSendMedia {
let _ = (ApplicationSpecificNotice.getChatMediaMediaRecordingTips(postbox: self.account.postbox)
|> deliverOnMainQueue).start(next: { [weak self] counter in
guard let strongSelf = self else {
return
}
var displayTip = false
if counter == 0 {
displayTip = true
} else if counter < 3 && arc4random_uniform(4) == 1 {
displayTip = true
}
if displayTip {
let _ = ApplicationSpecificNotice.incrementChatMediaMediaRecordingTips(postbox: strongSelf.account.postbox).start()
strongSelf.displayMediaRecordingTip()
}
})
}
}
}
}

View File

@ -134,7 +134,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
var isCreator = false
if let peer = entry.0.peers[entry.0.id.peerId] as? TelegramGroup, case .creator = peer.role {
isCreator = true
} else if let peer = entry.0.peers[entry.0.id.peerId] as? TelegramChannel, peer.flags.contains(.isCreator) {
} else if let peer = entry.0.peers[entry.0.id.peerId] as? TelegramChannel, case .group = peer.info, peer.flags.contains(.isCreator) {
isCreator = true
}
if isEmptyMedia && isCreator {

View File

@ -351,7 +351,9 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
if let channel = chatPeer as? TelegramChannel {
if case .broadcast = channel.info {
canClear = false
deleteTitle = strongSelf.presentationData.strings.Channel_LeaveChannel
deleteTitle = strongSelf.presentationData.strings.Channel_LeaveChannel
} else {
deleteTitle = strongSelf.presentationData.strings.Group_LeaveGroup
}
if let addressName = channel.addressName, !addressName.isEmpty {
canClear = false
@ -360,7 +362,7 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
canStop = true
deleteTitle = strongSelf.presentationData.strings.ChatList_DeleteChat
} else if let _ = chatPeer as? TelegramSecretChat {
deleteTitle = strongSelf.presentationData.strings.ChatList_DeleteChat
deleteTitle = strongSelf.presentationData.strings.ChatList_DeleteSecretChat
}
items.append(DeleteChatPeerActionSheetItem(account: strongSelf.account, peer: mainPeer, strings: strongSelf.presentationData.strings))
if canClear {
@ -406,40 +408,7 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
guard let strongSelf = self else {
return
}
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId)
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
state.pendingRemovalPeerIds.insert(peer.peerId)
return state
})
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
strongSelf.present(UndoOverlayController(account: strongSelf.account, text: strongSelf.presentationData.strings.Undo_ChatDeleted, action: { shouldCommit in
guard let strongSelf = self else {
return
}
if shouldCommit {
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId)
let _ = removePeerChat(postbox: strongSelf.account.postbox, peerId: peerId, reportChatSpam: false).start(completed: {
guard let strongSelf = self else {
return
}
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
state.pendingRemovalPeerIds.remove(peer.peerId)
return state
})
self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
})
} else {
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId)
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
state.pendingRemovalPeerIds.remove(peer.peerId)
return state
})
self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
}
}), in: .window(.root))
strongSelf.schedulePeerChatRemoval(peer: peer)
}))
if canStop {
@ -1081,4 +1050,59 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
self.present(actionSheet, in: .window(.root))
}
}
func schedulePeerChatRemoval(peer: RenderedPeer, deleteGloballyIfPossible: Bool = false) {
guard let chatPeer = peer.peers[peer.peerId] else {
return
}
let peerId = peer.peerId
self.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId)
self.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
state.pendingRemovalPeerIds.insert(peer.peerId)
return state
})
self.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
let statusText: String
if let channel = chatPeer as? TelegramChannel {
if case .broadcast = channel.info {
statusText = self.presentationData.strings.Undo_LeftChannel
} else {
statusText = self.presentationData.strings.Undo_LeftGroup
}
} else if let _ = chatPeer as? TelegramGroup {
statusText = self.presentationData.strings.Undo_LeftGroup
} else if let _ = chatPeer as? TelegramSecretChat {
statusText = self.presentationData.strings.Undo_SecretChatDeleted
} else {
statusText = self.presentationData.strings.Undo_ChatDeleted
}
self.present(UndoOverlayController(account: self.account, text: statusText, action: { [weak self] shouldCommit in
guard let strongSelf = self else {
return
}
if shouldCommit {
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId)
let _ = removePeerChat(postbox: strongSelf.account.postbox, peerId: peerId, reportChatSpam: false, deleteGloballyIfPossible: deleteGloballyIfPossible).start(completed: {
guard let strongSelf = self else {
return
}
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
state.pendingRemovalPeerIds.remove(peer.peerId)
return state
})
self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
})
} else {
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId)
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
state.pendingRemovalPeerIds.remove(peer.peerId)
return state
})
self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
}
}), in: .window(.root))
}
}

View File

@ -990,11 +990,11 @@ final class ChatListNode: ListView {
strongSelf.didSetReady = true
strongSelf._ready.set(true)
}
let isEmpty = transition.chatListView.filteredEntries.isEmpty
if strongSelf.wasEmpty != isEmpty {
strongSelf.wasEmpty = isEmpty
strongSelf.isEmptyUpdated?(isEmpty)
strongSelf.isEmptyUpdated?(isEmpty)
}
completion()

View File

@ -214,10 +214,12 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
continue loop
}
var updatedMessage = message
var updatedCombinedReadState = combinedReadState
if state.pendingClearHistoryPeerIds.contains(index.messageIndex.id.peerId) {
updatedMessage = nil
updatedCombinedReadState = nil
}
result.append(.PeerEntry(index: offsetPinnedIndex(index, offset: pinnedIndexOffset), presentationData: state.presentationData, message: updatedMessage, readState: combinedReadState, notificationSettings: notificationSettings, embeddedInterfaceState: embeddedState, peer: peer, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], isAd: false))
result.append(.PeerEntry(index: offsetPinnedIndex(index, offset: pinnedIndexOffset), presentationData: state.presentationData, message: updatedMessage, readState: updatedCombinedReadState, notificationSettings: notificationSettings, embeddedInterfaceState: embeddedState, peer: peer, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], isAd: false))
case let .HoleEntry(hole):
result.append(.HoleEntry(hole, theme: state.presentationData.theme))
case let .GroupReferenceEntry(groupId, index, message, topPeers, counters):
@ -246,10 +248,12 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
}
}
}
// if result.count >= 2, case .SearchEntry = result[result.count - 1], case .HoleEntry = result[result.count - 2] {
// return []
// } else
if result.count == 1, case .HoleEntry = result[0] {
// if result.count == 1, case .HoleEntry = result[0] {
if result.count >= 1, case .HoleEntry = result[result.count - 1] {
return []
} else if result.count == 1, case .HoleEntry = result[0] {
return []
}
return result

View File

@ -18,6 +18,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
private var swipeToReplyFeedback: HapticFeedback?
private var selectionNode: ChatMessageSelectionNode?
private var deliveryFailedNode: ChatMessageDeliveryFailedNode?
private var shareButtonNode: HighlightableButtonNode?
var telegramFile: TelegramMediaFile?
@ -154,7 +155,9 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
var needShareButton = false
if item.message.id.peerId == item.account.peerId {
if item.message.flags.contains(.Failed) {
needShareButton = false
} else if item.message.id.peerId == item.account.peerId {
for attribute in item.content.firstMessage.attributes {
if let _ = attribute as? SourceReferenceMessageAttribute {
needShareButton = true
@ -197,11 +200,16 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
layoutInsets.top += layoutConstants.timestampHeaderHeight
}
var deliveryFailedInset: CGFloat = 0.0
if item.content.firstMessage.flags.contains(.Failed) {
deliveryFailedInset += 24.0
}
let displayLeftInset = params.leftInset + layoutConstants.bubble.edgeInset + avatarInset
let innerImageInset: CGFloat = 10.0
let innerImageSize = CGSize(width: imageSize.width + innerImageInset * 2.0, height: imageSize.height + innerImageInset * 2.0)
let imageFrame = CGRect(origin: CGPoint(x: 0.0 + (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - innerImageSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left)), y: -innerImageInset), size: innerImageSize)
let imageFrame = CGRect(origin: CGPoint(x: 0.0 + (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - innerImageSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - deliveryFailedInset)), y: -innerImageInset), size: innerImageSize)
let arguments = TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(top: innerImageInset, left: innerImageInset, bottom: innerImageInset, right: innerImageInset))
@ -330,10 +338,17 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
return (ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets), { [weak self] animation, _ in
if let strongSelf = self {
var transition: ContainedViewLayoutTransition = .immediate
if case let .System(duration) = animation {
transition = .animated(duration: duration, curve: .spring)
}
let updatedImageFrame = imageFrame.offsetBy(dx: 0.0, dy: floor((contentHeight - imageSize.height) / 2.0))
strongSelf.imageNode.frame = updatedImageFrame
strongSelf.progressNode?.position = strongSelf.imageNode.position
transition.updateFrame(node: strongSelf.imageNode, frame: updatedImageFrame)
if let progressNode = strongSelf.progressNode {
transition.updatePosition(node: progressNode, position: strongSelf.imageNode.position)
}
imageApply()
if let updatedShareButtonNode = updatedShareButtonNode {
@ -354,11 +369,11 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
if let shareButtonNode = strongSelf.shareButtonNode {
shareButtonNode.frame = CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - 30.0 - 10.0), size: CGSize(width: 29.0, height: 29.0))
transition.updateFrame(node: shareButtonNode, frame: CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - 30.0 - 10.0), size: CGSize(width: 29.0, height: 29.0)))
}
dateAndStatusApply(false)
strongSelf.dateAndStatusNode.frame = CGRect(origin: CGPoint(x: max(displayLeftInset, updatedImageFrame.maxX - dateAndStatusSize.width - 4.0), y: updatedImageFrame.maxY - dateAndStatusSize.height - 16.0), size: dateAndStatusSize)
transition.updateFrame(node: strongSelf.dateAndStatusNode, frame: CGRect(origin: CGPoint(x: max(displayLeftInset, updatedImageFrame.maxX - dateAndStatusSize.width - 4.0), y: updatedImageFrame.maxY - dateAndStatusSize.height - 16.0), size: dateAndStatusSize))
if let updatedReplyBackgroundNode = updatedReplyBackgroundNode {
if strongSelf.replyBackgroundNode == nil {
@ -410,6 +425,37 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
strongSelf.replyInfoNode = nil
}
if item.content.firstMessage.flags.contains(.Failed) {
let deliveryFailedNode: ChatMessageDeliveryFailedNode
var isAppearing = false
if let current = strongSelf.deliveryFailedNode {
deliveryFailedNode = current
} else {
isAppearing = true
deliveryFailedNode = ChatMessageDeliveryFailedNode(tapped: {
if let item = self?.item {
item.controllerInteraction.requestRedeliveryOfFailedMessages(item.content.firstMessage.id)
}
})
strongSelf.deliveryFailedNode = deliveryFailedNode
strongSelf.addSubnode(deliveryFailedNode)
}
let deliveryFailedSize = deliveryFailedNode.updateLayout(theme: item.presentationData.theme.theme)
let deliveryFailedFrame = CGRect(origin: CGPoint(x: imageFrame.maxX + deliveryFailedInset - deliveryFailedSize.width, y: imageFrame.maxY - deliveryFailedSize.height - innerImageInset), size: deliveryFailedSize)
if isAppearing {
deliveryFailedNode.frame = deliveryFailedFrame
transition.animatePositionAdditive(node: deliveryFailedNode, offset: CGPoint(x: deliveryFailedInset, y: 0.0))
} else {
transition.updateFrame(node: deliveryFailedNode, frame: deliveryFailedFrame)
}
} else if let deliveryFailedNode = strongSelf.deliveryFailedNode {
strongSelf.deliveryFailedNode = nil
transition.updateAlpha(node: deliveryFailedNode, alpha: 0.0)
transition.updateFrame(node: deliveryFailedNode, frame: deliveryFailedNode.frame.offsetBy(dx: 24.0, dy: 0.0), completion: { [weak deliveryFailedNode] _ in
deliveryFailedNode?.removeFromSupernode()
})
}
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
var animated = false
if let _ = strongSelf.actionButtonsNode {

View File

@ -51,6 +51,8 @@ private final class DeleteChatPeerActionSheetItemNode: ActionSheetItemNode {
let text: (String, [(Int, NSRange)])
if peer is TelegramGroup || peer is TelegramChannel {
text = strings.ChatList_LeaveGroupConfirmation(peer.displayTitle)
} else if peer is TelegramSecretChat {
text = strings.ChatList_DeleteSecretChatConfirmation(peer.displayTitle)
} else {
text = strings.ChatList_DeleteChatConfirmation(peer.displayTitle)
}

View File

@ -675,7 +675,6 @@ private func canRemoveParticipant(account: Account, channel: TelegramChannel, pa
private func groupInfoEntries(account: Account, presentationData: PresentationData, view: PeerView, channelMembers: [RenderedChannelParticipant], globalNotificationSettings: GlobalNotificationSettings, state: GroupInfoState) -> [GroupInfoEntry] {
var entries: [GroupInfoEntry] = []
var highlightAdmins = false
var canEditGroupInfo = false
var canEditMembers = false
var canAddMembers = false
@ -685,7 +684,6 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
if case .creator = group.role {
isCreator = true
}
highlightAdmins = true
switch group.role {
case .admin, .creator:
canEditGroupInfo = true
@ -701,7 +699,6 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
canAddMembers = true
}
} else if let channel = view.peers[view.peerId] as? TelegramChannel {
highlightAdmins = true
isPublic = channel.username != nil
isCreator = channel.flags.contains(.isCreator)
if channel.hasPermission(.changeInfo) {
@ -841,7 +838,7 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
entries.append(GroupInfoEntry.addMember(presentationData.theme, presentationData.strings.GroupInfo_AddParticipant, editing: state.editingState != nil && canRemoveAnyMember))
}
if let cachedGroupData = view.cachedData as? CachedGroupData, let participants = cachedGroupData.participants {
if let group = view.peers[view.peerId] as? TelegramGroup, let cachedGroupData = view.cachedData as? CachedGroupData, let participants = cachedGroupData.participants {
var updatedParticipants = participants.participants
let existingParticipantIds = Set(updatedParticipants.map { $0.peerId })
@ -918,17 +915,51 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
for i in 0 ..< sortedParticipants.count {
if let peer = peers[sortedParticipants[i].peerId] {
let memberStatus: GroupInfoMemberStatus
if highlightAdmins {
switch sortedParticipants[i] {
case .admin, .creator:
memberStatus = .admin
case .member:
memberStatus = .member
}
} else {
memberStatus = .member
let participant: ChannelParticipant
switch sortedParticipants[i] {
case .creator:
participant = .creator(id: sortedParticipants[i].peerId)
memberStatus = .admin
case .admin:
participant = .member(id: sortedParticipants[i].peerId, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(flags: .groupSpecific), promotedBy: account.peerId, canBeEditedByAccountPeer: true), banInfo: nil)
memberStatus = .admin
case .member:
participant = .member(id: sortedParticipants[i].peerId, invitedAt: 0, adminInfo: nil, banInfo: nil)
memberStatus = .member
}
entries.append(GroupInfoEntry.member(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, index: i, peerId: peer.id, peer: peer, participant: nil, presence: peerPresences[peer.id], memberStatus: memberStatus, editing: ItemListPeerItemEditing(editable: canRemoveParticipant(account: account, isAdmin: canEditMembers, participantId: peer.id, invitedBy: sortedParticipants[i].invitedBy), editing: state.editingState != nil && canRemoveAnyMember, revealed: state.peerIdWithRevealedOptions == peer.id), revealActions: [ParticipantRevealAction(type: .destructive, title: presentationData.strings.Common_Delete, action: .remove)], enabled: !disabledPeerIds.contains(peer.id)))
var canPromote: Bool
var canRestrict: Bool
if sortedParticipants[i].peerId == account.peerId {
canPromote = false
canRestrict = false
} else {
switch sortedParticipants[i] {
case .creator:
canPromote = false
canRestrict = false
case .admin, .member:
switch group.role {
case .creator:
canPromote = true
canRestrict = true
default:
canPromote = false
canRestrict = false
}
}
}
var peerActions: [ParticipantRevealAction] = []
if canPromote {
peerActions.append(ParticipantRevealAction(type: .neutral, title: presentationData.strings.GroupInfo_ActionPromote, action: .promote))
}
if canRestrict {
peerActions.append(ParticipantRevealAction(type: .warning, title: presentationData.strings.GroupInfo_ActionRestrict, action: .restrict))
peerActions.append(ParticipantRevealAction(type: .destructive, title: presentationData.strings.Common_Delete, action: .remove))
}
entries.append(GroupInfoEntry.member(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, index: i, peerId: peer.id, peer: peer, participant: RenderedChannelParticipant(participant: participant, peer: peer), presence: peerPresences[peer.id], memberStatus: memberStatus, editing: ItemListPeerItemEditing(editable: canRemoveParticipant(account: account, isAdmin: canEditMembers, participantId: peer.id, invitedBy: sortedParticipants[i].invitedBy), editing: state.editingState != nil && canRemoveAnyMember, revealed: state.peerIdWithRevealedOptions == peer.id), revealActions: peerActions, enabled: !disabledPeerIds.contains(peer.id)))
}
}
} else if let channel = view.peers[view.peerId] as? TelegramChannel, let cachedChannelData = view.cachedData as? CachedChannelData, let memberCount = cachedChannelData.participantsSummary.memberCount {
@ -992,19 +1023,15 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
for i in 0 ..< sortedParticipants.count {
let participant = sortedParticipants[i]
let memberStatus: GroupInfoMemberStatus
if highlightAdmins {
switch participant.participant {
case .creator:
switch participant.participant {
case .creator:
memberStatus = .admin
case let .member(_, _, adminInfo, _):
if adminInfo != nil {
memberStatus = .admin
case let .member(_, _, adminInfo, _):
if adminInfo != nil {
memberStatus = .admin
} else {
memberStatus = .member
}
}
} else {
memberStatus = .member
} else {
memberStatus = .member
}
}
var canPromote: Bool
@ -1132,8 +1159,8 @@ public func groupInfoController(account: Account, peerId originalPeerId: PeerId,
var pushControllerImpl: ((ViewController) -> Void)?
var presentControllerImpl: ((ViewController, Any?) -> Void)?
var popToRootImpl: (() -> Void)?
var endEditingImpl: (() -> Void)?
var removePeerChatImpl: ((Peer, Bool) -> Void)?
let actionsDisposable = DisposableSet()
@ -1419,8 +1446,11 @@ public func groupInfoController(account: Account, peerId originalPeerId: PeerId,
var canCreateInviteLink = false
if let group = groupPeer as? TelegramGroup {
if case .creator = group.role {
canCreateInviteLink = true
switch group.role {
case .creator, .admin:
canCreateInviteLink = true
default:
break
}
} else if let channel = groupPeer as? TelegramChannel {
if channel.hasPermission(.manageInviteLink) {
@ -1657,7 +1687,9 @@ public func groupInfoController(account: Account, peerId originalPeerId: PeerId,
|> take(1)
|> deliverOnMainQueue).start(next: { peerView in
presentControllerImpl?(channelAdminController(account: account, peerId: peerView.peerId, adminId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
}, upgradedToSupergroup: { _, f in f() }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}, upgradedToSupergroup: { upgradedPeerId, f in
upgradedToSupergroupImpl?(upgradedPeerId, f)
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
})
}, restrictPeer: { participant in
let _ = (peerView.get()
@ -1744,17 +1776,14 @@ public func groupInfoController(account: Account, peerId originalPeerId: PeerId,
items.append(ActionSheetTextItem(title: presentationData.strings.ChannelInfo_DeleteGroupConfirmation))
items.append(ActionSheetButtonItem(title: presentationData.strings.ChannelInfo_DeleteGroup, color: .destructive, action: {
dismissAction()
let _ = (removePeerChat(postbox: account.postbox, peerId: peerView.peerId, reportChatSpam: false, deleteGloballyIfPossible: true)
|> deliverOnMainQueue).start(completed: {
popToRootImpl?()
})
removePeerChatImpl?(channel, true)
}))
controller.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
])
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
} else {
} else if let peer = peerView.peers[peerView.peerId] {
let controller = ActionSheetController(presentationTheme: presentationData.theme)
let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated()
@ -1766,10 +1795,7 @@ public func groupInfoController(account: Account, peerId originalPeerId: PeerId,
}
items.append(ActionSheetButtonItem(title: presentationData.strings.Group_LeaveGroup, color: .destructive, action: {
dismissAction()
let _ = (removePeerChat(postbox: account.postbox, peerId: peerView.peerId, reportChatSpam: false)
|> deliverOnMainQueue).start(completed: {
popToRootImpl?()
})
removePeerChatImpl?(peer, false)
}))
controller.setItemGroups([
ActionSheetItemGroup(items: items),
@ -2048,8 +2074,20 @@ public func groupInfoController(account: Account, peerId originalPeerId: PeerId,
navigationController.setViewControllers(viewControllers, animated: false)
})
}
popToRootImpl = { [weak controller] in
(controller?.navigationController as? NavigationController)?.popToRoot(animated: true)
removePeerChatImpl = { [weak controller] peer, deleteGloballyIfPossible in
guard let controller = controller, let navigationController = controller.navigationController as? NavigationController else {
return
}
guard let tabController = navigationController.viewControllers.first as? TabBarController else {
return
}
for childController in tabController.controllers {
if let chatListController = childController as? ChatListController {
navigationController.popToRoot(animated: true)
chatListController.schedulePeerChatRemoval(peer: RenderedPeer(peer: peer), deleteGloballyIfPossible: deleteGloballyIfPossible)
break
}
}
}
displayCopyContextMenuImpl = { [weak controller] text, tag in
if let strongController = controller {

View File

@ -119,7 +119,7 @@ func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect,
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if let previewImage = previewImage {
let resource = LocalFileMediaResource(fileId: arc4random64())
let thumbnailSize = finalDimensions.aspectFitted(CGSize(width: 90.0, height: 90.0))
let thumbnailSize = finalDimensions.aspectFitted(CGSize(width: 320.0, height: 320.0))
let thumbnailImage = TGScaleImageToPixelSize(previewImage, thumbnailSize)!
if let thumbnailData = UIImageJPEGRepresentation(thumbnailImage, 0.4) {
account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)

View File

@ -212,7 +212,7 @@ func legacyEnqueueGifMessage(account: Account, data: Data) -> Signal<EnqueueMess
let dimensions = previewImage.size
var previewRepresentations: [TelegramMediaImageRepresentation] = []
let thumbnailSize = dimensions.aspectFitted(CGSize(width: 90.0, height: 90.0))
let thumbnailSize = dimensions.aspectFitted(CGSize(width: 320.0, height: 320.0))
let thumbnailImage = TGScaleImageToPixelSize(previewImage, thumbnailSize)!
if let thumbnailData = UIImageJPEGRepresentation(thumbnailImage, 0.4) {
let resource = LocalFileMediaResource(fileId: arc4random64())
@ -260,7 +260,7 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa
var representations: [TelegramMediaImageRepresentation] = []
if let thumbnail = thumbnail {
let resource = LocalFileMediaResource(fileId: arc4random64())
let thumbnailSize = thumbnail.size.aspectFitted(CGSize(width: 90.0, height: 90.0))
let thumbnailSize = thumbnail.size.aspectFitted(CGSize(width: 320.0, height: 320.0))
let thumbnailImage = TGScaleImageToPixelSize(thumbnail, thumbnailSize)!
if let thumbnailData = UIImageJPEGRepresentation(thumbnailImage, 0.4) {
account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)
@ -366,7 +366,7 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if let thumbnail = thumbnail {
let resource = LocalFileMediaResource(fileId: arc4random64())
let thumbnailSize = finalDimensions.aspectFitted(CGSize(width: 90.0, height: 90.0))
let thumbnailSize = finalDimensions.aspectFitted(CGSize(width: 320.0, height: 320.0))
let thumbnailImage = TGScaleImageToPixelSize(thumbnail, thumbnailSize)!
if let thumbnailData = UIImageJPEGRepresentation(thumbnailImage, 0.4) {
account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)

View File

@ -194,40 +194,37 @@ func openResolvedUrl(_ resolvedUrl: ResolvedUrl, account: Account, context: Open
}
}
case let .wallpaper(parameter):
// let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
// var controller: OverlayStatusController?
//
// let wallpaperController = WallpaperListPreviewController(account: account, source: .wallpaper(wallpaper))
//
//
// let signal: Signal<TelegramWallpaper, GetWallpaperError>
// switch parameter {
// case let .slug(slug):
// signal = getWallpaper(account: account, slug: slug)
// controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil))
// present(controller!, nil)
// case let .color(color):
// signal = .single(.color(Int32(color.rgb)))
// }
//
// let _ = (signal
// |> deliverOnMainQueue).start(next: { [weak controller] wallpaper in
// controller?.dismiss()
// let wallpaperController = WallpaperListPreviewController(account: account, source: .wallpaper(wallpaper))
// present(wallpaperController, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
// }, error: { [weak controller] error in
// controller?.dismiss()
//
//// let text: String
//// switch error {
//// case .limitExceeded:
//// text = presentationData.strings.Login_CodeFloodError
//// case .generic:
//// text = presentationData.strings.Login_UnknownError
//// }
//// let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
//// present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
// })
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
var controller: OverlayStatusController?
let signal: Signal<TelegramWallpaper, GetWallpaperError>
switch parameter {
case let .slug(slug):
signal = getWallpaper(account: account, slug: slug)
controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil))
present(controller!, nil)
case let .color(color):
signal = .single(.color(Int32(color.rgb)))
}
let _ = (signal
|> deliverOnMainQueue).start(next: { [weak controller] wallpaper in
controller?.dismiss()
let wallpaperController = WallpaperListPreviewController(account: account, source: .wallpaper(wallpaper))
present(wallpaperController, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}, error: { [weak controller] error in
controller?.dismiss()
// let text: String
// switch error {
// case .limitExceeded:
// text = presentationData.strings.Login_CodeFloodError
// case .generic:
// text = presentationData.strings.Login_UnknownError
// }
// let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
// present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
})
dismissInput()
}
}

View File

@ -284,77 +284,76 @@ private func chatMessageImageFileThumbnailDatas(account: Account, fileReference:
}
private func chatMessageVideoDatas(postbox: Postbox, fileReference: FileMediaReference, thumbnailSize: Bool = false, onlyFullSize: Bool = false) -> Signal<(Data?, (Data, String)?, Bool), NoError> {
if let smallestRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
let thumbnailResource = smallestRepresentation.resource
let fullSizeResource = fileReference.media.resource
let maybeFullSize = postbox.mediaBox.cachedResourceRepresentation(fullSizeResource, representation: thumbnailSize ? CachedScaledVideoFirstFrameRepresentation(size: CGSize(width: 160.0, height: 160.0)) : CachedVideoFirstFrameRepresentation(), complete: false, fetch: false)
let fetchedFullSize = postbox.mediaBox.cachedResourceRepresentation(fullSizeResource, representation: thumbnailSize ? CachedScaledVideoFirstFrameRepresentation(size: CGSize(width: 160.0, height: 160.0)) : CachedVideoFirstFrameRepresentation(), complete: false, fetch: true)
let signal = maybeFullSize
|> take(1)
|> mapToSignal { maybeData -> Signal<(Data?, (Data, String)?, Bool), NoError> in
if maybeData.complete {
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: [])
return .single((nil, loadedData == nil ? nil : (loadedData!, maybeData.path), true))
} else {
let fetchedThumbnail = fetchedMediaResource(postbox: postbox, reference: fileReference.resourceReference(thumbnailResource), statsCategory: .video)
let thumbnail: Signal<Data?, NoError>
if onlyFullSize {
thumbnail = .single(nil)
} else {
thumbnail = Signal { subscriber in
let fetchedDisposable = fetchedThumbnail.start()
let thumbnailDisposable = postbox.mediaBox.resourceData(thumbnailResource).start(next: { next in
subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []))
}, error: subscriber.putError, completed: subscriber.putCompletion)
return ActionDisposable {
fetchedDisposable.dispose()
thumbnailDisposable.dispose()
}
}
}
let fullSizeDataAndPath = Signal<MediaResourceData, NoError> { subscriber in
let dataDisposable = fetchedFullSize.start(next: { next in
subscriber.putNext(next)
}, completed: {
subscriber.putCompletion()
})
//let fetchedDisposable = fetchedPartialVideoThumbnailData(postbox: postbox, fileReference: fileReference).start()
return ActionDisposable {
dataDisposable.dispose()
//fetchedDisposable.dispose()
}
}
|> map { next -> ((Data, String)?, Bool) in
let data = next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: .mappedIfSafe)
return (data == nil ? nil : (data!, next.path), next.complete)
}
return thumbnail
|> mapToSignal { thumbnailData in
return fullSizeDataAndPath
|> map { (dataAndPath, complete) in
return (thumbnailData, dataAndPath, complete)
}
}
}
} |> filter({
let fullSizeResource = fileReference.media.resource
let thumbnailResource = smallestImageRepresentation(fileReference.media.previewRepresentations)?.resource
let maybeFullSize = postbox.mediaBox.cachedResourceRepresentation(fullSizeResource, representation: thumbnailSize ? CachedScaledVideoFirstFrameRepresentation(size: CGSize(width: 160.0, height: 160.0)) : CachedVideoFirstFrameRepresentation(), complete: false, fetch: false)
let fetchedFullSize = postbox.mediaBox.cachedResourceRepresentation(fullSizeResource, representation: thumbnailSize ? CachedScaledVideoFirstFrameRepresentation(size: CGSize(width: 160.0, height: 160.0)) : CachedVideoFirstFrameRepresentation(), complete: false, fetch: true)
let signal = maybeFullSize
|> take(1)
|> mapToSignal { maybeData -> Signal<(Data?, (Data, String)?, Bool), NoError> in
if maybeData.complete {
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: [])
return .single((nil, loadedData == nil ? nil : (loadedData!, maybeData.path), true))
} else {
let thumbnail: Signal<Data?, NoError>
if onlyFullSize {
return $0.1 != nil || $0.2
thumbnail = .single(nil)
} else if let decodedThumbnailData = fileReference.media.immediateThumbnailData.flatMap(decodeTinyThumbnail) {
thumbnail = .single(decodedThumbnailData)
} else if let thumbnailResource = thumbnailResource {
thumbnail = Signal { subscriber in
let fetchedDisposable = fetchedMediaResource(postbox: postbox, reference: fileReference.resourceReference(thumbnailResource), statsCategory: .video).start()
let thumbnailDisposable = postbox.mediaBox.resourceData(thumbnailResource).start(next: { next in
subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []))
}, error: subscriber.putError, completed: subscriber.putCompletion)
return ActionDisposable {
fetchedDisposable.dispose()
thumbnailDisposable.dispose()
}
}
} else {
return true//$0.0 != nil || $0.1 != nil || $0.2
thumbnail = .single(nil)
}
})
return signal
} else {
return .single((nil, nil, true))
}
let fullSizeDataAndPath = Signal<MediaResourceData, NoError> { subscriber in
let dataDisposable = fetchedFullSize.start(next: { next in
subscriber.putNext(next)
}, completed: {
subscriber.putCompletion()
})
//let fetchedDisposable = fetchedPartialVideoThumbnailData(postbox: postbox, fileReference: fileReference).start()
return ActionDisposable {
dataDisposable.dispose()
//fetchedDisposable.dispose()
}
}
|> map { next -> ((Data, String)?, Bool) in
let data = next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: .mappedIfSafe)
return (data == nil ? nil : (data!, next.path), next.complete)
}
return thumbnail
|> mapToSignal { thumbnailData in
return fullSizeDataAndPath
|> map { (dataAndPath, complete) in
return (thumbnailData, dataAndPath, complete)
}
}
}
} |> filter({
if onlyFullSize {
return $0.1 != nil || $0.2
} else {
return true//$0.0 != nil || $0.1 != nil || $0.2
}
})
return signal
}
private enum Corner: Hashable {

File diff suppressed because it is too large Load Diff

View File

@ -42,7 +42,7 @@ public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, me
let image = UIImage(cgImage: cgImage, scale: 1.0, orientation: imageOrientation)
if let scaledImage = generateImage(image.size.fitted(CGSize(width: 90.0, height: 90.0)), contextGenerator: { size, context in
if let scaledImage = generateImage(image.size.fitted(CGSize(width: 320.0, height: 320.0)), contextGenerator: { size, context in
context.setBlendMode(.copy)
drawImage(context: context, image: image.cgImage!, orientation: image.imageOrientation, in: CGRect(origin: CGPoint(), size: size))
}, scale: 1.0), let thumbnailData = UIImageJPEGRepresentation(scaledImage, 0.6) {
@ -94,7 +94,7 @@ public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, me
} |> runOn(opportunistic ? Queue.mainQueue() : Queue.concurrentDefaultQueue())
} else if file.mimeType.hasPrefix("video/") {
return Signal { subscriber in
if let scaledImage = generateVideoFirstFrame(data.path, maxDimensions: CGSize(width: 90.0, height: 90.0)), let thumbnailData = UIImageJPEGRepresentation(scaledImage, 0.6) {
if let scaledImage = generateVideoFirstFrame(data.path, maxDimensions: CGSize(width: 320.0, height: 320.0)), let thumbnailData = UIImageJPEGRepresentation(scaledImage, 0.6) {
let thumbnailResource = LocalFileMediaResource(fileId: arc4random64())
postbox.mediaBox.storeResourceData(thumbnailResource.id, data: thumbnailData)