import { LeaderBoardReader } from './leaderboard-read.js' import { LeaderBoardWriter } from './leaderboard-write.js' /** * Generic, game- AND backend-agnostic leaderboard. Nothing here knows about * minesweeper or Firebase: the ranked value is a plain `score`, sorted in the * configured direction and displayed through an injected formatter, while all * storage I/O is delegated to an injected adapter (see adapters/). * * This is the COMBINED facade: it simply composes the two separable surfaces — * {@link LeaderBoardReader} (read/subscribe/render) and {@link LeaderBoardWriter} * (submit) — for consumers that want both in one object. Consumers who need only * one half import it directly and pull in nothing from the other: * * import { LeaderBoardReader } from '@cozy-games/leaderboard/leaderboard-read.js' * import { LeaderBoardWriter } from '@cozy-games/leaderboard/leaderboard-write.js' * * Public API is unchanged: `render()` (read) and `submit()` (write). The reader * and writer are also exposed as `.reader` / `.writer` for direct access. Reads * and writes may be wired to differently-privileged backend instances by passing * the surfaces different adapters (construct a Reader and Writer directly). * * An adapter implements: * - getConfig(): Promise * - listScores({ category, since, order, limit }): Promise // read * - addScore(category, entry): Promise // write * - archive(entry): Promise // optional personal history // write */ export class LeaderBoardService { /** * @param {Object} options - see {@link LeaderBoardReader} and {@link LeaderBoardWriter} for the full set * @param {Object} options.adapter - storage backend (e.g. FirebaseAdapter, SupabaseAdapter) * @param {'asc'|'desc'} [options.scoreOrder] * @param {(value: number) => string} [options.formatScore] * @param {(entry: Object) => boolean} [options.qualifies] * @param {Object} [options.labels] * @param {Object} [options.tooltips] * @param {string[]} [options.emptyMessages] * @param {string} [options.loadingText] * @param {string} [options.errorText] * @param {string} [options.anonymousName] */ constructor(options = {}) { this.adapter = options.adapter this.reader = new LeaderBoardReader(options) this.writer = new LeaderBoardWriter(options) } /** * Read surface — render the ranked list with a duration tab bar. * @see LeaderBoardReader#render */ render(category, title, duration) { return this.reader.render(category, title, duration) } /** * Write surface — submit a completed game (archive + ranked entry). * @see LeaderBoardWriter#submit */ submit(entry) { return this.writer.submit(entry) } }