import { css, html, LitElement, PropertyValues } from "lit";
import { property, state } from "lit/decorators.js";
import { RuntimeInternals } from "../lib/runtime.ts";
import { Task } from "@lit/task";
export class XFavoriteButtonElement extends LitElement {
static override styles = css`
x-button.emoji-button {
opacity: 0.7;
transition: opacity 0.2s;
font-size: 1rem;
}
x-button.emoji-button:hover {
opacity: 1;
}
x-button.auth-button {
font-size: 1rem;
}
`;
@property()
rt?: RuntimeInternals;
@property({ attribute: false })
charmId?: string;
// Local state for favoriting, used when
// modifying state inbetween server syncs.
@state()
isFavorite: boolean | undefined = undefined;
private async handleFavoriteClick(e: Event) {
e.preventDefault();
e.stopPropagation();
if (!this.rt || !this.charmId) return;
const manager = this.rt.cc().manager();
const isFavorite = this.deriveIsFavorite();
// Update local state, and use until overridden by
// syncing state, or another click.
this.isFavorite = !isFavorite;
try {
const charmCell = (await this.rt.cc().get(this.charmId, true)).getCell();
if (isFavorite) {
await manager.removeFavorite(charmCell);
} else {
await manager.addFavorite(charmCell);
}
} finally {
this.isFavoriteSync.run();
}
}
protected override willUpdate(changedProperties: PropertyValues): void {
if (changedProperties.has("charmId")) {
this.isFavorite = undefined;
}
}
private deriveIsFavorite(): boolean {
// If `isFavorite` is defined, we have local state that is not
// yet synced. Prefer local state if defined, otherwise use server state.
return this.isFavorite ?? this.isFavoriteSync.value ?? false;
}
isFavoriteSync = new Task(this, {
task: async (
[charmId, rt],
{ signal },
): Promise => {
const isFavorite = await isFavoriteSync(rt, charmId);
// If another favorite request was initiated, store
// the sync status, but don't overwrite the local state.
if (signal.aborted) return isFavorite;
// We update `this.isFavorite` here to `undefined`,
// indicating that the synced state should be preferred
// now that it's fresh.
this.isFavorite = undefined;
return isFavorite;
},
args: () => [this.charmId, this.rt],
});
override render() {
const isFavorite = this.deriveIsFavorite();
return html`
${isFavorite ? "⭐" : "☆"}
`;
}
}
globalThis.customElements.define("x-favorite-button", XFavoriteButtonElement);
async function isFavoriteSync(
rt?: RuntimeInternals,
charmId?: string,
): Promise {
if (!charmId || !rt) {
return false;
}
const manager = rt.cc().manager();
try {
const charm = await manager.get(charmId, true);
if (charm) {
const favorites = manager.getFavorites();
await favorites.sync();
return manager.isFavorite(charm);
} else {
return false;
}
} catch (_) {
//
}
return false;
}