Set up WebSocket for email sending
The actual sending doesn't happen yet, but all the frontend infrastructure is set up all right!
This commit is contained in:
parent
66056cbf0b
commit
9b630ae021
4 changed files with 220 additions and 75 deletions
|
@ -21,6 +21,7 @@
|
|||
"cors": "^2.8.5",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"express-ws": "^4.0.0",
|
||||
"importabular": "^0.2.9",
|
||||
"showdown": "^1.9.1",
|
||||
"sirv-cli": "^1.0.0"
|
||||
|
|
37
server.js
37
server.js
|
@ -3,6 +3,8 @@ const express = require('express')
|
|||
require('dotenv').config()
|
||||
|
||||
const app = express()
|
||||
var expressWs = require('express-ws')(app)
|
||||
|
||||
const port = process.env.port || 5000
|
||||
|
||||
const cors = require('cors')
|
||||
|
@ -18,10 +20,43 @@ app.get('/air', (req, res) => {
|
|||
})
|
||||
})
|
||||
|
||||
app.ws('/hit-send', (ws, request) => {
|
||||
console.log('Hitting send on some emails!')
|
||||
|
||||
ws.on('message', (message) => {
|
||||
try {
|
||||
message = JSON.parse(message)
|
||||
} catch(err) {
|
||||
ws.send(JSON.stringify({
|
||||
success: false,
|
||||
error: "We can't understand what you're saying! Me speak JSON only.",
|
||||
}))
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`\n\n-------------------------- BEGIN EMAIL --------------------------\n`)
|
||||
console.log(`From: ${message.from}\nTo:${message.to}\n\n${message.text}`)
|
||||
console.log(`\n--------------------------- END EMAIL ---------------------------`)
|
||||
|
||||
setTimeout(() => {
|
||||
ws.send(JSON.stringify({
|
||||
success: true,
|
||||
from: message.from,
|
||||
to: message.to,
|
||||
}))
|
||||
}, 2000)
|
||||
})
|
||||
|
||||
ws.on('close', () => {
|
||||
console.log('socket closed')
|
||||
})
|
||||
})
|
||||
|
||||
app.use(express.static('public'))
|
||||
app.get('*', (req, res) => {
|
||||
app.get('/', (req, res) => {
|
||||
res.sendFile(path.resolve(__dirname, 'public', 'index.html'))
|
||||
})
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server is up at port ${port}`)
|
||||
})
|
||||
|
|
238
src/App.svelte
238
src/App.svelte
|
@ -195,88 +195,178 @@
|
|||
function prev() {
|
||||
step -= 1
|
||||
}
|
||||
|
||||
let nowSending
|
||||
|
||||
/**
|
||||
* Hit Send
|
||||
*
|
||||
* collates all the data for sending, and passes it
|
||||
* on to the backend for actual processing. Note that here we
|
||||
* cookie-cut the emails on the frontend itself; the backend
|
||||
* just blindly takes what it gets. We're doing this so that we
|
||||
* can use the same getPreview() function and be consistent: we
|
||||
* don't want the final to suddenly end up looking very different
|
||||
* from the preview!
|
||||
*/
|
||||
function hitSend() {
|
||||
step = 4
|
||||
let emails = []
|
||||
|
||||
// process the 'i'th row of the sheet, one by one
|
||||
for (let i=0; i<sheetOne.getData().length; i++) {
|
||||
let previewData = getPreview(i)
|
||||
|
||||
// skip if email is blank
|
||||
if (!previewData.email) continue
|
||||
|
||||
// add it to the list of emails to be sent
|
||||
emails.push({
|
||||
from: fromEmail,
|
||||
to: previewData.email,
|
||||
text: previewData.text,
|
||||
html: converter.makeHtml(previewData.text),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// okay, now let's open a socket to the server!
|
||||
let socketProtocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:')
|
||||
let socketUrl = socketProtocol + '//' + window.location.host + window.location.pathname + 'hit-send/'
|
||||
let socket = new WebSocket(socketUrl)
|
||||
|
||||
socket.onopen = function() {
|
||||
nowSending = emails.pop()
|
||||
socket.send(JSON.stringify(nowSending))
|
||||
}
|
||||
|
||||
socket.onmessage = (message) => {
|
||||
try {
|
||||
message = JSON.parse(message.data)
|
||||
} catch (err) {
|
||||
console.error(`Received malformed response from server: ${message.data}`)
|
||||
return
|
||||
}
|
||||
|
||||
if (message.success) {
|
||||
console.log(`${message.to}'s email sent successfully!`)
|
||||
|
||||
// send the next one
|
||||
nowSending = emails.pop()
|
||||
if (!!nowSending) {
|
||||
socket.send(JSON.stringify(nowSending))
|
||||
console.log(`Sending: ${nowSending.to}`)
|
||||
} else {
|
||||
socket.close()
|
||||
step = 2
|
||||
}
|
||||
} else {
|
||||
console.error('Something went wrong :(')
|
||||
step = 3
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<img src="/assets/email-baker.png" class="main-pic" alt="Email Oven"/>
|
||||
<h1>Chip <span class="highlight">Choc</span></h1>
|
||||
<h2>Cookie-cutter emails made easy.</h2>
|
||||
{#if step < 4}
|
||||
<img src="/assets/email-baker.png" class="main-pic" alt="Email Oven"/>
|
||||
<h1>Chip <span class="highlight">Choc</span></h1>
|
||||
<h2>Cookie-cutter emails made easy.</h2>
|
||||
|
||||
{#if step >= 1}
|
||||
<h3 class="mt-3">Step 1</h3>
|
||||
{/if}
|
||||
|
||||
{#if step == 0}
|
||||
<button on:click={showEmail}>Start Drafting</button>
|
||||
{/if}
|
||||
|
||||
{#if step >= 1}
|
||||
<div class="instructions">
|
||||
<p>Compose your email below, leaving [TK stuff] to be replaced in the table. Make sure each TK is a single word: no spaces allowed!</p>
|
||||
</div>
|
||||
|
||||
<div class="email-content">
|
||||
<p>From: <input type="text" bind:value={fromEmail}/></p>
|
||||
<textarea bind:value={emailContent}/>
|
||||
<p>
|
||||
<strong>Detected fields:</strong>
|
||||
{#each tkList as tk (tk)}
|
||||
<span class="tag">{tk}</span>
|
||||
{/each}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if step >= 2}
|
||||
<h3 class="mt-3">Step 1</h3>
|
||||
<div class="instructions">
|
||||
<p>Now, fill in the fields for each user. (You can also copy-paste rows and columns directly from Air, if the ordering is right!)</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if step == 1}
|
||||
<button on:click={showSheet}>Next</button>
|
||||
{/if}
|
||||
|
||||
{#if step > 1}
|
||||
<button on:click={refreshSheet}>Refresh</button>
|
||||
{/if}
|
||||
|
||||
<div id="editor" bind:this={editor}></div>
|
||||
|
||||
{#if step >= 2 && !!cookiePreviewData}
|
||||
<h4 class="mt-3">Preview</h4>
|
||||
|
||||
<div class="cookie-preview">
|
||||
<div class="preview-header">
|
||||
<p>From: {fromEmail || '[NOBODY!!!]'}</p>
|
||||
<p>To: {cookiePreviewData.email || '[NOBODY!!!]'}</p>
|
||||
<hr />
|
||||
</div>
|
||||
{@html converter.makeHtml(cookiePreviewData.text)}
|
||||
</div>
|
||||
<button on:click={decrementPreviewRow}>< Prev</button>
|
||||
<button on:click={incrementPreviewRow}>Next ></button>
|
||||
|
||||
<h3>Step 3</h3>
|
||||
|
||||
{#if step == 2}
|
||||
<div class="instructions">
|
||||
<p>If everything looks okay, then we're done! Click on the button below to put the cutter in action.</p>
|
||||
</div>
|
||||
<button on:click={next}>Yup, send 'em out!</button>
|
||||
{#if step >= 1}
|
||||
<h3 class="mt-3">Step 1</h3>
|
||||
{/if}
|
||||
|
||||
{#if step == 3}
|
||||
<div class="instructions">
|
||||
<p>Are you <strong>sure</strong> you want to send them out?</p>
|
||||
<p>Are you ready?</p>
|
||||
<p>Did you look through all the previews?</p>
|
||||
<p>Have you double-checked the names and email IDs to make sure they match?</p>
|
||||
</div>
|
||||
<button>Hit send!</button> <button on:click={prev}>Ummmm..</button>
|
||||
{#if step == 0}
|
||||
<button on:click={showEmail}>Start Drafting</button>
|
||||
{/if}
|
||||
|
||||
{#if step >= 1}
|
||||
<div class="instructions">
|
||||
<p>Compose your email below, leaving [TK stuff] to be replaced in the table. Make sure each TK is a single word: no spaces allowed!</p>
|
||||
</div>
|
||||
|
||||
<div class="email-content">
|
||||
<p>From: <input type="text" bind:value={fromEmail}/></p>
|
||||
<textarea bind:value={emailContent}/>
|
||||
<p>
|
||||
<strong>Detected fields:</strong>
|
||||
{#each tkList as tk (tk)}
|
||||
<span class="tag">{tk}</span>
|
||||
{/each}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if step >= 2}
|
||||
<h3 class="mt-3">Step 2</h3>
|
||||
<div class="instructions">
|
||||
<p>Now, fill in the fields for each user. (You can also copy-paste rows and columns directly from Air, if the ordering is right!)</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if step == 1}
|
||||
<button on:click={showSheet}>Next</button>
|
||||
{/if}
|
||||
|
||||
{#if step > 1}
|
||||
<button on:click={refreshSheet}>Refresh</button>
|
||||
{/if}
|
||||
|
||||
<div id="editor" bind:this={editor}></div>
|
||||
|
||||
{#if step >= 2 && !!cookiePreviewData}
|
||||
<h4 class="mt-3">Preview</h4>
|
||||
|
||||
<div class="cookie-preview">
|
||||
<div class="preview-header">
|
||||
<p>From: {fromEmail || '[NOBODY!!!]'}</p>
|
||||
<p>To: {cookiePreviewData.email || '[NOBODY!!!]'}</p>
|
||||
<hr />
|
||||
</div>
|
||||
{@html converter.makeHtml(cookiePreviewData.text)}
|
||||
</div>
|
||||
<button on:click={decrementPreviewRow}>< Prev</button>
|
||||
<button on:click={incrementPreviewRow}>Next ></button>
|
||||
|
||||
<h3>Step 3</h3>
|
||||
|
||||
{#if step == 2}
|
||||
<div class="instructions">
|
||||
<p>If everything looks okay, then we're done! Click on the button below to put the cutter in action.</p>
|
||||
</div>
|
||||
<button on:click={next}>Yup, send 'em out!</button>
|
||||
{/if}
|
||||
|
||||
{#if step == 3}
|
||||
<div class="instructions">
|
||||
<p>Are you <strong>sure</strong> you want to send them out?</p>
|
||||
<p>Are you ready?</p>
|
||||
<p>Did you look through all the previews?</p>
|
||||
<p>Have you double-checked the names and email IDs to make sure they match?</p>
|
||||
</div>
|
||||
<button on:click={hitSend}>Hit send!</button> <button on:click={prev}>Ummmm..</button>
|
||||
{/if}
|
||||
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if step == 4}
|
||||
<div class="instructions">
|
||||
{#if !!nowSending }
|
||||
<div class="cookie-preview">
|
||||
<div class="preview-header">
|
||||
<p>From: {nowSending.from || '[NOBODY!!!]'}</p>
|
||||
<p>To: {nowSending.to || '[NOBODY!!!]'}</p>
|
||||
<hr />
|
||||
{@html nowSending.html}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<h1>Chip <span class="highlight">Choc</span></h1>
|
||||
<h2>Your cookies are being cut...</h2>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</main>
|
||||
|
|
19
yarn.lock
19
yarn.lock
|
@ -125,6 +125,11 @@ array-flatten@1.1.1:
|
|||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
||||
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
|
||||
|
||||
async-limiter@~1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
||||
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
|
@ -358,6 +363,13 @@ etag@~1.8.1:
|
|||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
||||
|
||||
express-ws@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/express-ws/-/express-ws-4.0.0.tgz#dabd8dc974516418902a41fe6e30ed949b4d36c4"
|
||||
integrity sha512-KEyUw8AwRET2iFjFsI1EJQrJ/fHeGiJtgpYgEWG3yDv4l/To/m3a2GaYfeGyB3lsWdvbesjF5XCMx+SVBgAAYw==
|
||||
dependencies:
|
||||
ws "^5.2.0"
|
||||
|
||||
express@^4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
|
||||
|
@ -1148,6 +1160,13 @@ wrappy@1:
|
|||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
ws@^5.2.0:
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f"
|
||||
integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==
|
||||
dependencies:
|
||||
async-limiter "~1.0.0"
|
||||
|
||||
ws@^7.4.3:
|
||||
version "7.4.5"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1"
|
||||
|
|
Loading…
Reference in a new issue