mirror of
https://github.com/slynn1324/tinypin.git
synced 2026-01-23 02:25:08 +00:00
added web socket support
This commit is contained in:
parent
8cdf21ed55
commit
b23208c0bf
12 changed files with 324 additions and 38 deletions
49
package-lock.json
generated
49
package-lock.json
generated
|
|
@ -12,6 +12,7 @@
|
|||
"body-parser": "^1.19.0",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"express": "^4.17.1",
|
||||
"express-ws": "^4.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"sharp": "^0.27.0",
|
||||
"yargs": "^16.2.0"
|
||||
|
|
@ -86,6 +87,11 @@
|
|||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz",
|
||||
"integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA=="
|
||||
},
|
||||
"node_modules/async-limiter": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
||||
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
|
|
@ -629,6 +635,20 @@
|
|||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express-ws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/express-ws/-/express-ws-4.0.0.tgz",
|
||||
"integrity": "sha512-KEyUw8AwRET2iFjFsI1EJQrJ/fHeGiJtgpYgEWG3yDv4l/To/m3a2GaYfeGyB3lsWdvbesjF5XCMx+SVBgAAYw==",
|
||||
"dependencies": {
|
||||
"ws": "^5.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express": "^4.0.0 || ^5.0.0-alpha.1"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
|
|
@ -1522,6 +1542,14 @@
|
|||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
|
||||
"integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
|
||||
"dependencies": {
|
||||
"async-limiter": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz",
|
||||
|
|
@ -1658,6 +1686,11 @@
|
|||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz",
|
||||
"integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA=="
|
||||
},
|
||||
"async-limiter": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
||||
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
|
|
@ -2080,6 +2113,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"express-ws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/express-ws/-/express-ws-4.0.0.tgz",
|
||||
"integrity": "sha512-KEyUw8AwRET2iFjFsI1EJQrJ/fHeGiJtgpYgEWG3yDv4l/To/m3a2GaYfeGyB3lsWdvbesjF5XCMx+SVBgAAYw==",
|
||||
"requires": {
|
||||
"ws": "^5.2.0"
|
||||
}
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
|
|
@ -2760,6 +2801,14 @@
|
|||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"ws": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
|
||||
"integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
"body-parser": "^1.19.0",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"express": "^4.17.1",
|
||||
"express-ws": "^4.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"sharp": "^0.27.0",
|
||||
"yargs": "^16.2.0"
|
||||
|
|
|
|||
52
server.js
52
server.js
|
|
@ -86,12 +86,12 @@ const COOKIE_KEY = Buffer.from(db.prepare("SELECT value FROM properties WHERE ke
|
|||
|
||||
// express config
|
||||
const app = express();
|
||||
app.use(express.static('public'));
|
||||
const expressWs = require('express-ws')(app);
|
||||
|
||||
app.use(express.static('public'));
|
||||
app.use(bodyParser.raw({type: 'image/jpeg', limit: '25mb'}));
|
||||
app.use(bodyParser.urlencoded({ extended: false }))
|
||||
app.use(bodyParser.json());
|
||||
|
||||
app.set('json spaces', 2);
|
||||
app.use(cookieParser());
|
||||
|
||||
|
|
@ -126,6 +126,20 @@ function decryptCookie(ciphertext){
|
|||
return JSON.parse(deciphered);
|
||||
}
|
||||
|
||||
// broadcast isn't user specific right now...
|
||||
app.ws('/ws/:uid', (ws, req) => {
|
||||
ws.on("message", (msg) => {
|
||||
//console.log("received messsage: " + msg);
|
||||
});
|
||||
console.log("socket opened for user " + req.params.uid);
|
||||
});
|
||||
|
||||
function broadcast(uid, msg){
|
||||
for ( let socket of expressWs.getWss('/ws/' + uid).clients ){
|
||||
socket.send(JSON.stringify(msg));
|
||||
}
|
||||
}
|
||||
|
||||
// handle auth
|
||||
app.use ( async (req, res, next) => {
|
||||
|
||||
|
|
@ -283,7 +297,7 @@ const SERVER_ERROR = {status: "error", error: "server error"};
|
|||
|
||||
|
||||
app.get("/api/whoami", (req, res) => {
|
||||
res.send({name: req.user.name, version: VERSION});
|
||||
res.send({name: req.user.name, version: VERSION, id: req.user.id});
|
||||
});
|
||||
|
||||
// list boards
|
||||
|
|
@ -335,7 +349,8 @@ app.post('/api/boards', (req, res) => {
|
|||
board.titlePinId = 0;
|
||||
res.send(board);
|
||||
console.log(`Created board#${id} ${req.body.name}`);
|
||||
|
||||
|
||||
broadcast(req.user.id, {b:id});
|
||||
} catch (err){
|
||||
console.log("Error creating board: " + err.message);
|
||||
if ( err.message.includes('UNIQUE constraint failed:') ){
|
||||
|
|
@ -383,6 +398,7 @@ app.delete("/api/boards/:boardId", async (req, res) => {
|
|||
|
||||
if ( result.changes == 1 ){
|
||||
res.send(OK);
|
||||
broadcast(req.user.id, {deleteBoard: req.params.boardId});
|
||||
} else {
|
||||
res.status(404).send(NOT_FOUND);
|
||||
}
|
||||
|
|
@ -411,6 +427,18 @@ app.get("/api/pins/:pinId", (req, res) => {
|
|||
app.post("/api/pins", async (req, res) => {
|
||||
try {
|
||||
|
||||
let boardId = req.body.boardId;
|
||||
|
||||
if ( boardId == "new" ){
|
||||
try {
|
||||
let result = db.prepare("INSERT INTO boards (name, userId, hidden, createDate) VALUES (@name, @userId, @hidden, @createDate)").run({name: req.body.newBoardName, userId: req.user.id, hidden: 0, createDate: new Date().toISOString()});
|
||||
boardId = result.lastInsertRowid;
|
||||
} catch (e){
|
||||
console.log("error creating new board: ", err);
|
||||
res.status(500).send(SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
// download the image first to make sure we can get it
|
||||
let image = await downloadImage(req.body.imageUrl);
|
||||
|
||||
|
|
@ -439,7 +467,7 @@ app.post("/api/pins", async (req, res) => {
|
|||
@userId,
|
||||
@createDate)
|
||||
`).run({
|
||||
boardId: req.body.boardId,
|
||||
boardId: boardId,
|
||||
imageUrl: req.body.imageUrl,
|
||||
siteUrl: req.body.siteUrl,
|
||||
description: req.body.description,
|
||||
|
|
@ -460,12 +488,15 @@ app.post("/api/pins", async (req, res) => {
|
|||
let pin = db.prepare("SELECT * FROM pins WHERE userId = @userId and id = @pinId").get({userId: req.user.id, pinId: id});
|
||||
res.send(pin);
|
||||
|
||||
broadcast(req.user.id, {updateBoard:boardId});
|
||||
|
||||
} catch (err) {
|
||||
console.log(`Error creating pin: ${err.message}`, err);
|
||||
res.status(500).send(SERVER_ERROR);
|
||||
}
|
||||
});
|
||||
|
||||
// create pin
|
||||
app.post("/api/pins/:pinId", (req,res) => {
|
||||
|
||||
try {
|
||||
|
|
@ -487,6 +518,7 @@ app.post("/api/pins/:pinId", (req,res) => {
|
|||
if ( result.changes == 1 ){
|
||||
console.log(`updated pin#${req.params.pinId}`)
|
||||
res.send(OK);
|
||||
broadcast(req.user.id, {updateBoard:req.body.boardId});
|
||||
} else {
|
||||
res.status(404).send(NOT_FOUND);
|
||||
}
|
||||
|
|
@ -497,9 +529,12 @@ app.post("/api/pins/:pinId", (req,res) => {
|
|||
|
||||
});
|
||||
|
||||
// delete pin
|
||||
app.delete("/api/pins/:pinId", async (req, res) => {
|
||||
try {
|
||||
|
||||
let pin = db.prepare('SELECT * FROM pins WHERE userId = @userId and id = @pinId').get({userId: req.user.id, pinId:req.params.pinId});
|
||||
|
||||
let result = db.prepare('DELETE FROM pins WHERE userId = @userId and id = @pinId').run({userId: req.user.id, pinId:req.params.pinId});
|
||||
|
||||
if ( result.changes == 1 ){
|
||||
|
|
@ -512,6 +547,9 @@ app.delete("/api/pins/:pinId", async (req, res) => {
|
|||
|
||||
console.log(`deleted pin#${req.params.pinId}`);
|
||||
res.send(OK);
|
||||
|
||||
broadcast(req.user.id, {updateBoard:pin.boardId});
|
||||
|
||||
} else {
|
||||
res.status(404).send(NOT_FOUND);
|
||||
}
|
||||
|
|
@ -592,6 +630,10 @@ app.post("/up/", async (req, res) => {
|
|||
return;
|
||||
});
|
||||
|
||||
app.get("/otl/:l", (req, res) => {
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
// start listening
|
||||
|
|
|
|||
|
|
@ -25,13 +25,56 @@ app.addSetter("load.boards", async (data) => {
|
|||
store.do("loader.hide");
|
||||
});
|
||||
|
||||
app.addSetter('load.board', async (data) => {
|
||||
// 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;
|
||||
|
||||
let boardExists = false;
|
||||
for ( let i = 0; i < data.boards.length; ++i ){
|
||||
if ( data.boards[i].id == boardId ){
|
||||
boardExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if it's a new board
|
||||
if ( !boardExists ){
|
||||
store.do("load.boards");
|
||||
}
|
||||
|
||||
// if we are currently viewing this board, reload the pins
|
||||
if ( data.board && 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.board || data.board.id != data.hash.board ){
|
||||
let res = await fetch("/api/boards/" + data.hash.board);
|
||||
data.board = await res.json();
|
||||
}
|
||||
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");
|
||||
});
|
||||
|
|
@ -42,6 +85,9 @@ app.addSetter('load.user', async (data) => {
|
|||
let res = await fetch("/api/whoami");
|
||||
data.user = await res.json();
|
||||
|
||||
window.uid = data.user.id;
|
||||
window.socketConnect();
|
||||
|
||||
store.do("loader.hide");
|
||||
});
|
||||
|
||||
|
|
@ -225,4 +271,12 @@ store.do('load.user');
|
|||
store.do('load.boards');
|
||||
store.do('hash.update');
|
||||
|
||||
appComponent.render();
|
||||
appComponent.render();
|
||||
|
||||
|
||||
// refresh on focus
|
||||
window.addEventListener("focus", () => {
|
||||
store.do("load.boards");
|
||||
store.do("load.board");
|
||||
window.dispatchEvent(new CustomEvent("socket-connect"));
|
||||
});
|
||||
|
|
@ -286,6 +286,21 @@
|
|||
margin-top: -2px;
|
||||
}
|
||||
|
||||
/* https://thenounproject.com/search/?q=connected&i=88200 */
|
||||
#socketConnected {
|
||||
background-image: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9JzMwMHB4JyB3aWR0aD0nMzAwcHgnICBmaWxsPSIjMWExYTFhIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTAwIDEwMCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTAwIDEwMCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTkwLjMsNDguMUM5MC41LDYxLjksNzkuNCw3My41LDY1LjYsNzRjLTMuNywwLjEtNy4yLTAuNi0xMC40LTEuOWMtMS42LTAuNy0yLjMtMi42LTEuNS00LjJsMC0wLjFjMC43LTEuMywyLjMtMS45LDMuNi0xLjMgIGMzLDEuMyw2LjQsMS44LDEwLDEuM2M4LjktMS4yLDE1LjktOC41LDE2LjgtMTcuNGMxLjItMTIuMi05LTIyLjUtMjEuMy0yMS4zQzU0LDI5LjksNDYuNiwzNyw0NS41LDQ2Yy0wLjMsMi41LTAuMiw0LjksMC40LDcuMSAgYzAuMywxLjEtMC4xLDIuMi0wLjksMi45YzAsMCwwLDAsMCwwYy0xLjcsMS41LTQuNCwwLjctNC45LTEuNWMtMC44LTMuMy0xLTYuOS0wLjMtMTAuNmMyLTEwLjksMTEtMTkuNCwyMi0yMC43ICBDNzcsMjEuNCw5MC4xLDMzLjIsOTAuMyw0OC4xeiBNMzYuOSw3NC44YzExLTEuMywyMC05LjgsMjItMjAuN2MwLjctMy43LDAuNS03LjMtMC4zLTEwLjdjLTAuNS0yLjItMy4yLTMuMS00LjktMS41bDAsMCAgYy0wLjgsMC43LTEuMSwxLjgtMC45LDIuOWMwLjYsMi4zLDAuNyw0LjcsMC40LDcuMmMtMS4yLDguOS04LjUsMTYtMTcuNSwxNi45QzIzLjQsNzAsMTMuMiw1OS44LDE0LjQsNDcuNiAgYzAuOS04LjksNy45LTE2LjIsMTYuOC0xNy40YzMuNi0wLjUsNywwLjEsMTAsMS4zYzEuNCwwLjYsMi45LDAsMy42LTEuM2wwLTAuMWMwLjgtMS42LDAuMi0zLjUtMS41LTQuMmMtMy43LTEuNS03LjktMi4yLTEyLjMtMS43ICBDMTksMjUuNCw5LjQsMzUuMyw4LjQsNDcuNEM3LjEsNjMuNCwyMC43LDc2LjcsMzYuOSw3NC44eiI+PC9wYXRoPjwvc3ZnPg==");
|
||||
background-size: 24px 24px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 6px 8px;
|
||||
opacity: 0.1;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
body.socketConnected #socketConnected {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
.modal-card {
|
||||
max-width: 95%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,26 +48,9 @@ app.addSetter('addPinModal.save', async (data) => {
|
|||
|
||||
let boardId = data.addPinModal.boardId;
|
||||
|
||||
let newBoard = null;
|
||||
|
||||
if ( boardId == "new" ){
|
||||
let res = await fetch('api/boards', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
"name": data.addPinModal.newBoardName
|
||||
})
|
||||
});
|
||||
|
||||
if ( res.status == 200 ){
|
||||
newBoard = await res.json();
|
||||
boardId = newBoard.id;
|
||||
data.boards.push(newBoard);
|
||||
}
|
||||
}
|
||||
|
||||
let postData = {
|
||||
boardId: boardId,
|
||||
newBoardName: data.addPinModal.newBoardName,
|
||||
imageUrl: data.addPinModal.imageUrl,
|
||||
siteUrl: data.addPinModal.siteUrl,
|
||||
description: data.addPinModal.description
|
||||
|
|
@ -80,20 +63,21 @@ app.addSetter('addPinModal.save', async (data) => {
|
|||
},
|
||||
body: JSON.stringify(postData)
|
||||
});
|
||||
|
||||
|
||||
if ( res.status == 200 ){
|
||||
|
||||
let body = await res.json();
|
||||
if ( data.board && data.board.id == boardId ){
|
||||
data.board.pins.push(body);
|
||||
}
|
||||
|
||||
if ( newBoard ){
|
||||
newBoard.titlePinId = body.id;
|
||||
}
|
||||
|
||||
window.localStorage.addPinLastBoardId = boardId;
|
||||
store.do("addPinModal.close");
|
||||
|
||||
// if we don't have a listening socket, we need to trigger our own update
|
||||
if ( boardId == "new" && !window.socketConnected ){
|
||||
store.do("load.boards");
|
||||
}
|
||||
}
|
||||
|
||||
store.do("loader.hide");
|
||||
|
|
|
|||
|
|
@ -76,7 +76,9 @@ app.addComponent('navbar', (store) => { return new Reef("#navbar", {
|
|||
${boardName}
|
||||
<span id="loader-mobile" class="navbar-item" style="position: relative; margin-left: auto;">
|
||||
<div id="loader" class="button is-text ${data.loading ? 'is-loading' : ''}"></div>
|
||||
<div id="socketConnected" class="button is-text"></div>
|
||||
</span>
|
||||
|
||||
|
||||
<a role="button" class="navbar-burger ${data.menuOpen ? 'is-active' : ''}" aria-label="menu" aria-expanded="false" data-onclick="navbar.toggleMenu">
|
||||
<span aria-hidden="true"></span>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<link rel="apple-touch-icon" sizes="180x180" href="pub/icons/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="pub/icons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="pub/icons/favicon-16x16.png">
|
||||
<link rel="manifest" href="pub/icons/site.webmanifest">
|
||||
<!--<link rel="manifest" href="pub/icons/site.webmanifest"> not working -->
|
||||
<link rel="mask-icon" href="pub/icons/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<link rel="shortcut icon" href="pub/icons/favicon.ico">
|
||||
<meta name="msapplication-TileColor" content="#da532c">
|
||||
|
|
@ -46,6 +46,7 @@
|
|||
<script src="components/editpin.js"></script>
|
||||
<script src="components/about.js"></script>
|
||||
<script src="app.js"></script>
|
||||
<script src="ws.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<square150x150logo src="/pub/icons/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"src": "/pub/icons/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"src": "/pub/icons/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
|
|
|
|||
51
static/ws.html
Normal file
51
static/ws.html
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
ws test
|
||||
<button onclick="sendMessage()">send</button>
|
||||
<script>
|
||||
|
||||
let socket = new WebSocket(getSocketUrl());
|
||||
|
||||
socket.onopen = (e) => {
|
||||
console.log("[open] connection establised");
|
||||
console.log("sending to server");
|
||||
socket.send("hello");
|
||||
};
|
||||
|
||||
socket.onmessage = (e) => {
|
||||
console.log("got message: " + e.data);
|
||||
}
|
||||
|
||||
socket.onclose = (e) => {
|
||||
console.log("socket closed - wasClean=" + e.wasClean + " code=" + e.code + " reason=" + e.reason);
|
||||
}
|
||||
|
||||
socket.onerror = (e) => {
|
||||
console.log("error: " + e.message);
|
||||
}
|
||||
|
||||
function sendMessage(){
|
||||
socket.send("hello");
|
||||
}
|
||||
|
||||
|
||||
function getSocketUrl(){
|
||||
var loc = window.location, new_uri;
|
||||
if (loc.protocol === "https:") {
|
||||
new_uri = "wss:";
|
||||
} else {
|
||||
new_uri = "ws:";
|
||||
}
|
||||
new_uri += "//" + loc.host;
|
||||
// if ( loc.port ){
|
||||
// new_uri += ":" + loc.port
|
||||
// }
|
||||
new_uri += "/ws";
|
||||
return new_uri;
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
87
static/ws.js
Normal file
87
static/ws.js
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// decorate on web socket functions. if web sockets fail to work, the app should keep working anyway.
|
||||
// if you want to permanently disable websockets, just remove the src include
|
||||
|
||||
window.socketConnected = false;
|
||||
window.socket = null;
|
||||
|
||||
// keep-alive
|
||||
setInterval(() => {
|
||||
if ( window.socket && window.socket.readyState != WebSocket.OPEN ){
|
||||
console.log("web socket reconnect");
|
||||
window.socket = socketConnect();
|
||||
} else {
|
||||
window.socket.send(".");
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
window.addEventListener("socket-connect", () => {
|
||||
socketConnect();
|
||||
});
|
||||
|
||||
function socketConnect(){
|
||||
|
||||
if ( !window.uid ){
|
||||
console.log("no user id, can't open a socket");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( window.socketConnected ){
|
||||
console.log("web socket already connected");
|
||||
return;
|
||||
}
|
||||
|
||||
window.socketConnected = false;
|
||||
|
||||
let s = new WebSocket(getSocketUrl());
|
||||
|
||||
s.onopen = (e) => {
|
||||
console.log("web socket connected");
|
||||
|
||||
// wait 10ms to see if the socket stays connected
|
||||
setTimeout( () => {
|
||||
if ( s.readyState == WebSocket.OPEN ){
|
||||
console.log("web socket appears operational");
|
||||
document.body.classList.add("socketConnected");
|
||||
window.socketConnected = true;
|
||||
window.socketConnectFailureCount = 0;
|
||||
|
||||
store.do("load.boards");
|
||||
store.do("load.board");
|
||||
} else {
|
||||
console.log("web socket connect failed");
|
||||
}
|
||||
}, 10);
|
||||
};
|
||||
|
||||
s.onmessage = (e) => {
|
||||
console.log("web socket message: " + e.data);
|
||||
let msg = JSON.parse(e.data);
|
||||
window.dispatchEvent(new CustomEvent("broadcast", {detail: msg}));
|
||||
}
|
||||
|
||||
s.onclose = (e) => {
|
||||
console.log("web socket closed");
|
||||
document.body.classList.remove("socketConnected");
|
||||
window.socketConnected = false;
|
||||
}
|
||||
|
||||
s.onerror = (e) => {
|
||||
console.log("web socket error: " + e.message);
|
||||
}
|
||||
|
||||
window.socket = s;
|
||||
}
|
||||
|
||||
|
||||
function getSocketUrl(){
|
||||
var loc = window.location, new_uri;
|
||||
if (loc.protocol === "https:") {
|
||||
new_uri = "wss:";
|
||||
} else {
|
||||
new_uri = "ws:";
|
||||
}
|
||||
new_uri += "//" + loc.host;
|
||||
new_uri += "/ws/" + window.uid;
|
||||
return new_uri;
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue