mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-05-01 04:07:51 +02:00
Merge pull request #1 from ichmagmaus111/ghostgram-update
Ghostgram update
This commit is contained in:
@@ -6695,20 +6695,10 @@ private final class ChatListLocationContext {
|
||||
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
|
||||
// GHOSTGRAM: Fetch first so the resource is populated by the time
|
||||
// resourceData emits a complete result. The old order (subscribe→fetch)
|
||||
// had a race where `completed` fired before data arrived, causing
|
||||
// buildButton(nil) to be called and the avatar to never show.
|
||||
if let peerReference = PeerReference(nextPeer) {
|
||||
let _ = fetchedMediaResource(
|
||||
mediaBox: account.postbox.mediaBox,
|
||||
@@ -6717,6 +6707,19 @@ private final class ChatListLocationContext {
|
||||
reference: .avatar(peer: peerReference, resource: resource)
|
||||
).start()
|
||||
}
|
||||
|
||||
self.accountSwitcherAvatarDisposable = (account.postbox.mediaBox
|
||||
.resourceData(resource)
|
||||
|> filter { $0.complete }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue)
|
||||
.start(next: { data in
|
||||
if let uiImage = UIImage(contentsOfFile: data.path) {
|
||||
buildButton(uiImage)
|
||||
} else {
|
||||
buildButton(nil)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// No photo — show placeholder
|
||||
buildButton(nil)
|
||||
|
||||
@@ -4378,9 +4378,18 @@ func replayFinalState(
|
||||
|
||||
// ANTI-DELETE: Mark messages as deleted instead of removing them
|
||||
if AntiDeleteManager.shared.isEnabled {
|
||||
// GHOSTGRAM: Collect non-Cloud IDs (scheduled/local) that must be
|
||||
// physically removed even when AntiDelete is on. Without this, sent
|
||||
// scheduled messages stay stuck in the scheduled list forever because
|
||||
// the `continue` guard skips them but nothing else removes them.
|
||||
var nonCloudIdsToDelete: [MessageId] = []
|
||||
|
||||
for messageId in ids {
|
||||
// Skip scheduled/local/quick-reply messages — they get deleted when sent, not by the remote peer
|
||||
guard messageId.namespace == Namespaces.Message.Cloud else { continue }
|
||||
guard messageId.namespace == Namespaces.Message.Cloud else {
|
||||
nonCloudIdsToDelete.append(messageId)
|
||||
continue
|
||||
}
|
||||
|
||||
// Mark as deleted for icon display
|
||||
AntiDeleteManager.shared.markAsDeleted(peerId: messageId.peerId.toInt64(), messageId: messageId.id)
|
||||
@@ -4396,6 +4405,13 @@ func replayFinalState(
|
||||
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
})
|
||||
}
|
||||
|
||||
// Physically remove scheduled/local messages that were skipped above
|
||||
if !nonCloudIdsToDelete.isEmpty {
|
||||
_internal_deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: nonCloudIdsToDelete, manualAddMessageThreadStatsDifference: { id, add, remove in
|
||||
addMessageThreadStatsDifference(threadKey: id, remove: remove, addedMessagePeer: nil, addedMessageId: nil, isOutgoing: false)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
_internal_deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: ids, manualAddMessageThreadStatsDifference: { id, add, remove in
|
||||
addMessageThreadStatsDifference(threadKey: id, remove: remove, addedMessagePeer: nil, addedMessageId: nil, isOutgoing: false)
|
||||
|
||||
+3
-3
@@ -152,6 +152,9 @@ func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentat
|
||||
items[.myProfile]!.append(PeerInfoScreenDisclosureItem(id: 0, text: presentationData.strings.Settings_MyProfile, icon: PresentationResourcesSettings.myProfile, action: {
|
||||
interaction.openSettings(.profile)
|
||||
}))
|
||||
items[.myProfile]!.append(PeerInfoScreenDisclosureItem(id: 1001, text: "Ghostgram Settings", icon: UIImage(bundleImageName: "Settings/Menu/GhostgramSettings"), action: {
|
||||
interaction.openSettings(.ghostgram)
|
||||
}))
|
||||
|
||||
if !settings.proxySettings.servers.isEmpty {
|
||||
let proxyType: String
|
||||
@@ -232,9 +235,6 @@ func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentat
|
||||
items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 1, text: presentationData.strings.Settings_PrivacySettings, icon: PresentationResourcesSettings.security, action: {
|
||||
interaction.openSettings(.privacyAndSecurity)
|
||||
}))
|
||||
items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 1001, text: "Ghostgram Settings", icon: UIImage(bundleImageName: "Settings/Menu/Appearance"), action: {
|
||||
interaction.openSettings(.ghostgram)
|
||||
}))
|
||||
items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 2, text: presentationData.strings.Settings_ChatSettings, icon: PresentationResourcesSettings.dataAndStorage, action: {
|
||||
interaction.openSettings(.dataAndStorage)
|
||||
}))
|
||||
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "ghost_settings.png",
|
||||
"idiom": "universal",
|
||||
"scale": "1x"
|
||||
},
|
||||
{
|
||||
"filename": "ghost_settings@2x.png",
|
||||
"idiom": "universal",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"filename": "ghost_settings@3x.png",
|
||||
"idiom": "universal",
|
||||
"scale": "3x"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
submodules/TelegramUI/Images.xcassets/Settings/Menu/GhostgramSettings.imageset/ghost_settings@2x.png
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
submodules/TelegramUI/Images.xcassets/Settings/Menu/GhostgramSettings.imageset/ghost_settings@3x.png
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -208,7 +208,16 @@ extension ChatControllerImpl {
|
||||
}
|
||||
|
||||
var usedCorrelationId = false
|
||||
if scheduleTime == nil, shouldAnimateMessageTransition, let extractedView = videoController.extractVideoSnapshot() {
|
||||
|
||||
// GHOSTGRAM: When SendDelayManager is active the message lands in
|
||||
// ScheduledLocal namespace, NOT in the main history. This means
|
||||
// setupSendActionOnViewUpdate's callback would NEVER fire (it waits
|
||||
// for the message to appear in the normal chat view), causing the
|
||||
// video recorder overlay to stay on screen and the app to freeze.
|
||||
// Solution: dismiss the recorder immediately and skip the animation.
|
||||
let isSendDelayActive = SendDelayManager.shared.isEnabled
|
||||
|
||||
if !isSendDelayActive, scheduleTime == nil, shouldAnimateMessageTransition, let extractedView = videoController.extractVideoSnapshot() {
|
||||
usedCorrelationId = true
|
||||
self.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .videoMessage(ChatMessageTransitionNodeImpl.Source.VideoMessage(view: extractedView)), initiated: { [weak videoController, weak self] in
|
||||
videoController?.hideVideoSnapshot()
|
||||
@@ -221,15 +230,25 @@ extension ChatControllerImpl {
|
||||
self.videoRecorder.set(.single(nil))
|
||||
}
|
||||
|
||||
self.chatDisplayNode.setupSendActionOnViewUpdate({ [weak self] in
|
||||
if let self {
|
||||
self.chatDisplayNode.collapseInput()
|
||||
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedMediaDraftState(nil).withUpdatedPostSuggestionState(nil) }
|
||||
})
|
||||
}
|
||||
}, usedCorrelationId ? correlationId : nil)
|
||||
if isSendDelayActive {
|
||||
// Dismiss recorder and clear state immediately without waiting
|
||||
// for the scheduled message to appear in history.
|
||||
self.chatDisplayNode.collapseInput()
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedMediaDraftState(nil).withUpdatedPostSuggestionState(nil) }
|
||||
})
|
||||
} else {
|
||||
self.chatDisplayNode.setupSendActionOnViewUpdate({ [weak self] in
|
||||
if let self {
|
||||
self.chatDisplayNode.collapseInput()
|
||||
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedMediaDraftState(nil).withUpdatedPostSuggestionState(nil) }
|
||||
})
|
||||
}
|
||||
}, usedCorrelationId ? correlationId : nil)
|
||||
}
|
||||
|
||||
|
||||
let messages = [message]
|
||||
let transformedMessages: [EnqueueMessage]
|
||||
|
||||
Reference in New Issue
Block a user