TrashView.tsx

5.7 KB · raw

1import { createSignal, For, onMount, Show } from "solid-js";2import type { VaultEntry } from "../../shared/types";3import { sendMessage } from "../App";45interface TrashViewProps {6  onBack: () => void;7  onRestored: () => void;8}910export function TrashView(props: TrashViewProps) {11  const [deleted, setDeleted] = createSignal<VaultEntry[]>([]);12  const [loading, setLoading] = createSignal(true);13  const [confirmPurgeId, setConfirmPurgeId] = createSignal<string | null>(null);14  const [confirmPurgeAll, setConfirmPurgeAll] = createSignal(false);1516  onMount(async () => {17    await reload();18  });1920  async function reload() {21    setLoading(true);22    const res = await sendMessage({ type: "GET_DELETED_ENTRIES" });23    setDeleted((res as { ok: boolean; data?: VaultEntry[] }).data ?? []);24    setLoading(false);25  }2627  async function restore(id: string) {28    await sendMessage({ type: "RESTORE_ENTRY", payload: { id } });29    props.onRestored();30    await reload();31  }3233  async function purge(id: string) {34    await sendMessage({ type: "PURGE_ENTRY", payload: { id } });35    setConfirmPurgeId(null);36    await reload();37  }3839  async function purgeAll() {40    for (const entry of deleted()) {41      await sendMessage({ type: "PURGE_ENTRY", payload: { id: entry.id } });42    }43    setConfirmPurgeAll(false);44    await reload();45  }4647  function formatDate(ts?: number): string {48    if (!ts) return "";49    return new Date(ts).toLocaleDateString(undefined, { month: "short", day: "numeric" });50  }5152  return (53    <div class="peach-overlay-view ov-enter-right" style="display:flex">54      <div class="peach-overlay-header">55        <button56          class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn"57          onClick={props.onBack}58          aria-label="Back"59        >60          <svg width="7" height="12" viewBox="0 0 7 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" stroke-linejoin="round">61            <path d="M6 1L1 6L6 11" />62          </svg>63        </button>64        <span class="peach-overlay-title">trash</span>65        <div class="peach-overlay-spacer" />66      </div>6768      <div class="peach-settings-body">69        <Show when={loading()}>70          <div class="peach-list-empty"><span>loading...</span></div>71        </Show>7273        <Show when={!loading() && deleted().length === 0}>74          <div class="peach-list-empty" style="padding:48px 20px;flex-direction:column;gap:6px">75            <span style="font-size:13px;color:var(--peach-t1)">Trash is empty.</span>76            <span style="font-size:11px;color:var(--peach-t2)">Deleted entries appear here before being removed permanently.</span>77          </div>78        </Show>7980        <Show when={!loading() && deleted().length > 0}>81          <div class="peach-settings-group peach-trash-list">82            <p class="peach-settings-sub-label">83              {deleted().length} {deleted().length === 1 ? "entry" : "entries"}84            </p>8586            <For each={deleted()}>87              {(entry) => (88                <div class="peach-trash-entry-row">89                  <div class="peach-trash-entry-info">90                    <span class="peach-trash-entry-title">{entry.title}</span>91                    <span class="peach-trash-entry-meta">92                      {formatDate(entry.deletedAt)}93                      {entry.url ? ` · ${entry.url.replace(/^https?:\/\//, "").split("/")[0]}` : ""}94                    </span>95                  </div>96                  <div class="peach-trash-entry-actions">97                    <button98                      class="peach-btn peach-btn--subtle peach-btn--sm"99                      onClick={() => void restore(entry.id)}100                    >101                      restore102                    </button>103                    <Show104                      when={confirmPurgeId() === entry.id}105                      fallback={106                        <button107                          class="peach-trash-delete-btn"108                          onClick={() => setConfirmPurgeId(entry.id)}109                          aria-label="Delete permanently"110                        >111                          ×112                        </button>113                      }114                    >115                      <button116                        class="peach-btn peach-btn--sm peach-btn--danger"117                        onClick={() => void purge(entry.id)}118                      >119                        delete120                      </button>121                      <button122                        class="peach-btn peach-btn--ghost peach-btn--sm"123                        onClick={() => setConfirmPurgeId(null)}124                      >125                        cancel126                      </button>127                    </Show>128                  </div>129                </div>130              )}131            </For>132133            <div class="peach-trash-footer">134              <Show135                when={confirmPurgeAll()}136                fallback={137                  <button class="peach-trash-purge-all" onClick={() => setConfirmPurgeAll(true)}>138                    permanently delete all139                  </button>140                }141              >142                <span class="peach-trash-purge-confirm-label">delete all permanently?</span>143                <button144                  class="peach-btn peach-btn--sm peach-btn--danger"145                  onClick={() => void purgeAll()}146                >147                  yes148                </button>149                <button150                  class="peach-btn peach-btn--ghost peach-btn--sm"151                  onClick={() => setConfirmPurgeAll(false)}152                >153                  cancel154                </button>155              </Show>156            </div>157          </div>158        </Show>159      </div>160    </div>161  );162}