@ztimson/momentum 0.28.2 → 0.28.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  <br />
4
4
 
5
5
  <!-- Logo -->
6
- <img src="server/public/favicon.svg" alt="Logo" width="200" height="200">
6
+ <img src="server/public/favicon.png" alt="Logo" width="200" height="200">
7
7
 
8
8
  <!-- Title -->
9
9
  ### Momentum
package/dist/api.d.ts CHANGED
@@ -5,6 +5,16 @@ export type ApiEvents = TypedEvents & {
5
5
  REJECTED: (response: Error, options: HttpRequestOptions) => any;
6
6
  TOKEN: (token: string | null) => any;
7
7
  };
8
+ export type Health = {
9
+ status: 'ok' | 'degraded' | 'failing';
10
+ database: boolean;
11
+ email: boolean;
12
+ payments: boolean;
13
+ server: boolean;
14
+ sockets: boolean;
15
+ version: string;
16
+ commit: string;
17
+ };
8
18
  export declare class Api extends Http {
9
19
  readonly url: string;
10
20
  readonly opts: HttpDefaults;
@@ -17,11 +27,7 @@ export declare class Api extends Http {
17
27
  off: <K extends string | symbol = string>(event: K, listener: ApiEvents[K]) => void;
18
28
  on: <K extends string | symbol = string>(event: K, listener: ApiEvents[K]) => () => void;
19
29
  once: <K extends string | symbol = string>(event: K, listener?: ApiEvents[K] | undefined) => Promise<any>;
20
- healthcheck(): PromiseProgress<{
21
- database: boolean;
22
- server: boolean;
23
- version: string;
24
- }>;
30
+ healthcheck(): PromiseProgress<Health>;
25
31
  request<T>(options: HttpRequestOptions): PromiseProgress<T>;
26
32
  }
27
33
  //# sourceMappingURL=api.d.ts.map
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,IAAI,EAAE,YAAY,EAClB,kBAAkB,EAClB,eAAe,EAEf,KAAK,WAAW,EAChB,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG;IACrC,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,kBAAkB,KAAK,GAAG,CAAC;IACrE,QAAQ,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,kBAAkB,KAAK,GAAG,CAAC;IAC9D,QAAQ,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,KAAK,GAAG,CAAC;IAChE,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,GAAG,CAAA;CACpC,CAAA;AAED,qBAAa,GAAI,SAAQ,IAAI;aAYA,GAAG,EAAE,MAAM;aAAoC,IAAI,EAAE,YAAY;IAX7F,OAAO,CAAC,OAAO,CAAiC;IAEhD,OAAO,CAAC,MAAM,CAAuB;IACrC,IAAI,KAAK,IAAI,MAAM,GAAG,IAAI,CAAwB;IAClD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAK7B;gBAE2B,GAAG,GAAE,MAAwB,EAAkB,IAAI,GAAE,YAAiB;IAKlG,IAAI,mFAAwC;IAC5C,GAAG,iFAAuC;IAC1C,EAAE,uFAAsC;IACxC,IAAI,sGAAwC;IAE5C,WAAW;kBACqB,OAAO;gBAAU,OAAO;iBAAW,MAAM;;IAGzE,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,kBAAkB,GAAG,eAAe,CAAC,CAAC,CAAC;CAa3D"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,IAAI,EAAE,YAAY,EAClB,kBAAkB,EAClB,eAAe,EAEf,KAAK,WAAW,EAChB,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG;IACrC,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,kBAAkB,KAAK,GAAG,CAAC;IACrE,QAAQ,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,kBAAkB,KAAK,GAAG,CAAC;IAC9D,QAAQ,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,KAAK,GAAG,CAAC;IAChE,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,GAAG,CAAA;CACpC,CAAA;AAED,MAAM,MAAM,MAAM,GAAG;IACpB,MAAM,EAAE,IAAI,GAAG,UAAU,GAAG,SAAS,CAAC;IACtC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CACf,CAAA;AAED,qBAAa,GAAI,SAAQ,IAAI;aAYA,GAAG,EAAE,MAAM;aAAoC,IAAI,EAAE,YAAY;IAX7F,OAAO,CAAC,OAAO,CAAiC;IAEhD,OAAO,CAAC,MAAM,CAAuB;IACrC,IAAI,KAAK,IAAI,MAAM,GAAG,IAAI,CAAwB;IAClD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAK7B;gBAE2B,GAAG,GAAE,MAAwB,EAAkB,IAAI,GAAE,YAAiB;IAKlG,IAAI,mFAAwC;IAC5C,GAAG,iFAAuC;IAC1C,EAAE,uFAAsC;IACxC,IAAI,sGAAwC;IAE5C,WAAW;IAIX,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,kBAAkB,GAAG,eAAe,CAAC,CAAC,CAAC;CAa3D"}
package/dist/index.cjs CHANGED
@@ -6,9 +6,9 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
6
6
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
7
7
 
8
8
  var Y = Object.defineProperty;
