mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Add bio editing and photo setup in voice chats
This commit is contained in:
parent
1fd84c653b
commit
2c404a8b5c
@ -6317,6 +6317,7 @@ Sorry for the inconvenience.";
|
|||||||
"VoiceChat.EditBioText" = "Any details such as age, occupation or city.";
|
"VoiceChat.EditBioText" = "Any details such as age, occupation or city.";
|
||||||
"VoiceChat.EditBioPlaceholder" = "Bio";
|
"VoiceChat.EditBioPlaceholder" = "Bio";
|
||||||
"VoiceChat.EditBioSave" = "Save";
|
"VoiceChat.EditBioSave" = "Save";
|
||||||
|
"VoiceChat.EditBioSuccess" = "Your bio is saved.";
|
||||||
|
|
||||||
"VoiceChat.SendPublicLinkText" = "%1$@ isn't a member of \"%2$@\" yet. Send them a public invite link instead?";
|
"VoiceChat.SendPublicLinkText" = "%1$@ isn't a member of \"%2$@\" yet. Send them a public invite link instead?";
|
||||||
"VoiceChat.SendPublicLinkSend" = "Send";
|
"VoiceChat.SendPublicLinkSend" = "Send";
|
||||||
|
@ -41,6 +41,7 @@ public final class OpenChatMessageParams {
|
|||||||
public let actionInteraction: GalleryControllerActionInteraction?
|
public let actionInteraction: GalleryControllerActionInteraction?
|
||||||
public let playlistLocation: PeerMessagesPlaylistLocation?
|
public let playlistLocation: PeerMessagesPlaylistLocation?
|
||||||
public let gallerySource: GalleryControllerItemSource?
|
public let gallerySource: GalleryControllerItemSource?
|
||||||
|
public let centralItemUpdated: ((MessageId) -> Void)?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
@ -65,7 +66,8 @@ public final class OpenChatMessageParams {
|
|||||||
chatAvatarHiddenMedia: @escaping (Signal<MessageId?, NoError>, Media) -> Void,
|
chatAvatarHiddenMedia: @escaping (Signal<MessageId?, NoError>, Media) -> Void,
|
||||||
actionInteraction: GalleryControllerActionInteraction? = nil,
|
actionInteraction: GalleryControllerActionInteraction? = nil,
|
||||||
playlistLocation: PeerMessagesPlaylistLocation? = nil,
|
playlistLocation: PeerMessagesPlaylistLocation? = nil,
|
||||||
gallerySource: GalleryControllerItemSource? = nil
|
gallerySource: GalleryControllerItemSource? = nil,
|
||||||
|
centralItemUpdated: ((MessageId) -> Void)? = nil
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.chatLocation = chatLocation
|
self.chatLocation = chatLocation
|
||||||
@ -90,5 +92,6 @@ public final class OpenChatMessageParams {
|
|||||||
self.actionInteraction = actionInteraction
|
self.actionInteraction = actionInteraction
|
||||||
self.playlistLocation = playlistLocation
|
self.playlistLocation = playlistLocation
|
||||||
self.gallerySource = gallerySource
|
self.gallerySource = gallerySource
|
||||||
|
self.centralItemUpdated = centralItemUpdated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -384,6 +384,8 @@ public class GalleryController: ViewController, StandalonePresentableController
|
|||||||
|
|
||||||
private var screenCaptureEventsDisposable: Disposable?
|
private var screenCaptureEventsDisposable: Disposable?
|
||||||
|
|
||||||
|
public var centralItemUpdated: ((MessageId) -> Void)?
|
||||||
|
|
||||||
public init(context: AccountContext, source: GalleryControllerItemSource, invertItemOrder: Bool = false, streamSingleVideo: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, synchronousLoad: Bool = false, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, baseNavigationController: NavigationController?, actionInteraction: GalleryControllerActionInteraction? = nil) {
|
public init(context: AccountContext, source: GalleryControllerItemSource, invertItemOrder: Bool = false, streamSingleVideo: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, synchronousLoad: Bool = false, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, baseNavigationController: NavigationController?, actionInteraction: GalleryControllerActionInteraction? = nil) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.source = source
|
self.source = source
|
||||||
@ -1192,6 +1194,9 @@ public class GalleryController: ViewController, StandalonePresentableController
|
|||||||
}
|
}
|
||||||
if strongSelf.didSetReady {
|
if strongSelf.didSetReady {
|
||||||
strongSelf._hiddenMedia.set(.single(hiddenItem))
|
strongSelf._hiddenMedia.set(.single(hiddenItem))
|
||||||
|
if let hiddenItem = hiddenItem {
|
||||||
|
strongSelf.centralItemUpdated?(hiddenItem.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,7 @@ typedef enum {
|
|||||||
|
|
||||||
- (TGNavigationBarPallete *)navigationBarPallete;
|
- (TGNavigationBarPallete *)navigationBarPallete;
|
||||||
- (TGMenuSheetPallete *)menuSheetPallete;
|
- (TGMenuSheetPallete *)menuSheetPallete;
|
||||||
|
- (TGMenuSheetPallete *)darkMenuSheetPallete;
|
||||||
- (TGMediaAssetsPallete *)mediaAssetsPallete;
|
- (TGMediaAssetsPallete *)mediaAssetsPallete;
|
||||||
- (TGCheckButtonPallete *)checkButtonPallete;
|
- (TGCheckButtonPallete *)checkButtonPallete;
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@ typedef void (^TGMediaAvatarPresentImpl)(id<LegacyComponentsContext>, void (^)(U
|
|||||||
|
|
||||||
@interface TGMediaAvatarMenuMixin : NSObject
|
@interface TGMediaAvatarMenuMixin : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, assign) bool forceDark;
|
||||||
|
|
||||||
@property (nonatomic, copy) void (^didFinishWithImage)(UIImage *image);
|
@property (nonatomic, copy) void (^didFinishWithImage)(UIImage *image);
|
||||||
@property (nonatomic, copy) void (^didFinishWithVideo)(UIImage *image, AVAsset *asset, TGVideoEditAdjustments *adjustments);
|
@property (nonatomic, copy) void (^didFinishWithVideo)(UIImage *image, AVAsset *asset, TGVideoEditAdjustments *adjustments);
|
||||||
@property (nonatomic, copy) void (^didFinishWithDelete)(void);
|
@property (nonatomic, copy) void (^didFinishWithDelete)(void);
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
@property (nonatomic, assign) bool dismissesByOutsideTap;
|
@property (nonatomic, assign) bool dismissesByOutsideTap;
|
||||||
@property (nonatomic, assign) bool hasSwipeGesture;
|
@property (nonatomic, assign) bool hasSwipeGesture;
|
||||||
|
|
||||||
|
@property (nonatomic, assign) bool forceDark;
|
||||||
|
|
||||||
@property (nonatomic, assign) bool followsKeyboard;
|
@property (nonatomic, assign) bool followsKeyboard;
|
||||||
|
|
||||||
@property (nonatomic, assign) bool ignoreNextDismissal;
|
@property (nonatomic, assign) bool ignoreNextDismissal;
|
||||||
|
@ -73,7 +73,7 @@
|
|||||||
- (TGMenuSheetController *)_presentAvatarMenu
|
- (TGMenuSheetController *)_presentAvatarMenu
|
||||||
{
|
{
|
||||||
__weak TGMediaAvatarMenuMixin *weakSelf = self;
|
__weak TGMediaAvatarMenuMixin *weakSelf = self;
|
||||||
TGMenuSheetController *controller = [[TGMenuSheetController alloc] initWithContext:_context dark:false];
|
TGMenuSheetController *controller = [[TGMenuSheetController alloc] initWithContext:_context dark:self.forceDark];
|
||||||
controller.dismissesByOutsideTap = true;
|
controller.dismissesByOutsideTap = true;
|
||||||
controller.hasSwipeGesture = true;
|
controller.hasSwipeGesture = true;
|
||||||
controller.didDismiss = ^(bool manual)
|
controller.didDismiss = ^(bool manual)
|
||||||
|
@ -95,7 +95,9 @@ typedef enum
|
|||||||
_permittedArrowDirections = UIPopoverArrowDirectionDown;
|
_permittedArrowDirections = UIPopoverArrowDirectionDown;
|
||||||
_requiuresDimView = true;
|
_requiuresDimView = true;
|
||||||
|
|
||||||
if (!dark && [[LegacyComponentsGlobals provider] respondsToSelector:@selector(menuSheetPallete)])
|
if (dark && [[LegacyComponentsGlobals provider] respondsToSelector:@selector(darkMenuSheetPallete)])
|
||||||
|
self.pallete = [[LegacyComponentsGlobals provider] darkMenuSheetPallete];
|
||||||
|
else if (!dark && [[LegacyComponentsGlobals provider] respondsToSelector:@selector(menuSheetPallete)])
|
||||||
self.pallete = [[LegacyComponentsGlobals provider] menuSheetPallete];
|
self.pallete = [[LegacyComponentsGlobals provider] menuSheetPallete];
|
||||||
|
|
||||||
self.wantsFullScreenLayout = true;
|
self.wantsFullScreenLayout = true;
|
||||||
|
@ -301,7 +301,7 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, chatLocati
|
|||||||
|
|
||||||
let presentationDisposable = context.sharedContext.presentationData.start(next: { [weak legacyController] presentationData in
|
let presentationDisposable = context.sharedContext.presentationData.start(next: { [weak legacyController] presentationData in
|
||||||
if let legacyController = legacyController, let controller = legacyController.legacyController as? TGMenuSheetController {
|
if let legacyController = legacyController, let controller = legacyController.legacyController as? TGMenuSheetController {
|
||||||
controller.pallete = legacyMenuPaletteFromTheme(presentationData.theme)
|
controller.pallete = legacyMenuPaletteFromTheme(presentationData.theme, forceDark: false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
legacyController.disposables.add(presentationDisposable)
|
legacyController.disposables.add(presentationDisposable)
|
||||||
@ -378,9 +378,14 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, chatLocati
|
|||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
public func legacyMenuPaletteFromTheme(_ theme: PresentationTheme) -> TGMenuSheetPallete {
|
public func legacyMenuPaletteFromTheme(_ theme: PresentationTheme, forceDark: Bool) -> TGMenuSheetPallete {
|
||||||
let sheetTheme = theme.actionSheet
|
let sheetTheme: PresentationThemeActionSheet
|
||||||
return TGMenuSheetPallete(dark: theme.overallDarkAppearance, backgroundColor: sheetTheme.opaqueItemBackgroundColor, selectionColor: sheetTheme.opaqueItemHighlightedBackgroundColor, separatorColor: sheetTheme.opaqueItemSeparatorColor, accentColor: sheetTheme.controlAccentColor, destructiveColor: sheetTheme.destructiveActionTextColor, textColor: sheetTheme.primaryTextColor, secondaryTextColor: sheetTheme.secondaryTextColor, spinnerColor: sheetTheme.secondaryTextColor, badgeTextColor: sheetTheme.controlAccentColor, badgeImage: nil, cornersImage: generateStretchableFilledCircleImage(diameter: 11.0, color: nil, strokeColor: nil, strokeWidth: nil, backgroundColor: sheetTheme.opaqueItemBackgroundColor))
|
if forceDark && !theme.overallDarkAppearance {
|
||||||
|
sheetTheme = defaultDarkColorPresentationTheme.actionSheet
|
||||||
|
} else {
|
||||||
|
sheetTheme = theme.actionSheet
|
||||||
|
}
|
||||||
|
return TGMenuSheetPallete(dark: forceDark || theme.overallDarkAppearance, backgroundColor: sheetTheme.opaqueItemBackgroundColor, selectionColor: sheetTheme.opaqueItemHighlightedBackgroundColor, separatorColor: sheetTheme.opaqueItemSeparatorColor, accentColor: sheetTheme.controlAccentColor, destructiveColor: sheetTheme.destructiveActionTextColor, textColor: sheetTheme.primaryTextColor, secondaryTextColor: sheetTheme.secondaryTextColor, spinnerColor: sheetTheme.secondaryTextColor, badgeTextColor: sheetTheme.controlAccentColor, badgeImage: nil, cornersImage: generateStretchableFilledCircleImage(diameter: 11.0, color: nil, strokeColor: nil, strokeWidth: nil, backgroundColor: sheetTheme.opaqueItemBackgroundColor))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func presentLegacyPasteMenu(context: AccountContext, peer: Peer, chatLocation: ChatLocation, saveEditedPhotos: Bool, allowGrouping: Bool, hasSchedule: Bool, presentationData: PresentationData, images: [UIImage], presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?, present: (ViewController, Any?) -> Void, initialLayout: ContainerViewLayout? = nil) -> ViewController {
|
public func presentLegacyPasteMenu(context: AccountContext, peer: Peer, chatLocation: ChatLocation, saveEditedPhotos: Bool, allowGrouping: Bool, hasSchedule: Bool, presentationData: PresentationData, images: [UIImage], presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?, present: (ViewController, Any?) -> Void, initialLayout: ContainerViewLayout? = nil) -> ViewController {
|
||||||
@ -441,7 +446,7 @@ public func presentLegacyPasteMenu(context: AccountContext, peer: Peer, chatLoca
|
|||||||
|
|
||||||
let presentationDisposable = context.sharedContext.presentationData.start(next: { [weak legacyController] presentationData in
|
let presentationDisposable = context.sharedContext.presentationData.start(next: { [weak legacyController] presentationData in
|
||||||
if let legacyController = legacyController, let controller = legacyController.legacyController as? TGMenuSheetController {
|
if let legacyController = legacyController, let controller = legacyController.legacyController as? TGMenuSheetController {
|
||||||
controller.pallete = legacyMenuPaletteFromTheme(presentationData.theme)
|
controller.pallete = legacyMenuPaletteFromTheme(presentationData.theme, forceDark: false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
legacyController.disposables.add(presentationDisposable)
|
legacyController.disposables.add(presentationDisposable)
|
||||||
|
@ -279,6 +279,22 @@ private final class LegacyComponentsGlobalsProviderImpl: NSObject, LegacyCompone
|
|||||||
return TGMenuSheetPallete(dark: theme.overallDarkAppearance, backgroundColor: sheetTheme.opaqueItemBackgroundColor, selectionColor: sheetTheme.opaqueItemHighlightedBackgroundColor, separatorColor: sheetTheme.opaqueItemSeparatorColor, accentColor: sheetTheme.controlAccentColor, destructiveColor: sheetTheme.destructiveActionTextColor, textColor: sheetTheme.primaryTextColor, secondaryTextColor: sheetTheme.secondaryTextColor, spinnerColor: sheetTheme.secondaryTextColor, badgeTextColor: sheetTheme.controlAccentColor, badgeImage: nil, cornersImage: generateStretchableFilledCircleImage(diameter: 11.0, color: nil, strokeColor: nil, strokeWidth: nil, backgroundColor: sheetTheme.opaqueItemBackgroundColor))
|
return TGMenuSheetPallete(dark: theme.overallDarkAppearance, backgroundColor: sheetTheme.opaqueItemBackgroundColor, selectionColor: sheetTheme.opaqueItemHighlightedBackgroundColor, separatorColor: sheetTheme.opaqueItemSeparatorColor, accentColor: sheetTheme.controlAccentColor, destructiveColor: sheetTheme.destructiveActionTextColor, textColor: sheetTheme.primaryTextColor, secondaryTextColor: sheetTheme.secondaryTextColor, spinnerColor: sheetTheme.secondaryTextColor, badgeTextColor: sheetTheme.controlAccentColor, badgeImage: nil, cornersImage: generateStretchableFilledCircleImage(diameter: 11.0, color: nil, strokeColor: nil, strokeWidth: nil, backgroundColor: sheetTheme.opaqueItemBackgroundColor))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func darkMenuSheetPallete() -> TGMenuSheetPallete! {
|
||||||
|
let theme: PresentationTheme
|
||||||
|
if let legacyContext = legacyContext {
|
||||||
|
let presentationData = legacyContext.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
if presentationData.theme.overallDarkAppearance {
|
||||||
|
theme = presentationData.theme
|
||||||
|
} else {
|
||||||
|
theme = defaultDarkColorPresentationTheme
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
theme = defaultDarkColorPresentationTheme
|
||||||
|
}
|
||||||
|
let sheetTheme = theme.actionSheet
|
||||||
|
return TGMenuSheetPallete(dark: theme.overallDarkAppearance, backgroundColor: sheetTheme.opaqueItemBackgroundColor, selectionColor: sheetTheme.opaqueItemHighlightedBackgroundColor, separatorColor: sheetTheme.opaqueItemSeparatorColor, accentColor: sheetTheme.controlAccentColor, destructiveColor: sheetTheme.destructiveActionTextColor, textColor: sheetTheme.primaryTextColor, secondaryTextColor: sheetTheme.secondaryTextColor, spinnerColor: sheetTheme.secondaryTextColor, badgeTextColor: sheetTheme.controlAccentColor, badgeImage: nil, cornersImage: generateStretchableFilledCircleImage(diameter: 11.0, color: nil, strokeColor: nil, strokeWidth: nil, backgroundColor: sheetTheme.opaqueItemBackgroundColor))
|
||||||
|
}
|
||||||
|
|
||||||
func mediaAssetsPallete() -> TGMediaAssetsPallete! {
|
func mediaAssetsPallete() -> TGMediaAssetsPallete! {
|
||||||
let presentationTheme: PresentationTheme
|
let presentationTheme: PresentationTheme
|
||||||
if let legacyContext = legacyContext {
|
if let legacyContext = legacyContext {
|
||||||
|
@ -69,6 +69,10 @@ public enum AvatarGalleryEntry: Equatable {
|
|||||||
case topImage([ImageRepresentationWithReference], [VideoRepresentationWithReference], Peer?, GalleryItemIndexData?, Data?, String?)
|
case topImage([ImageRepresentationWithReference], [VideoRepresentationWithReference], Peer?, GalleryItemIndexData?, Data?, String?)
|
||||||
case image(MediaId, TelegramMediaImageReference?, [ImageRepresentationWithReference], [VideoRepresentationWithReference], Peer?, Int32?, GalleryItemIndexData?, MessageId?, Data?, String?)
|
case image(MediaId, TelegramMediaImageReference?, [ImageRepresentationWithReference], [VideoRepresentationWithReference], Peer?, Int32?, GalleryItemIndexData?, MessageId?, Data?, String?)
|
||||||
|
|
||||||
|
public init(representation: TelegramMediaImageRepresentation, peer: Peer) {
|
||||||
|
self = .topImage([ImageRepresentationWithReference(representation: representation, reference: MediaResourceReference.standalone(resource: representation.resource))], [], peer, nil, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
public var id: AvatarGalleryEntryId {
|
public var id: AvatarGalleryEntryId {
|
||||||
switch self {
|
switch self {
|
||||||
case let .topImage(representations, _, _, _, _, _):
|
case let .topImage(representations, _, _, _, _, _):
|
||||||
|
@ -125,8 +125,9 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
|||||||
private let statusNode: RadialStatusNode
|
private let statusNode: RadialStatusNode
|
||||||
|
|
||||||
private var playerStatus: MediaPlayerStatus?
|
private var playerStatus: MediaPlayerStatus?
|
||||||
private var isLoading = ValuePromise<Bool>(false)
|
private var isLoading = Promise<Bool>(false)
|
||||||
private var loadingProgress = ValuePromise<Float?>(nil)
|
private var loadingProgress = Promise<Float?>(nil)
|
||||||
|
private var progress: Signal<Float?, NoError>?
|
||||||
private var loadingProgressDisposable = MetaDisposable()
|
private var loadingProgressDisposable = MetaDisposable()
|
||||||
private var hasProgress = false
|
private var hasProgress = false
|
||||||
|
|
||||||
@ -241,8 +242,11 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
|||||||
bufferingProgress = nil
|
bufferingProgress = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.loadingProgress.set(bufferingProgress)
|
|
||||||
self.isLoading.set(bufferingProgress != nil)
|
if self.progress == nil {
|
||||||
|
self.loadingProgress.set(.single(bufferingProgress))
|
||||||
|
self.isLoading.set(.single(bufferingProgress != nil))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateTransitionFraction(_ fraction: CGFloat, transition: ContainedViewLayoutTransition) {
|
public func updateTransitionFraction(_ fraction: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
@ -310,8 +314,16 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
|||||||
self.isReady.set(videoNode.ready |> map { return true })
|
self.isReady.set(videoNode.ready |> map { return true })
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(item: PeerInfoAvatarListItem, synchronous: Bool, fullSizeOnly: Bool = false) {
|
func setup(item: PeerInfoAvatarListItem, progress: Signal<Float?, NoError>? = nil, synchronous: Bool, fullSizeOnly: Bool = false) {
|
||||||
self.item = item
|
self.item = item
|
||||||
|
self.progress = progress
|
||||||
|
|
||||||
|
if let progress = progress {
|
||||||
|
self.loadingProgress.set((progress
|
||||||
|
|> beforeNext { [weak self] next in
|
||||||
|
self?.isLoading.set(.single(next != nil))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
let representations: [ImageRepresentationWithReference]
|
let representations: [ImageRepresentationWithReference]
|
||||||
let videoRepresentations: [VideoRepresentationWithReference]
|
let videoRepresentations: [VideoRepresentationWithReference]
|
||||||
@ -912,7 +924,8 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
|||||||
return items.count == 0
|
return items.count == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
public func update(size: CGSize, peer: Peer?, isExpanded: Bool, transition: ContainedViewLayoutTransition) {
|
private var additionalEntryProgress: Signal<Float?, NoError>? = nil
|
||||||
|
public func update(size: CGSize, peer: Peer?, additionalEntry: Signal<(TelegramMediaImageRepresentation, Float)?, NoError> = .single(nil), isExpanded: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
self.validLayout = size
|
self.validLayout = size
|
||||||
let previousExpanded = self.isExpanded
|
let previousExpanded = self.isExpanded
|
||||||
self.isExpanded = isExpanded
|
self.isExpanded = isExpanded
|
||||||
@ -924,17 +937,23 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
|||||||
|
|
||||||
if let peer = peer, !self.initializedList {
|
if let peer = peer, !self.initializedList {
|
||||||
self.initializedList = true
|
self.initializedList = true
|
||||||
self.disposable.set((peerInfoProfilePhotosWithCache(context: self.context, peerId: peer.id)
|
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] (complete, entries) in
|
let entry = additionalEntry
|
||||||
|
|> map { representation -> AvatarGalleryEntry? in
|
||||||
|
return representation.flatMap { AvatarGalleryEntry(representation: $0.0, peer: peer) }
|
||||||
|
}
|
||||||
|
|
||||||
|
self.disposable.set(combineLatest(queue: Queue.mainQueue(), peerInfoProfilePhotosWithCache(context: self.context, peerId: peer.id), entry).start(next: { [weak self] completeAndEntries, entry in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (complete, entries) = completeAndEntries
|
||||||
|
|
||||||
if strongSelf.galleryEntries.count > 1, entries.count == 1 && !complete {
|
if strongSelf.galleryEntries.count > 1, entries.count == 1 && !complete {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var entries = entries
|
|
||||||
var synchronous = false
|
var synchronous = false
|
||||||
if !strongSelf.galleryEntries.isEmpty, let updated = entries.first, case let .image(image) = updated, !image.3.isEmpty, let previous = strongSelf.galleryEntries.first, case let .topImage(topImage) = previous {
|
if !strongSelf.galleryEntries.isEmpty, let updated = entries.first, case let .image(image) = updated, !image.3.isEmpty, let previous = strongSelf.galleryEntries.first, case let .topImage(topImage) = previous {
|
||||||
let firstEntry = AvatarGalleryEntry.image(image.0, image.1, topImage.0, image.3, image.4, image.5, image.6, image.7, image.8, image.9)
|
let firstEntry = AvatarGalleryEntry.image(image.0, image.1, topImage.0, image.3, image.4, image.5, image.6, image.7, image.8, image.9)
|
||||||
@ -943,6 +962,15 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
|||||||
synchronous = true
|
synchronous = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let entry = entry {
|
||||||
|
entries.insert(entry, at: 0)
|
||||||
|
|
||||||
|
strongSelf.additionalEntryProgress = additionalEntry
|
||||||
|
|> map { value -> Float? in
|
||||||
|
return value?.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if strongSelf.ignoreNextProfilePhotoUpdate {
|
if strongSelf.ignoreNextProfilePhotoUpdate {
|
||||||
if entries.count == 1, let first = entries.first, case .topImage = first {
|
if entries.count == 1, let first = entries.first, case .topImage = first {
|
||||||
return
|
return
|
||||||
@ -1067,7 +1095,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
|||||||
wasAdded = true
|
wasAdded = true
|
||||||
let addedItemNode = PeerInfoAvatarListItemNode(context: self.context, peer: peer)
|
let addedItemNode = PeerInfoAvatarListItemNode(context: self.context, peer: peer)
|
||||||
itemNode = addedItemNode
|
itemNode = addedItemNode
|
||||||
addedItemNode.setup(item: self.items[i], synchronous: (i == 0 && i == self.currentIndex) || (synchronous && i == self.currentIndex), fullSizeOnly: self.firstFullSizeOnly && i == 0)
|
addedItemNode.setup(item: self.items[i], progress: i == 0 ? self.additionalEntryProgress : nil, synchronous: (i == 0 && i == self.currentIndex) || (synchronous && i == self.currentIndex), fullSizeOnly: self.firstFullSizeOnly && i == 0)
|
||||||
self.itemNodes[self.items[i].id] = addedItemNode
|
self.itemNodes[self.items[i].id] = addedItemNode
|
||||||
self.contentNode.addSubnode(addedItemNode)
|
self.contentNode.addSubnode(addedItemNode)
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ swift_library(
|
|||||||
"//submodules/TemporaryCachedPeerDataManager:TemporaryCachedPeerDataManager",
|
"//submodules/TemporaryCachedPeerDataManager:TemporaryCachedPeerDataManager",
|
||||||
"//submodules/PeerInfoAvatarListNode:PeerInfoAvatarListNode",
|
"//submodules/PeerInfoAvatarListNode:PeerInfoAvatarListNode",
|
||||||
"//submodules/WebSearchUI:WebSearchUI",
|
"//submodules/WebSearchUI:WebSearchUI",
|
||||||
|
"//submodules/MapResourceToAvatarSizes:MapResourceToAvatarSizes",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -1401,13 +1401,15 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
)
|
)
|
||||||
self.temporaryParticipantsContext = nil
|
self.temporaryParticipantsContext = nil
|
||||||
self.participantsContext = participantsContext
|
self.participantsContext = participantsContext
|
||||||
let myPeer = self.accountContext.account.postbox.transaction { transaction -> (Peer, CachedPeerData?)? in
|
let myPeer = self.accountContext.account.postbox.peerView(id: myPeerId)
|
||||||
if let peer = transaction.getPeer(myPeerId) {
|
|> map { view -> (Peer, CachedPeerData?)? in
|
||||||
return (peer, transaction.getPeerCachedData(peerId: myPeerId))
|
if let peer = peerViewMainPeer(view) {
|
||||||
|
return (peer, view.cachedData)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.participantsContextStateDisposable.set(combineLatest(queue: .mainQueue(),
|
self.participantsContextStateDisposable.set(combineLatest(queue: .mainQueue(),
|
||||||
participantsContext.state,
|
participantsContext.state,
|
||||||
participantsContext.activeSpeakers,
|
participantsContext.activeSpeakers,
|
||||||
@ -1507,6 +1509,19 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if participant.peer.id == strongSelf.joinAsPeerId {
|
if participant.peer.id == strongSelf.joinAsPeerId {
|
||||||
|
if let (myPeer, cachedData) = myPeerAndCachedData {
|
||||||
|
let about: String?
|
||||||
|
if let cachedData = cachedData as? CachedUserData {
|
||||||
|
about = cachedData.about
|
||||||
|
} else if let cachedData = cachedData as? CachedChannelData {
|
||||||
|
about = cachedData.about
|
||||||
|
} else {
|
||||||
|
about = nil
|
||||||
|
}
|
||||||
|
participant.peer = myPeer
|
||||||
|
participant.about = about
|
||||||
|
}
|
||||||
|
|
||||||
var filteredMuteState = participant.muteState
|
var filteredMuteState = participant.muteState
|
||||||
if isReconnectingAsSpeaker || strongSelf.currentConnectionMode != .rtc {
|
if isReconnectingAsSpeaker || strongSelf.currentConnectionMode != .rtc {
|
||||||
filteredMuteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: false)
|
filteredMuteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: false)
|
||||||
|
@ -28,6 +28,7 @@ import LegacyUI
|
|||||||
import LegacyComponents
|
import LegacyComponents
|
||||||
import LegacyMediaPickerUI
|
import LegacyMediaPickerUI
|
||||||
import WebSearchUI
|
import WebSearchUI
|
||||||
|
import MapResourceToAvatarSizes
|
||||||
|
|
||||||
private let panelBackgroundColor = UIColor(rgb: 0x1c1c1e)
|
private let panelBackgroundColor = UIColor(rgb: 0x1c1c1e)
|
||||||
private let secondaryPanelBackgroundColor = UIColor(rgb: 0x2c2c2e)
|
private let secondaryPanelBackgroundColor = UIColor(rgb: 0x2c2c2e)
|
||||||
@ -381,6 +382,8 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
private var audioLevels: [PeerId: ValuePipe<Float>] = [:]
|
private var audioLevels: [PeerId: ValuePipe<Float>] = [:]
|
||||||
|
|
||||||
|
var updateAvatarPromise = Promise<(TelegramMediaImageRepresentation, Float)?>(nil)
|
||||||
|
|
||||||
init(
|
init(
|
||||||
updateIsMuted: @escaping (PeerId, Bool) -> Void,
|
updateIsMuted: @escaping (PeerId, Bool) -> Void,
|
||||||
openPeer: @escaping (PeerId) -> Void,
|
openPeer: @escaping (PeerId) -> Void,
|
||||||
@ -652,7 +655,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
let revealOptions: [VoiceChatParticipantItem.RevealOption] = []
|
let revealOptions: [VoiceChatParticipantItem.RevealOption] = []
|
||||||
|
|
||||||
return VoiceChatParticipantItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: peer, ssrc: peerEntry.ssrc, presence: peerEntry.presence, text: text, expandedText: expandedText, icon: icon, enabled: true, selectable: !peerEntry.isMyPeer || peerEntry.canManageCall || peerEntry.raisedHand, getAudioLevel: { return interaction.getAudioLevel(peer.id) }, getVideo: {
|
return VoiceChatParticipantItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: peer, ssrc: peerEntry.ssrc, presence: peerEntry.presence, text: text, expandedText: expandedText, icon: icon, enabled: true, selectable: true, getAudioLevel: { return interaction.getAudioLevel(peer.id) }, getVideo: {
|
||||||
if let ssrc = peerEntry.ssrc {
|
if let ssrc = peerEntry.ssrc {
|
||||||
return interaction.getPeerVideo(ssrc)
|
return interaction.getPeerVideo(ssrc)
|
||||||
} else {
|
} else {
|
||||||
@ -664,6 +667,8 @@ public final class VoiceChatController: ViewController {
|
|||||||
interaction.peerContextAction(peerEntry, node, nil)
|
interaction.peerContextAction(peerEntry, node, nil)
|
||||||
}, contextAction: nil, getIsExpanded: {
|
}, contextAction: nil, getIsExpanded: {
|
||||||
return interaction.isExpanded
|
return interaction.isExpanded
|
||||||
|
}, getUpdatingAvatar: {
|
||||||
|
return interaction.updateAvatarPromise.get()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -787,6 +792,10 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
private var currentDominantSpeakerWithVideo: (PeerId, UInt32)?
|
private var currentDominantSpeakerWithVideo: (PeerId, UInt32)?
|
||||||
|
|
||||||
|
private var updateAvatarDisposable = MetaDisposable()
|
||||||
|
private let updateAvatarPromise = Promise<(TelegramMediaImageRepresentation, Float)?>(nil)
|
||||||
|
private var currentUpdatingAvatar: TelegramMediaImageRepresentation?
|
||||||
|
|
||||||
init(controller: VoiceChatController, sharedContext: SharedAccountContext, call: PresentationGroupCall) {
|
init(controller: VoiceChatController, sharedContext: SharedAccountContext, call: PresentationGroupCall) {
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.sharedContext = sharedContext
|
self.sharedContext = sharedContext
|
||||||
@ -1239,6 +1248,12 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if peer.id == strongSelf.callState?.myPeerId {
|
if peer.id == strongSelf.callState?.myPeerId {
|
||||||
|
let maxLength: Int
|
||||||
|
if peer.id.namespace == Namespaces.Peer.CloudUser {
|
||||||
|
maxLength = 70
|
||||||
|
} else {
|
||||||
|
maxLength = 100
|
||||||
|
}
|
||||||
if entry.raisedHand {
|
if entry.raisedHand {
|
||||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_CancelSpeakRequest, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_CancelSpeakRequest, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Call/Context Menu/RevokeSpeak"), color: theme.actionSheet.primaryTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Call/Context Menu/RevokeSpeak"), color: theme.actionSheet.primaryTextColor)
|
||||||
@ -1251,35 +1266,41 @@ public final class VoiceChatController: ViewController {
|
|||||||
f(.default)
|
f(.default)
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
// items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_ChangePhoto, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_ChangePhoto, icon: { theme in
|
||||||
// return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Camera"), color: theme.actionSheet.primaryTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Camera"), color: theme.actionSheet.primaryTextColor)
|
||||||
// }, action: { _, f in
|
}, action: { _, f in
|
||||||
// guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// f(.default)
|
f(.default)
|
||||||
//
|
|
||||||
// strongSelf.openAvatarForEditing(fromGallery: false, completion: {})
|
Queue.mainQueue().after(0.1) {
|
||||||
// })))
|
strongSelf.openAvatarForEditing(fromGallery: false, completion: {})
|
||||||
// items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_EditBio, icon: { theme in
|
}
|
||||||
// return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Pencil"), color: theme.actionSheet.primaryTextColor)
|
})))
|
||||||
// }, action: { _, f in
|
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_EditBio, icon: { theme in
|
||||||
// guard let strongSelf = self else {
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Pencil"), color: theme.actionSheet.primaryTextColor)
|
||||||
// return
|
}, action: { _, f in
|
||||||
// }
|
guard let strongSelf = self else {
|
||||||
// f(.default)
|
return
|
||||||
//
|
}
|
||||||
// let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_EditBioTitle, text: presentationData.strings.VoiceChat_EditBioText, placeholder: presentationData.strings.VoiceChat_EditBioPlaceholder, doneButtonTitle: presentationData.strings.VoiceChat_EditBioSave, value: entry.about, apply: { bio in
|
f(.default)
|
||||||
// if let strongSelf = self {
|
|
||||||
// let _ = updateAbout(account: strongSelf.context.account, about: bio)
|
Queue.mainQueue().after(0.1) {
|
||||||
// |> `catch` { _ -> Signal<Void, NoError> in
|
let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_EditBioTitle, text: presentationData.strings.VoiceChat_EditBioText, placeholder: presentationData.strings.VoiceChat_EditBioPlaceholder, doneButtonTitle: presentationData.strings.VoiceChat_EditBioSave, value: entry.about, maxLength: maxLength, apply: { bio in
|
||||||
// return .complete()
|
if let strongSelf = self {
|
||||||
// }
|
let _ = (updateAbout(account: strongSelf.context.account, about: bio)
|
||||||
// }
|
|> `catch` { _ -> Signal<Void, NoError> in
|
||||||
// })
|
return .complete()
|
||||||
// self?.controller?.present(controller, in: .window(.root))
|
}).start()
|
||||||
// })))
|
|
||||||
|
strongSelf.presentUndoOverlay(content: .voiceChatFlag(text: strongSelf.presentationData.strings.VoiceChat_EditBioSuccess), action: { _ in return false })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self?.controller?.present(controller, in: .window(.root))
|
||||||
|
}
|
||||||
|
})))
|
||||||
} else {
|
} else {
|
||||||
if let callState = strongSelf.callState, (callState.canManageCall || callState.adminIds.contains(strongSelf.context.account.peerId)) {
|
if let callState = strongSelf.callState, (callState.canManageCall || callState.adminIds.contains(strongSelf.context.account.peerId)) {
|
||||||
if callState.adminIds.contains(peer.id) {
|
if callState.adminIds.contains(peer.id) {
|
||||||
@ -1355,8 +1376,8 @@ public final class VoiceChatController: ViewController {
|
|||||||
openTitle = strongSelf.presentationData.strings.VoiceChat_OpenChannel
|
openTitle = strongSelf.presentationData.strings.VoiceChat_OpenChannel
|
||||||
openIcon = UIImage(bundleImageName: "Chat/Context Menu/Channels")
|
openIcon = UIImage(bundleImageName: "Chat/Context Menu/Channels")
|
||||||
} else {
|
} else {
|
||||||
openTitle = strongSelf.presentationData.strings.Conversation_ContextMenuOpenProfile
|
openTitle = strongSelf.presentationData.strings.Conversation_ContextMenuSendMessage
|
||||||
openIcon = UIImage(bundleImageName: "Chat/Context Menu/User")
|
openIcon = UIImage(bundleImageName: "Chat/Context Menu/Message")
|
||||||
}
|
}
|
||||||
items.append(.action(ContextMenuActionItem(text: openTitle, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: openTitle, icon: { theme in
|
||||||
return generateTintedImage(image: openIcon, color: theme.actionSheet.primaryTextColor)
|
return generateTintedImage(image: openIcon, color: theme.actionSheet.primaryTextColor)
|
||||||
@ -1368,24 +1389,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
let context = strongSelf.context
|
let context = strongSelf.context
|
||||||
strongSelf.controller?.dismiss(completion: {
|
strongSelf.controller?.dismiss(completion: {
|
||||||
Queue.mainQueue().after(0.3) {
|
Queue.mainQueue().after(0.3) {
|
||||||
if peer.id.namespace == Namespaces.Peer.CloudUser {
|
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer.id), keepStack: .always, purposefulAction: {}, peekData: nil))
|
||||||
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peer.id)
|
|
||||||
|> take(1)
|
|
||||||
|> deliverOnMainQueue).start(next: { peer in
|
|
||||||
var expandAvatar = true
|
|
||||||
if peer.smallProfileImage == nil {
|
|
||||||
expandAvatar = false
|
|
||||||
}
|
|
||||||
if let (validLayout, _) = strongSelf.validLayout, validLayout.deviceMetrics.type == .tablet {
|
|
||||||
expandAvatar = false
|
|
||||||
}
|
|
||||||
if let strongSelf = self, let controller = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: expandAvatar, fromChat: false) {
|
|
||||||
navigationController.pushViewController(controller)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer.id), keepStack: .always, purposefulAction: {}, peekData: nil))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1473,6 +1477,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
self.itemInteraction?.updateAvatarPromise = self.updateAvatarPromise
|
||||||
|
|
||||||
self.topPanelNode.addSubnode(self.topPanelEdgeNode)
|
self.topPanelNode.addSubnode(self.topPanelEdgeNode)
|
||||||
self.topPanelNode.addSubnode(self.topPanelBackgroundNode)
|
self.topPanelNode.addSubnode(self.topPanelBackgroundNode)
|
||||||
@ -1851,6 +1856,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.memberEventsDisposable.dispose()
|
self.memberEventsDisposable.dispose()
|
||||||
self.reconnectedAsEventsDisposable.dispose()
|
self.reconnectedAsEventsDisposable.dispose()
|
||||||
self.voiceSourcesDisposable.dispose()
|
self.voiceSourcesDisposable.dispose()
|
||||||
|
self.updateAvatarDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func openContextMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) {
|
private func openContextMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) {
|
||||||
@ -1911,7 +1917,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_EditTitleTitle, text: presentationData.strings.VoiceChat_EditTitleText, placeholder: chatPeer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), value: strongSelf.callState?.title, apply: { title in
|
let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_EditTitleTitle, text: presentationData.strings.VoiceChat_EditTitleText, placeholder: chatPeer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), value: strongSelf.callState?.title, maxLength: 40, apply: { title in
|
||||||
if let strongSelf = self, let title = title {
|
if let strongSelf = self, let title = title {
|
||||||
strongSelf.call.updateTitle(title)
|
strongSelf.call.updateTitle(title)
|
||||||
|
|
||||||
@ -1989,7 +1995,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_StartRecordingTitle, text: presentationData.strings.VoiceChat_StartRecordingText, placeholder: presentationData.strings.VoiceChat_RecordingTitlePlaceholder, value: nil, apply: { title in
|
let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_StartRecordingTitle, text: presentationData.strings.VoiceChat_StartRecordingText, placeholder: presentationData.strings.VoiceChat_RecordingTitlePlaceholder, value: nil, maxLength: 40, apply: { title in
|
||||||
if let strongSelf = self, let title = title {
|
if let strongSelf = self, let title = title {
|
||||||
strongSelf.call.setShouldBeRecording(true, title: title)
|
strongSelf.call.setShouldBeRecording(true, title: title)
|
||||||
|
|
||||||
@ -3251,8 +3257,13 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var memberPeer = member.peer
|
||||||
|
if member.peer.id == self.callState?.myPeerId, let user = memberPeer as? TelegramUser, let photo = self.currentUpdatingAvatar {
|
||||||
|
memberPeer = user.withUpdatedPhoto([photo])
|
||||||
|
}
|
||||||
|
|
||||||
entries.append(.peer(PeerEntry(
|
entries.append(.peer(PeerEntry(
|
||||||
peer: member.peer,
|
peer: memberPeer,
|
||||||
about: member.about,
|
about: member.about,
|
||||||
isMyPeer: self.callState?.myPeerId == member.peer.id,
|
isMyPeer: self.callState?.myPeerId == member.peer.id,
|
||||||
ssrc: member.ssrc,
|
ssrc: member.ssrc,
|
||||||
@ -3567,7 +3578,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
let presentationData = strongSelf.presentationData
|
let presentationData = strongSelf.presentationData
|
||||||
|
|
||||||
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
|
let legacyController = LegacyController(presentation: .custom, theme: strongSelf.darkTheme)
|
||||||
legacyController.statusBar.statusBarStyle = .Ignore
|
legacyController.statusBar.statusBarStyle = .Ignore
|
||||||
|
|
||||||
let emptyController = LegacyEmptyController(context: legacyController.context)!
|
let emptyController = LegacyEmptyController(context: legacyController.context)!
|
||||||
@ -3598,6 +3609,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasPhotos && !fromGallery, hasViewButton: false, personalPhoto: peerId.namespace == Namespaces.Peer.CloudUser, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
|
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasPhotos && !fromGallery, hasViewButton: false, personalPhoto: peerId.namespace == Namespaces.Peer.CloudUser, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
|
||||||
|
mixin.forceDark = true
|
||||||
mixin.stickersContext = paintStickersContext
|
mixin.stickersContext = paintStickersContext
|
||||||
let _ = strongSelf.currentAvatarMixin.swap(mixin)
|
let _ = strongSelf.currentAvatarMixin.swap(mixin)
|
||||||
mixin.requestSearchController = { [weak self] assetsController in
|
mixin.requestSearchController = { [weak self] assetsController in
|
||||||
@ -3606,7 +3618,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
let controller = WebSearchController(context: strongSelf.context, peer: peer, chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: peer.id.namespace == Namespaces.Peer.CloudUser ? nil : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), completion: { [weak self] result in
|
let controller = WebSearchController(context: strongSelf.context, peer: peer, chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: peer.id.namespace == Namespaces.Peer.CloudUser ? nil : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), completion: { [weak self] result in
|
||||||
assetsController?.dismiss()
|
assetsController?.dismiss()
|
||||||
// self?.updateProfilePhoto(result)
|
self?.updateProfilePhoto(result)
|
||||||
}))
|
}))
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
strongSelf.controller?.push(controller)
|
strongSelf.controller?.push(controller)
|
||||||
@ -3618,7 +3630,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
mixin.didFinishWithImage = { [weak self] image in
|
mixin.didFinishWithImage = { [weak self] image in
|
||||||
if let image = image {
|
if let image = image {
|
||||||
completion()
|
completion()
|
||||||
// self?.updateProfilePhoto(image)
|
self?.updateProfilePhoto(image)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mixin.didFinishWithVideo = { [weak self] image, asset, adjustments in
|
mixin.didFinishWithVideo = { [weak self] image, asset, adjustments in
|
||||||
@ -3697,6 +3709,41 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateProfilePhoto(_ image: UIImage) {
|
||||||
|
guard let data = image.jpegData(compressionQuality: 0.6), let peerId = self.callState?.myPeerId else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let resource = LocalFileMediaResource(fileId: arc4random64())
|
||||||
|
self.call.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
|
||||||
|
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [])
|
||||||
|
|
||||||
|
self.currentUpdatingAvatar = representation
|
||||||
|
self.updateAvatarPromise.set(.single((representation, 0.0)))
|
||||||
|
|
||||||
|
let postbox = self.call.account.postbox
|
||||||
|
let signal = peerId.namespace == Namespaces.Peer.CloudUser ? updateAccountPhoto(account: self.call.account, resource: resource, videoResource: nil, videoStartTimestamp: nil, mapResourceToAvatarSizes: { resource, representations in
|
||||||
|
return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
|
||||||
|
}) : updatePeerPhoto(postbox: postbox, network: self.call.account.network, stateManager: self.call.account.stateManager, accountPeerId: self.context.account.peerId, peerId: peerId, photo: uploadedPeerPhoto(postbox: postbox, network: self.call.account.network, resource: resource), mapResourceToAvatarSizes: { resource, representations in
|
||||||
|
return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
|
||||||
|
})
|
||||||
|
|
||||||
|
self.updateAvatarDisposable.set((signal
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch result {
|
||||||
|
case .complete:
|
||||||
|
strongSelf.updateAvatarPromise.set(.single(nil))
|
||||||
|
case let .progress(value):
|
||||||
|
strongSelf.updateAvatarPromise.set(.single((representation, value)))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? ([], nil), invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let sharedContext: SharedAccountContext
|
private let sharedContext: SharedAccountContext
|
||||||
|
@ -79,8 +79,9 @@ final class VoiceChatParticipantItem: ListViewItem {
|
|||||||
let action: ((ASDisplayNode) -> Void)?
|
let action: ((ASDisplayNode) -> Void)?
|
||||||
let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
let getIsExpanded: () -> Bool
|
let getIsExpanded: () -> Bool
|
||||||
|
let getUpdatingAvatar: () -> Signal<(TelegramMediaImageRepresentation, Float)?, NoError>
|
||||||
|
|
||||||
public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: Peer, ssrc: UInt32?, presence: PeerPresence?, text: ParticipantText, expandedText: ParticipantText?, icon: Icon, enabled: Bool, selectable: Bool, getAudioLevel: (() -> Signal<Float, NoError>)?, getVideo: @escaping () -> GroupVideoNode?, revealOptions: [RevealOption], revealed: Bool?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, action: ((ASDisplayNode) -> Void)?, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, getIsExpanded: @escaping () -> Bool) {
|
public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: Peer, ssrc: UInt32?, presence: PeerPresence?, text: ParticipantText, expandedText: ParticipantText?, icon: Icon, enabled: Bool, selectable: Bool, getAudioLevel: (() -> Signal<Float, NoError>)?, getVideo: @escaping () -> GroupVideoNode?, revealOptions: [RevealOption], revealed: Bool?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, action: ((ASDisplayNode) -> Void)?, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, getIsExpanded: @escaping () -> Bool, getUpdatingAvatar: @escaping () -> Signal<(TelegramMediaImageRepresentation, Float)?, NoError>) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.dateTimeFormat = dateTimeFormat
|
self.dateTimeFormat = dateTimeFormat
|
||||||
self.nameDisplayOrder = nameDisplayOrder
|
self.nameDisplayOrder = nameDisplayOrder
|
||||||
@ -101,6 +102,7 @@ final class VoiceChatParticipantItem: ListViewItem {
|
|||||||
self.action = action
|
self.action = action
|
||||||
self.contextAction = contextAction
|
self.contextAction = contextAction
|
||||||
self.getIsExpanded = getIsExpanded
|
self.getIsExpanded = getIsExpanded
|
||||||
|
self.getUpdatingAvatar = getUpdatingAvatar
|
||||||
}
|
}
|
||||||
|
|
||||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||||
@ -408,7 +410,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
|||||||
avatarListContainerNode.addSubnode(avatarListNode.controlsClippingOffsetNode)
|
avatarListContainerNode.addSubnode(avatarListNode.controlsClippingOffsetNode)
|
||||||
avatarListWrapperNode.addSubnode(avatarListContainerNode)
|
avatarListWrapperNode.addSubnode(avatarListContainerNode)
|
||||||
|
|
||||||
avatarListNode.update(size: targetRect.size, peer: item.peer, isExpanded: true, transition: .immediate)
|
avatarListNode.update(size: targetRect.size, peer: item.peer, additionalEntry: item.getUpdatingAvatar(), isExpanded: true, transition: .immediate)
|
||||||
strongSelf.offsetContainerNode.supernode?.addSubnode(avatarListWrapperNode)
|
strongSelf.offsetContainerNode.supernode?.addSubnode(avatarListWrapperNode)
|
||||||
|
|
||||||
strongSelf.audioLevelView?.alpha = 0.0
|
strongSelf.audioLevelView?.alpha = 0.0
|
||||||
|
@ -41,8 +41,11 @@ private final class VoiceChatTitleEditInputFieldNode: ASDisplayNode, ASEditableT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(theme: PresentationTheme, placeholder: String) {
|
private let maxLength: Int
|
||||||
|
|
||||||
|
init(theme: PresentationTheme, placeholder: String, maxLength: Int) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
self.maxLength = maxLength
|
||||||
|
|
||||||
self.backgroundNode = ASImageNode()
|
self.backgroundNode = ASImageNode()
|
||||||
self.backgroundNode.isLayerBacked = true
|
self.backgroundNode.isLayerBacked = true
|
||||||
@ -135,7 +138,7 @@ private final class VoiceChatTitleEditInputFieldNode: ASDisplayNode, ASEditableT
|
|||||||
|
|
||||||
func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||||
let updatedText = (editableTextNode.textView.text as NSString).replacingCharacters(in: range, with: text)
|
let updatedText = (editableTextNode.textView.text as NSString).replacingCharacters(in: range, with: text)
|
||||||
if updatedText.count > 40 {
|
if updatedText.count > maxLength {
|
||||||
self.textInputNode.layer.addShakeAnimation()
|
self.textInputNode.layer.addShakeAnimation()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -205,7 +208,7 @@ private final class VoiceChatTitleEditAlertContentNode: AlertContentNode {
|
|||||||
return self.isUserInteractionEnabled
|
return self.isUserInteractionEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], title: String, text: String, placeholder: String, value: String?) {
|
init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], title: String, text: String, placeholder: String, value: String?, maxLength: Int) {
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.title = title
|
self.title = title
|
||||||
self.text = text
|
self.text = text
|
||||||
@ -215,7 +218,7 @@ private final class VoiceChatTitleEditAlertContentNode: AlertContentNode {
|
|||||||
self.textNode = ASTextNode()
|
self.textNode = ASTextNode()
|
||||||
self.textNode.maximumNumberOfLines = 8
|
self.textNode.maximumNumberOfLines = 8
|
||||||
|
|
||||||
self.inputFieldNode = VoiceChatTitleEditInputFieldNode(theme: ptheme, placeholder: placeholder)
|
self.inputFieldNode = VoiceChatTitleEditInputFieldNode(theme: ptheme, placeholder: placeholder, maxLength: maxLength)
|
||||||
self.inputFieldNode.text = value ?? ""
|
self.inputFieldNode.text = value ?? ""
|
||||||
|
|
||||||
self.actionNodesSeparator = ASDisplayNode()
|
self.actionNodesSeparator = ASDisplayNode()
|
||||||
@ -408,7 +411,7 @@ private final class VoiceChatTitleEditAlertContentNode: AlertContentNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func voiceChatTitleEditController(sharedContext: SharedAccountContext, account: Account, forceTheme: PresentationTheme?, title: String, text: String, placeholder: String, doneButtonTitle: String? = nil, value: String?, apply: @escaping (String?) -> Void) -> AlertController {
|
func voiceChatTitleEditController(sharedContext: SharedAccountContext, account: Account, forceTheme: PresentationTheme?, title: String, text: String, placeholder: String, doneButtonTitle: String? = nil, value: String?, maxLength: Int, apply: @escaping (String?) -> Void) -> AlertController {
|
||||||
var presentationData = sharedContext.currentPresentationData.with { $0 }
|
var presentationData = sharedContext.currentPresentationData.with { $0 }
|
||||||
if let forceTheme = forceTheme {
|
if let forceTheme = forceTheme {
|
||||||
presentationData = presentationData.withUpdated(theme: forceTheme)
|
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||||
@ -423,7 +426,7 @@ func voiceChatTitleEditController(sharedContext: SharedAccountContext, account:
|
|||||||
applyImpl?()
|
applyImpl?()
|
||||||
})]
|
})]
|
||||||
|
|
||||||
let contentNode = VoiceChatTitleEditAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, text: text, placeholder: placeholder, value: value)
|
let contentNode = VoiceChatTitleEditAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, text: text, placeholder: placeholder, value: value, maxLength: maxLength)
|
||||||
contentNode.complete = {
|
contentNode.complete = {
|
||||||
applyImpl?()
|
applyImpl?()
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -8527,7 +8527,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
let presentationDisposable = strongSelf.context.sharedContext.presentationData.start(next: { [weak controller] presentationData in
|
let presentationDisposable = strongSelf.context.sharedContext.presentationData.start(next: { [weak controller] presentationData in
|
||||||
if let controller = controller {
|
if let controller = controller {
|
||||||
controller.pallete = legacyMenuPaletteFromTheme(presentationData.theme)
|
controller.pallete = legacyMenuPaletteFromTheme(presentationData.theme, forceDark: false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
legacyController.disposables.add(presentationDisposable)
|
legacyController.disposables.add(presentationDisposable)
|
||||||
|
@ -166,6 +166,9 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
|||||||
params.dismissInput()
|
params.dismissInput()
|
||||||
let _ = (gallery
|
let _ = (gallery
|
||||||
|> deliverOnMainQueue).start(next: { gallery in
|
|> deliverOnMainQueue).start(next: { gallery in
|
||||||
|
gallery.centralItemUpdated = { messageId in
|
||||||
|
params.centralItemUpdated?(messageId)
|
||||||
|
}
|
||||||
params.present(gallery, GalleryControllerPresentationArguments(transitionArguments: { messageId, media in
|
params.present(gallery, GalleryControllerPresentationArguments(transitionArguments: { messageId, media in
|
||||||
let selectedTransitionNode = params.transitionNode(messageId, media)
|
let selectedTransitionNode = params.transitionNode(messageId, media)
|
||||||
if let selectedTransitionNode = selectedTransitionNode {
|
if let selectedTransitionNode = selectedTransitionNode {
|
||||||
|
@ -124,6 +124,9 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
|||||||
self.disposable?.dispose()
|
self.disposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ensureMessageIsVisible(id: MessageId) {
|
||||||
|
}
|
||||||
|
|
||||||
func scrollToTop() -> Bool {
|
func scrollToTop() -> Bool {
|
||||||
if !self.listNode.scrollToOffsetFromTop(0.0) {
|
if !self.listNode.scrollToOffsetFromTop(0.0) {
|
||||||
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||||
|
@ -138,6 +138,10 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
|||||||
self.playlistPreloadDisposable?.dispose()
|
self.playlistPreloadDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ensureMessageIsVisible(id: MessageId) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func scrollToTop() -> Bool {
|
func scrollToTop() -> Bool {
|
||||||
let offset = self.listNode.visibleContentOffset()
|
let offset = self.listNode.visibleContentOffset()
|
||||||
switch offset {
|
switch offset {
|
||||||
|
@ -168,6 +168,9 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
|||||||
deinit {
|
deinit {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ensureMessageIsVisible(id: MessageId) {
|
||||||
|
}
|
||||||
|
|
||||||
func scrollToTop() -> Bool {
|
func scrollToTop() -> Bool {
|
||||||
if !self.listNode.scrollToOffsetFromTop(0.0) {
|
if !self.listNode.scrollToOffsetFromTop(0.0) {
|
||||||
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||||
|
@ -872,6 +872,21 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
self.animationTimer?.invalidate()
|
self.animationTimer?.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ensureMessageIsVisible(id: MessageId) {
|
||||||
|
let activeRect = self.scrollNode.bounds
|
||||||
|
for item in self.mediaItems {
|
||||||
|
if item.message.id == id {
|
||||||
|
if let itemNode = self.visibleMediaItems[item.message.stableId] {
|
||||||
|
if !activeRect.contains(itemNode.frame) {
|
||||||
|
let targetContentOffset = CGPoint(x: 0.0, y: max(-self.scrollNode.view.contentInset.top, itemNode.frame.minY - (self.scrollNode.frame.height - itemNode.frame.height) / 2.0))
|
||||||
|
self.scrollNode.view.setContentOffset(targetContentOffset, animated: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func requestHistoryAroundVisiblePosition() {
|
private func requestHistoryAroundVisiblePosition() {
|
||||||
if self.isRequestingView {
|
if self.isRequestingView {
|
||||||
return
|
return
|
||||||
|
@ -24,6 +24,7 @@ protocol PeerInfoPaneNode: ASDisplayNode {
|
|||||||
func addToTransitionSurface(view: UIView)
|
func addToTransitionSurface(view: UIView)
|
||||||
func updateHiddenMedia()
|
func updateHiddenMedia()
|
||||||
func updateSelectedMessages(animated: Bool)
|
func updateSelectedMessages(animated: Bool)
|
||||||
|
func ensureMessageIsVisible(id: MessageId)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class PeerInfoPaneWrapper {
|
final class PeerInfoPaneWrapper {
|
||||||
|
@ -3107,7 +3107,10 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})))
|
}), centralItemUpdated: { [weak self] messageId in
|
||||||
|
self?.paneContainerNode.requestExpandTabs?()
|
||||||
|
self?.paneContainerNode.currentPane?.node.ensureMessageIsVisible(id: messageId)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
private func openResolved(_ result: ResolvedUrl) {
|
private func openResolved(_ result: ResolvedUrl) {
|
||||||
guard let navigationController = self.controller?.navigationController as? NavigationController else {
|
guard let navigationController = self.controller?.navigationController as? NavigationController else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user