mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-01 16:06:59 +00:00
Merge commit '3f13dea5daa9e27a5e17210235d8ac87004de3fd' into experimental-3
This commit is contained in:
commit
d9061b0771
@ -8648,16 +8648,17 @@ Sorry for the inconvenience.";
|
||||
"RequestPeer.Requirement.Group.HasUsernameOff" = "The group should be private.";
|
||||
"RequestPeer.Requirement.Group.HasUsernameOn" = "The group should be public.";
|
||||
|
||||
"RequestPeer.Requirement.Group.ForumOff" = "The group should have topics off.";
|
||||
"RequestPeer.Requirement.Group.ForumOn" = "The group should have topics on.";
|
||||
"RequestPeer.Requirement.Group.ForumOff" = "The group should have topics turned off.";
|
||||
"RequestPeer.Requirement.Group.ForumOn" = "The group should have topics turned on.";
|
||||
|
||||
"RequestPeer.Requirement.Group.ParticipantOn" = "Bot should be in the group.";
|
||||
"RequestPeer.Requirement.Group.CreatorOn" = "You should be the owner of the group.";
|
||||
|
||||
"RequestPeer.Requirement.Group.Rights" = "You have the admin rights to %@.";
|
||||
"RequestPeer.Requirement.Group.Rights.Info" = "change group info";
|
||||
"RequestPeer.Requirement.Group.Rights.Send" = "post messages";
|
||||
"RequestPeer.Requirement.Group.Rights.Delete" = "delete messages";
|
||||
"RequestPeer.Requirement.Group.Rights.Edit" = "delete messages";
|
||||
"RequestPeer.Requirement.Group.Rights.Edit" = "edit messages";
|
||||
"RequestPeer.Requirement.Group.Rights.Ban" = "ban users";
|
||||
"RequestPeer.Requirement.Group.Rights.Invite" = "invite users via link";
|
||||
"RequestPeer.Requirement.Group.Rights.Pin" = "pin messages";
|
||||
@ -8672,10 +8673,10 @@ Sorry for the inconvenience.";
|
||||
"RequestPeer.Requirement.Channel.CreatorOn" = "You should be the owner of the channel.";
|
||||
|
||||
"RequestPeer.Requirement.Channel.Rights" = "You have the admin rights to %@.";
|
||||
"RequestPeer.Requirement.Channel.Rights.Info" = "change group info";
|
||||
"RequestPeer.Requirement.Channel.Rights.Info" = "change channel info";
|
||||
"RequestPeer.Requirement.Channel.Rights.Send" = "post messages";
|
||||
"RequestPeer.Requirement.Channel.Rights.Delete" = "delete messages";
|
||||
"RequestPeer.Requirement.Channel.Rights.Edit" = "delete messages";
|
||||
"RequestPeer.Requirement.Channel.Rights.Edit" = "edit messages";
|
||||
"RequestPeer.Requirement.Channel.Rights.Ban" = "ban users";
|
||||
"RequestPeer.Requirement.Channel.Rights.Invite" = "invite users via link";
|
||||
"RequestPeer.Requirement.Channel.Rights.Pin" = "pin messages";
|
||||
@ -8697,7 +8698,6 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Conversation.ViewInChannel" = "View in Channel";
|
||||
|
||||
|
||||
"Conversation.HoldForAudioOnly" = "Hold to record audio.";
|
||||
"Conversation.HoldForVideoOnly" = "Hold to record video.";
|
||||
"Conversation.Translation.TranslateTo" = "Translate to %@";
|
||||
@ -8708,13 +8708,17 @@ Sorry for the inconvenience.";
|
||||
"Conversation.Translation.AddedToDoNotTranslateText" = "**%@** is added to the Do Not Translate list.";
|
||||
"Conversation.Translation.TranslationBarHiddenText" = "Translation bar is now hidden for this channel.";
|
||||
|
||||
"ProfilePhoto.SetEmoji" = "Set Emoji";
|
||||
|
||||
"AvatarEditor.Background" = "BACKGROUND";
|
||||
"AvatarEditor.EmojiOrSticker" = "EMOJI OR STICKER";
|
||||
"AvatarEditor.Emoji" = "EMOJI";
|
||||
"AvatarEditor.Stickers" = "STICKERS";
|
||||
"AvatarEditor.Emoji" = "Emoji";
|
||||
"AvatarEditor.Stickers" = "Stickers";
|
||||
"AvatarEditor.SwitchToEmoji" = "SWITCH TO EMOJI";
|
||||
"AvatarEditor.SwitchToStickers" = "SWITCH TO STICKERS";
|
||||
|
||||
"AvatarEditor.SetVideo" = "Set Video";
|
||||
"AvatarEditor.SetProfilePhoto" = "Set as Profile Photo";
|
||||
"AvatarEditor.SetGroupPhoto" = "Set as Group Photo";
|
||||
"AvatarEditor.SetChannelPhoto" = "Set as Group Photo";
|
||||
|
||||
"AvatarEditor.Set" = "Set";
|
||||
|
@ -194,7 +194,7 @@
|
||||
[itemViews addObject:galleryItem];
|
||||
|
||||
if (!_signup) {
|
||||
TGMenuSheetButtonItemView *viewItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:@"Emoji or Sticker" type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
|
||||
TGMenuSheetButtonItemView *viewItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"ProfilePhoto.SetEmoji") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
|
||||
{
|
||||
__strong TGMediaAvatarMenuMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
|
@ -23,6 +23,7 @@ struct ParsedDialogs {
|
||||
let topMessageIds: [PeerId: MessageId]
|
||||
let storeMessages: [StoreMessage]
|
||||
let ttlPeriods: [PeerId: CachedPeerAutoremoveTimeout]
|
||||
let hiddenTranslations: [PeerId: Bool]
|
||||
|
||||
let lowerNonPinnedIndex: MessageIndex?
|
||||
let referencedFolders: [PeerGroupId: PeerGroupUnreadCountersSummary]
|
||||
@ -55,6 +56,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message],
|
||||
var channelStates: [PeerId: Int32] = [:]
|
||||
var topMessageIds: [PeerId: MessageId] = [:]
|
||||
var ttlPeriods: [PeerId: CachedPeerAutoremoveTimeout] = [:]
|
||||
var hiddenTranslations: [PeerId: Bool] = [:]
|
||||
|
||||
var storeMessages: [StoreMessage] = []
|
||||
var nonPinnedDialogsTopMessageIds = Set<MessageId>()
|
||||
@ -112,6 +114,10 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message],
|
||||
|
||||
ttlPeriods[peer.peerId] = .known(ttlPeriod.flatMap(CachedPeerAutoremoveTimeout.Value.init(peerValue:)))
|
||||
|
||||
if (flags & (1 << 6)) != 0 {
|
||||
hiddenTranslations[peer.peerId] = true
|
||||
}
|
||||
|
||||
let isPinned = (flags & (1 << 2)) != 0
|
||||
if !isPinned {
|
||||
nonPinnedDialogsTopMessageIds.insert(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: topMessage))
|
||||
@ -190,6 +196,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message],
|
||||
topMessageIds: topMessageIds,
|
||||
storeMessages: storeMessages,
|
||||
ttlPeriods: ttlPeriods,
|
||||
hiddenTranslations: hiddenTranslations,
|
||||
|
||||
lowerNonPinnedIndex: lowerNonPinnedIndex,
|
||||
referencedFolders: referencedFolders
|
||||
@ -208,6 +215,7 @@ struct FetchedChatList {
|
||||
var channelStates: [PeerId: Int32]
|
||||
var storeMessages: [StoreMessage]
|
||||
var topMessageIds: [PeerId: MessageId]
|
||||
var hiddenTranslations: [PeerId: Bool]
|
||||
|
||||
var lowerNonPinnedIndex: MessageIndex?
|
||||
|
||||
@ -316,6 +324,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo
|
||||
var channelStates: [PeerId: Int32] = [:]
|
||||
var storeMessages: [StoreMessage] = []
|
||||
var topMessageIds: [PeerId: MessageId] = [:]
|
||||
var hiddenTranslations: [PeerId: Bool] = [:]
|
||||
|
||||
peers.append(contentsOf: parsedRemoteChats.peers)
|
||||
peerPresences.merge(parsedRemoteChats.peerPresences, uniquingKeysWith: { _, updated in updated })
|
||||
@ -327,6 +336,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo
|
||||
channelStates.merge(parsedRemoteChats.channelStates, uniquingKeysWith: { _, updated in updated })
|
||||
storeMessages.append(contentsOf: parsedRemoteChats.storeMessages)
|
||||
topMessageIds.merge(parsedRemoteChats.topMessageIds, uniquingKeysWith: { _, updated in updated })
|
||||
hiddenTranslations.merge(parsedRemoteChats.hiddenTranslations, uniquingKeysWith: { _, updated in updated })
|
||||
|
||||
if let parsedPinnedChats = parsedPinnedChats {
|
||||
peers.append(contentsOf: parsedPinnedChats.peers)
|
||||
@ -339,6 +349,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo
|
||||
channelStates.merge(parsedPinnedChats.channelStates, uniquingKeysWith: { _, updated in updated })
|
||||
storeMessages.append(contentsOf: parsedPinnedChats.storeMessages)
|
||||
topMessageIds.merge(parsedPinnedChats.topMessageIds, uniquingKeysWith: { _, updated in updated })
|
||||
hiddenTranslations.merge(parsedPinnedChats.hiddenTranslations, uniquingKeysWith: { _, updated in updated })
|
||||
}
|
||||
|
||||
var peerGroupIds: [PeerId: PeerGroupId] = [:]
|
||||
@ -362,6 +373,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo
|
||||
reactionTagSummaries.merge(folderChats.reactionTagSummaries, uniquingKeysWith: { _, updated in updated })
|
||||
channelStates.merge(folderChats.channelStates, uniquingKeysWith: { _, updated in updated })
|
||||
storeMessages.append(contentsOf: folderChats.storeMessages)
|
||||
hiddenTranslations.merge(folderChats.hiddenTranslations, uniquingKeysWith: { _, updated in updated })
|
||||
}
|
||||
|
||||
var pinnedItemIds: [PeerId]?
|
||||
@ -398,6 +410,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo
|
||||
channelStates: channelStates,
|
||||
storeMessages: storeMessages,
|
||||
topMessageIds: topMessageIds,
|
||||
hiddenTranslations: hiddenTranslations,
|
||||
|
||||
lowerNonPinnedIndex: parsedRemoteChats.lowerNonPinnedIndex,
|
||||
|
||||
|
@ -835,6 +835,19 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId
|
||||
})
|
||||
}
|
||||
|
||||
for (peerId, _) in fetchedChats.hiddenTranslations {
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
let current = (current as? CachedChannelData) ?? CachedChannelData()
|
||||
var updatedFlags = current.flags
|
||||
updatedFlags.insert(.translationHidden)
|
||||
return current.withUpdatedFlags(updatedFlags)
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
transaction.replaceChatListHole(groupId: groupId, index: hole.index, hole: fetchedChats.lowerNonPinnedIndex.flatMap(ChatListHole.init))
|
||||
|
||||
for peerId in fetchedChats.chatPeerIds {
|
||||
|
@ -19,6 +19,7 @@ public struct CachedChannelFlags: OptionSet {
|
||||
public static let canChangePeerGeoLocation = CachedChannelFlags(rawValue: 1 << 5)
|
||||
public static let canDeleteHistory = CachedChannelFlags(rawValue: 1 << 6)
|
||||
public static let antiSpamEnabled = CachedChannelFlags(rawValue: 1 << 7)
|
||||
public static let translationHidden = CachedChannelFlags(rawValue: 1 << 8)
|
||||
}
|
||||
|
||||
public struct CachedChannelParticipantsSummary: PostboxCoding, Equatable {
|
||||
|
@ -77,6 +77,19 @@ func _internal_translateMessages(account: Account, messageIds: [EngineMessage.Id
|
||||
|
||||
func _internal_togglePeerMessagesTranslationHidden(account: Account, peerId: EnginePeer.Id, hidden: Bool) -> Signal<Never, NoError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
if let cachedData = cachedData as? CachedChannelData {
|
||||
var updatedFlags = cachedData.flags
|
||||
if hidden {
|
||||
updatedFlags.insert(.translationHidden)
|
||||
} else {
|
||||
updatedFlags.remove(.translationHidden)
|
||||
}
|
||||
return cachedData.withUpdatedFlags(updatedFlags)
|
||||
} else {
|
||||
return cachedData
|
||||
}
|
||||
})
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<Never, NoError> in
|
||||
|
@ -375,7 +375,56 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { photo -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> in
|
||||
if peer.id != accountPeerId {
|
||||
if peer.id == accountPeerId {
|
||||
var updatedImage: TelegramMediaImage?
|
||||
var representations: [TelegramMediaImageRepresentation] = []
|
||||
switch photo {
|
||||
case let .photo(apiPhoto, _):
|
||||
updatedImage = telegramMediaImageFromApiPhoto(apiPhoto)
|
||||
switch apiPhoto {
|
||||
case .photoEmpty:
|
||||
break
|
||||
case let .photo(_, id, _, _, _, sizes, _, dcId):
|
||||
var sizes = sizes
|
||||
if sizes.count == 3 {
|
||||
sizes.remove(at: 1)
|
||||
}
|
||||
for size in sizes {
|
||||
switch size {
|
||||
case let .photoSize(_, w, h, _):
|
||||
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: CloudPeerPhotoSizeMediaResource(datacenterId: dcId, photoId: id, sizeSpec: w <= 200 ? .small : .fullSize, volumeId: nil, localId: nil), progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
||||
case let .photoSizeProgressive(_, w, h, sizes):
|
||||
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: CloudPeerPhotoSizeMediaResource(datacenterId: dcId, photoId: id, sizeSpec: w <= 200 ? .small : .fullSize, volumeId: nil, localId: nil), progressiveSizes: sizes, immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return postbox.transaction { transaction -> UpdatePeerPhotoStatus in
|
||||
if let peer = transaction.getPeer(peer.id) {
|
||||
updatePeers(transaction: transaction, peers: [peer], update: { (_, peer) -> Peer? in
|
||||
if let peer = peer as? TelegramUser {
|
||||
if customPeerPhotoMode == .suggest || fallback {
|
||||
return peer
|
||||
} else {
|
||||
return peer.withUpdatedPhoto(representations)
|
||||
}
|
||||
} else {
|
||||
return peer
|
||||
}
|
||||
})
|
||||
transaction.updatePeerCachedData(peerIds: Set([peer.id])) { peerId, cachedPeerData in
|
||||
if let cachedPeerData = cachedPeerData as? CachedUserData {
|
||||
return cachedPeerData.withUpdatedPersonalPhoto(.known(updatedImage))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return .complete([])
|
||||
} |> mapError { _ -> UploadPeerPhotoError in }
|
||||
} else {
|
||||
var updatedUsers: [TelegramUser] = []
|
||||
switch photo {
|
||||
case let .photo(_, apiUsers):
|
||||
@ -405,7 +454,6 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state
|
||||
return .complete([])
|
||||
} |> mapError { _ -> UploadPeerPhotoError in }
|
||||
}
|
||||
return .single(.complete([]))
|
||||
}
|
||||
} else {
|
||||
let request: Signal<Api.Updates, MTRpcError>
|
||||
|
@ -469,6 +469,9 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
|
||||
if (flags2 & Int32(1 << 1)) != 0 {
|
||||
channelFlags.insert(.antiSpamEnabled)
|
||||
}
|
||||
if (flags2 & Int32(1 << 3)) != 0 {
|
||||
channelFlags.insert(.translationHidden)
|
||||
}
|
||||
|
||||
let sendAsPeerId = defaultSendAs?.peerId
|
||||
|
||||
|
@ -74,17 +74,20 @@ final class AvatarEditorScreenComponent: Component {
|
||||
|
||||
let context: AccountContext
|
||||
let ready: Promise<Bool>
|
||||
let peerType: AvatarEditorScreen.PeerType
|
||||
let initialFileId: Int64?
|
||||
let initialBackgroundColors: [Int32]?
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
ready: Promise<Bool>,
|
||||
peerType: AvatarEditorScreen.PeerType,
|
||||
initialFileId: Int64?,
|
||||
initialBackgroundColors: [Int32]?
|
||||
) {
|
||||
self.context = context
|
||||
self.ready = ready
|
||||
self.peerType = peerType
|
||||
self.initialFileId = initialFileId
|
||||
self.initialBackgroundColors = initialBackgroundColors
|
||||
}
|
||||
@ -93,6 +96,9 @@ final class AvatarEditorScreenComponent: Component {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.peerType != rhs.peerType {
|
||||
return false
|
||||
}
|
||||
if lhs.initialFileId != rhs.initialFileId {
|
||||
return false
|
||||
}
|
||||
@ -241,6 +247,7 @@ final class AvatarEditorScreenComponent: Component {
|
||||
}
|
||||
|
||||
let query = rawQuery.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
if query.isEmpty {
|
||||
strongSelf.emojiSearchDisposable.set(nil)
|
||||
@ -351,7 +358,7 @@ final class AvatarEditorScreenComponent: Component {
|
||||
EmojiPagerContentComponent.ItemGroup(
|
||||
supergroupId: "search",
|
||||
groupId: "emoji",
|
||||
title: "Emoji",
|
||||
title: presentationData.strings.AvatarEditor_Emoji,
|
||||
subtitle: nil,
|
||||
actionButtonTitle: nil,
|
||||
isFeatured: false,
|
||||
@ -370,7 +377,7 @@ final class AvatarEditorScreenComponent: Component {
|
||||
EmojiPagerContentComponent.ItemGroup(
|
||||
supergroupId: "search",
|
||||
groupId: "stickers",
|
||||
title: "Stickers",
|
||||
title: presentationData.strings.AvatarEditor_Stickers,
|
||||
subtitle: nil,
|
||||
actionButtonTitle: nil,
|
||||
isFeatured: false,
|
||||
@ -1136,11 +1143,21 @@ final class AvatarEditorScreenComponent: Component {
|
||||
contentHeight += 16.0
|
||||
}
|
||||
|
||||
let buttonText: String
|
||||
switch component.peerType {
|
||||
case .user:
|
||||
buttonText = strings.AvatarEditor_SetProfilePhoto
|
||||
case .group:
|
||||
buttonText = strings.AvatarEditor_SetGroupPhoto
|
||||
case .channel:
|
||||
buttonText = strings.AvatarEditor_SetChannelPhoto
|
||||
}
|
||||
|
||||
let buttonSize = self.buttonView.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
SolidRoundedButtonComponent(
|
||||
title: strings.AvatarEditor_SetVideo,
|
||||
title: buttonText,
|
||||
theme: SolidRoundedButtonComponent.Theme(theme: environment.theme),
|
||||
fontSize: 17.0,
|
||||
height: 50.0,
|
||||
@ -1179,7 +1196,7 @@ final class AvatarEditorScreenComponent: Component {
|
||||
entity.scale = 3.3
|
||||
|
||||
var documentId: Int64 = 0
|
||||
if case let .file(file) = entity.content, !file.isCustomEmoji {
|
||||
if case let .file(file) = entity.content, file.isCustomEmoji {
|
||||
documentId = file.fileId.id
|
||||
}
|
||||
|
||||
@ -1192,7 +1209,7 @@ final class AvatarEditorScreenComponent: Component {
|
||||
entitiesData: entitiesData,
|
||||
image: nil,
|
||||
stillImage: nil,
|
||||
hasAnimation: true,
|
||||
hasAnimation: entity.isAnimated,
|
||||
stickers: []
|
||||
)
|
||||
|
||||
@ -1238,6 +1255,11 @@ final class AvatarEditorScreenComponent: Component {
|
||||
}
|
||||
|
||||
public final class AvatarEditorScreen: ViewControllerComponentContainer {
|
||||
public enum PeerType {
|
||||
case user
|
||||
case group
|
||||
case channel
|
||||
}
|
||||
fileprivate let context: AccountContext
|
||||
|
||||
private let readyValue = Promise<Bool>()
|
||||
@ -1247,16 +1269,18 @@ public final class AvatarEditorScreen: ViewControllerComponentContainer {
|
||||
|
||||
public var completion: (UIImage, URL, TGVideoEditAdjustments, @escaping () -> Void) -> Void = { _, _, _, _ in }
|
||||
|
||||
public init(context: AccountContext, initialFileId: Int64?, initialBackgroundColors: [Int32]?) {
|
||||
public init(context: AccountContext, peerType: PeerType, initialFileId: Int64?, initialBackgroundColors: [Int32]?) {
|
||||
self.context = context
|
||||
|
||||
let componentReady = Promise<Bool>()
|
||||
super.init(context: context, component: AvatarEditorScreenComponent(context: context, ready: componentReady, initialFileId: initialFileId, initialBackgroundColors: initialBackgroundColors), navigationBarAppearance: .transparent)
|
||||
super.init(context: context, component: AvatarEditorScreenComponent(context: context, ready: componentReady, peerType: peerType, initialFileId: initialFileId, initialBackgroundColors: initialBackgroundColors), navigationBarAppearance: .transparent)
|
||||
self.navigationPresentation = .modal
|
||||
|
||||
self.readyValue.set(componentReady.get() |> timeout(0.3, queue: .mainQueue(), alternate: .single(true)))
|
||||
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView())
|
||||
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
|
@ -202,7 +202,7 @@ final class AvatarPreviewComponent: Component {
|
||||
let dimensions = file.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 384.0, height: 384.0))
|
||||
let source = AnimatedStickerResourceSource(account: component.context.account, resource: file.resource, isVideo: file.isVideoSticker || file.mimeType == "video/webm")
|
||||
self.animationNode?.setup(source: source, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .count(2), mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode?.setup(source: source, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .count(1), mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode?.visibility = true
|
||||
|
||||
self.cachedDisposable.set((source.cachedDataPath(width: 384, height: 384)
|
||||
|
@ -6699,11 +6699,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
self.translationStateDisposable = (chatTranslationState(context: self.context, peerId: peerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] translationState in
|
||||
self.translationStateDisposable = combineLatest(
|
||||
queue: .mainQueue(),
|
||||
chatTranslationState(context: self.context, peerId: peerId),
|
||||
self.chatDisplayNode.historyNode.cachedPeerDataAndMessages
|
||||
).start(next: { [weak self] translationState, cachedDataAndMessages in
|
||||
if let strongSelf = self {
|
||||
let (cachedData, _) = cachedDataAndMessages
|
||||
var isHidden = false
|
||||
if let cachedData = cachedData as? CachedChannelData, cachedData.flags.contains(.translationHidden) {
|
||||
isHidden = true
|
||||
}
|
||||
var chatTranslationState: ChatPresentationTranslationState?
|
||||
if let translationState, translationState.isHidden != true && !translationState.fromLang.isEmpty {
|
||||
if let translationState, !isHidden && !translationState.fromLang.isEmpty {
|
||||
chatTranslationState = ChatPresentationTranslationState(isEnabled: translationState.isEnabled, fromLang: translationState.fromLang, toLang: translationState.toLang ?? strongSelf.presentationData.strings.baseLanguageCode)
|
||||
}
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: strongSelf.willAppear, interactive: strongSelf.willAppear, { state in
|
||||
@ -10055,16 +10063,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
let context = strongSelf.context
|
||||
let _ = updateChatTranslationStateInteractively(engine: context.engine, peerId: peerId, { current in
|
||||
return current?.withIsHidden(true)
|
||||
}).start()
|
||||
|
||||
let _ = context.engine.messages.togglePeerMessagesTranslationHidden(peerId: peerId, hidden: true).start()
|
||||
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .image(image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Title Panels/Translate"), color: .white)!, title: nil, text: presentationData.strings.Conversation_Translation_TranslationBarHiddenText, round: false, undoText: presentationData.strings.Undo_Undo), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||
if case .undo = action {
|
||||
let _ = updateChatTranslationStateInteractively(engine: context.engine, peerId: peerId, { current in
|
||||
return current?.withIsHidden(false)
|
||||
}).start()
|
||||
let _ = context.engine.messages.togglePeerMessagesTranslationHidden(peerId: peerId, hidden: false).start()
|
||||
}
|
||||
return true
|
||||
}), in: .current)
|
||||
|
@ -444,31 +444,33 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuCopy, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
let copyTextWithEntities = {
|
||||
var messageEntities: [MessageTextEntity]?
|
||||
var restrictedText: String?
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? TextEntitiesMessageAttribute {
|
||||
messageEntities = attribute.entities
|
||||
}
|
||||
if let attribute = attribute as? RestrictedContentMessageAttribute {
|
||||
restrictedText = attribute.platformText(platform: "ios", contentSettings: context.currentContentSettings.with { $0 }) ?? ""
|
||||
}
|
||||
var messageEntities: [MessageTextEntity]?
|
||||
var restrictedText: String?
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? TextEntitiesMessageAttribute {
|
||||
messageEntities = attribute.entities
|
||||
}
|
||||
if let attribute = attribute as? RestrictedContentMessageAttribute {
|
||||
restrictedText = attribute.platformText(platform: "ios", contentSettings: context.currentContentSettings.with { $0 }) ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
if let restrictedText = restrictedText {
|
||||
storeMessageTextInPasteboard(restrictedText, entities: nil)
|
||||
if let restrictedText = restrictedText {
|
||||
storeMessageTextInPasteboard(restrictedText, entities: nil)
|
||||
} else {
|
||||
if let translationState = chatPresentationInterfaceState.translationState, translationState.isEnabled,
|
||||
let translation = message.attributes.first(where: { ($0 as? TranslationMessageAttribute)?.toLang == translationState.toLang }) as? TranslationMessageAttribute, !translation.text.isEmpty {
|
||||
storeMessageTextInPasteboard(translation.text, entities: translation.entities)
|
||||
} else {
|
||||
storeMessageTextInPasteboard(message.text, entities: messageEntities)
|
||||
}
|
||||
|
||||
Queue.mainQueue().after(0.2, {
|
||||
let content: UndoOverlayContent = .copy(text: chatPresentationInterfaceState.strings.Conversation_MessageCopied)
|
||||
controllerInteraction.displayUndo(content)
|
||||
})
|
||||
}
|
||||
|
||||
copyTextWithEntities()
|
||||
Queue.mainQueue().after(0.2, {
|
||||
let content: UndoOverlayContent = .copy(text: chatPresentationInterfaceState.strings.Conversation_MessageCopied)
|
||||
controllerInteraction.displayUndo(content)
|
||||
})
|
||||
|
||||
f(.default)
|
||||
})))
|
||||
}
|
||||
@ -1093,7 +1095,12 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
if let restrictedText = restrictedText {
|
||||
storeMessageTextInPasteboard(restrictedText, entities: nil)
|
||||
} else {
|
||||
storeMessageTextInPasteboard(messageText, entities: messageEntities)
|
||||
if let translationState = chatPresentationInterfaceState.translationState, translationState.isEnabled,
|
||||
let translation = message.attributes.first(where: { ($0 as? TranslationMessageAttribute)?.toLang == translationState.toLang }) as? TranslationMessageAttribute, !translation.text.isEmpty {
|
||||
storeMessageTextInPasteboard(translation.text, entities: translation.entities)
|
||||
} else {
|
||||
storeMessageTextInPasteboard(messageText, entities: messageEntities)
|
||||
}
|
||||
}
|
||||
|
||||
Queue.mainQueue().after(0.2, {
|
||||
@ -1160,7 +1167,12 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuSpeak, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Message"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
controllerInteraction.performTextSelectionAction(!isCopyProtected, NSAttributedString(string: messageText), .speak)
|
||||
var text = messageText
|
||||
if let translationState = chatPresentationInterfaceState.translationState, translationState.isEnabled,
|
||||
let translation = message.attributes.first(where: { ($0 as? TranslationMessageAttribute)?.toLang == translationState.toLang }) as? TranslationMessageAttribute, !translation.text.isEmpty {
|
||||
text = translation.text
|
||||
}
|
||||
controllerInteraction.performTextSelectionAction(!isCopyProtected, NSAttributedString(string: text), .speak)
|
||||
f(.default)
|
||||
})))
|
||||
}
|
||||
|
@ -4849,31 +4849,37 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
items.insert(.separator, at: itemsCount)
|
||||
}
|
||||
} else if let channel = peer as? TelegramChannel {
|
||||
if let cachedData = strongSelf.data?.cachedData as? CachedChannelData, cachedData.flags.contains(.canViewStats) {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChannelInfo_Stats, icon: { theme in
|
||||
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
self?.openStats()
|
||||
})))
|
||||
}
|
||||
|
||||
if let _ = strongSelf.translationState {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuTranslate, icon: { theme in
|
||||
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
if let strongSelf = self {
|
||||
Queue.mainQueue().after(0.3, {
|
||||
let _ = updateChatTranslationStateInteractively(engine: strongSelf.context.engine, peerId: strongSelf.peerId, { state in
|
||||
return state?.withIsHidden(false)
|
||||
if let cachedData = strongSelf.data?.cachedData as? CachedChannelData {
|
||||
if cachedData.flags.contains(.canViewStats) {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChannelInfo_Stats, icon: { theme in
|
||||
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
self?.openStats()
|
||||
})))
|
||||
}
|
||||
|
||||
if cachedData.flags.contains(.translationHidden) {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuTranslate, icon: { theme in
|
||||
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
if let strongSelf = self {
|
||||
let _ = updateChatTranslationStateInteractively(engine: strongSelf.context.engine, peerId: strongSelf.peerId, { current in
|
||||
return current?.withIsEnabled(true)
|
||||
}).start()
|
||||
})
|
||||
self?.openChatForTranslation()
|
||||
}
|
||||
})))
|
||||
|
||||
Queue.mainQueue().after(0.2, {
|
||||
let _ = (strongSelf.context.engine.messages.togglePeerMessagesTranslationHidden(peerId: strongSelf.peerId, hidden: false)
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
self?.openChatForTranslation()
|
||||
})
|
||||
})
|
||||
}
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
var canReport = true
|
||||
@ -7263,7 +7269,19 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
guard let strongSelf = self, let completion else {
|
||||
return
|
||||
}
|
||||
let controller = AvatarEditorScreen(context: strongSelf.context, initialFileId: emojiMarkup?.fileId, initialBackgroundColors: emojiMarkup?.backgroundColors)
|
||||
let peerType: AvatarEditorScreen.PeerType
|
||||
if case .legacyGroup = peer {
|
||||
peerType = .group
|
||||
} else if case let .channel(channel) = peer {
|
||||
if case .group = channel.info {
|
||||
peerType = .group
|
||||
} else {
|
||||
peerType = .channel
|
||||
}
|
||||
} else {
|
||||
peerType = .user
|
||||
}
|
||||
let controller = AvatarEditorScreen(context: strongSelf.context, peerType: peerType, initialFileId: emojiMarkup?.fileId, initialBackgroundColors: emojiMarkup?.backgroundColors)
|
||||
controller.completion = completion
|
||||
(strongSelf.controller?.navigationController?.topViewController as? ViewController)?.push(controller)
|
||||
}
|
||||
|
@ -1148,6 +1148,9 @@ private func stringForRequestPeerType(strings: PresentationStrings, peerType: Re
|
||||
append(strings.RequestPeer_Requirement_Group_ForumOff)
|
||||
}
|
||||
}
|
||||
if group.botParticipant {
|
||||
append(strings.RequestPeer_Requirement_Group_ParticipantOn)
|
||||
}
|
||||
if let adminRights = group.userAdminRights, !group.isCreator {
|
||||
var rights: [String] = []
|
||||
if adminRights.rights.contains(.canChangeInfo) {
|
||||
|
@ -10,33 +10,25 @@ public struct ChatTranslationState: Codable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case baseLang
|
||||
case fromLang
|
||||
case forcedFromLang
|
||||
case toLang
|
||||
case isEnabled
|
||||
case isHidden
|
||||
}
|
||||
|
||||
public let baseLang: String
|
||||
public let fromLang: String
|
||||
public let forcedFromLang: String?
|
||||
public let toLang: String?
|
||||
public let isEnabled: Bool
|
||||
public let isHidden: Bool?
|
||||
|
||||
public init(
|
||||
baseLang: String,
|
||||
fromLang: String,
|
||||
forcedFromLang: String?,
|
||||
toLang: String?,
|
||||
isEnabled: Bool,
|
||||
isHidden: Bool?
|
||||
isEnabled: Bool
|
||||
) {
|
||||
self.baseLang = baseLang
|
||||
self.fromLang = fromLang
|
||||
self.forcedFromLang = forcedFromLang
|
||||
self.toLang = toLang
|
||||
self.isEnabled = isEnabled
|
||||
self.isHidden = isHidden
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
@ -44,10 +36,8 @@ public struct ChatTranslationState: Codable {
|
||||
|
||||
self.baseLang = try container.decode(String.self, forKey: .baseLang)
|
||||
self.fromLang = try container.decode(String.self, forKey: .fromLang)
|
||||
self.forcedFromLang = try container.decodeIfPresent(String.self, forKey: .forcedFromLang)
|
||||
self.toLang = try container.decodeIfPresent(String.self, forKey: .toLang)
|
||||
self.isEnabled = try container.decode(Bool.self, forKey: .isEnabled)
|
||||
self.isHidden = try container.decodeIfPresent(Bool.self, forKey: .isHidden)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
@ -55,20 +45,16 @@ public struct ChatTranslationState: Codable {
|
||||
|
||||
try container.encode(self.baseLang, forKey: .baseLang)
|
||||
try container.encode(self.fromLang, forKey: .fromLang)
|
||||
try container.encodeIfPresent(self.forcedFromLang, forKey: .forcedFromLang)
|
||||
try container.encodeIfPresent(self.toLang, forKey: .toLang)
|
||||
try container.encode(self.isEnabled, forKey: .isEnabled)
|
||||
try container.encodeIfPresent(self.isHidden, forKey: .isHidden)
|
||||
}
|
||||
|
||||
public func withToLang(_ toLang: String?) -> ChatTranslationState {
|
||||
return ChatTranslationState(
|
||||
baseLang: self.baseLang,
|
||||
fromLang: self.fromLang,
|
||||
forcedFromLang: self.forcedFromLang,
|
||||
toLang: toLang,
|
||||
isEnabled: self.isEnabled,
|
||||
isHidden: self.isHidden
|
||||
isEnabled: self.isEnabled
|
||||
)
|
||||
}
|
||||
|
||||
@ -76,21 +62,8 @@ public struct ChatTranslationState: Codable {
|
||||
return ChatTranslationState(
|
||||
baseLang: self.baseLang,
|
||||
fromLang: self.fromLang,
|
||||
forcedFromLang: self.forcedFromLang,
|
||||
toLang: self.toLang,
|
||||
isEnabled: isEnabled,
|
||||
isHidden: self.isHidden
|
||||
)
|
||||
}
|
||||
|
||||
public func withIsHidden(_ isHidden: Bool) -> ChatTranslationState {
|
||||
return ChatTranslationState(
|
||||
baseLang: self.baseLang,
|
||||
fromLang: self.fromLang,
|
||||
forcedFromLang: self.forcedFromLang,
|
||||
toLang: self.toLang,
|
||||
isEnabled: self.isEnabled,
|
||||
isHidden: isHidden
|
||||
isEnabled: isEnabled
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -210,7 +183,7 @@ public func chatTranslationState(context: AccountContext, peerId: EnginePeer.Id)
|
||||
}
|
||||
}
|
||||
let fromLang = mostFrequent?.0 ?? ""
|
||||
let state = ChatTranslationState(baseLang: baseLang, fromLang: fromLang, forcedFromLang: nil, toLang: nil, isEnabled: false, isHidden: false)
|
||||
let state = ChatTranslationState(baseLang: baseLang, fromLang: fromLang, toLang: nil, isEnabled: false)
|
||||
let _ = updateChatTranslationState(engine: context.engine, peerId: peerId, state: state).start()
|
||||
return state
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user