From 1455a817fc5548f6215f1345291497546e987279 Mon Sep 17 00:00:00 2001 From: heapwolf Date: Mon, 29 Apr 2024 15:51:56 +0200 Subject: [PATCH] wip signup --- socket.ini | 2 +- src/components/account.js | 82 +++++++++++++++++ src/components/confirm.js | 5 +- src/components/project.js | 17 ++-- src/components/properties.js | 29 +++--- src/css/component-account.css | 52 +++++++++++ src/css/component-confirm.css | 4 +- src/css/component-project.css | 7 +- src/css/component-properties.css | 5 ++ src/css/index.css | 2 +- src/css/page-account.css | 149 ++++++------------------------- src/css/tonic-overrides.css | 9 ++ src/index.html | 1 + src/index.js | 93 ++++++++++++++----- src/pages/account.html | 29 ++++-- src/pages/account.js | 118 ++++++++++++++++++------ src/preview.js | 2 +- src/vendor.js | 11 ++- src/views/home.js | 1 - src/worker.js | 3 +- 20 files changed, 412 insertions(+), 209 deletions(-) create mode 100644 src/components/account.js create mode 100644 src/css/component-account.css diff --git a/socket.ini b/socket.ini index 8e42fa6..e25e41f 100644 --- a/socket.ini +++ b/socket.ini @@ -370,7 +370,7 @@ width = 80% ; Minimum height of the window in pixels or as a percentage of the screen. ; default value: 0 -min_height = 500 +min_height = 650 ; Minimum width of the window in pixels or as a percentage of the screen. ; default value: 0 diff --git a/src/components/account.js b/src/components/account.js new file mode 100644 index 0000000..69b9fe0 --- /dev/null +++ b/src/components/account.js @@ -0,0 +1,82 @@ +import { TonicDialog } from '@socketsupply/components/dialog' + +class DialogAccount extends TonicDialog { + constructor () { + super() + + this.handleResponse = this.handleResponse.bind(this) + } + + async show () { + super.show() + window.addEventListener('message', this.handleResponse, { once: true }) + } + + async hide () { + super.hide() + window.removeEventListener('message', this.handleResponse) + } + + async handleResponse (event) { + const iframe = this.querySelector('iframe') + if (event.source !== iframe.contentWindow) return + + const app = this.props.app + + if (!event.data.success) { + console.error('Unsuccessful data returned', event) + this.resolve(event.data) + await this.hide() + return + } + + console.log('GOT RESPONSE FROM STRIPE PAGE', event.data) + + try { + const res = await fetch('https://api.socketsupply.co/signup', { + method: 'POST', + mode: 'cors', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(event.data) + }) + + console.log('SIGNUP RESPONSE', res) + + if (res.ok) { + const { data: dataUser } = await this.db.state.get('user') + dataUser.buildKeys = await res.json() + console.log('dataUser', dataUser) + await this.db.state.put('user', dataUser) + await this.hide() + } + } catch (err) { + if (err.name === "AbortError") { + this.resolve({ data: event.data }) + await this.hide() + } + console.log(err) + } + } + + async prompt () { + await this.show() + const { promise, resolve } = Promise.withResolvers() + this.resolve = resolve + return promise + } + + render () { + const src = `pages/account.html?dev=${process.env.DEV ? 'true' : 'false'}` + + return this.html` +
Account
+ +
+ +
+ ` + } +} + +export default DialogAccount +export { DialogAccount } diff --git a/src/components/confirm.js b/src/components/confirm.js index 0f29a77..f12e64a 100644 --- a/src/components/confirm.js +++ b/src/components/confirm.js @@ -35,6 +35,7 @@ class DialogConfirm extends TonicDialog { ${button.label}` }) } @@ -49,10 +50,6 @@ class DialogConfirm extends TonicDialog { return this.html`
${title}
-
${message}
diff --git a/src/components/project.js b/src/components/project.js index 98c0e58..d6e222d 100644 --- a/src/components/project.js +++ b/src/components/project.js @@ -654,15 +654,15 @@ class AppProject extends Tonic { children: [] } - child.icon = entry.isDirectory() ? 'folder' : 'file' + child.icon = entry.isDirectory() ? '' : 'file' - if (parent.id === 'root' && entry.isDirectory()) { + /* if (parent.id === 'root' && entry.isDirectory()) { if (!this.props.parent.state.currentProject) { this.props.parent.state.currentProject = child } child.icon = 'package' - } + } */ parent.children.push(child) @@ -711,7 +711,7 @@ class AppProject extends Tonic { isDirectory: false, nonMovable: true, type: 'project', - icon: 'package', + icon: '', children: [] } @@ -750,9 +750,9 @@ class AppProject extends Tonic { const title = (typeof child.title) === 'string' ? child.title : '' let icon = child.icon - if (!icon || icon === 'folder') { - icon = child.state === 1 ? 'folder-open' : 'folder' - } + // if (!icon || icon === 'folder') { + // icon = child.state === 1 ? 'folder-open' : 'folder' + // } const iconColor = node.iconColor || 'var(--tonic-primary)' @@ -770,7 +770,8 @@ class AppProject extends Tonic { } } - const hasToggle = hasChildren > 0 || (icon === 'folder') + const hasToggle = hasChildren > 0 // || (icon === 'folder') + children.push(this.html`
${previewWindows} -
- - - - - - - -
+ + + + + +
+ + + + + +

Project Settings

diff --git a/src/css/component-account.css b/src/css/component-account.css new file mode 100644 index 0000000..c9a6061 --- /dev/null +++ b/src/css/component-account.css @@ -0,0 +1,52 @@ +dialog-account { + display: grid; + grid-template-rows: auto 1fr 60px; + max-width: 900px; + min-width: 650px; + min-height: 580px; + display: grid; + font-family: var(--tonic-monospace); + text-align: center; +} + +dialog-account header { + height: 46px; + text-align: center; + padding: 14px; + font-size: 12px; + color: var(--tonic-info); + app-region: drag; + --app-region: drag; +} + +dialog-account footer { + height: 60px; + display: grid; + grid-template-columns: 1fr auto; + gap: 12px; + padding: 0 14px; +} + +dialog-account tonic-button { + app-region: unset; + --app-region: unset; + place-self: center end; +} + +dialog-account p { + margin: 0; +} + +dialog-account main { + justify-content: center; + align-content: center; + padding: 0 6%; +} + +dialog-account main iframe { + width: 100%; + height: 100%; + border: 0; + app-region: initial; + --app-region: initial; +} diff --git a/src/css/component-confirm.css b/src/css/component-confirm.css index 0498fb8..b234a64 100644 --- a/src/css/component-confirm.css +++ b/src/css/component-confirm.css @@ -2,7 +2,6 @@ dialog-confirm { display: grid; grid-template-rows: auto 1fr; max-width: 500px; - min-height: 280px; display: grid; font-family: var(--tonic-monospace); text-align: center; @@ -39,5 +38,6 @@ dialog-confirm p { dialog-confirm main { justify-content: center; align-content: center; - padding: 0 6%; + padding: 4% 6%; + text-align: left; } diff --git a/src/css/component-project.css b/src/css/component-project.css index edebe7d..5773e8f 100644 --- a/src/css/component-project.css +++ b/src/css/component-project.css @@ -2,7 +2,7 @@ app-project { user-select: none; content-visibility: auto; -webkit-user-select: none; - padding: 14px 20px; + padding: 14px 14px; display: block; position: absolute; background-color: var(--tonic-window); @@ -131,7 +131,7 @@ app-project .item .handle { color: var(--tonic-primary); background: var(--tonic-window); border-radius: 4px; - margin-left: 24px; + margin-left: 0px; position: relative; display: flex; padding: 4px 0px; @@ -153,9 +153,10 @@ app-project .item .handle[data-state="1"] + .node { app-project .toggle { position: absolute; top: 8px; - left: -14px; + left: 14px; width: 14px; height: 14px; + z-index: 10; } app-project .item .handle[data-state="0"][data-toggle="false"] .toggle { diff --git a/src/css/component-properties.css b/src/css/component-properties.css index d901a96..ece197f 100644 --- a/src/css/component-properties.css +++ b/src/css/component-properties.css @@ -8,6 +8,11 @@ app-properties { bottom: 0; } +app-properties h3 { + border-bottom: 1px solid; + padding-bottom: 6px; +} + app-properties #project-status { border: 1px solid var(--tonic-border); padding: 20px 12px; diff --git a/src/css/index.css b/src/css/index.css index bbf8481..39a0427 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -190,7 +190,7 @@ header.component tonic-button.selected svg use { header.component#header-properties { display: grid; - grid-template-columns: 34px 34px 34px 1fr; + grid-template-columns: 34px 34px 34px 34px 1fr; } header.component#header-project { diff --git a/src/css/page-account.css b/src/css/page-account.css index 3a4eaa4..313d506 100644 --- a/src/css/page-account.css +++ b/src/css/page-account.css @@ -1,51 +1,23 @@ -/* Variables */ * { box-sizing: border-box; } body { - font-family: -apple-system, BlinkMacSystemFont, sans-serif; - font-size: 16px; -webkit-font-smoothing: antialiased; - display: flex; - justify-content: center; - align-content: center; overflow: hidden; -} - -form { - width: 30vw; - min-width: 500px; - align-self: center; - box-shadow: 0px 0px 0px 0.5px rgba(50, 50, 93, 0.1), - 0px 2px 5px 0px rgba(50, 50, 93, 0.1), 0px 1px 1.5px 0px rgba(0, 0, 0, 0.07); - border-radius: 7px; - padding: 40px; -} - -.hidden { - display: none; -} - -#card-element { - padding: 10px; - border: 1px solid var(--tonic-border); - border-radius: 4px; - width: 100%; - height: 42px; + background-color: var(--tonic-window); + color: var(--tonic-primary); + font-family: var(--tonic-body); } .StripeElement { display: block; - margin: 10px 0; padding: 10px; border: 1px solid #ccc; border-radius: 5px; font-size: 16px; } - - #payment-message { color: rgb(105, 115, 134); font-size: 16px; @@ -58,103 +30,36 @@ form { margin-bottom: 24px; } -/* Buttons and links */ -button { - background: #5469d4; - font-family: Arial, sans-serif; - color: #ffffff; +tonic-form#form-stripe { + position: absolute; top: 0; left: 0; right: 0; bottom: 0; + display: grid; + align-content: center; + justify-content: center; + align-items: end; + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: 20px; + padding: 2px; +} + +#card-element { + padding: 10px; + border: 1px solid var(--tonic-border); border-radius: 4px; - border: 0; - padding: 12px 16px; - font-size: 16px; - font-weight: 600; - cursor: pointer; - display: block; - transition: all 0.2s ease; - box-shadow: 0px 4px 5.5px 0px rgba(0, 0, 0, 0.07); width: 100%; -} -button:hover { - filter: contrast(115%); -} -button:disabled { - opacity: 0.5; - cursor: default; + height: 38px; + grid-area: 1 / span 4; + background-color: var(--tonic-background); } -/* spinner/processing state, errors */ -.spinner, -.spinner:before, -.spinner:after { - border-radius: 50%; -} -.spinner { - color: #ffffff; - font-size: 22px; - text-indent: -99999px; - margin: 0px auto; - position: relative; - width: 20px; - height: 20px; - box-shadow: inset 0 0 0 2px; - -webkit-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); -} -.spinner:before, -.spinner:after { - position: absolute; - content: ""; -} -.spinner:before { - width: 10.4px; - height: 20.4px; - background: #5469d4; - border-radius: 20.4px 0 0 20.4px; - top: -0.2px; - left: -0.2px; - -webkit-transform-origin: 10.4px 10.2px; - transform-origin: 10.4px 10.2px; - -webkit-animation: loading 2s infinite ease 1.5s; - animation: loading 2s infinite ease 1.5s; -} -.spinner:after { - width: 10.4px; - height: 10.2px; - background: #5469d4; - border-radius: 0 10.2px 10.2px 0; - top: -0.1px; - left: 10.2px; - -webkit-transform-origin: 0px 10.2px; - transform-origin: 0px 10.2px; - -webkit-animation: loading 2s infinite ease; - animation: loading 2s infinite ease; +#cardholder-name, #cardholder-address { + grid-area: 2 / span 2; } -@-webkit-keyframes loading { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} -@keyframes loading { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } +#cardholder-email { + grid-area: 4 / span 3; } -@media only screen and (max-width: 600px) { - form { - width: 80vw; - min-width: initial; - } +#billing-info { + grid-area: 5 / span 4; + display: grid; } diff --git a/src/css/tonic-overrides.css b/src/css/tonic-overrides.css index e7842f2..5ac4016 100644 --- a/src/css/tonic-overrides.css +++ b/src/css/tonic-overrides.css @@ -115,3 +115,12 @@ tonic-button[type="icon"] tonic-icon { tonic-input[symbol-id] .tonic--right tonic-icon, tonic-input[src] .tonic--right tonic-icon { display: grid; } + +.tonic--dialog { + border-radius: 12px !important; +} + +tonic-toaster-inline .tonic--title { + text-transform: uppercase; + margin: 0 0 2px; +} diff --git a/src/index.html b/src/index.html index bd8f1f5..ffb5b26 100644 --- a/src/index.html +++ b/src/index.html @@ -19,6 +19,7 @@ + diff --git a/src/index.js b/src/index.js index fd13c70..ee4063b 100644 --- a/src/index.js +++ b/src/index.js @@ -22,6 +22,7 @@ import { AppTerminal } from './components/terminal.js' import { AppProject } from './components/project.js' import { AppProperties } from './components/properties.js' import { AppSprite } from './components/sprite.js' +import { DialogAccount } from './components/account.js' import { DialogConfirm } from './components/confirm.js' import { DialogPublish } from './components/publish.js' import { DialogSubscribe } from './components/subscribe.js' @@ -473,20 +474,44 @@ class AppView extends Tonic { } } - // - // this app must bundle the platform-specific ssc binary - // - async exportProject () { - const args = [ - 'build', - '-r' - ] - - const coDevice = document.querySelector('#device') - if (coDevice.option.dataset.value) { - args.push(coDevice.option.dataset.value) // --platform=P + async packageProject () { + const coDialogConfirm = document.querySelector('dialog-confirm') + const result = await coDialogConfirm.prompt({ + type: 'question', + message: `You're on ${process.platform} but you're targeting other operating systems. Do you want to use the Socket Supply Co. build service to handle this for you?`, + buttons: [ + { label: 'Abandon', value: 'abandon' }, + { label: 'Continue', value: 'consent' } + ] + }) + + if (!result.abandon && !result.consent) return + + if (result.consent) { + // + // Check if the user has an account if not, sign up for one + // + const { data: dataUser } = await this.db.state.get('user') + if (!dataUser.card) { + const coDialogAccount = document.querySelector('dialog-account') + const { err, data } = await coDialogAccount.prompt() + + if (err) { + // + // show a new prompt with the error and the option to call this.packageProject() again + // + return + } + } + + // + // User has an account. go ahead and submit the request for a build + // + } + } + async runSSC (...args) { const term = document.querySelector('app-terminal') term.info(`ssc ${args.join(' ')}`) @@ -717,7 +742,15 @@ class AppView extends Tonic { } if (event === 'run') { - this.exportProject() + const args = ['build', '-r'] + + // TODO(@heapwolf) check all checked archs in properties panel + + this.runSSC(...args) + } + + if (event === 'package') { + this.packageProject() } if (event === 'preview-mode') { @@ -799,16 +832,25 @@ class AppView extends Tonic { type="icon" size="18px" symbol-id="play-icon" - title="Build & Run The Project" + title="Build & Run" data-event="run" > + + + @@ -830,6 +872,19 @@ class AppView extends Tonic { + + + + + + - - - ` } @@ -868,6 +916,7 @@ window.onload = () => { Tonic.add(AppView) Tonic.add(GitStatus) Tonic.add(PatchRequests) + Tonic.add(DialogAccount) Tonic.add(DialogConfirm) Tonic.add(DialogPublish) Tonic.add(DialogSubscribe) diff --git a/src/pages/account.html b/src/pages/account.html index cf243b4..4c84eaf 100644 --- a/src/pages/account.html +++ b/src/pages/account.html @@ -6,21 +6,40 @@ + + + - -
- + + + + + + + + + + + + + + + + + diff --git a/src/pages/account.js b/src/pages/account.js index 744541f..2fa4512 100644 --- a/src/pages/account.js +++ b/src/pages/account.js @@ -1,39 +1,105 @@ -import Tonic from '@socketsupply/tonic' -import components from '@socketsupply/components' +import { Tonic, components } from '../vendor.js' components(Tonic) -console.log(Tonic) +class AppView extends Tonic { + constructor () { + super() -const sharedKeys = { - test: 'pk_test_51JpgUIFV3Il51eBDUO1s6JVOy9P3rFCkvX1Mbjvq4Qtkrj0ARg0CmXtYnpecsTyliVwvSJnEOOQXqUo0w48EKOP000oEdk14R2', - live: 'pk_live_51JpgUIFV3Il51eBDWQworOndEE0S5T2HUqjowum8lPhSfpaboVz5iJlS1PfsWicfNtdUhTZhPSYtpJpZgI9Jc40800MkE0liSP' -} + const sharedKeys = { + test: 'pk_test_51JpgUIFV3Il51eBDUO1s6JVOy9P3rFCkvX1Mbjvq4Qtkrj0ARg0CmXtYnpecsTyliVwvSJnEOOQXqUo0w48EKOP000oEdk14R2', + live: 'pk_live_51JpgUIFV3Il51eBDWQworOndEE0S5T2HUqjowum8lPhSfpaboVz5iJlS1PfsWicfNtdUhTZhPSYtpJpZgI9Jc40800MkE0liSP' + } -window.addEventListener('DOMContentLoaded', e => { - const url = new URL(globalThis.location.href) - const key = url.searchParams.get('dev') === 'true' ? sharedKeys.test : sharedKeys.live - const stripe = Stripe(key) - const elements = stripe.elements() - const card = elements.create('card') + const url = new URL(globalThis.location.href) + const key = url.searchParams.get('dev') === 'true' ? sharedKeys.test : sharedKeys.live + this.stripe = Stripe(key) + } + + async show () { + super.show() + const coForm = this.querySelector('tonic-form') + if (coForm) coForm.setValid() + } + + async input (e) { + const coForm = this.querySelector('tonic-form') + const coSubmit = this.querySelector('#form-submit') - card.mount('#card-element') + if (!coForm) return - const submitButton = document.getElementById('submit') + const isValid = coForm.validate({ decorate: false }) + coSubmit.disabled = !isValid + } - submitButton.addEventListener('click', async event => { - event.preventDefault() + async click (e) { + const el = Tonic.match(e.target, '#form-submit') + if (!el) return - const result = await stripe.createPaymentMethod({ - type: 'card', - card: card - }) + el.loading(true) - if (result.error) { - console.log(result.error.message) - return + const coForm = this.querySelector('#form-stripe') + const result = await this.stripe.createToken(this.card, coForm.value) + + el.loading(false) + + const data = { + ...result, + success: !result.error, + email: coForm.value.email } - console.log('TOKEN!', result.paymentMethod.id) - }) + window.parent.postMessage(data, '*') + } + + async connected () { + const elements = this.stripe.elements() + + const style = { + style: { + base: { + fontFamily: 'monospace' + } + } + } + + this.card = elements.create('card', style) + this.card.mount('#card-element') + } + + async render () { + return this.html` + +
+ + + + + + + + + + + + + + + + + Submit + + + This frame only talks to api.stripe.com. We never see your sensitive credit card data. Stripe returns a token that we store at + api.socketsupply.co, in a secure, encrypted database. You are only billed for builds that you authorize and only when they successfully complete. + +
+ ` + } +} + +Tonic.add(AppView) + +window.addEventListener('DOMContentLoaded', e => { + document.body.append(new AppView()) }) diff --git a/src/preview.js b/src/preview.js index 54fb0fc..6e1af96 100644 --- a/src/preview.js +++ b/src/preview.js @@ -56,5 +56,5 @@ const scaleToFit = e => { } if (process.platform === 'ios' || process.platform === 'android') { - window.addEventListener('resize', scaleToFit) + window.addEventListener('resize', () => requestAnimationFrame(scaleToFit)) } diff --git a/src/vendor.js b/src/vendor.js index 3c2bc9c..e549040 100644 --- a/src/vendor.js +++ b/src/vendor.js @@ -1,6 +1,15 @@ +// +// For editing and for the terminal +// import * as monaco from 'monaco-editor' import { Terminal } from 'xterm' import { FitAddon as Resizer } from 'xterm-addon-fit' import { SearchAddon as Search } from 'xterm-addon-search' -export { monaco, Terminal, Resizer, Search } +// +// To use in the stripe window +// +import Tonic from '@socketsupply/tonic' +import components from '@socketsupply/components' + +export { monaco, Terminal, Resizer, Search, Tonic, components } diff --git a/src/views/home.js b/src/views/home.js index 576c3dd..c1ca577 100644 --- a/src/views/home.js +++ b/src/views/home.js @@ -173,7 +173,6 @@ class ViewHome extends Tonic { id="profile-public-key" > -
diff --git a/src/worker.js b/src/worker.js index be1597a..72abbd0 100644 --- a/src/worker.js +++ b/src/worker.js @@ -101,7 +101,8 @@ export default async function (req, env, ctx) { let html = data if (url.pathname.endsWith('index.html')) { - html = html.replace(//, ``) + console.log('SERVING') + html = html.replace(//, ``) html = html.replace('', ``) }