mirror of
https://github.com/BlowaterNostr/blowater.git
synced 2024-10-18 15:43:20 +00:00
fix order issue (#336)
This commit is contained in:
parent
6e19e89fc9
commit
270a427767
13
UI/app.tsx
13
UI/app.tsx
@ -30,10 +30,12 @@ import { ProfileGetter } from "./search.tsx";
|
|||||||
import { DirectedMessageController } from "../features/dm.ts";
|
import { DirectedMessageController } from "../features/dm.ts";
|
||||||
import { ConnectionPool } from "../lib/nostr-ts/relay-pool.ts";
|
import { ConnectionPool } from "../lib/nostr-ts/relay-pool.ts";
|
||||||
import { LamportTime } from "../time.ts";
|
import { LamportTime } from "../time.ts";
|
||||||
|
import { lastPathSegment } from "https://deno.land/std@0.186.0/path/_util.ts";
|
||||||
|
|
||||||
export async function Start(database: DexieDatabase) {
|
export async function Start(database: DexieDatabase) {
|
||||||
console.log("Start the application");
|
console.log("Start the application");
|
||||||
|
|
||||||
|
const lamport = new LamportTime();
|
||||||
const model = initialModel();
|
const model = initialModel();
|
||||||
const eventBus = new EventBus<UI_Interaction_Event>();
|
const eventBus = new EventBus<UI_Interaction_Event>();
|
||||||
const pool = new ConnectionPool();
|
const pool = new ConnectionPool();
|
||||||
@ -53,7 +55,7 @@ export async function Start(database: DexieDatabase) {
|
|||||||
if (ctx instanceof Error) {
|
if (ctx instanceof Error) {
|
||||||
console.error(ctx);
|
console.error(ctx);
|
||||||
} else if (ctx) {
|
} else if (ctx) {
|
||||||
const otherConfig = await OtherConfig.FromLocalStorage(ctx, newNostrEventChannel);
|
const otherConfig = await OtherConfig.FromLocalStorage(ctx, newNostrEventChannel, lamport);
|
||||||
const app = await App.Start({
|
const app = await App.Start({
|
||||||
database: dbView,
|
database: dbView,
|
||||||
model,
|
model,
|
||||||
@ -62,6 +64,7 @@ export async function Start(database: DexieDatabase) {
|
|||||||
pool,
|
pool,
|
||||||
popOverInputChan,
|
popOverInputChan,
|
||||||
otherConfig,
|
otherConfig,
|
||||||
|
lamport,
|
||||||
});
|
});
|
||||||
model.app = app;
|
model.app = app;
|
||||||
}
|
}
|
||||||
@ -84,6 +87,7 @@ export async function Start(database: DexieDatabase) {
|
|||||||
pool,
|
pool,
|
||||||
popOver: popOverInputChan,
|
popOver: popOverInputChan,
|
||||||
newNostrEventChannel: newNostrEventChannel,
|
newNostrEventChannel: newNostrEventChannel,
|
||||||
|
lamport,
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
const t = Date.now();
|
const t = Date.now();
|
||||||
@ -128,8 +132,9 @@ export class App {
|
|||||||
pool: ConnectionPool;
|
pool: ConnectionPool;
|
||||||
popOverInputChan: PopOverInputChannel;
|
popOverInputChan: PopOverInputChannel;
|
||||||
otherConfig: OtherConfig;
|
otherConfig: OtherConfig;
|
||||||
|
lamport: LamportTime;
|
||||||
}) {
|
}) {
|
||||||
const lamport = LamportTime.FromEvents(args.database.getAllEvents());
|
args.lamport.fromEvents(args.database.getAllEvents());
|
||||||
const eventSyncer = new EventSyncer(args.pool, args.database);
|
const eventSyncer = new EventSyncer(args.pool, args.database);
|
||||||
|
|
||||||
// init relay config
|
// init relay config
|
||||||
@ -209,7 +214,7 @@ export class App {
|
|||||||
conversationLists,
|
conversationLists,
|
||||||
relayConfig,
|
relayConfig,
|
||||||
groupChatController,
|
groupChatController,
|
||||||
lamport,
|
args.lamport,
|
||||||
dmController,
|
dmController,
|
||||||
);
|
);
|
||||||
await app.initApp();
|
await app.initApp();
|
||||||
@ -223,7 +228,7 @@ export class App {
|
|||||||
(async () => {
|
(async () => {
|
||||||
const stream = await this.pool.newSub(OtherConfig.name, {
|
const stream = await this.pool.newSub(OtherConfig.name, {
|
||||||
authors: [this.ctx.publicKey.hex],
|
authors: [this.ctx.publicKey.hex],
|
||||||
kinds: [NostrKind.Custom_App_Data],
|
kinds: [NostrKind.Encrypted_Custom_App_Data],
|
||||||
});
|
});
|
||||||
if (stream instanceof Error) {
|
if (stream instanceof Error) {
|
||||||
throw stream; // crash the app
|
throw stream; // crash the app
|
||||||
|
@ -81,6 +81,7 @@ export async function* UI_Interaction_Update(args: {
|
|||||||
pool: ConnectionPool;
|
pool: ConnectionPool;
|
||||||
popOver: PopOverInputChannel;
|
popOver: PopOverInputChannel;
|
||||||
newNostrEventChannel: Channel<NostrEvent>;
|
newNostrEventChannel: Channel<NostrEvent>;
|
||||||
|
lamport: LamportTime;
|
||||||
}) {
|
}) {
|
||||||
const { model, dbView, eventBus, pool } = args;
|
const { model, dbView, eventBus, pool } = args;
|
||||||
const events = eventBus.onChange();
|
const events = eventBus.onChange();
|
||||||
@ -91,7 +92,11 @@ export async function* UI_Interaction_Update(args: {
|
|||||||
const ctx = event.ctx;
|
const ctx = event.ctx;
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
console.log("sign in as", ctx.publicKey.bech32());
|
console.log("sign in as", ctx.publicKey.bech32());
|
||||||
const otherConfig = await OtherConfig.FromLocalStorage(ctx, args.newNostrEventChannel);
|
const otherConfig = await OtherConfig.FromLocalStorage(
|
||||||
|
ctx,
|
||||||
|
args.newNostrEventChannel,
|
||||||
|
args.lamport,
|
||||||
|
);
|
||||||
const app = await App.Start({
|
const app = await App.Start({
|
||||||
database: dbView,
|
database: dbView,
|
||||||
model,
|
model,
|
||||||
@ -100,6 +105,7 @@ export async function* UI_Interaction_Update(args: {
|
|||||||
pool,
|
pool,
|
||||||
popOverInputChan: args.popOver,
|
popOverInputChan: args.popOver,
|
||||||
otherConfig,
|
otherConfig,
|
||||||
|
lamport: args.lamport,
|
||||||
});
|
});
|
||||||
model.app = app;
|
model.app = app;
|
||||||
} else {
|
} else {
|
||||||
@ -545,7 +551,7 @@ export async function* Database_Update(
|
|||||||
await database.remove(e.id);
|
await database.remove(e.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (e.kind == NostrKind.Custom_App_Data) {
|
} else if (e.kind == NostrKind.Encrypted_Custom_App_Data) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
const err = await args.otherConfig.addEvent(e);
|
const err = await args.otherConfig.addEvent(e);
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
|
@ -2,12 +2,14 @@ import { assertEquals, fail } from "https://deno.land/std@0.176.0/testing/assert
|
|||||||
import { OtherConfig } from "./config-other.ts";
|
import { OtherConfig } from "./config-other.ts";
|
||||||
import { InMemoryAccountContext, NostrEvent } from "../lib/nostr-ts/nostr.ts";
|
import { InMemoryAccountContext, NostrEvent } from "../lib/nostr-ts/nostr.ts";
|
||||||
import { Channel } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
|
import { Channel } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
|
||||||
|
import { LamportTime } from "../time.ts";
|
||||||
|
|
||||||
Deno.test("Pin List", async () => {
|
Deno.test("Pin List", async () => {
|
||||||
const ctx = InMemoryAccountContext.Generate();
|
const ctx = InMemoryAccountContext.Generate();
|
||||||
const pusher = new Channel<NostrEvent>();
|
const pusher = new Channel<NostrEvent>();
|
||||||
const _ = new Channel<NostrEvent>();
|
const _ = new Channel<NostrEvent>();
|
||||||
const config = OtherConfig.Empty(pusher, ctx);
|
const lamport = new LamportTime();
|
||||||
|
const config = OtherConfig.Empty(pusher, ctx, lamport);
|
||||||
|
|
||||||
await config.addPin("a");
|
await config.addPin("a");
|
||||||
assertEquals(config.getPinList(), new Set(["a"]));
|
assertEquals(config.getPinList(), new Set(["a"]));
|
||||||
@ -16,34 +18,47 @@ Deno.test("Pin List", async () => {
|
|||||||
assertEquals(config.getPinList(), new Set(["a", "b"]));
|
assertEquals(config.getPinList(), new Set(["a", "b"]));
|
||||||
|
|
||||||
// able to restore the config from local storage
|
// able to restore the config from local storage
|
||||||
const config2 = await OtherConfig.FromLocalStorage(ctx, _);
|
{
|
||||||
|
const config2 = await OtherConfig.FromLocalStorage(ctx, _, lamport);
|
||||||
assertEquals(config2.getPinList(), new Set(["a", "b"]));
|
assertEquals(config2.getPinList(), new Set(["a", "b"]));
|
||||||
assertEquals(config2.getPinList(), config.getPinList());
|
assertEquals(config2.getPinList(), config.getPinList());
|
||||||
|
|
||||||
// able to restore the config from event logs
|
|
||||||
const config3 = OtherConfig.Empty(_, ctx);
|
|
||||||
const event1 = await pusher.pop() as NostrEvent;
|
|
||||||
const event2 = await pusher.pop() as NostrEvent;
|
|
||||||
{
|
|
||||||
const err = await config3.addEvent(event1);
|
|
||||||
if (err instanceof Error) fail(err.message);
|
|
||||||
}
|
}
|
||||||
{
|
|
||||||
const err = await config3.addEvent(event2);
|
|
||||||
if (err instanceof Error) fail(err.message);
|
|
||||||
}
|
|
||||||
assertEquals(config3.getPinList(), new Set(["a", "b"]));
|
|
||||||
assertEquals(config3.getPinList(), config.getPinList());
|
|
||||||
|
|
||||||
// remove 1 pin from config1
|
// remove 1 pin from config1
|
||||||
await config.removePin("a");
|
await config.removePin("a");
|
||||||
assertEquals(config.getPinList(), new Set(["b"]));
|
assertEquals(config.getPinList(), new Set(["b"]));
|
||||||
|
|
||||||
// config3 is able to sync with config1
|
// config3 is able to sync with config1
|
||||||
const event3 = await pusher.pop() as NostrEvent;
|
// able to restore the config from event logs
|
||||||
|
const config3 = OtherConfig.Empty(_, ctx, lamport);
|
||||||
|
const event1 = await pusher.pop() as NostrEvent; // +a
|
||||||
|
const event2 = await pusher.pop() as NostrEvent; // +b
|
||||||
|
const event3 = await pusher.pop() as NostrEvent; // -a
|
||||||
|
|
||||||
|
{
|
||||||
|
const err = await config3.addEvent(event2);
|
||||||
|
if (err instanceof Error) fail(err.message);
|
||||||
|
}
|
||||||
|
assertEquals(config3.getPinList(), new Set(["b"]));
|
||||||
|
|
||||||
|
// apply -a before +a
|
||||||
{
|
{
|
||||||
const err = await config3.addEvent(event3);
|
const err = await config3.addEvent(event3);
|
||||||
if (err instanceof Error) fail(err.message);
|
if (err instanceof Error) fail(err.message);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
const err = await config3.addEvent(event1);
|
||||||
|
if (err instanceof Error) fail(err.message);
|
||||||
|
}
|
||||||
assertEquals(config3.getPinList(), new Set(["b"]));
|
assertEquals(config3.getPinList(), new Set(["b"]));
|
||||||
|
|
||||||
|
// +a again
|
||||||
|
await config.addPin("a");
|
||||||
|
assertEquals(config.getPinList(), new Set(["a", "b"]));
|
||||||
|
const event4 = await pusher.pop() as NostrEvent;
|
||||||
|
{
|
||||||
|
const err = await config3.addEvent(event4);
|
||||||
|
if (err instanceof Error) fail(err.message);
|
||||||
|
assertEquals(config3.getPinList(), new Set(["a", "b"]));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
@ -3,77 +3,102 @@ import { NostrAccountContext, NostrEvent, NostrKind, verifyEvent } from "../lib/
|
|||||||
import { PinListGetter } from "./conversation-list.tsx";
|
import { PinListGetter } from "./conversation-list.tsx";
|
||||||
import { parseJSON } from "../features/profile.ts";
|
import { parseJSON } from "../features/profile.ts";
|
||||||
import { Channel } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
|
import { Channel } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
|
||||||
import { PinConversation, UnpinConversation } from "../nostr.ts";
|
import {
|
||||||
|
PinConversation,
|
||||||
|
PinConversationRelay,
|
||||||
|
UnpinConversation,
|
||||||
|
UnpinConversationRelay,
|
||||||
|
} from "../nostr.ts";
|
||||||
|
import { LamportTime } from "../time.ts";
|
||||||
|
|
||||||
export type NostrEventAdder = {
|
export type NostrEventAdder = {
|
||||||
addEvent(event: NostrEvent): Promise<undefined | Error>;
|
addEvent(event: NostrEvent): Promise<undefined | Error>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class OtherConfig implements PinListGetter, NostrEventAdder {
|
export class OtherConfig implements PinListGetter, NostrEventAdder {
|
||||||
static Empty(nostrEventPusher: Channel<NostrEvent>, ctx: NostrAccountContext) {
|
static Empty(nostrEventPusher: Channel<NostrEvent>, ctx: NostrAccountContext, lamport: LamportTime) {
|
||||||
return new OtherConfig(nostrEventPusher, ctx);
|
return new OtherConfig(nostrEventPusher, ctx, lamport);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async FromLocalStorage(ctx: NostrAccountContext, eventPusher: Channel<NostrEvent>) {
|
static async FromLocalStorage(
|
||||||
|
ctx: NostrAccountContext,
|
||||||
|
eventPusher: Channel<NostrEvent>,
|
||||||
|
lamport: LamportTime,
|
||||||
|
) {
|
||||||
const item = localStorage.getItem(`${OtherConfig.name}:${ctx.publicKey.bech32()}`);
|
const item = localStorage.getItem(`${OtherConfig.name}:${ctx.publicKey.bech32()}`);
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
return OtherConfig.Empty(eventPusher, ctx);
|
return OtherConfig.Empty(eventPusher, ctx, lamport);
|
||||||
}
|
}
|
||||||
const event = parseJSON<NostrEvent>(item);
|
const event = parseJSON<NostrEvent>(item);
|
||||||
if (event instanceof Error) {
|
if (event instanceof Error) {
|
||||||
console.error(event);
|
console.error(event);
|
||||||
return OtherConfig.Empty(eventPusher, ctx);
|
return OtherConfig.Empty(eventPusher, ctx, lamport);
|
||||||
}
|
}
|
||||||
const ok = await verifyEvent(event);
|
const ok = await verifyEvent(event);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return OtherConfig.Empty(eventPusher, ctx);
|
return OtherConfig.Empty(eventPusher, ctx, lamport);
|
||||||
}
|
}
|
||||||
if (event.kind == NostrKind.Custom_App_Data) {
|
if (event.kind == NostrKind.Encrypted_Custom_App_Data) {
|
||||||
const config = await OtherConfig.FromNostrEvent(
|
const config = await OtherConfig.FromNostrEvent(
|
||||||
// @ts-ignore
|
{
|
||||||
event,
|
...event,
|
||||||
|
kind: event.kind,
|
||||||
|
},
|
||||||
ctx,
|
ctx,
|
||||||
eventPusher,
|
eventPusher,
|
||||||
|
lamport,
|
||||||
);
|
);
|
||||||
if (config instanceof Error) {
|
if (config instanceof Error) {
|
||||||
return OtherConfig.Empty(eventPusher, ctx);
|
return OtherConfig.Empty(eventPusher, ctx, lamport);
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
return OtherConfig.Empty(eventPusher, ctx);
|
return OtherConfig.Empty(eventPusher, ctx, lamport);
|
||||||
}
|
}
|
||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
private readonly nostrEventPusher: Channel<NostrEvent>,
|
private readonly nostrEventPusher: Channel<NostrEvent>,
|
||||||
private readonly ctx: NostrAccountContext,
|
private readonly ctx: NostrAccountContext,
|
||||||
|
private readonly lamport: LamportTime,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private pinList = new Set<string>(); // set of pubkeys in npub format
|
private pinList = new Map<string, PinConversationRelay | UnpinConversationRelay>(); // set of pubkeys in npub format
|
||||||
|
|
||||||
getPinList(): Set<string> {
|
getPinList(): Set<string> {
|
||||||
return this.pinList;
|
const set = new Set<string>();
|
||||||
|
for (const event of this.pinList.values()) {
|
||||||
|
if (event.type == "PinConversation") {
|
||||||
|
set.add(event.pubkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
async addPin(pubkey: string) {
|
async addPin(pubkey: string) {
|
||||||
if (this.pinList.has(pubkey)) {
|
const currentPin = this.pinList.get(pubkey);
|
||||||
|
if (currentPin && currentPin.type == "PinConversation") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.pinList.add(pubkey);
|
|
||||||
const err = await this.saveToLocalStorage();
|
const pin: PinConversationRelay = {
|
||||||
if (err instanceof Error) {
|
pubkey,
|
||||||
return err;
|
|
||||||
}
|
|
||||||
const event = await prepareEncryptedNostrEvent(this.ctx, {
|
|
||||||
content: JSON.stringify({
|
|
||||||
type: "PinConversation",
|
type: "PinConversation",
|
||||||
pubkey: pubkey,
|
lamport: this.lamport.now(),
|
||||||
}),
|
};
|
||||||
|
|
||||||
|
const event = await prepareEncryptedNostrEvent(this.ctx, {
|
||||||
|
content: JSON.stringify(pin),
|
||||||
encryptKey: this.ctx.publicKey,
|
encryptKey: this.ctx.publicKey,
|
||||||
kind: NostrKind.Custom_App_Data,
|
kind: NostrKind.Encrypted_Custom_App_Data,
|
||||||
});
|
});
|
||||||
if (event instanceof Error) {
|
if (event instanceof Error) {
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
this.pinList.set(pubkey, pin);
|
||||||
|
const err = await this.saveToLocalStorage();
|
||||||
|
if (err instanceof Error) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
/* no await */ this.nostrEventPusher.put(event);
|
/* no await */ this.nostrEventPusher.put(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,24 +107,33 @@ export class OtherConfig implements PinListGetter, NostrEventAdder {
|
|||||||
if (!exist) {
|
if (!exist) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const event = await prepareEncryptedNostrEvent(this.ctx, {
|
|
||||||
content: JSON.stringify({
|
const unpin: UnpinConversationRelay = {
|
||||||
|
pubkey,
|
||||||
type: "UnpinConversation",
|
type: "UnpinConversation",
|
||||||
pubkey: pubkey,
|
lamport: this.lamport.now(),
|
||||||
}),
|
};
|
||||||
|
const event = await prepareEncryptedNostrEvent(this.ctx, {
|
||||||
|
content: JSON.stringify(unpin),
|
||||||
encryptKey: this.ctx.publicKey,
|
encryptKey: this.ctx.publicKey,
|
||||||
kind: NostrKind.Custom_App_Data,
|
kind: NostrKind.Encrypted_Custom_App_Data,
|
||||||
});
|
});
|
||||||
if (event instanceof Error) {
|
if (event instanceof Error) {
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
this.pinList.set(pubkey, unpin);
|
||||||
|
const err = await this.saveToLocalStorage();
|
||||||
|
if (err instanceof Error) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
/* no await */ this.nostrEventPusher.put(event);
|
/* no await */ this.nostrEventPusher.put(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async FromNostrEvent(
|
static async FromNostrEvent(
|
||||||
event: NostrEvent<NostrKind.Custom_App_Data>,
|
event: NostrEvent<NostrKind.Encrypted_Custom_App_Data>,
|
||||||
ctx: NostrAccountContext,
|
ctx: NostrAccountContext,
|
||||||
pusher: Channel<NostrEvent>,
|
pusher: Channel<NostrEvent>,
|
||||||
|
lamport: LamportTime,
|
||||||
) {
|
) {
|
||||||
const decrypted = await ctx.decrypt(ctx.publicKey.hex, event.content);
|
const decrypted = await ctx.decrypt(ctx.publicKey.hex, event.content);
|
||||||
if (decrypted instanceof Error) {
|
if (decrypted instanceof Error) {
|
||||||
@ -118,7 +152,7 @@ export class OtherConfig implements PinListGetter, NostrEventAdder {
|
|||||||
pinList = [];
|
pinList = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const c = new OtherConfig(pusher, ctx);
|
const c = new OtherConfig(pusher, ctx, lamport);
|
||||||
for (const pin of pinList) {
|
for (const pin of pinList) {
|
||||||
const err = await c.addPin(pin);
|
const err = await c.addPin(pin);
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
@ -131,8 +165,8 @@ export class OtherConfig implements PinListGetter, NostrEventAdder {
|
|||||||
private async toNostrEvent(ctx: NostrAccountContext) {
|
private async toNostrEvent(ctx: NostrAccountContext) {
|
||||||
const event = await prepareEncryptedNostrEvent(ctx, {
|
const event = await prepareEncryptedNostrEvent(ctx, {
|
||||||
encryptKey: ctx.publicKey,
|
encryptKey: ctx.publicKey,
|
||||||
content: JSON.stringify(Array.from(this.pinList)),
|
content: JSON.stringify(Array.from(this.getPinList())),
|
||||||
kind: NostrKind.Custom_App_Data,
|
kind: NostrKind.Encrypted_Custom_App_Data,
|
||||||
tags: [],
|
tags: [],
|
||||||
});
|
});
|
||||||
return event;
|
return event;
|
||||||
@ -147,7 +181,7 @@ export class OtherConfig implements PinListGetter, NostrEventAdder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async addEvent(event: NostrEvent) {
|
async addEvent(event: NostrEvent) {
|
||||||
if (event.kind != NostrKind.Custom_App_Data) {
|
if (event.kind != NostrKind.Encrypted_Custom_App_Data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const decrypted = await this.ctx.decrypt(this.ctx.publicKey.hex, event.content);
|
const decrypted = await this.ctx.decrypt(this.ctx.publicKey.hex, event.content);
|
||||||
@ -160,14 +194,15 @@ export class OtherConfig implements PinListGetter, NostrEventAdder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pin.type == "PinConversation" || pin.type == "UnpinConversation") {
|
if (pin.type == "PinConversation" || pin.type == "UnpinConversation") {
|
||||||
if (pin.type == "PinConversation") {
|
const currentEvent = this.pinList.get(pin.pubkey);
|
||||||
if (this.pinList.has(pin.pubkey)) {
|
|
||||||
return;
|
if (currentEvent && pin.lamport < currentEvent.lamport) {
|
||||||
}
|
return; // ignore because the current event is newer
|
||||||
this.pinList.add(pin.pubkey);
|
|
||||||
} else {
|
|
||||||
this.pinList.delete(pin.pubkey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.lamport.set(pin.lamport);
|
||||||
|
this.pinList.set(pin.pubkey, pin);
|
||||||
|
|
||||||
const err = await this.saveToLocalStorage();
|
const err = await this.saveToLocalStorage();
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
return err;
|
return err;
|
||||||
@ -176,4 +211,4 @@ export class OtherConfig implements PinListGetter, NostrEventAdder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ConfigEvent = PinConversation | UnpinConversation;
|
export type ConfigEvent = PinConversationRelay | UnpinConversationRelay;
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
"https://deno.land/std@0.176.0/testing/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea",
|
"https://deno.land/std@0.176.0/testing/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea",
|
||||||
"https://deno.land/std@0.176.0/testing/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7",
|
"https://deno.land/std@0.176.0/testing/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7",
|
||||||
"https://deno.land/std@0.176.0/testing/asserts.ts": "984ab0bfb3faeed92ffaa3a6b06536c66811185328c5dd146257c702c41b01ab",
|
"https://deno.land/std@0.176.0/testing/asserts.ts": "984ab0bfb3faeed92ffaa3a6b06536c66811185328c5dd146257c702c41b01ab",
|
||||||
|
"https://deno.land/std@0.186.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0",
|
||||||
|
"https://deno.land/std@0.186.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b",
|
||||||
|
"https://deno.land/std@0.186.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0",
|
||||||
"https://esm.sh/@noble/hashes@1.3.2/utils": "20c519683900b5873b16ff15377049f6e86e183b612a0e442f6acbc056667e6a",
|
"https://esm.sh/@noble/hashes@1.3.2/utils": "20c519683900b5873b16ff15377049f6e86e183b612a0e442f6acbc056667e6a",
|
||||||
"https://esm.sh/@scure/bip32@1.3.2": "8f8111ae2b0865644daf69d6b0d8ea76bb15112453f5dc697ba29b44b527b26c",
|
"https://esm.sh/@scure/bip32@1.3.2": "8f8111ae2b0865644daf69d6b0d8ea76bb15112453f5dc697ba29b44b527b26c",
|
||||||
"https://esm.sh/@scure/bip39@1.2.1": "7d6cdfce191281c81406e55de5216714dd22b19790123b652421bbf9718e5057",
|
"https://esm.sh/@scure/bip39@1.2.1": "7d6cdfce191281c81406e55de5216714dd22b19790123b652421bbf9718e5057",
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 8afbf3999af91d9f7320789509abbfdb1405d17c
|
Subproject commit be759ead56a6eb8984e024296f79506b42c89199
|
12
nostr.ts
12
nostr.ts
@ -58,11 +58,23 @@ export type PinConversation = {
|
|||||||
pubkey: string;
|
pubkey: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PinConversationRelay = {
|
||||||
|
type: "PinConversation";
|
||||||
|
pubkey: string;
|
||||||
|
lamport: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type UnpinConversation = {
|
export type UnpinConversation = {
|
||||||
type: "UnpinConversation";
|
type: "UnpinConversation";
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UnpinConversationRelay = {
|
||||||
|
type: "UnpinConversation";
|
||||||
|
pubkey: string;
|
||||||
|
lamport: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type UserLogin = {
|
export type UserLogin = {
|
||||||
type: "UserLogin";
|
type: "UserLogin";
|
||||||
};
|
};
|
||||||
|
10
time.ts
10
time.ts
@ -2,17 +2,15 @@ import { NostrEvent } from "./lib/nostr-ts/nostr.ts";
|
|||||||
import { getTags } from "./nostr.ts";
|
import { getTags } from "./nostr.ts";
|
||||||
|
|
||||||
export class LamportTime {
|
export class LamportTime {
|
||||||
constructor(private time: number) {}
|
private time = 0;
|
||||||
|
|
||||||
static FromEvents(events: Iterable<NostrEvent>) {
|
fromEvents(events: Iterable<NostrEvent>) {
|
||||||
let time = 0;
|
|
||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
const ts = getTags(event).lamport_timestamp;
|
const ts = getTags(event).lamport_timestamp;
|
||||||
if (ts && ts > time) {
|
if (ts) {
|
||||||
time = ts;
|
this.set(ts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new LamportTime(time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
now() {
|
now() {
|
||||||
|
Loading…
Reference in New Issue
Block a user