Back to Funclet
SOURCE / PINNED RELEASE

Made of little things.

Funclet

Release
0b497cfc2f66…
Author-recorded commit
1e531b24f490…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-6b7a37cac1d

Archive hash verified: 71d97a89ed8c40c2…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

src/volume-preference.ts
interface VolumeStorage {
  getItem(key: string): Promise<string | null>;
  setItem(key: string, value: string): Promise<void>;
}


/** Serialize writes and never overwrite a new gesture with a late startup read. */
class StoredPreference<T> {
  private revision = 0;
  private pending: string | undefined;
  private writing = false;
  constructor(private storage: VolumeStorage | undefined, private key: string, private decode: (raw: string) => T | null, private encode: (value: T) => string) {}

  async load(): Promise<T | null> {
    const revision = this.revision;
    try {
      const raw = await this.storage?.getItem(this.key);
      if (revision !== this.revision || raw == null || raw.trim() === '') return null;
      return this.decode(raw);
    } catch { return null; }
  }

  save(value: T): void {
    ++this.revision;
    if (!this.storage) return;
    this.pending = this.encode(value);
    void this.flush();
  }

  private async flush(): Promise<void> {
    if (this.writing || !this.storage) return;
    this.writing = true;
    try {
      while (this.pending !== undefined) {
        const value = this.pending;
        this.pending = undefined;
        try { await this.storage.setItem(this.key, value); } catch { /* Session controls remain usable. */ }
      }
    } finally { this.writing = false; }
  }
}

export class VolumePreference extends StoredPreference<number> {
  constructor(storage: VolumeStorage | undefined) {
    super(storage, 'receiver-volume', raw => {
      const value = Number(raw);
      return Number.isFinite(value) && value >= 0 && value <= 1 ? value : null;
    }, String);
  }
}

export class StationPreference extends StoredPreference<string> {
  constructor(storage: VolumeStorage | undefined) {
    super(storage, 'receiver-station', raw => /^31237:[a-f0-9]{64}:.{1,1024}$/i.test(raw) ? raw : null, String);
  }
}