All files / src/stores download.store.ts

20.83% Statements 10/48
5.88% Branches 2/34
14.28% Functions 2/14
25.71% Lines 9/35

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94                                        3x 2x 2x 2x     5x 1x 1x 1x                                                                                                             2x                  
import { defineStore } from 'pinia';
import { ref } from 'vue';
 
type ProgressLike = {
  Completed?: number;
  Total?: number;
  IsDecryption?: boolean;
  Percentage?: number;
  Title?: string;
  StatusText?: string;
  FileName?: string;
  filename?: string;
};
 
type Info = ProgressLike & {
  Id: string;
  Closer?: ReturnType<typeof setTimeout> | null;
  LastUpdated?: number;
};
 
export const useDownloadStore = defineStore('download', () => {
  const infos = ref<Info[]>([]);
  let bc: BroadcastChannel | null = null;
  let _initialized = false;
 
  function initCrossTab() {
    if (_initialized) return;
    _initialized = true;
    bc = new BroadcastChannel('downloads');
    bc.onmessage = (ev: MessageEvent<any>) => {
      const msg = ev.data;
      if (!msg || !msg.type) return;
      if (msg.type === 'dl:start') upsert(msg.info);
      else if (msg.type === 'dl:update') upsert(msg.info);
      else if (msg.type === 'dl:finish') finish(msg.id);
    };
  }
 
  function upsert(info: any) {
    const id = String(info.Id ?? info.id);
    const i = infos.value.findIndex((x) => x.Id === id);
    const completed = Number(info.Completed ?? 0);
    const total = Number(info.Total ?? 0);
    const pct =
      total > 0 ? (completed / total) * 100 : Number(info.Percentage ?? 0);
    const now = Date.now();
    const next: Info = {
      ...(infos.value[i] ?? {}),
      ...info,
      Id: id,
      Completed: completed,
      Total: total,
      Percentage: pct,
      LastUpdated: now,
    };
    if (i >= 0) infos.value[i] = next;
    else infos.value.push(next);
  }
 
  function finish(id: string) {
    const i = infos.value.findIndex((x) => x.Id === String(id));
    if (i >= 0) {
      infos.value[i].LastUpdated = Date.now();
      if (infos.value[i].Closer) clearTimeout(infos.value[i].Closer!);
      infos.value[i].Closer = setTimeout(() => {
        const idx = infos.value.findIndex((x) => x.Id === String(id));
        if (idx >= 0) infos.value.splice(idx, 1);
      }, 3000);
    }
  }
  function touch(id: string) {
    const i = infos.value.findIndex((x) => x.Id === String(id));
    if (i >= 0) infos.value[i].LastUpdated = Date.now();
  }
  function requestPause(id: string) {
    bc?.postMessage({ type: 'dl:pause', id });
  }
  function requestResume(id: string) {
    bc?.postMessage({ type: 'dl:resume', id });
  }
  function requestCancel(id: string) {
    bc?.postMessage({ type: 'dl:cancel', id });
  }
 
  return {
    infos,
    initCrossTab,
    requestPause,
    requestResume,
    requestCancel,
    touch,
  };
});