9
- var v = (n, e, t) => e in n ? Y(n, e, { enumerable: true, configurable: true, writable: true, value: t }) : n[e] = t;
10
- var a = (n, e, t) => (v(n, typeof e != "symbol" ? e + "" : e, t), t);
11
- function F(n, e = false) {
9
+ var F = (n, e, t) => e in n ? Y(n, e, { enumerable: true, configurable: true, writable: true, value: t }) : n[e] = t;
10
+ var a = (n, e, t) => (F(n, typeof e != "symbol" ? e + "" : e, t), t);
11
+ function v(n, e = false) {
12
12
  if (n == null)
13
13
  throw new Error("Cannot clean a NULL value");
14
14
  return Array.isArray(n) ? n = n.filter((t) => t != null) : Object.entries(n).forEach(([t, r]) => {
@@ -60,11 +60,11 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
60
60
  const t = document.createElement("a");
61
61
  t.href = n, t.download = e, document.body.appendChild(t), t.click(), document.body.removeChild(t);
62
62
  }
63
- function wt(n, e) {
63
+ function At(n, e) {
64
64
  const t = URL.createObjectURL(n);
65
65
  V(t, e), URL.revokeObjectURL(t);
66
66
  }
67
- function xt(n = {}) {
67
+ function Bt(n = {}) {
68
68
  return new Promise((e) => {
69
69
  const t = document.createElement("input");
70
70
  t.type = "file", t.accept = n.accept || "*", t.style.display = "none", t.multiple = !!n.multiple, t.onblur = t.onchange = async () => {
@@ -72,10 +72,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
72
72
  }, document.body.appendChild(t), t.click();
73
73
  });
74
74
  }
75
- function At(n) {
75
+ function bt(n) {
76
76
  return new E((e, t, r) => {
77
77
  const o = new XMLHttpRequest(), s = new FormData();
78
- n.files.forEach((i) => s.append("file", i)), o.withCredentials = !!n.withCredentials, o.upload.addEventListener("progress", (i) => i.lengthComputable ? r(i.loaded / i.total) : null), o.upload.addEventListener("load", (i) => e(i)), o.upload.addEventListener("error", (i) => t(i)), o.open("POST", n.url), Object.entries(n.headers || {}).forEach(([i, y]) => o.setRequestHeader(i, y)), o.send(s);
78
+ n.files.forEach((i) => s.append("file", i)), o.withCredentials = !!n.withCredentials, o.upload.addEventListener("progress", (i) => i.lengthComputable ? r(i.loaded / i.total) : null), o.upload.addEventListener("load", (i) => e(i)), o.upload.addEventListener("error", (i) => t(i)), o.open("POST", n.url), Object.entries(n.headers || {}).forEach(([i, p]) => o.setRequestHeader(i, p)), o.send(s);
79
79
  });
80
80
  }
81
81
  class P {
@@ -221,13 +221,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
221
221
  const s = Array.isArray(e.query) ? e.query : Object.keys(e.query).map((i) => ({ key: i, value: e.query[i] }));
222
222
  t += (t.includes("?") ? "&" : "?") + s.map((i) => `${i.key}=${i.value}`).join("&");
223
223
  }
224
- const r = F({
224
+ const r = v({
225
225
  "Content-Type": e.body ? e.body instanceof FormData ? "multipart/form-data" : "application/json" : void 0,
226
226
  ...g2.headers,
227
227
  ...this.headers,
228
228
  ...e.headers
229
229
  });
230
- return typeof e.body == "object" && e.body != null && r["Content-Type"] == "application/json" && (e.body = JSON.stringify(e.body)), new E((s, i, y) => {
230
+ return typeof e.body == "object" && e.body != null && r["Content-Type"] == "application/json" && (e.body = JSON.stringify(e.body)), new E((s, i, p) => {
231
231
  fetch(t, {
232
232
  headers: r,
233
233
  method: e.method || (e.body ? "POST" : "GET"),
@@ -235,19 +235,19 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
235
235
  }).then(async (c) => {
236
236
  var k, G;
237
237
  for (let u of [...Object.values(g2.interceptors), ...Object.values(this.interceptors)])
238
- await new Promise((L) => u(c, () => L()));
238
+ await new Promise((O) => u(c, () => O()));
239
239
  const R = c.headers.get("Content-Length"), N = R ? parseInt(R, 10) : 0;
240
240
  let j = 0;
241
241
  const I = (k = c.body) == null ? void 0 : k.getReader(), H = new ReadableStream({
242
242
  start(u) {
243
- function L() {
243
+ function O() {
244
244
  I == null || I.read().then((x) => {
245
245
  if (x.done)
246
246
  return u.close();
247
- j += x.value.byteLength, y(j / N), u.enqueue(x.value), L();
247
+ j += x.value.byteLength, p(j / N), u.enqueue(x.value), O();
248
248
  }).catch((x) => u.error(x));
249
249
  }
250
- L();
250
+ O();
251
251
  }
252
252
  });
253
253
  if (c.data = new Response(H), e.decode == null || e.decode) {
@@ -269,7 +269,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
269
269
  BLINK: "\x1B[5m",
270
270
  REVERSE: "\x1B[7m",
271
271
  HIDDEN: "\x1B[8m"
272
- }, O = {
272
+ }, L = {
273
273
  BLACK: "\x1B[30m",
274
274
  RED: "\x1B[31m",
275
275
  GREEN: "\x1B[32m",
@@ -288,7 +288,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
288
288
  WHITE: "\x1B[97m"
289
289
  };
290
290
  var et = /* @__PURE__ */ ((n) => (n[n.ERROR = 0] = "ERROR", n[n.WARN = 1] = "WARN", n[n.INFO = 2] = "INFO", n[n.LOG = 3] = "LOG", n[n.DEBUG = 4] = "DEBUG", n))(et || {});
291
- const p = class p2 extends P {
291
+ const y = class y2 extends P {
292
292
  constructor(e) {
293
293
  super(), this.namespace = e;
294
294
  }
@@ -296,45 +296,45 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
296
296
  const s = e.toString(), i = t - s.length;
297
297
  if (i <= 0)
298
298
  return s;
299
- const y = Array(~~(i / r.length)).fill(r).join("");
300
- return o ? s + y : y + s;
299
+ const p = Array(~~(i / r.length)).fill(r).join("");
300
+ return o ? s + p : p + s;
301
301
  }
302
302
  format(...e) {
303
303
  const t = /* @__PURE__ */ new Date();
304
304
  return `${`${t.getFullYear()}-${t.getMonth() + 1}-${t.getDate()} ${this.pad(t.getHours().toString(), 2, "0")}:${this.pad(t.getMinutes().toString(), 2, "0")}:${this.pad(t.getSeconds().toString(), 2, "0")}.${this.pad(t.getMilliseconds().toString(), 3, "0", true)}`}${this.namespace ? ` [${this.namespace}]` : ""} ${e.join(" ")}`;
305
305
  }
306
306
  debug(...e) {
307
- if (p2.LOG_LEVEL < 4)
307
+ if (y2.LOG_LEVEL < 4)
308
308
  return;
309
309
  const t = this.format(...e);
310
- p2.emit(4, t), console.debug(O.LIGHT_GREY + t + A.CLEAR);
310
+ y2.emit(4, t), console.debug(L.LIGHT_GREY + t + A.CLEAR);
311
311
  }
312
312
  log(...e) {
313
- if (p2.LOG_LEVEL < 3)
313
+ if (y2.LOG_LEVEL < 3)
314
314
  return;
315
315
  const t = this.format(...e);
316
- p2.emit(3, t), console.log(A.CLEAR + t);
316
+ y2.emit(3, t), console.log(A.CLEAR + t);
317
317
  }
318
318
  info(...e) {
319
- if (p2.LOG_LEVEL < 2)
319
+ if (y2.LOG_LEVEL < 2)
320
320
  return;
321
321
  const t = this.format(...e);
322
- p2.emit(2, t), console.info(O.BLUE + t + A.CLEAR);
322
+ y2.emit(2, t), console.info(L.BLUE + t + A.CLEAR);
323
323
  }
324
324
  warn(...e) {
325
- if (p2.LOG_LEVEL < 1)
325
+ if (y2.LOG_LEVEL < 1)
326
326
  return;
327
327
  const t = this.format(...e);
328
- p2.emit(1, t), console.warn(O.YELLOW + t + A.CLEAR);
328
+ y2.emit(1, t), console.warn(L.YELLOW + t + A.CLEAR);
329
329
  }
330
330
  error(...e) {
331
- if (p2.LOG_LEVEL < 0)
331
+ if (y2.LOG_LEVEL < 0)
332
332
  return;
333
333
  const t = this.format(...e);
334
- p2.emit(0, t), console.error(O.RED + t + A.CLEAR);
334
+ y2.emit(0, t), console.error(L.RED + t + A.CLEAR);
335
335
  }
336
336
  };
337
- a(p, "LOG_LEVEL", 4);
337
+ a(y, "LOG_LEVEL", 4);
338
338
  class Api extends M {
339
339
  constructor(url = location.origin, opts = {}) {
340
340
  opts.url = url;
@@ -377,7 +377,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
377
377
  extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d2, b3) {
378
378
  d2.__proto__ = b3;
379
379
  } || function(d2, b3) {
380
- for (var p2 in b3) if (Object.prototype.hasOwnProperty.call(b3, p2)) d2[p2] = b3[p2];
380
+ for (var p in b3) if (Object.prototype.hasOwnProperty.call(b3, p)) d2[p] = b3[p];
381
381
  };
382
382
  return extendStatics(d, b2);
383
383
  };
@@ -1155,7 +1155,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1155
1155
  return this.api.request({ url: `/api/auth/totp/${username}`, method: "POST" });
1156
1156
  }
1157
1157
  setup(username, method = "app", totp) {
1158
- return this.api.request({ url: `/api/auth/totp/${username}`, body: F({
1158
+ return this.api.request({ url: `/api/auth/totp/${username}`, body: v({
1159
1159
  method,
1160
1160
  totp
1161
1161
  }) });
@@ -1590,37 +1590,37 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1590
1590
  });
1591
1591
  }
1592
1592
  open(path, target = "_blank") {
1593
- const p2 = (path.startsWith("/api/storage/") ? path : "/api/storage/" + path).replaceAll(/\/{2,}/g, "/");
1594
- const link = `${this.api.url}${p2}${this.api.token ? `?token=${this.api.token}` : ""}`;
1593
+ const p = (path.startsWith("/api/storage/") ? path : "/api/storage/" + path).replaceAll(/\/{2,}/g, "/");
1594
+ const link = `${this.api.url}${p}${this.api.token ? `?token=${this.api.token}` : ""}`;
1595
1595
  if (!target) return link;
1596
1596
  this.emit("OPEN", path);
1597
1597
  return window.open(link, target);
1598
1598
  }
1599
1599
  mkdir(path) {
1600
- const p2 = (path.startsWith("/api/storage/") ? path : "/api/storage/" + path).replaceAll(/\/{2,}/g, "/");
1601
- return this.api.request({ url: p2 + "?directory", method: "POST" });
1600
+ const p = (path.startsWith("/api/storage/") ? path : "/api/storage/" + path).replaceAll(/\/{2,}/g, "/");
1601
+ return this.api.request({ url: p + "?directory", method: "POST" });
1602
1602
  }
1603
1603
  download(path, opts = {}) {
1604
- const p2 = ("/api/storage/" + path).replaceAll("//", "/");
1605
- return this.api.request({ ...opts, url: p2, decode: false }).then(async (response) => {
1604
+ const p = ("/api/storage/" + path).replaceAll("//", "/");
1605
+ return this.api.request({ ...opts, url: p, decode: false }).then(async (response) => {
1606
1606
  const blob = await response.blob();
1607
1607
  const name = opts.downloadAs || new URL(path).pathname.split("/").pop();
1608
1608
  this.emit("DOWNLOAD", path, blob);
1609
- wt(blob, name);
1609
+ At(blob, name);
1610
1610
  return response;
1611
1611
  });
1612
1612
  }
1613
1613
  upload(files, opts = "") {
1614
1614
  return new E(async (res, rej, prog) => {
1615
- if (!files) files = await xt();
1615
+ if (!files) files = await Bt();
1616
1616
  if (!files || Array.isArray(files) && !files.length) return [];
1617
1617
  const url = this.api.url + ("/api/storage/" + (typeof opts == "string" ? opts : opts == null ? void 0 : opts.path)).replaceAll("//", "/");
1618
- return At({
1618
+ return bt({
1619
1619
  url,
1620
1620
  files: Array.isArray(files) ? files : [files],
1621
1621
  headers: this.api.headers
1622
- }).onProgress((p2) => {
1623
- prog(p2);
1622
+ }).onProgress((p) => {
1623
+ prog(p);
1624
1624
  }).then((resp) => {
1625
1625
  this.emit("UPLOAD", resp);
1626
1626
  res(resp);
@@ -1634,6 +1634,36 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1634
1634
  }
1635
1635
  async inject(reload = false) {
1636
1636
  if (!Object.keys(this.settings.cache).length || reload) await this.settings.list();
1637
+ if (!document.querySelector('meta[name="apple-mobile-web-app-capable"]')) {
1638
+ const meta = document.createElement("meta");
1639
+ meta.name = "apple-mobile-web-app-capable";
1640
+ meta.content = "yes";
1641
+ document.head.append(meta);
1642
+ }
1643
+ if (!document.querySelector('meta[name="apple-mobile-web-app-status-bar-style"]')) {
1644
+ const meta = document.createElement("meta");
1645
+ meta.name = "apple-mobile-web-app-status-bar-style";
1646
+ meta.content = "default";
1647
+ document.head.append(meta);
1648
+ }
1649
+ if (!document.querySelector('meta[name="apple-mobile-web-app-title"]')) {
1650
+ const meta = document.createElement("meta");
1651
+ meta.name = "apple-mobile-web-app-title";
1652
+ meta.content = this.settings.cache.title;
1653
+ document.head.append(meta);
1654
+ }
1655
+ if (!document.querySelector('link[rel="apple-touch-icon"]')) {
1656
+ const meta = document.createElement("link");
1657
+ meta.rel = "apple-touch-icon";
1658
+ meta.href = this.settings.cache.logo;
1659
+ document.head.append(meta);
1660
+ }
1661
+ if (!document.querySelector('link[rel="apple-touch-startup-image"]')) {
1662
+ const meta = document.createElement("link");
1663
+ meta.rel = "apple-touch-startup-image";
1664
+ meta.href = this.settings.cache.logo;
1665
+ document.head.append(meta);
1666
+ }
1637
1667
  window.document.body.classList.add(this.settings.cache.theme.darkMode ? "theme-dark" : "theme-light");
1638
1668
  window.document.body.classList.remove(this.settings.cache.theme.darkMode ? "theme-light" : "theme-dark");
1639
1669
  if (this.settings.cache.title)
@@ -1670,7 +1700,136 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1670
1700
  link.setAttribute("rel", "manifest");
1671
1701
  link.setAttribute("href", this.settings.api.url + "/manifest.json");
1672
1702
  window.document.head.append(link);
1703
+ this.iosPrompt();
1704
+ }
1705
+ }
1706
+ iosPrompt() {
1707
+ const url = this.settings.api.url;
1708
+ const settings = this.settings.cache;
1709
+ let dismissed = !!localStorage.getItem("ios-prompt");
1710
+ const ios = /iPad|iPhone|iPod/.test(navigator.platform) || navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
1711
+ const pwa = window.matchMedia("(display-mode: standalone)").matches || (navigator == null ? void 0 : navigator.standalone) || document.referrer.includes("android-app://");
1712
+ function open() {
1713
+ let style = document.querySelector("style.ios-prompt");
1714
+ if (!style) {
1715
+ style = document.createElement("style");
1716
+ style.innerHTML = `
1717
+ .ios-prompt-backdrop {
1718
+ position: fixed;
1719
+ display: relative;
1720
+ left: 0;
1721
+ top: 0;
1722
+ right: 0;
1723
+ bottom: 0;
1724
+ background: rgba(0,0,0,.5);
1725
+ z-index: 9999;
1726
+ animation: fadeIn 0.5s ease-in-out forwards;
1727
+ opacity: 0;
1728
+ }
1729
+ .ios-prompt-backdrop.exit {
1730
+ animation: fadeOut 0.5s ease-in-out forwards !important;
1731
+ }
1732
+ .ios-prompt {
1733
+ position: fixed;
1734
+ background: #fff;
1735
+ color: black;
1736
+ bottom: 0;
1737
+ left: 50%;
1738
+ width: min(100vw, 450px);
1739
+ transform: translate(-50%, 0);
1740
+ animation: slideUp 0.5s ease-in-out forwards;
1741
+ }
1742
+ .ios-prompt.exit {
1743
+ animation: slideDown 0.5s ease-in-out forwards !important;
1744
+ }
1745
+ .ios-prompt img {
1746
+ width: 18px;
1747
+ height: 18px;
1748
+ }
1749
+ .ios-prompt h1 {
1750
+ font-size: 1.25rem;
1751
+ font-weight: bold;
1752
+ }
1753
+ .ios-prompt-close {
1754
+ position: absolute;
1755
+ right: 5px;
1756
+ top: 10px;
1757
+ background: transparent;
1758
+ border: none;
1759
+ cursor: pointer;
1760
+ }
1761
+
1762
+ @keyframes fadeIn {
1763
+ from { opacity: 0; }
1764
+ to { opacity: 1; }
1765
+ }
1766
+ @keyframes fadeOut {
1767
+ from { opacity: 1; }
1768
+ to { opacity: 0; }
1769
+ }
1770
+
1771
+ @keyframes slideUp {
1772
+ from { transform: translate(-50%, 100%); }
1773
+ to { transform: translate(-50%, 0); }
1774
+ }
1775
+ @keyframes slideDown {
1776
+ from { transform: translate(-50%, 0); }
1777
+ to { transform: translate(-50%, 100%); }
1778
+ }
1779
+ `;
1780
+ document.head.append(style);
1781
+ }
1782
+ const backdrop = document.createElement("div");
1783
+ backdrop.classList.add("ios-prompt-backdrop");
1784
+ const prompt = document.createElement("div");
1785
+ prompt.classList.add("ios-prompt");
1786
+ prompt.innerHTML = `
1787
+ <div style="display: flex; padding: 1rem; align-items: center">
1788
+ <img src="${url}${settings.logo}" alt="Logo" style="height: 30px; width: auto; margin-right: .5rem;" />
1789
+ <h1 style="margin: 0">Install ${settings.title}</h1>
1790
+ </div>
1791
+ <div style="display: flex; flex-direction: column; align-items: center">
1792
+ <div style="border-top: 2px solid #00000020; border-bottom: 2px solid #00000020; padding: 0 1rem">
1793
+ <p style="margin-top: 1rem; text-align: center">This website can be installed as an App! Add it to your home screen for quick access & fullscreen use.</p>
1794
+ </div>
1795
+ <table style="margin: 1.5rem 0">
1796
+ <tr>
1797
+ <td style="width: 50px">
1798
+ <svg viewBox="0 0 566 670" xmlns="http://www.w3.org/2000/svg" fill="#0B76FC" height="40px"><path d="M255 12c4-4 10-8 16-8s12 3 16 8l94 89c3 4 6 7 8 12 2 6 0 14-5 19-7 8-20 9-28 2l-7-7-57-60 2 54v276c0 12-10 22-22 22-12 1-24-10-23-22V110l1-43-60 65c-5 5-13 8-21 6a19 19 0 0 1-16-17c-1-7 2-13 7-18l95-91z" /><path d="M43 207c16-17 40-23 63-23h83v46h-79c-12 0-25 3-33 13-8 9-10 21-10 33v260c0 13 0 27 6 38 5 12 18 18 30 19l14 1h302c14 0 28 0 40-8 11-7 16-21 16-34V276c0-11-2-24-9-33-8-10-22-13-34-13h-78v-46h75c13 0 25 1 37 4 16 4 31 13 41 27 11 17 14 37 14 57v280c0 20-3 41-15 58a71 71 0 0 1-45 27c-11 2-23 3-34 3H109c-19-1-40-4-56-15-14-9-23-23-27-38-4-12-5-25-5-38V270c1-22 6-47 22-63z" /></svg>
1799
+ </td>
1800
+ <td>
1801
+ <p style="margin: 1rem 0">1) Press the "Share" button</p>
1802
+ </td>
1803
+ </tr>
1804
+ <tr>
1805
+ <td>
1806
+ <svg viewBox="0 0 578 584" xmlns="http://www.w3.org/2000/svg" fill="black" height="34px"><path d="M101 35l19-1h333c12 0 23 0 35 3 17 3 34 12 44 27 13 16 16 38 16 58v329c0 19 0 39-8 57a65 65 0 0 1-37 37c-18 7-38 7-57 7H130c-21 1-44 0-63-10-14-7-25-20-30-34-6-15-8-30-8-45V121c1-21 5-44 19-61 13-16 33-23 53-25m7 46c-10 1-19 6-24 14-7 8-9 20-9 31v334c0 12 2 25 10 34 9 10 23 12 35 12h336c14 1 30-3 38-15 6-9 8-20 8-31V125c0-12-2-24-10-33-9-9-22-12-35-12H121l-13 1z" /><path d="M271 161c9-11 31-10 38 4 3 5 3 11 3 17v87h88c7 0 16 1 21 7 6 6 7 14 6 22a21 21 0 0 1-10 14c-5 4-11 5-17 5h-88v82c0 7-1 15-6 20-10 10-29 10-37-2-3-6-4-13-4-19v-81h-87c-8-1-17-3-23-9-5-6-6-15-4-22a21 21 0 0 1 11-14c6-3 13-3 19-3h84v-88c0-7 1-14 6-20z" /></svg>
1807
+ </td>
1808
+ <td>
1809
+ <p style="margin: 1rem 0">2) Press "Add to Home Screen"</p>
1810
+ </td>
1811
+ </tr>
1812
+ </table>
1813
+ </div>`;
1814
+ const close = document.createElement("button");
1815
+ close.classList.add("ios-prompt-close");
1816
+ close.innerText = "X";
1817
+ close.onclick = () => {
1818
+ prompt.classList.add("exit");
1819
+ backdrop.classList.add("exit");
1820
+ localStorage.setItem("ios-prompt", "dismissed");
1821
+ setTimeout(() => {
1822
+ prompt.remove();
1823
+ backdrop.remove();
1824
+ }, 500);
1825
+ };
1826
+ prompt.append(close);
1827
+ backdrop.append(prompt);
1828
+ document.body.append(backdrop);
1673
1829
  }
1830
+ setTimeout(() => {
1831
+ if (ios && !dismissed && !pwa) open();
1832
+ }, 1e3);
1674
1833
  }
1675
1834
  }
1676
1835
  class Users extends P {
@@ -1738,7 +1897,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1738
1897
  });
1739
1898
  }
1740
1899
  uploadImage(username, file) {
1741
- return At({
1900
+ return bt({
1742
1901
  url: this.api.url + `/api/users/${username}/image`,
1743
1902
  files: [file],
1744
1903
  headers: this.api.headers
@@ -1809,11 +1968,17 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1809
1968
  });
1810
1969
  }
1811
1970
  upload(files, path = "/") {
1812
- const data = new FormData();
1813
- (Array.isArray(files) ? files : [files]).forEach((f) => data.append("file", f));
1814
- return this.api.request({ url: "/api/static" + path, body: data }).then((resp) => {
1815
- this.emit("UPLOAD", resp || []);
1816
- return resp;
1971
+ return new E(async (res, rej, prog) => {
1972
+ return bt({
1973
+ url: this.api.url + ("/api/static/" + path).replaceAll("//", "/"),
1974
+ files: Array.isArray(files) ? files : [files],
1975
+ headers: this.api.headers
1976
+ }).onProgress((p) => {
1977
+ prog(p);
1978
+ }).then((resp) => {
1979
+ this.emit("UPLOAD", resp);
1980
+ res(resp);
1981
+ }).catch((err) => rej(err));
1817
1982
  });
1818
1983
  }
1819
1984
  }
package/dist/index.mjs CHANGED
@@ -2,9 +2,9 @@ var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  var Y = Object.defineProperty;
5
- var v = (n, e, t) => e in n ? Y(n, e, { enumerable: true, configurable: true, writable: true, value: t }) : n[e] = t;
6
- var a = (n, e, t) => (v(n, typeof e != "symbol" ? e + "" : e, t), t);
7
- function F(n, e = false) {
5
+ var F = (n, e, t) => e in n ? Y(n, e, { enumerable: true, configurable: true, writable: true, value: t }) : n[e] = t;
6
+ var a = (n, e, t) => (F(n, typeof e != "symbol" ? e + "" : e, t), t);
7
+ function v(n, e = false) {
8
8
  if (n == null)
9
9
  throw new Error("Cannot clean a NULL value");
10
10
  return Array.isArray(n) ? n = n.filter((t) => t != null) : Object.entries(n).forEach(([t, r]) => {
@@ -56,11 +56,11 @@ function V(n, e) {
56
56
  const t = document.createElement("a");
57
57
  t.href = n, t.download = e, document.body.appendChild(t), t.click(), document.body.removeChild(t);
58
58
  }
59
- function wt(n, e) {
59
+ function At(n, e) {
60
60
  const t = URL.createObjectURL(n);
61
61
  V(t, e), URL.revokeObjectURL(t);
62
62
  }
63
- function xt(n = {}) {
63
+ function Bt(n = {}) {
64
64
  return new Promise((e) => {
65
65
  const t = document.createElement("input");
66
66
  t.type = "file", t.accept = n.accept || "*", t.style.display = "none", t.multiple = !!n.multiple, t.onblur = t.onchange = async () => {
@@ -68,10 +68,10 @@ function xt(n = {}) {
68
68
  }, document.body.appendChild(t), t.click();
69
69
  });
70
70
  }
71
- function At(n) {
71
+ function bt(n) {
72
72
  return new E((e, t, r) => {
73
73
  const o = new XMLHttpRequest(), s = new FormData();
74
- n.files.forEach((i) => s.append("file", i)), o.withCredentials = !!n.withCredentials, o.upload.addEventListener("progress", (i) => i.lengthComputable ? r(i.loaded / i.total) : null), o.upload.addEventListener("load", (i) => e(i)), o.upload.addEventListener("error", (i) => t(i)), o.open("POST", n.url), Object.entries(n.headers || {}).forEach(([i, y]) => o.setRequestHeader(i, y)), o.send(s);
74
+ n.files.forEach((i) => s.append("file", i)), o.withCredentials = !!n.withCredentials, o.upload.addEventListener("progress", (i) => i.lengthComputable ? r(i.loaded / i.total) : null), o.upload.addEventListener("load", (i) => e(i)), o.upload.addEventListener("error", (i) => t(i)), o.open("POST", n.url), Object.entries(n.headers || {}).forEach(([i, p]) => o.setRequestHeader(i, p)), o.send(s);
75
75
  });
76
76
  }
77
77
  class P {
@@ -217,13 +217,13 @@ const g = class g2 {
217
217
  const s = Array.isArray(e.query) ? e.query : Object.keys(e.query).map((i) => ({ key: i, value: e.query[i] }));
218
218
  t += (t.includes("?") ? "&" : "?") + s.map((i) => `${i.key}=${i.value}`).join("&");
219
219
  }
220
- const r = F({
220
+ const r = v({
221
221
  "Content-Type": e.body ? e.body instanceof FormData ? "multipart/form-data" : "application/json" : void 0,
222
222
  ...g2.headers,
223
223
  ...this.headers,
224
224
  ...e.headers
225
225
  });
226
- return typeof e.body == "object" && e.body != null && r["Content-Type"] == "application/json" && (e.body = JSON.stringify(e.body)), new E((s, i, y) => {
226
+ return typeof e.body == "object" && e.body != null && r["Content-Type"] == "application/json" && (e.body = JSON.stringify(e.body)), new E((s, i, p) => {
227
227
  fetch(t, {
228
228
  headers: r,
229
229
  method: e.method || (e.body ? "POST" : "GET"),
@@ -231,19 +231,19 @@ const g = class g2 {
231
231
  }).then(async (c) => {
232
232
  var k, G;
233
233
  for (let u of [...Object.values(g2.interceptors), ...Object.values(this.interceptors)])
234
- await new Promise((L) => u(c, () => L()));
234
+ await new Promise((O) => u(c, () => O()));
235
235
  const R = c.headers.get("Content-Length"), N = R ? parseInt(R, 10) : 0;
236
236
  let j = 0;
237
237
  const I = (k = c.body) == null ? void 0 : k.getReader(), H = new ReadableStream({
238
238
  start(u) {
239
- function L() {
239
+ function O() {
240
240
  I == null || I.read().then((x) => {
241
241
  if (x.done)
242
242
  return u.close();
243
- j += x.value.byteLength, y(j / N), u.enqueue(x.value), L();
243
+ j += x.value.byteLength, p(j / N), u.enqueue(x.value), O();
244
244
  }).catch((x) => u.error(x));
245
245
  }
246
- L();
246
+ O();
247
247
  }
248
248
  });
249
249
  if (c.data = new Response(H), e.decode == null || e.decode) {
@@ -265,7 +265,7 @@ const A = {
265
265
  BLINK: "\x1B[5m",
266
266
  REVERSE: "\x1B[7m",
267
267
  HIDDEN: "\x1B[8m"
268
- }, O = {
268
+ }, L = {
269
269
  BLACK: "\x1B[30m",
270
270
  RED: "\x1B[31m",
271
271
  GREEN: "\x1B[32m",
@@ -284,7 +284,7 @@ const A = {
284
284
  WHITE: "\x1B[97m"
285
285
  };
286
286
  var et = /* @__PURE__ */ ((n) => (n[n.ERROR = 0] = "ERROR", n[n.WARN = 1] = "WARN", n[n.INFO = 2] = "INFO", n[n.LOG = 3] = "LOG", n[n.DEBUG = 4] = "DEBUG", n))(et || {});
287
- const p = class p2 extends P {
287
+ const y = class y2 extends P {
288
288
  constructor(e) {
289
289
  super(), this.namespace = e;
290
290
  }
@@ -292,45 +292,45 @@ const p = class p2 extends P {
292
292
  const s = e.toString(), i = t - s.length;
293
293
  if (i <= 0)
294
294
  return s;
295
- const y = Array(~~(i / r.length)).fill(r).join("");
296
- return o ? s + y : y + s;
295
+ const p = Array(~~(i / r.length)).fill(r).join("");
296
+ return o ? s + p : p + s;
297
297
  }
298
298
  format(...e) {
299
299
  const t = /* @__PURE__ */ new Date();
300
300
  return `${`${t.getFullYear()}-${t.getMonth() + 1}-${t.getDate()} ${this.pad(t.getHours().toString(), 2, "0")}:${this.pad(t.getMinutes().toString(), 2, "0")}:${this.pad(t.getSeconds().toString(), 2, "0")}.${this.pad(t.getMilliseconds().toString(), 3, "0", true)}`}${this.namespace ? ` [${this.namespace}]` : ""} ${e.join(" ")}`;
301
301
  }
302
302
  debug(...e) {
303
- if (p2.LOG_LEVEL < 4)
303
+ if (y2.LOG_LEVEL < 4)
304
304
  return;
305
305
  const t = this.format(...e);
306
- p2.emit(4, t), console.debug(O.LIGHT_GREY + t + A.CLEAR);
306
+ y2.emit(4, t), console.debug(L.LIGHT_GREY + t + A.CLEAR);
307
307
  }
308
308
  log(...e) {
309
- if (p2.LOG_LEVEL < 3)
309
+ if (y2.LOG_LEVEL < 3)
310
310
  return;
311
311
  const t = this.format(...e);
312
- p2.emit(3, t), console.log(A.CLEAR + t);
312
+ y2.emit(3, t), console.log(A.CLEAR + t);
313
313
  }
314
314
  info(...e) {
315
- if (p2.LOG_LEVEL < 2)
315
+ if (y2.LOG_LEVEL < 2)
316
316
  return;
317
317
  const t = this.format(...e);
318
- p2.emit(2, t), console.info(O.BLUE + t + A.CLEAR);
318
+ y2.emit(2, t), console.info(L.BLUE + t + A.CLEAR);
319
319
  }
320
320
  warn(...e) {
321
- if (p2.LOG_LEVEL < 1)
321
+ if (y2.LOG_LEVEL < 1)
322
322
  return;
323
323
  const t = this.format(...e);
324
- p2.emit(1, t), console.warn(O.YELLOW + t + A.CLEAR);
324
+ y2.emit(1, t), console.warn(L.YELLOW + t + A.CLEAR);
325
325
  }
326
326
  error(...e) {
327
- if (p2.LOG_LEVEL < 0)
327
+ if (y2.LOG_LEVEL < 0)
328
328
  return;
329
329
  const t = this.format(...e);
330
- p2.emit(0, t), console.error(O.RED + t + A.CLEAR);
330
+ y2.emit(0, t), console.error(L.RED + t + A.CLEAR);
331
331
  }
332
332
  };
333
- a(p, "LOG_LEVEL", 4);
333
+ a(y, "LOG_LEVEL", 4);
334
334
  class Api extends M {
335
335
  constructor(url = location.origin, opts = {}) {
336
336
  opts.url = url;
@@ -373,7 +373,7 @@ var extendStatics = function(d, b2) {
373
373
  extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d2, b3) {
374
374
  d2.__proto__ = b3;
375
375
  } || function(d2, b3) {
376
- for (var p3 in b3) if (Object.prototype.hasOwnProperty.call(b3, p3)) d2[p3] = b3[p3];
376
+ for (var p in b3) if (Object.prototype.hasOwnProperty.call(b3, p)) d2[p] = b3[p];
377
377
  };
378
378
  return extendStatics(d, b2);
379
379
  };
@@ -1151,7 +1151,7 @@ class Totp {
1151
1151
  return this.api.request({ url: `/api/auth/totp/${username}`, method: "POST" });
1152
1152
  }
1153
1153
  setup(username, method = "app", totp) {
1154
- return this.api.request({ url: `/api/auth/totp/${username}`, body: F({
1154
+ return this.api.request({ url: `/api/auth/totp/${username}`, body: v({
1155
1155
  method,
1156
1156
  totp
1157
1157
  }) });
@@ -1586,37 +1586,37 @@ class Storage extends P {
1586
1586
  });
1587
1587
  }
1588
1588
  open(path, target = "_blank") {
1589
- const p3 = (path.startsWith("/api/storage/") ? path : "/api/storage/" + path).replaceAll(/\/{2,}/g, "/");
1590
- const link = `${this.api.url}${p3}${this.api.token ? `?token=${this.api.token}` : ""}`;
1589
+ const p = (path.startsWith("/api/storage/") ? path : "/api/storage/" + path).replaceAll(/\/{2,}/g, "/");
1590
+ const link = `${this.api.url}${p}${this.api.token ? `?token=${this.api.token}` : ""}`;
1591
1591
  if (!target) return link;
1592
1592
  this.emit("OPEN", path);
1593
1593
  return window.open(link, target);
1594
1594
  }
1595
1595
  mkdir(path) {
1596
- const p3 = (path.startsWith("/api/storage/") ? path : "/api/storage/" + path).replaceAll(/\/{2,}/g, "/");
1597
- return this.api.request({ url: p3 + "?directory", method: "POST" });
1596
+ const p = (path.startsWith("/api/storage/") ? path : "/api/storage/" + path).replaceAll(/\/{2,}/g, "/");
1597
+ return this.api.request({ url: p + "?directory", method: "POST" });
1598
1598
  }
1599
1599
  download(path, opts = {}) {
1600
- const p3 = ("/api/storage/" + path).replaceAll("//", "/");
1601
- return this.api.request({ ...opts, url: p3, decode: false }).then(async (response) => {
1600
+ const p = ("/api/storage/" + path).replaceAll("//", "/");
1601
+ return this.api.request({ ...opts, url: p, decode: false }).then(async (response) => {
1602
1602
  const blob = await response.blob();
1603
1603
  const name = opts.downloadAs || new URL(path).pathname.split("/").pop();
1604
1604
  this.emit("DOWNLOAD", path, blob);
1605
- wt(blob, name);
1605
+ At(blob, name);
1606
1606
  return response;
1607
1607
  });
1608
1608
  }
1609
1609
  upload(files, opts = "") {
1610
1610
  return new E(async (res, rej, prog) => {
1611
- if (!files) files = await xt();
1611
+ if (!files) files = await Bt();
1612
1612
  if (!files || Array.isArray(files) && !files.length) return [];
1613
1613
  const url = this.api.url + ("/api/storage/" + (typeof opts == "string" ? opts : opts == null ? void 0 : opts.path)).replaceAll("//", "/");
1614
- return At({
1614
+ return bt({
1615
1615
  url,
1616
1616
  files: Array.isArray(files) ? files : [files],
1617
1617
  headers: this.api.headers
1618
- }).onProgress((p3) => {
1619
- prog(p3);
1618
+ }).onProgress((p) => {
1619
+ prog(p);
1620
1620
  }).then((resp) => {
1621
1621
  this.emit("UPLOAD", resp);
1622
1622
  res(resp);
@@ -1630,6 +1630,36 @@ class UI {
1630
1630
  }
1631
1631
  async inject(reload = false) {
1632
1632
  if (!Object.keys(this.settings.cache).length || reload) await this.settings.list();
1633
+ if (!document.querySelector('meta[name="apple-mobile-web-app-capable"]')) {
1634
+ const meta = document.createElement("meta");
1635
+ meta.name = "apple-mobile-web-app-capable";
1636
+ meta.content = "yes";
1637
+ document.head.append(meta);
1638
+ }
1639
+ if (!document.querySelector('meta[name="apple-mobile-web-app-status-bar-style"]')) {
1640
+ const meta = document.createElement("meta");
1641
+ meta.name = "apple-mobile-web-app-status-bar-style";
1642
+ meta.content = "default";
1643
+ document.head.append(meta);
1644
+ }
1645
+ if (!document.querySelector('meta[name="apple-mobile-web-app-title"]')) {
1646
+ const meta = document.createElement("meta");
1647
+ meta.name = "apple-mobile-web-app-title";
1648
+ meta.content = this.settings.cache.title;
1649
+ document.head.append(meta);
1650
+ }
1651
+ if (!document.querySelector('link[rel="apple-touch-icon"]')) {
1652
+ const meta = document.createElement("link");
1653
+ meta.rel = "apple-touch-icon";
1654
+ meta.href = this.settings.cache.logo;
1655
+ document.head.append(meta);
1656
+ }
1657
+ if (!document.querySelector('link[rel="apple-touch-startup-image"]')) {
1658
+ const meta = document.createElement("link");
1659
+ meta.rel = "apple-touch-startup-image";
1660
+ meta.href = this.settings.cache.logo;
1661
+ document.head.append(meta);
1662
+ }
1633
1663
  window.document.body.classList.add(this.settings.cache.theme.darkMode ? "theme-dark" : "theme-light");
1634
1664
  window.document.body.classList.remove(this.settings.cache.theme.darkMode ? "theme-light" : "theme-dark");
1635
1665
  if (this.settings.cache.title)
@@ -1666,7 +1696,136 @@ class UI {
1666
1696
  link.setAttribute("rel", "manifest");
1667
1697
  link.setAttribute("href", this.settings.api.url + "/manifest.json");
1668
1698
  window.document.head.append(link);
1699
+ this.iosPrompt();
1700
+ }
1701
+ }
1702
+ iosPrompt() {
1703
+ const url = this.settings.api.url;
1704
+ const settings = this.settings.cache;
1705
+ let dismissed = !!localStorage.getItem("ios-prompt");
1706
+ const ios = /iPad|iPhone|iPod/.test(navigator.platform) || navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
1707
+ const pwa = window.matchMedia("(display-mode: standalone)").matches || (navigator == null ? void 0 : navigator.standalone) || document.referrer.includes("android-app://");
1708
+ function open() {
1709
+ let style = document.querySelector("style.ios-prompt");
1710
+ if (!style) {
1711
+ style = document.createElement("style");
1712
+ style.innerHTML = `
1713
+ .ios-prompt-backdrop {
1714
+ position: fixed;
1715
+ display: relative;
1716
+ left: 0;
1717
+ top: 0;
1718
+ right: 0;
1719
+ bottom: 0;
1720
+ background: rgba(0,0,0,.5);
1721
+ z-index: 9999;
1722
+ animation: fadeIn 0.5s ease-in-out forwards;
1723
+ opacity: 0;
1724
+ }
1725
+ .ios-prompt-backdrop.exit {
1726
+ animation: fadeOut 0.5s ease-in-out forwards !important;
1727
+ }
1728
+ .ios-prompt {
1729
+ position: fixed;
1730
+ background: #fff;
1731
+ color: black;
1732
+ bottom: 0;
1733
+ left: 50%;
1734
+ width: min(100vw, 450px);
1735
+ transform: translate(-50%, 0);
1736
+ animation: slideUp 0.5s ease-in-out forwards;
1737
+ }
1738
+ .ios-prompt.exit {
1739
+ animation: slideDown 0.5s ease-in-out forwards !important;
1740
+ }
1741
+ .ios-prompt img {
1742
+ width: 18px;
1743
+ height: 18px;
1744
+ }
1745
+ .ios-prompt h1 {
1746
+ font-size: 1.25rem;
1747
+ font-weight: bold;
1748
+ }
1749
+ .ios-prompt-close {
1750
+ position: absolute;
1751
+ right: 5px;
1752
+ top: 10px;
1753
+ background: transparent;
1754
+ border: none;
1755
+ cursor: pointer;
1756
+ }
1757
+
1758
+ @keyframes fadeIn {
1759
+ from { opacity: 0; }
1760
+ to { opacity: 1; }
1761
+ }
1762
+ @keyframes fadeOut {
1763
+ from { opacity: 1; }
1764
+ to { opacity: 0; }
1765
+ }
1766
+
1767
+ @keyframes slideUp {
1768
+ from { transform: translate(-50%, 100%); }
1769
+ to { transform: translate(-50%, 0); }
1770
+ }
1771
+ @keyframes slideDown {
1772
+ from { transform: translate(-50%, 0); }
1773
+ to { transform: translate(-50%, 100%); }
1774
+ }
1775
+ `;
1776
+ document.head.append(style);
1777
+ }
1778
+ const backdrop = document.createElement("div");
1779
+ backdrop.classList.add("ios-prompt-backdrop");
1780
+ const prompt = document.createElement("div");
1781
+ prompt.classList.add("ios-prompt");
1782
+ prompt.innerHTML = `
1783
+ <div style="display: flex; padding: 1rem; align-items: center">
1784
+ <img src="${url}${settings.logo}" alt="Logo" style="height: 30px; width: auto; margin-right: .5rem;" />
1785
+ <h1 style="margin: 0">Install ${settings.title}</h1>
1786
+ </div>
1787
+ <div style="display: flex; flex-direction: column; align-items: center">
1788
+ <div style="border-top: 2px solid #00000020; border-bottom: 2px solid #00000020; padding: 0 1rem">
1789
+ <p style="margin-top: 1rem; text-align: center">This website can be installed as an App! Add it to your home screen for quick access & fullscreen use.</p>
1790
+ </div>
1791
+ <table style="margin: 1.5rem 0">
1792
+ <tr>
1793
+ <td style="width: 50px">
1794
+ <svg viewBox="0 0 566 670" xmlns="http://www.w3.org/2000/svg" fill="#0B76FC" height="40px"><path d="M255 12c4-4 10-8 16-8s12 3 16 8l94 89c3 4 6 7 8 12 2 6 0 14-5 19-7 8-20 9-28 2l-7-7-57-60 2 54v276c0 12-10 22-22 22-12 1-24-10-23-22V110l1-43-60 65c-5 5-13 8-21 6a19 19 0 0 1-16-17c-1-7 2-13 7-18l95-91z" /><path d="M43 207c16-17 40-23 63-23h83v46h-79c-12 0-25 3-33 13-8 9-10 21-10 33v260c0 13 0 27 6 38 5 12 18 18 30 19l14 1h302c14 0 28 0 40-8 11-7 16-21 16-34V276c0-11-2-24-9-33-8-10-22-13-34-13h-78v-46h75c13 0 25 1 37 4 16 4 31 13 41 27 11 17 14 37 14 57v280c0 20-3 41-15 58a71 71 0 0 1-45 27c-11 2-23 3-34 3H109c-19-1-40-4-56-15-14-9-23-23-27-38-4-12-5-25-5-38V270c1-22 6-47 22-63z" /></svg>
1795
+ </td>
1796
+ <td>
1797
+ <p style="margin: 1rem 0">1) Press the "Share" button</p>
1798
+ </td>
1799
+ </tr>
1800
+ <tr>
1801
+ <td>
1802
+ <svg viewBox="0 0 578 584" xmlns="http://www.w3.org/2000/svg" fill="black" height="34px"><path d="M101 35l19-1h333c12 0 23 0 35 3 17 3 34 12 44 27 13 16 16 38 16 58v329c0 19 0 39-8 57a65 65 0 0 1-37 37c-18 7-38 7-57 7H130c-21 1-44 0-63-10-14-7-25-20-30-34-6-15-8-30-8-45V121c1-21 5-44 19-61 13-16 33-23 53-25m7 46c-10 1-19 6-24 14-7 8-9 20-9 31v334c0 12 2 25 10 34 9 10 23 12 35 12h336c14 1 30-3 38-15 6-9 8-20 8-31V125c0-12-2-24-10-33-9-9-22-12-35-12H121l-13 1z" /><path d="M271 161c9-11 31-10 38 4 3 5 3 11 3 17v87h88c7 0 16 1 21 7 6 6 7 14 6 22a21 21 0 0 1-10 14c-5 4-11 5-17 5h-88v82c0 7-1 15-6 20-10 10-29 10-37-2-3-6-4-13-4-19v-81h-87c-8-1-17-3-23-9-5-6-6-15-4-22a21 21 0 0 1 11-14c6-3 13-3 19-3h84v-88c0-7 1-14 6-20z" /></svg>
1803
+ </td>
1804
+ <td>
1805
+ <p style="margin: 1rem 0">2) Press "Add to Home Screen"</p>
1806
+ </td>
1807
+ </tr>
1808
+ </table>
1809
+ </div>`;
1810
+ const close = document.createElement("button");
1811
+ close.classList.add("ios-prompt-close");
1812
+ close.innerText = "X";
1813
+ close.onclick = () => {
1814
+ prompt.classList.add("exit");
1815
+ backdrop.classList.add("exit");
1816
+ localStorage.setItem("ios-prompt", "dismissed");
1817
+ setTimeout(() => {
1818
+ prompt.remove();
1819
+ backdrop.remove();
1820
+ }, 500);
1821
+ };
1822
+ prompt.append(close);
1823
+ backdrop.append(prompt);
1824
+ document.body.append(backdrop);
1669
1825
  }
1826
+ setTimeout(() => {
1827
+ if (ios && !dismissed && !pwa) open();
1828
+ }, 1e3);
1670
1829
  }
1671
1830
  }
1672
1831
  class Users extends P {
@@ -1734,7 +1893,7 @@ class Users extends P {
1734
1893
  });
1735
1894
  }
1736
1895
  uploadImage(username, file) {
1737
- return At({
1896
+ return bt({
1738
1897
  url: this.api.url + `/api/users/${username}/image`,
1739
1898
  files: [file],
1740
1899
  headers: this.api.headers
@@ -1805,11 +1964,17 @@ class Static extends P {
1805
1964
  });
1806
1965
  }
1807
1966
  upload(files, path = "/") {
1808
- const data = new FormData();
1809
- (Array.isArray(files) ? files : [files]).forEach((f) => data.append("file", f));
1810
- return this.api.request({ url: "/api/static" + path, body: data }).then((resp) => {
1811
- this.emit("UPLOAD", resp || []);
1812
- return resp;
1967
+ return new E(async (res, rej, prog) => {
1968
+ return bt({
1969
+ url: this.api.url + ("/api/static/" + path).replaceAll("//", "/"),
1970
+ files: Array.isArray(files) ? files : [files],
1971
+ headers: this.api.headers
1972
+ }).onProgress((p) => {
1973
+ prog(p);
1974
+ }).then((resp) => {
1975
+ this.emit("UPLOAD", resp);
1976
+ res(resp);
1977
+ }).catch((err) => rej(err));
1813
1978
  });
1814
1979
  }
1815
1980
  }
package/dist/static.d.ts CHANGED
@@ -4,13 +4,13 @@ import { FileMeta } from './storage';
4
4
  export type StaticEvents = TypedEvents & {
5
5
  LIST: (path: string, response: string[] | FileMeta) => any;
6
6
  DELETE: (path: string) => any;
7
- UPLOAD: (files: FileMeta[]) => any;
7
+ UPLOAD: (files: string[]) => any;
8
8
  };
9
9
  export declare class Static extends TypedEmitter<StaticEvents> {
10
10
  private readonly api;
11
11
  constructor(api: Api | string);
12
12
  delete(path: string): Promise<void>;
13
13
  list(path: string): Promise<string[] | FileMeta>;
14
- upload(files: File | File[], path?: string): Promise<FileMeta[]>;
14
+ upload(files: File | File[], path?: string): Promise<string[]>;
15
15
  }
16
16
  //# sourceMappingURL=static.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"static.d.ts","sourceRoot":"","sources":["../src/static.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAC,YAAY,EAAE,KAAK,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAC,KAAK,QAAQ,EAAC,MAAM,WAAW,CAAC;AAExC,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG;IACxC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,QAAQ,KAAK,GAAG,CAAC;IAC3D,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,CAAC;IAC9B,MAAM,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,GAAG,CAAC;CACnC,CAAC;AAEF,qBAAa,MAAO,SAAQ,YAAY,CAAC,YAAY,CAAC;IACrD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAO;gBAEf,GAAG,EAAE,GAAG,GAAG,MAAM;IAK7B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMnC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC;IAQhD,MAAM,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,EAAE,EAAE,IAAI,SAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;CAQ7D"}
1
+ {"version":3,"file":"static.d.ts","sourceRoot":"","sources":["../src/static.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,OAAO,CAAC;AAC1B,OAAO,EAA+B,YAAY,EAAE,KAAK,WAAW,EAAqB,MAAM,gBAAgB,CAAC;AAChH,OAAO,EAAC,KAAK,QAAQ,EAAC,MAAM,WAAW,CAAC;AAExC,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG;IACxC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,QAAQ,KAAK,GAAG,CAAC;IAC3D,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,CAAC;IAC9B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC;CACjC,CAAC;AAEF,qBAAa,MAAO,SAAQ,YAAY,CAAC,YAAY,CAAC;IACrD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAO;gBAEf,GAAG,EAAE,GAAG,GAAG,MAAM;IAK7B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMnC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC;IAQhD,MAAM,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,EAAE,EAAE,IAAI,SAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAc3D"}
package/dist/ui.d.ts CHANGED
@@ -3,5 +3,6 @@ export declare class UI {
3
3
  private readonly settings;
4
4
  constructor(settings: Settings);
5
5
  inject(reload?: boolean): Promise<void>;
6
+ iosPrompt(): void;
6
7
  }
7
8
  //# sourceMappingURL=ui.d.ts.map
package/dist/ui.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AAEpC,qBAAa,EAAE;IAEF,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,QAAQ;IAEzC,MAAM,CAAC,MAAM,UAAQ;CAkD3B"}
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AAEpC,qBAAa,EAAE;IAEF,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,QAAQ;IAEzC,MAAM,CAAC,MAAM,UAAQ;IAsF3B,SAAS;CAoIT"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ztimson/momentum",
3
- "version": "0.28.2",
3
+ "version": "0.28.4",
4
4
  "description": "Client library for momentum",
5
5
  "keywords": [
6
6
  "Momentum"
@@ -26,7 +26,7 @@
26
26
  "watch": "vite build --watch"
27
27
  },
28
28
  "dependencies": {
29
- "@ztimson/utils": "^0.14.11",
29
+ "@ztimson/utils": "^0.14.12",
30
30
  "rxjs": "^7.8.1"
31
31
  },
32
32
  "devDependencies": {