mirror of
https://github.com/slynn1324/tinypin.git
synced 2026-07-18 00:44:45 +00:00
almost done...
This commit is contained in:
parent
82c7b447bd
commit
98197c98ea
7 changed files with 264 additions and 26 deletions
29
server.js
29
server.js
|
|
@ -9,6 +9,7 @@ const fetch = require('node-fetch');
|
|||
|
||||
const IMAGE_PATH = "./images";
|
||||
|
||||
|
||||
// express config
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
|
|
@ -18,6 +19,14 @@ app.use(bodyParser.urlencoded({ extended: false }))
|
|||
app.use(bodyParser.json());
|
||||
app.set('json spaces', 2);
|
||||
|
||||
// emulate slow down
|
||||
app.use( (req,res,next) => {
|
||||
console.log("slow...");
|
||||
setTimeout(() => {
|
||||
next();
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
const OK = {status: "ok"};
|
||||
const NOT_FOUND = {status: "error", error: "not found"};
|
||||
const ALREADY_EXISTS = {status: "error", error: "already exists"};
|
||||
|
|
@ -28,7 +37,6 @@ initDb();
|
|||
// list boards
|
||||
app.get("/api/boards", async (req, res) => {
|
||||
try{
|
||||
await sleep(1000);
|
||||
let boards = db.prepare("SELECT * FROM boards").all();
|
||||
|
||||
for( let i = 0; i < boards.length; ++i ){
|
||||
|
|
@ -50,7 +58,8 @@ app.get("/api/boards", async (req, res) => {
|
|||
// get board
|
||||
app.get("/api/boards/:boardId", async (req, res) => {
|
||||
try{
|
||||
await sleep(1000);
|
||||
|
||||
|
||||
let board = db.prepare("SELECT * FROM boards WHERE id = ?").get(req.params.boardId);
|
||||
if ( board ){
|
||||
|
||||
|
|
@ -103,8 +112,7 @@ app.post("/api/boards/:boardId", (req, res) =>{
|
|||
|
||||
// delete board
|
||||
app.delete("/api/boards/:boardId", async (req, res) => {
|
||||
try{
|
||||
await sleep(1000);
|
||||
try{
|
||||
|
||||
let pins = db.prepare("SELECT id FROM pins WHERE boardId = ?").all(req.params.boardId);
|
||||
for ( let i = 0; i < pins.length; ++i ){
|
||||
|
|
@ -141,20 +149,10 @@ app.get("/api/pins/:pinId", (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
async function sleep(millis){
|
||||
return new Promise( (resolve,reject) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, millis)
|
||||
});
|
||||
}
|
||||
|
||||
// create pin
|
||||
app.post("/api/pins", async (req, res) => {
|
||||
try {
|
||||
|
||||
await sleep(1000);
|
||||
|
||||
console.log(req.body);
|
||||
|
||||
let image = await downloadImage(req.body.imageUrl);
|
||||
|
|
@ -248,9 +246,6 @@ app.post("/api/pins/:pinId", (req,res) => {
|
|||
});
|
||||
|
||||
app.delete("/api/pins/:pinId", async (req, res) => {
|
||||
|
||||
await sleep(1000);
|
||||
|
||||
try {
|
||||
|
||||
await fs.unlink(getThumbnailImagePath(req.params.pinId).file);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ app.addSetter("load.boards", async (data) => {
|
|||
|
||||
let res = await fetch("/api/boards");
|
||||
data.boards = await res.json();
|
||||
|
||||
data.initialized = true;
|
||||
|
||||
store.do("loader.hide");
|
||||
});
|
||||
|
|
@ -54,10 +56,12 @@ let store = new Reef.Store({
|
|||
hash: {
|
||||
board: null
|
||||
},
|
||||
initialized: false,
|
||||
loading: 0,
|
||||
boards: [],
|
||||
board: null,
|
||||
addPinModal: {
|
||||
pinId: null,
|
||||
active: false,
|
||||
boardId: "",
|
||||
newBoardName: null,
|
||||
|
|
@ -78,6 +82,12 @@ let store = new Reef.Store({
|
|||
editBoardModal: {
|
||||
active: false,
|
||||
name: ""
|
||||
},
|
||||
editPinModal: {
|
||||
active: false,
|
||||
pin: null,
|
||||
newBoardName: null,
|
||||
saveInProgress: false
|
||||
}
|
||||
},
|
||||
getters: app.getGetters(),
|
||||
|
|
@ -108,6 +118,7 @@ const appComponent = new Reef("#app", {
|
|||
<div id="pinZoomModal"></div>
|
||||
<div id="editBoardModal"></div>
|
||||
<div id="aboutModal"></div>
|
||||
<div id="editPinModal"></div>
|
||||
`
|
||||
//<div id="loader" class="button is-text ${data.loading ? 'is-loading' : ''}"></div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,33 +64,34 @@
|
|||
object-fit: contain;
|
||||
}
|
||||
|
||||
#addPinModal-boardName {
|
||||
#addPinModal-boardName, #editPinModal-boardName {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#addPinModal .add-pin-flex {
|
||||
#addPinModal .add-pin-flex, #editPinModal .add-pin-flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#addPinModal .add-pin-flex-left {
|
||||
#addPinModal .add-pin-flex-left, #editPinModal .add-pin-flex-left {
|
||||
flex: 1;
|
||||
|
||||
margin: 10px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
#addPinModal .add-pin-flex-left img{
|
||||
#addPinModal .add-pin-flex-left img, #editPinModal .add-pin-flex-left img{
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#addPinModal .add-pin-flex-right {
|
||||
#addPinModal .add-pin-flex-right, #editPinModal .add-pin-flex-right {
|
||||
flex: 2;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
|
||||
#footer {
|
||||
padding: 1rem 1.5rem 1rem;
|
||||
}
|
||||
|
|
@ -156,8 +157,6 @@
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pinZoomModal .pinZoomModal-description {
|
||||
position: fixed;
|
||||
height: 24px;
|
||||
|
|
|
|||
|
|
@ -4,10 +4,27 @@ app.addComponent('brickwall', (store) => { return new Reef('#brickwall', {
|
|||
template: (data, el) => {
|
||||
|
||||
// if the hash says we are supposed to be drawing a board, but it hasn't loaded yet... draw an empty div.
|
||||
if ( data.hash.board && !data.board ){
|
||||
if ( !data.initialized || (data.hash.board && !data.board) ){
|
||||
return '<div></div>';
|
||||
}
|
||||
|
||||
if ( data.hash.board && data.board.pins.length == 0 ){
|
||||
return `<div class="has-text-centered is-flex-grow-1">
|
||||
<h1>This board has no pins :(</h1>
|
||||
<div class="mt-4">
|
||||
<a data-onclick="addPinModal.open">Pin Something</a>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
} else if ( !data.hash.board && data.boards.length == 0 ){
|
||||
return `<div class="has-text-centered is-flex-grow-1">
|
||||
<h1>There are no boards :(</h1>
|
||||
<div class="mt-4">
|
||||
<a data-onclick="addPinModal.open">Pin Something</a>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
let numberOfColumns = 1;
|
||||
let width = el.offsetWidth;
|
||||
// matching bulma breakpoints - https://bulma.io/documentation/overview/responsiveness/
|
||||
|
|
|
|||
203
static/components/editpin.js
Normal file
203
static/components/editpin.js
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
app.addSetter('editPinModal.open', (data) => {
|
||||
data.editPinModal.active = true;
|
||||
});
|
||||
|
||||
app.addSetter('editPinModal.close', (data) => {
|
||||
data.editPinModal.active = false;
|
||||
|
||||
data.editPinModal.pin = null;
|
||||
data.editPinModal.newBoardName = null;
|
||||
data.editPinModal.saveInProgress = false;
|
||||
});
|
||||
|
||||
app.addGetter('editPinModal.isValid', (data) => {
|
||||
|
||||
if ( !data.editPinModal.pin ){
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( data.editPinModal.boardId == "new"){
|
||||
if ( !data.editPinModal.newBoardName ){
|
||||
return false;
|
||||
} else if ( data.editPinModal.newBoardName.trim().length < 1 ){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let pin = getPinById(data.editPinModal.pin.id);
|
||||
|
||||
if ( pin.siteUrl == data.editPinModal.pin.siteUrl &&
|
||||
pin.description == data.editPinModal.pin.description &&
|
||||
pin.boardId == data.editPinModal.pin.boardId ){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
app.addSetter("editPinModal.save", async (data) => {
|
||||
|
||||
store.do("loader.show");
|
||||
|
||||
data.editPinModal.saveInProgress = true;
|
||||
|
||||
let pin = data.editPinModal.pin;
|
||||
|
||||
let boardId = data.editPinModal.pin.boardId;
|
||||
|
||||
let newBoard = null;
|
||||
|
||||
if ( boardId == "new" ){
|
||||
|
||||
// TODO: make a helper method
|
||||
let res = await fetch("/api/boards", {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
"name": data.editPinModal.newBoardName
|
||||
})
|
||||
});
|
||||
|
||||
if ( res.status == 200 ){
|
||||
newBoard = await res.json();
|
||||
boardId = newBoard.id;
|
||||
data.boards.push(newBoard);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let postData = {
|
||||
boardId: boardId,
|
||||
siteUrl: pin.siteUrl,
|
||||
description: pin.description
|
||||
};
|
||||
|
||||
let res = await fetch('api/pins/' + pin.id, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(postData)
|
||||
});
|
||||
|
||||
if ( res.status == 200 ){
|
||||
console.log(`updated pin#${pin.id}`);
|
||||
|
||||
// update the local copy of the pin
|
||||
let idx = getPinIndexById(pin.id);
|
||||
data.board.pins[idx].boardId = boardId;
|
||||
data.board.pins[idx].siteUrl = pin.siteUrl;
|
||||
data.board.pins[idx].description = pin.description;
|
||||
|
||||
console.log(data.board.pins[idx]);
|
||||
|
||||
store.do("editPinModal.close");
|
||||
} else {
|
||||
console.log(`error updating pin#${pin.id}`);
|
||||
}
|
||||
|
||||
store.do("loader.hide");
|
||||
|
||||
});
|
||||
|
||||
|
||||
app.addComponent('editPinModal', (store) => { return new Reef("#editPinModal", {
|
||||
|
||||
store: store,
|
||||
template: (data) => {
|
||||
|
||||
let options = "";
|
||||
for ( let i = 0; i < data.boards.length; ++i ){
|
||||
options += `<option value="${data.boards[i].id}">${data.boards[i].name}</option>`;
|
||||
}
|
||||
|
||||
console.log("render", data);
|
||||
|
||||
console.log("set new board field");
|
||||
let newBoardField = '';
|
||||
if ( data.editPinModal.pin && data.editPinModal.pin.boardId == "new" ){
|
||||
// if ( true ) {
|
||||
console.log("is new");
|
||||
newBoardField = /*html*/`
|
||||
<div class="field">
|
||||
<label class="label">Board Name</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" data-bind="editPinModal.newBoardName" />
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if ( data.editPinModal.pin ){
|
||||
console.log(getThumbnailImagePath(data.editPinModal.pin.id));
|
||||
}
|
||||
|
||||
|
||||
|
||||
return /*html*/`
|
||||
<div class="modal ${data.editPinModal.active ? 'is-active' : ''}">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">Edit Pin</p>
|
||||
<button class="delete" aria-label="close" data-onclick="editPinModal.close"></button>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<div class="add-pin-flex">
|
||||
|
||||
<div class="add-pin-flex-left">
|
||||
<img src="${data.editPinModal.pin ? getThumbnailImagePath(data.editPinModal.pin.id) : ''}" />
|
||||
</div>
|
||||
|
||||
<div class="add-pin-flex-right">
|
||||
|
||||
<form>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Board</label>
|
||||
<div class="select">
|
||||
<select data-bind="editPinModal.pin.boardId">
|
||||
${options}
|
||||
<option value="new">Create New Board</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${newBoardField}
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Image Url (read-only)</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" data-bind="editPinModal.pin.imageUrl" readonly />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Website Url</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" data-bind="editPinModal.pin.siteUrl" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Description</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea" data-bind="editPinModal.pin.description"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="modal-card-foot">
|
||||
<button class="button is-success ${data.editPinModal.saveInProgress ? 'is-loading' : ''}" ${!store.get('editPinModal.isValid') || data.editPinModal.saveInProgress ? 'disabled' : ''} data-onclick="editPinModal.save">Save Pin</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
}); });
|
||||
|
|
@ -32,7 +32,9 @@ app.addSetter('pinZoomModal.open', (data, el) => {
|
|||
let pinId = el.getAttribute("data-pinid");
|
||||
|
||||
if( pinId ){
|
||||
data.pinZoomModal.pin = getPinById(pinId);
|
||||
let idx = getPinIndexById(pinId);
|
||||
|
||||
data.pinZoomModal.pin = data.board.pins[idx];
|
||||
data.pinZoomModal.active = true;
|
||||
}
|
||||
|
||||
|
|
@ -101,6 +103,16 @@ app.addSetter('pinZoomModal.deletePin', async (data) => {
|
|||
store.do('loader.hide');
|
||||
});
|
||||
|
||||
app.addSetter('pinZoomModal.editPin', (data) => {
|
||||
|
||||
// intentially read from store so we get an immutable copy
|
||||
data.editPinModal.pin = store.data.pinZoomModal.pin;
|
||||
|
||||
console.log(data.editPinModal.pin);
|
||||
store.do('editPinModal.open');
|
||||
|
||||
});
|
||||
|
||||
app.addComponent('pinZoomModal', (store) => { return new Reef("#pinZoomModal", {
|
||||
store: store,
|
||||
template: (data) => {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
<script src="components/pinzoom.js"></script>
|
||||
<script src="components/addpin.js"></script>
|
||||
<script src="components/editboard.js"></script>
|
||||
<script src="components/editpin.js"></script>
|
||||
<script src="components/about.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue