Back to Pickle Rustlers
SOURCE / PINNED RELEASE

Made of little things.

Pickle Rustlers

Release
039e51f55e34…
Author-recorded commit
40608586a8a1…
License
LICENSE
Author’s source reference
nostr://npub12lm3rxsva8kfempyjkxupt09e02n3fezurkyadyqfejdc5e0r2kscqty6r/wss%3A%2F%2Fgit.napplet.soy%2F/n-bad44e7ab35

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

src/share/share.ts
// Optional score-card sharing: offered only to a signed-in viewer when the runtime
// injects identity, upload and outbox. Missing or denied domains just hide it.
import { identity, upload } from '@napplet/sdk';
import { runtimeHasDomain } from '../domain-availability.js';
import { postScoreCard } from './post.js';
import { CARD_H, CARD_W, drawScoreCard, toPng } from './scorecard.js';
import type { ScoreShare } from './types.js';

const REQUIRED = ['identity', 'upload', 'outbox'] as const;

async function viewerName(): Promise<string> {
  const fallback = 'A lone cowboy';
  try {
    const profile = await Promise.race([
      identity.getProfile(),
      new Promise<null>((resolve) => window.setTimeout(() => resolve(null), 1500)),
    ]);
    const name = (profile?.displayName || profile?.name || '').trim();
    return name ? Array.from(name).slice(0, 24).join('') : fallback;
  } catch {
    return fallback;
  }
}

/** Upload info is advisory; only a clear "no PNG uploads" answer hides sharing. */
async function canUploadPng(): Promise<boolean> {
  try {
    const info = await upload.info();
    if (info.rails.length > 0 && !info.rails.some((r) => r.enabled)) return false;
    if (info.maxBytes !== undefined && info.maxBytes < 256 * 1024) return false;
    return !info.mimeTypes?.length || info.mimeTypes.some((m) => m === 'image/png' || m === 'image/*' || m === '*/*');
  } catch {
    return false;
  }
}

export function createScoreShare(): ScoreShare | null {
  if (!REQUIRED.every((domain) => runtimeHasDomain(domain))) return null;

  let pubkey = '';
  let uploadOk = false;
  const listeners = new Set<() => void>();
  const notify = () => listeners.forEach((fn) => fn());

  const setPubkey = (next: string) => {
    if (next === pubkey) return;
    pubkey = next;
    notify();
  };

  try {
    const sub = identity.onChanged((next) => setPubkey(next || ''));
    window.addEventListener('pagehide', () => sub.close(), { once: true });
  } catch {
    // An identity domain that cannot subscribe still answers getPublicKey below.
  }
  identity
    .getPublicKey()
    .then((next) => setPubkey(next || ''))
    .catch(() => undefined);
  void canUploadPng().then((ok) => {
    uploadOk = ok;
    if (ok) notify();
  });

  return {
    get available() {
      return uploadOk && pubkey !== '';
    },
    onChange(listener) {
      listeners.add(listener);
    },
    async makeCard(run, scene) {
      const preview = drawScoreCard(run, scene, await viewerName());
      return { preview, png: await toPng(preview) };
    },
    post(png, text, onStatus) {
      return postScoreCard(png, text, { width: CARD_W, height: CARD_H }, onStatus);
    },
  };
}