mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-05-08 15:24:56 +02:00
feat: новые функции, исправлены критические ошибки сборки и баги интерфейса, больше подписей в файлах
This commit is contained in:
@@ -3,9 +3,14 @@ load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
swift_library(
|
||||
name = "ChatListUI",
|
||||
module_name = "ChatListUI",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
srcs = glob(
|
||||
[
|
||||
"Sources/**/*.swift",
|
||||
],
|
||||
exclude = [
|
||||
"Sources/ChatListFilterTabContainerNode.swift",
|
||||
],
|
||||
),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
|
||||
@@ -6595,8 +6595,17 @@ private final class ChatListLocationContext {
|
||||
var proxyButton: AnyComponentWithIdentity<NavigationButtonComponentEnvironment>?
|
||||
var storyButton: AnyComponentWithIdentity<NavigationButtonComponentEnvironment>?
|
||||
|
||||
// GHOSTGRAM: Account switcher — liquid glass avatar button for the next account
|
||||
var accountSwitcherButton: AnyComponentWithIdentity<NavigationButtonComponentEnvironment>?
|
||||
private var accountSwitcherDisposable: Disposable?
|
||||
private var accountSwitcherAvatarDisposable: Disposable?
|
||||
|
||||
var rightButtons: [AnyComponentWithIdentity<NavigationButtonComponentEnvironment>] {
|
||||
var result: [AnyComponentWithIdentity<NavigationButtonComponentEnvironment>] = []
|
||||
// Account switcher is first — leftmost of the right-side buttons
|
||||
if let accountSwitcherButton = self.accountSwitcherButton {
|
||||
result.append(accountSwitcherButton)
|
||||
}
|
||||
if let rightButton = self.rightButton {
|
||||
result.append(rightButton)
|
||||
}
|
||||
@@ -6631,6 +6640,90 @@ private final class ChatListLocationContext {
|
||||
self.location = location
|
||||
self.parentController = parentController
|
||||
|
||||
// GHOSTGRAM: Subscribe to account list and maintain the switcher button
|
||||
if case .chatList(.root) = location {
|
||||
self.accountSwitcherDisposable = (context.sharedContext.activeAccountsWithInfo
|
||||
|> deliverOnMainQueue)
|
||||
.start(next: { [weak self] (info: (primary: AccountRecordId?, accounts: [AccountWithInfo])) in
|
||||
guard let self else { return }
|
||||
|
||||
let primaryId = info.primary
|
||||
let accounts = info.accounts
|
||||
|
||||
// Only show when there is more than one account
|
||||
guard accounts.count > 1, let primaryId = primaryId else {
|
||||
if self.accountSwitcherButton != nil {
|
||||
self.accountSwitcherButton = nil
|
||||
let _ = self.parentController?.updateHeaderContent()
|
||||
self.parentController?.requestLayout(transition: .immediate)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Find next account cyclically
|
||||
let currentIndex = accounts.firstIndex(where: { $0.account.id == primaryId }) ?? 0
|
||||
let nextIndex = (currentIndex + 1) % accounts.count
|
||||
let nextAccount = accounts[nextIndex]
|
||||
let nextPeer = nextAccount.peer
|
||||
let nextPeerId = "\(nextAccount.account.id)"
|
||||
|
||||
// Build button placeholder immediately (image loads async)
|
||||
let buildButton: (UIImage?) -> Void = { [weak self] image in
|
||||
guard let self else { return }
|
||||
guard case .chatList(.root) = self.location else { return }
|
||||
|
||||
let sharedContext = self.context.sharedContext
|
||||
let nextAccountId = nextAccount.account.id
|
||||
|
||||
self.accountSwitcherButton = AnyComponentWithIdentity(
|
||||
id: "accountSwitcher",
|
||||
component: AnyComponent(NavigationButtonComponent(
|
||||
content: .avatar(peerId: nextPeerId, avatarImage: image),
|
||||
pressed: { [weak sharedContext] _ in
|
||||
sharedContext?.switchToAccount(id: nextAccountId, fromSettingsController: nil, withChatListController: nil)
|
||||
}
|
||||
))
|
||||
)
|
||||
// Trigger header rebuild
|
||||
let _ = self.parentController?.updateHeaderContent()
|
||||
self.parentController?.requestLayout(transition: .immediate)
|
||||
}
|
||||
|
||||
// Attempt to load the peer's avatar from mediaBox
|
||||
if let representation = nextPeer.smallProfileImage {
|
||||
self.accountSwitcherAvatarDisposable?.dispose()
|
||||
let resource = representation.resource
|
||||
let account = nextAccount.account
|
||||
|
||||
// Try to read cached data first; if not ready, trigger a fetch then watch for completion
|
||||
self.accountSwitcherAvatarDisposable = (account.postbox.mediaBox
|
||||
.resourceData(resource)
|
||||
|> deliverOnMainQueue)
|
||||
.start(next: { data in
|
||||
if data.complete, let uiImage = UIImage(contentsOfFile: data.path) {
|
||||
buildButton(uiImage)
|
||||
}
|
||||
}, completed: {
|
||||
// If resource was never complete after signal ended, show placeholder
|
||||
buildButton(nil)
|
||||
})
|
||||
|
||||
// Trigger the actual network fetch so mediaBox populates the resource
|
||||
if let peerReference = PeerReference(nextPeer) {
|
||||
let _ = fetchedMediaResource(
|
||||
mediaBox: account.postbox.mediaBox,
|
||||
userLocation: .peer(nextPeer.id),
|
||||
userContentType: .avatar,
|
||||
reference: .avatar(peer: peerReference, resource: resource)
|
||||
).start()
|
||||
}
|
||||
} else {
|
||||
// No photo — show placeholder
|
||||
buildButton(nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let hasProxy = context.sharedContext.accountManager.sharedData(keys: [SharedDataKeys.proxySettings])
|
||||
|> map { sharedData -> (Bool, Bool) in
|
||||
if let settings = sharedData.entries[SharedDataKeys.proxySettings]?.get(ProxySettings.self) {
|
||||
@@ -6949,6 +7042,8 @@ private final class ChatListLocationContext {
|
||||
deinit {
|
||||
self.titleDisposable?.dispose()
|
||||
self.stateDisposable?.dispose()
|
||||
self.accountSwitcherDisposable?.dispose()
|
||||
self.accountSwitcherAvatarDisposable?.dispose()
|
||||
}
|
||||
|
||||
private func updateChatList(
|
||||
@@ -6982,6 +7077,7 @@ private final class ChatListLocationContext {
|
||||
if case .chatList(.root) = self.location {
|
||||
self.rightButton = nil
|
||||
self.storyButton = nil
|
||||
self.accountSwitcherButton = nil
|
||||
}
|
||||
let title = !stateAndFilterId.state.selectedPeerIds.isEmpty ? presentationData.strings.ChatList_SelectedChats(Int32(stateAndFilterId.state.selectedPeerIds.count)) : defaultTitle
|
||||
|
||||
@@ -6997,6 +7093,7 @@ private final class ChatListLocationContext {
|
||||
if case .chatList(.root) = self.location {
|
||||
self.rightButton = nil
|
||||
self.storyButton = nil
|
||||
self.accountSwitcherButton = nil
|
||||
}
|
||||
self.leftButton = AnyComponentWithIdentity(id: "done", component: AnyComponent(NavigationButtonComponent(
|
||||
content: .text(title: presentationData.strings.Common_Done, isBold: true),
|
||||
|
||||
@@ -14,6 +14,7 @@ import MergedAvatarsNode
|
||||
import TextNodeWithEntities
|
||||
import TextFormat
|
||||
import AvatarNode
|
||||
import GlobalControlPanelsContext
|
||||
|
||||
class ChatListNoticeItem: ListViewItem {
|
||||
enum Action {
|
||||
@@ -25,12 +26,12 @@ class ChatListNoticeItem: ListViewItem {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let notice: ChatListNotice
|
||||
let notice: GlobalControlPanelsContext.ChatListNotice
|
||||
let action: (Action) -> Void
|
||||
|
||||
let selectable: Bool = true
|
||||
|
||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, notice: ChatListNotice, action: @escaping (Action) -> Void) {
|
||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, notice: GlobalControlPanelsContext.ChatListNotice, action: @escaping (Action) -> Void) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
@@ -130,7 +131,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
||||
self.arrowNode = ASImageNode()
|
||||
self.separatorNode = ASDisplayNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||
super.init(layerBacked: false, rotated: false, seeThrough: false)
|
||||
|
||||
self.contentContainer.clipsToBounds = true
|
||||
self.clipsToBounds = true
|
||||
|
||||
Reference in New Issue
Block a user