uiShare.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. $(() => {
  2. var windowDisplayed = false;
  3. var data = null;
  4. const GRANT_TEXT = ["None", "Read Access", "Write Access", "Read Access (Strip meta)"];
  5. const GRANT_ICON = ["bi-question-lg", "bi-eye", "bi-pencil", "bi-eye-slash"];
  6. const ACCESS_ICON = ["bi-question-lg", "bi-database", "bi-envelope-at", "bi-link-45deg", "bi-people"];
  7. const TYPE_ICON = ["bi-question-lg", "bi-diagram-2", "bi-tags", "bi-braces-asterisk", "bi-images", "bi-gear"];
  8. const TYPE_TEXT = ["Unknown", "Single photo", "Tags", "Meta", "Everything", "Admin access"];
  9. class ShareData {
  10. dbId = 0;
  11. typeId = "";
  12. typeData = "";
  13. accessToId = 0;
  14. accessToData = "";
  15. grant = 0;
  16. constructor(data) {
  17. if (data) {
  18. this.dbId = data.id;
  19. this.typeId = [ "unknown", "ldapAccount", "email", "link", "every one" ].indexOf(data.type);
  20. this.typeData = data.typeData;
  21. this.accessToId = [ "unknown", "item", "tag", "meta", "everything", "admin"].indexOf(data.accessTo);
  22. this.accessToData = data.accessToData;
  23. this.grant = [ "none", "read", "write", "read without meta"].indexOf(data.grant);
  24. }
  25. }
  26. objectify() {
  27. return {
  28. id: this.dbId,
  29. type: [ "unknown", "ldapAccount", "email", "link", "every one" ][this.typeId],
  30. typeData: this.typeData,
  31. accessTo: [ "unknown", "item", "tag", "meta", "everything", "admin"][this.accessToId],
  32. accessToData: this.accessToData,
  33. grant: [ "none", "read", "write", "read without meta"][this.grant]
  34. };
  35. }
  36. compare(b) {
  37. if (this.typeId != b.typeId) return b.typeId - this.typeId;
  38. if (this.accessToId != b.accessToId) return b.accessToId - this.accessToId;
  39. if (this.grant != b.grant) return b.grant - this.grant;
  40. return 0;
  41. }
  42. }
  43. function getData() {
  44. return new Promise((ok) => {
  45. if (data)
  46. return ok(data);
  47. $.get("/api/accessAdmin/list", {}, okData => {
  48. data = (okData || []).map(x => new ShareData(x)).sort((a, b) => a.compare(b));
  49. ok(data);
  50. });
  51. });
  52. }
  53. function createData(data) {
  54. return new Promise(ok => {
  55. $.ajax({
  56. url: "/api/accessAdmin/create",
  57. type: "POST",
  58. data: data,
  59. success: item => {
  60. ok(new ShareData(item));
  61. },
  62. error: err => ok(false),
  63. });
  64. });
  65. }
  66. function updateData(data) {
  67. return new Promise(ok => {
  68. $.ajax({
  69. url: `/api/accessAdmin/${data.dbId}`,
  70. type: "POST",
  71. data: data,
  72. success: allData => {
  73. ok(true);
  74. },
  75. error: err => ok(false),
  76. });
  77. });
  78. }
  79. function revokeData(dbId) {
  80. return new Promise(ok => {
  81. $.ajax({
  82. url: `/api/accessAdmin/${dbId}`,
  83. type: "DELETE",
  84. success: allData => {
  85. ok(true);
  86. },
  87. error: err => ok(false),
  88. });
  89. });
  90. }
  91. async function buildTypeDepandentDiv(htmlElement, data) {
  92. htmlElement.textContent = "";
  93. let input = document.createElement("input");
  94. input.type = "text";
  95. if (data.accessToId === 1) {
  96. input.value = data.accessToData;
  97. htmlElement.appendChild(input);
  98. let span = document.createElement("span");
  99. span.textContent = " (" +((await MediaStorage.Instance.getMedia(data.accessToData))?.fileName || "File not found") + ")";
  100. htmlElement.appendChild(span);
  101. input.addEventListener("change", async () => {
  102. span.textContent = " (" +((await MediaStorage.Instance.getMedia(input.value))?.fileName || "File not found") + ")";
  103. });
  104. }
  105. else if (data.accessToId === 2) {
  106. input.value = data.accessToData;
  107. htmlElement.appendChild(input);
  108. }
  109. else if (data.accessToId === 3) {
  110. input.value = data.accessToData;
  111. htmlElement.appendChild(input);
  112. }
  113. if (htmlElement.children.length) {
  114. input.addEventListener("change", async () => {
  115. data.accessToData = input.value;
  116. await updateData(data);
  117. });
  118. }
  119. }
  120. async function buildShareItem(data) {
  121. const htmlId = `collapseShareItem${data.dbId}`;
  122. let container = document.createElement("li");
  123. container.className = "accordion-item";
  124. let header = document.createElement("h2");
  125. header.className = "accordion-header";
  126. let headerButton = document.createElement("button");
  127. headerButton.className = `accordion-button bi ${ACCESS_ICON[data.typeId]}`;
  128. headerButton.type = "button";
  129. headerButton.dataset.bsToggle = "collapse";
  130. headerButton.dataset.bsTarget = `#${htmlId}`;
  131. headerButton.textContent = data.typeData;
  132. headerButton.ariaExpanded = false;
  133. headerButton.ariaControls = htmlId;
  134. let accordionBodyContainer = document.createElement("div");
  135. accordionBodyContainer.className = "accordion-collapse collapse";
  136. accordionBodyContainer.dataset.bsParent = "#pch-share-container";
  137. accordionBodyContainer.id = htmlId;
  138. let accordionBody = document.createElement("div");
  139. accordionBody.className = "accordion-body";
  140. let typeDivRow = document.createElement("div");
  141. typeDivRow.className = "row";
  142. let typeDepandentDiv = document.createElement("div");
  143. let grantDiv = document.createElement("div");
  144. if (data.typeId === 3) { // Link type
  145. let linkContainer = document.createElement("div");
  146. linkContainer.className = "input-group";
  147. let text = document.createElement("span");
  148. text.className = "input-group-text bi bi-link-45deg";
  149. linkContainer.appendChild(text);
  150. let input = document.createElement("input");
  151. input.value = `${document.location.origin}/linkLogin?link=${data.typeData}`;
  152. input.className = "form-control";
  153. input.type = "text";
  154. input.disabled = true;
  155. linkContainer.appendChild(input);
  156. let bt = document.createElement("button");
  157. bt.className = "btn btn-primary btn-outline-secondary";
  158. bt.placeholder = "Copy"
  159. let btContent = document.createElement("span");
  160. btContent.className = "bi bi-clipboard-fill";
  161. bt.appendChild(btContent);
  162. linkContainer.appendChild(bt);
  163. typeDivRow.appendChild(linkContainer);
  164. bt.addEventListener("click", () => { navigator.clipboard.writeText(input.value); });
  165. }
  166. {
  167. let typeDiv = document.createElement("div");
  168. typeDiv.className = "dropdown";
  169. typeDiv.id = `pch-share-${data.dbId}-typeDropdown`
  170. let button = document.createElement("button");
  171. button.className = "btn btn-secondary dropdown-toggle bi";
  172. button.classList.add(TYPE_ICON[data.accessToId]);
  173. button.textContent = TYPE_TEXT[data.accessToId];
  174. button.type = "button";
  175. button.setAttribute("data-bs-toggle", "dropdown");
  176. button.setAttribute("aria-expanded", false);
  177. button.id = `${typeDiv.id}-button`;
  178. let dropdownMenu = document.createElement("ul");
  179. dropdownMenu.className = "dropdown-menu";
  180. for (let i =1; i <= TYPE_TEXT.length; ++i) {
  181. let li = document.createElement("li");
  182. let a = document.createElement("a");
  183. a.className = "dropdown-item bi";
  184. if (i === data.accessToId)
  185. a.classList.add("active");
  186. a.href="#";
  187. a.classList.add(TYPE_ICON[i]);
  188. a.textContent = TYPE_TEXT[i];
  189. a.addEventListener("click", async () => {
  190. let dbData = await getData();
  191. let dataIdx = dbData.findIndex(item => item.dbId === data.dbId);
  192. if (dataIdx === -1 || dbData[dataIdx].accessToId === i)
  193. return;
  194. dbData[dataIdx].accessToId = i;
  195. button.className = "btn btn-secondary dropdown-toggle bi";
  196. button.textContent = TYPE_TEXT[data.accessToId];
  197. button.classList.add(TYPE_ICON[data.accessToId]);
  198. button.setAttribute("aria-expanded", false);
  199. dropdownMenu.classList.remove("show");
  200. dropdownMenu.querySelectorAll(".active").forEach(i => i.classList.remove("active"));
  201. a.classList.add("active");
  202. if (dbData[dataIdx].accessToId === 5)
  203. grantDiv.classList.add("hidden");
  204. else
  205. grantDiv.classList.remove("hidden");
  206. await buildTypeDepandentDiv(typeDepandentDiv, dbData[dataIdx]);
  207. await updateData(dbData[dataIdx]);
  208. });
  209. li.appendChild(a);
  210. dropdownMenu.appendChild(li);
  211. }
  212. typeDiv.appendChild(button);
  213. typeDiv.appendChild(dropdownMenu);
  214. typeDivRow.appendChild(typeDiv);
  215. await buildTypeDepandentDiv(typeDepandentDiv, data);
  216. typeDivRow.appendChild(typeDepandentDiv);
  217. }
  218. grantDiv.className = "dropdown";
  219. if (data.accessToId === 5)
  220. grantDiv.classList.add("hidden");
  221. grantDiv.id = `pch-share-${data.dbId}-grantDropdown`;
  222. let button = document.createElement("button");
  223. button.className = "btn btn-secondary dropdown-toggle bi";
  224. button.type = "button";
  225. button.setAttribute("data-bs-toggle", "dropdown");
  226. button.setAttribute("aria-expanded", false);
  227. button.id = `${grantDiv.id}-button`;
  228. button.textContent = GRANT_TEXT[data.grant];
  229. button.classList.add(GRANT_ICON[data.grant]);
  230. let dropdownMenu = document.createElement("ul");
  231. dropdownMenu.className = "dropdown-menu";
  232. for (let i =1; i <= GRANT_TEXT.length; ++i) {
  233. let li = document.createElement("li");
  234. let a = document.createElement("a");
  235. a.className = "dropdown-item bi";
  236. if (i === data.grant)
  237. a.classList.add("active");
  238. a.href="#";
  239. a.classList.add(GRANT_ICON[i]);
  240. a.textContent = GRANT_TEXT[i];
  241. a.addEventListener("click", async () => {
  242. let dbData = await getData();
  243. let dataIdx = dbData.findIndex(item => item.dbId === data.dbId);
  244. if (dataIdx === -1 || dbData[dataIdx].grant === i)
  245. return;
  246. dbData[dataIdx].grant = i;
  247. button.className = "btn btn-secondary dropdown-toggle bi";
  248. button.textContent = GRANT_TEXT[data.grant];
  249. button.classList.add(GRANT_ICON[data.grant]);
  250. button.setAttribute("aria-expanded", false);
  251. dropdownMenu.classList.remove("show");
  252. dropdownMenu.querySelectorAll(".active").forEach(i => i.classList.remove("active"));
  253. a.classList.add("active");
  254. await updateData(dbData[dataIdx]);
  255. });
  256. li.appendChild(a);
  257. dropdownMenu.appendChild(li);
  258. }
  259. grantDiv.appendChild(button);
  260. grantDiv.appendChild(dropdownMenu);
  261. typeDivRow.appendChild(grantDiv);
  262. let deleteButtonRow = document.createElement("div");
  263. let deleteButtonDiv = document.createElement("div");
  264. let deleteButton = document.createElement("button");
  265. deleteButtonRow.className = "row";
  266. deleteButtonDiv.className = "col align-self-end";
  267. deleteButton.className = "btn btn-danger";
  268. deleteButton.textContent = "Revoke";
  269. deleteButtonRow.appendChild(deleteButtonDiv);
  270. deleteButtonDiv.appendChild(deleteButton);
  271. deleteButton.addEventListener("click", async () => {
  272. revokeData(data.dbId).then(success => {
  273. if (success) {
  274. container.remove();
  275. }
  276. });
  277. });
  278. header.appendChild(headerButton);
  279. container.appendChild(header);
  280. accordionBody.appendChild(typeDivRow);
  281. accordionBody.appendChild(deleteButtonRow);
  282. accordionBodyContainer.appendChild(accordionBody);
  283. container.appendChild(accordionBodyContainer);
  284. return container;
  285. }
  286. async function buildShareItems(data) {
  287. let container = document.createElement("ul");
  288. for (let i of data)
  289. container.appendChild(await buildShareItem(i));
  290. return container;
  291. }
  292. window.showShareUi = async () => {
  293. if (windowDisplayed)
  294. return;
  295. document.getElementById("pch-share-wrapper").classList.remove("hidden");
  296. document.Title.pushTitle("Share");
  297. document.body.classList.add("overlay-visible");
  298. const data = await getData();
  299. document.getElementById('pch-share-loading').classList.add("hidden");
  300. const container = document.getElementById('pch-share-container');
  301. container.innerHTML = "";
  302. container.appendChild(await buildShareItems(data));
  303. windowDisplayed = true;
  304. }
  305. window.closeShareUi = () => {
  306. if (!windowDisplayed)
  307. return;
  308. document.getElementById("pch-share-wrapper").classList.add("hidden");
  309. document.body.classList.remove("overlay-visible");
  310. windowDisplayed = false;
  311. data = null;
  312. document.Title.pop();
  313. }
  314. document.onClosePopinRequested(() => { window.closeShareUi(); });
  315. document.getElementById("pch-share-closeBt").addEventListener("click", window.closeShareUi);
  316. async function createShareData(typeId, typeData) {
  317. let share = new ShareData();
  318. share.typeId = typeId;
  319. share.typeData = typeData;
  320. share.accessToId = 0;
  321. share.grant = 0;
  322. share = await createData(share);
  323. data.push(share);
  324. return share;
  325. }
  326. document.getElementById("pch-share-addLdap").addEventListener("click", () => {});
  327. document.getElementById("pch-share-addEmail").addEventListener("click", () => {});
  328. document.getElementById("pch-share-addLink").addEventListener("click", async () => {
  329. if (!windowDisplayed)
  330. return;
  331. document.getElementById('pch-share-container').querySelector("ul").appendChild(await buildShareItem(await createShareData(3, crypto.randomUUID().replaceAll('-', ''))));
  332. });
  333. document.getElementById("pch-share-addEveryone").addEventListener("click", async () => {
  334. if (!windowDisplayed)
  335. return;
  336. document.getElementById('pch-share-container').querySelector("ul").appendChild(await buildShareItem(await createShareData(4, "")));
  337. });
  338. //setTimeout(() => LoadingTasks.push(window.showShareUi), 1500);
  339. });