mirror of
https://github.com/block/buzz.git
synced 2026-10-31 06:21:34 +00:00
fix(theme): recognize migration publish echoes
Keep the submitted migration coordinate so its relay echo can complete the original acknowledgement instead of being treated as competing remote state. Co-authored-by: Kenny Lopez <klopez4212@gmail.com> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Signed-off-by: Kenny Lopez <klopez4212@gmail.com> Signed-off-by: kenny lopez <klopez4212@gmail.com>
This commit is contained in:
@@ -228,6 +228,7 @@ export function CommunityThemeController() {
|
||||
clearCommunityThemeMigrationOutbox(pubkey, relayUrl);
|
||||
const migrationWasSubmitted = manager.cancelPendingPublish(
|
||||
remote.preference,
|
||||
remote,
|
||||
);
|
||||
cacheAndApplyCommunityTheme(
|
||||
pubkey,
|
||||
|
||||
@@ -745,6 +745,74 @@ test("remote accepted after submit is republished after the stale event settles"
|
||||
}
|
||||
});
|
||||
|
||||
test("migration self-echo during submission acknowledges without replacement", async () => {
|
||||
const timer = installFakeTimer();
|
||||
const publish = Promise.withResolvers();
|
||||
const published = [];
|
||||
const acknowledgements = [];
|
||||
let liveCallback;
|
||||
let signed = 0;
|
||||
globalThis.window.__TAURI_INTERNALS__ = {
|
||||
invoke(command, args) {
|
||||
if (command === "nip44_encrypt_to_self") return Promise.resolve("cipher");
|
||||
if (command === "nip44_decrypt_from_self") {
|
||||
return Promise.resolve(JSON.stringify(preference));
|
||||
}
|
||||
if (command === "sign_event") {
|
||||
signed += 1;
|
||||
return Promise.resolve(
|
||||
JSON.stringify(
|
||||
relayEvent({
|
||||
id: `event-${signed}`,
|
||||
content: args.content,
|
||||
created_at: args.createdAt,
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
throw new Error(`unexpected command: ${command}`);
|
||||
},
|
||||
};
|
||||
mock.method(relayClient, "subscribeLive", (_filter, callback) => {
|
||||
liveCallback = callback;
|
||||
return Promise.resolve(async () => {});
|
||||
});
|
||||
mock.method(relayClient, "publishEvent", (event) => {
|
||||
published.push(event);
|
||||
return publish.promise;
|
||||
});
|
||||
try {
|
||||
const manager = new CommunityThemeSyncManager("alice", (event) => {
|
||||
acknowledgements.push(event);
|
||||
});
|
||||
await manager.subscribe((remote) => {
|
||||
manager.acceptRemote(remote);
|
||||
manager.cancelPendingPublish(remote.preference, remote);
|
||||
});
|
||||
manager.publish(preference);
|
||||
timer.fire();
|
||||
await waitUntil(() => published.length === 1);
|
||||
|
||||
liveCallback(published[0]);
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
publish.resolve();
|
||||
await waitUntil(() => acknowledgements.length === 1);
|
||||
|
||||
assert.equal(published.length, 1);
|
||||
assert.equal(timer.pending(), false);
|
||||
assert.deepEqual(manager.getPending(), null);
|
||||
assert.deepEqual(acknowledgements[0], {
|
||||
preference,
|
||||
createdAt: published[0].created_at,
|
||||
eventId: published[0].id,
|
||||
});
|
||||
} finally {
|
||||
delete globalThis.window.__TAURI_INTERNALS__;
|
||||
timer.restore();
|
||||
mock.reset();
|
||||
}
|
||||
});
|
||||
|
||||
test("live legacy updates inherit the latest complete appearance", async () => {
|
||||
const initial = { ...preference, glassOpacity: 40 };
|
||||
const latest = {
|
||||
|
||||
@@ -105,7 +105,7 @@ export class CommunityThemeSyncManager {
|
||||
private readonly onPublished: (published: PublishedCommunityTheme) => void;
|
||||
private readonly getLegacyFallback: () => CommunityThemePreference;
|
||||
private publishGeneration = 0;
|
||||
private activePublishSubmitted = false;
|
||||
private activeSubmittedPublish: PublishedCommunityTheme | null = null;
|
||||
private replacementAfterCancel: CommunityThemePreference | null = null;
|
||||
|
||||
constructor(
|
||||
@@ -174,12 +174,12 @@ export class CommunityThemeSyncManager {
|
||||
private startPublish(): void {
|
||||
if (this.destroyed || this.publishInFlight || !this.pending) return;
|
||||
this.publishInFlight = true;
|
||||
this.activePublishSubmitted = false;
|
||||
this.activeSubmittedPublish = null;
|
||||
const preference = this.pending;
|
||||
const generation = this.publishGeneration;
|
||||
void this.doPublish(preference, generation).finally(() => {
|
||||
this.publishInFlight = false;
|
||||
this.activePublishSubmitted = false;
|
||||
this.activeSubmittedPublish = null;
|
||||
if (this.replacementAfterCancel) {
|
||||
this.pending = this.replacementAfterCancel;
|
||||
this.replacementAfterCancel = null;
|
||||
@@ -222,8 +222,17 @@ export class CommunityThemeSyncManager {
|
||||
|
||||
cancelPendingPublish(
|
||||
replacementAfterSubmit?: CommunityThemePreference,
|
||||
acceptedRemote?: RemoteCommunityTheme,
|
||||
): boolean {
|
||||
const submitted = this.activePublishSubmitted;
|
||||
if (
|
||||
this.activeSubmittedPublish &&
|
||||
acceptedRemote &&
|
||||
acceptedRemote.createdAt === this.activeSubmittedPublish.createdAt &&
|
||||
acceptedRemote.eventId === this.activeSubmittedPublish.eventId
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const submitted = this.activeSubmittedPublish !== null;
|
||||
this.replacementAfterCancel =
|
||||
submitted && replacementAfterSubmit ? replacementAfterSubmit : null;
|
||||
this.publishGeneration += 1;
|
||||
@@ -272,7 +281,11 @@ export class CommunityThemeSyncManager {
|
||||
],
|
||||
});
|
||||
if (wasCancelled()) return;
|
||||
this.activePublishSubmitted = true;
|
||||
this.activeSubmittedPublish = {
|
||||
preference,
|
||||
createdAt: event.created_at,
|
||||
eventId: event.id,
|
||||
};
|
||||
await relayClient.publishEvent(
|
||||
event,
|
||||
"Timed out publishing community theme.",
|
||||
|
||||
Reference in New Issue
Block a user