Reef.debug(true); // force a re-render app.addSetter("render", (data) => { appComponent.render(); }); app.addSetter("loader.show", (data) => { data.loading++; }); app.addSetter("loader.hide", (data) => { data.loading--; }); app.addSetter("load.boards", async (data) => { store.do("loader.show"); let res = await fetch("/api/boards"); data.boards = await res.json(); data.initialized = true; store.do("loader.hide"); }); // handle update events window.addEventListener("broadcast", async (e) => { let data = store.data; if ( e.detail.updateBoard ){ console.log("updating board"); let boardId = e.detail.updateBoard; store.do("load.boards"); // if we are currently viewing this board, reload the pins if ( data.board && boardId && boardId == data.board.id ){ store.do("load.board", true); } } else if ( e.detail.deleteBoard ) { console.log("deleting board"); let boardId = e.detail.deleteBoard; // reload the boards store.do("load.boards"); // we're currently looking at this board... alert and error if ( data.board && boardId == data.board.id ){ window.alert("this board has been deleted on another device"); window.location.hash = "#"; } } }); app.addSetter('load.board', async (data, force) => { store.do("loader.show"); if ( data.hash.board ){ if ( force || !data.board || data.board.id != data.hash.board ){ let res = await fetch("/api/boards/" + data.hash.board); data.board = await res.json(); } } store.do("loader.hide"); }); app.addSetter('load.user', async (data) => { console.log("load.user"); store.do("loader.show"); let res = await fetch("/api/whoami"); data.user = await res.json(); window.uid = data.user.id; window.csrfToken = data.user.csrf; dispatchSocketConnect(); store.do("loader.hide"); }); app.addSetter("hash.update", (data) => { data.hash = parseQueryString(window.location.hash.substr(1)); if ( data.hash.board ){ store.do('load.board'); } else { data.board = null; data.pinZoomModal.active = false; data.addPinModal.active = false; data.aboutModal.active = false; } }); app.addSetter("app.uploadDroppedFiles", async (data, evt) => { let boardId = store.data.board.id; const supportedTypes = ["image/jpeg","image/png","image/webp"]; if ( boardId ){ let hasFiles = event.dataTransfer.types.find(i => i == "Files") == "Files"; if ( hasFiles ){ if ( evt.dataTransfer.items ){ let files = []; for ( let i = 0; i < evt.dataTransfer.items.length; ++i ){ if ( evt.dataTransfer.items[i].kind === "file" ){ let file = evt.dataTransfer.items[i].getAsFile(); if ( !supportedTypes.includes(file.type)){ window.alert("Unsupported file type. JPEG, PNG, and WebP images are supported."); console.log("Unsupported file type: " + file.type); return; } // check size if ( file.size >= 26214400 ){ window.alert("File size exceeds the 25MB limit."); console.log("File size exceeds the 25MB limit. size=" + file.size); document.getElementById("fileInput").value = ""; return; } files.push(file); } } console.log("Number of files=" + files.length); for ( let i = 0; i < files.length; ++i ){ data.dropUploadMessage = `Uploading ${i+1} of ${files.length}`; try { let newPin = await multipartUpload(files[i], boardId); if ( data.board && data.board.id == boardId ){ data.board.pins.push(newPin); } } catch (e){ window.alert("Error uploading images."); break; } } data.dropUploadMessage = null; } } } }); function PostException(statusCode, errorMessage){ this.statusCode = statusCode; this.errorMessage = errorMessage; } async function multipartUpload(file, boardId, newBoardName, siteUrl, description){ console.log("attempting multipart upload"); let formData = new FormData(); formData.append("file", file); formData.append("boardId", boardId); if ( newBoardName ){ formData.append("newBoardName", newBoardName); } if ( siteUrl ){ formData.append("siteUrl", siteUrl); } if ( description ){ formData.append("description", description); } let res = await fetch("./multiup", { method: "POST", body: formData, headers: { "x-csrf-token": window.csrfToken } }); if ( res.status == 200 ){ return res.json(); } else { console.error("error uploading status=" + res.status + " body=" + await res.text()); throw new PostException(res.status); } } function dispatchSocketConnect(){ window.dispatchEvent(new CustomEvent("socket-connect")); } let store = new Reef.Store({ data: { hash: { board: null }, initialized: false, menuOpen: false, loading: 0, user: null, showHiddenBoards: window.localStorage.showHiddenBoards == "true" || false, boards: [], board: null, addPinModal: { pinId: null, active: false, boardId: "", newBoardName: null, imageUrl: "", previewImageUrl: null, siteUrl: "", description: "", saveInProgress: false, didYouKnowDragAndDropMessageDisabled: window.localStorage.addPinModal_didYouKnowDragAndDropMessageDisabled == "true" || false }, pinZoomModal: { active: false, pin: null, fullDescriptionOpen: false }, aboutModal: { active: false }, editBoardModal: { active: false, name: "", hidden: 0 }, editPinModal: { active: false, pin: null, newBoardName: null, saveInProgress: false } }, getters: app.getGetters(), setters: app.getSetters() }); app.freeze(); // init the app component const appComponent = new Reef("#app", { store: store, template: (data) => { return /*html*/`