From 6b77029b05e5335ca71dd49f0045e0605e525346 Mon Sep 17 00:00:00 2001 From: Lumi Pakkanen Date: Thu, 16 May 2024 20:11:24 +0300 Subject: [PATCH] Upload scales to a server for sharing Add privacy policy and terms of service. ref #456 --- .env.development | 1 + .gitignore | 3 + README.md | 4 + cypress/e2e/basic.cy.ts | 2 +- package-lock.json | 12 +- package.json | 4 +- src/App.vue | 24 ++- src/__tests__/util.spec.ts | 33 +++- src/components/ExporterButtons.vue | 109 ++++++++--- src/constants.ts | 3 + src/main.ts | 26 +++ src/router/index.ts | 15 ++ src/scale.ts | 33 ++++ src/stores/__tests__/scale.spec.ts | 42 ++++ src/stores/audio.ts | 53 +++-- src/stores/scale.ts | 89 +++++++-- src/stores/state.ts | 13 +- src/utils.ts | 78 ++++++++ src/views/LoadScaleView.vue | 70 +++++++ src/views/NotFoundView.vue | 2 +- src/views/PreferencesView.vue | 9 + src/views/PrivacyPolicy.vue | 209 ++++++++++++++++++++ src/views/ScaleView.vue | 5 +- src/views/SynthView.vue | 4 +- src/views/TermsOfService.vue | 304 +++++++++++++++++++++++++++++ src/views/VirtualKeyboardView.vue | 4 +- src/views/VirtualQwerty.vue | 4 +- 27 files changed, 1071 insertions(+), 84 deletions(-) create mode 100644 .env.development create mode 100644 src/stores/__tests__/scale.spec.ts create mode 100644 src/views/LoadScaleView.vue create mode 100644 src/views/PrivacyPolicy.vue create mode 100644 src/views/TermsOfService.vue diff --git a/.env.development b/.env.development new file mode 100644 index 00000000..8ba9c0cf --- /dev/null +++ b/.env.development @@ -0,0 +1 @@ +VITE_API_URL=http://localhost:17461/ diff --git a/.gitignore b/.gitignore index 78710161..a1040d94 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ coverage *.njsproj *.sln *.sw? + +# Production secrets +.env.production diff --git a/README.md b/README.md index b822df06..fc234b94 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,10 @@ npm run test:e2e npm run lint ``` +### Run the backend server + +See [sw-server](https://github.com/xenharmonic-devs/sw-server) for the backend component for storing and retrieving scales. + ## License MIT, see [LICENCE](LICENSE) for details. diff --git a/cypress/e2e/basic.cy.ts b/cypress/e2e/basic.cy.ts index df334961..c7016060 100644 --- a/cypress/e2e/basic.cy.ts +++ b/cypress/e2e/basic.cy.ts @@ -25,7 +25,7 @@ describe("404 page", () => { it("creates an octaplex", () => { cy.visit("/non-existing-page"); cy.contains("h2", "Not found"); - cy.get("a").last().click(); + cy.get("#octaplex").click(); cy.get("button").first().click(); cy.contains("h2", "Scale data"); cy.get("#scale-name").should( diff --git a/package-lock.json b/package-lock.json index 0df40f59..1caef265 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "scale-workshop", - "version": "3.0.0-beta.27", + "version": "3.0.0-beta.28", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "scale-workshop", - "version": "3.0.0-beta.27", + "version": "3.0.0-beta.28", "dependencies": { "isomorphic-qwerty": "^0.0.2", "ji-lattice": "^0.0.3", @@ -14,7 +14,7 @@ "moment-of-symmetry": "^0.5.2", "pinia": "^2.1.7", "qs": "^6.12.0", - "sonic-weave": "^0.1.1", + "sonic-weave": "^0.2.0", "sw-synth": "^0.1.0", "temperaments": "^0.5.3", "values.js": "^2.1.1", @@ -5455,9 +5455,9 @@ } }, "node_modules/sonic-weave": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/sonic-weave/-/sonic-weave-0.1.1.tgz", - "integrity": "sha512-zhCZ046qwdQ8+skDQkRrr70Sf3yNwEwZxxVy+zS4L4c3Vv9u/4MmxiRYr7d7o7LJuZMwE6QmmD1P6fELXjyYmA==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/sonic-weave/-/sonic-weave-0.2.0.tgz", + "integrity": "sha512-dc9Gd0V5hSR2Xc1nXopLjuqas39UcDEfqRDl6GmQQ3riHW/eaLCSlSfhh2Xo91L2sZHnL1n9oJEbnEbxNSmQBw==", "dependencies": { "moment-of-symmetry": "^0.5.3", "xen-dev-utils": "^0.7.0" diff --git a/package.json b/package.json index 0475f02e..76267354 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scale-workshop", - "version": "3.0.0-beta.27", + "version": "3.0.0-beta.28", "scripts": { "dev": "vite", "build": "run-p type-check \"build-only {@}\" --", @@ -21,7 +21,7 @@ "moment-of-symmetry": "^0.5.2", "pinia": "^2.1.7", "qs": "^6.12.0", - "sonic-weave": "^0.1.1", + "sonic-weave": "^0.2.0", "sw-synth": "^0.1.0", "temperaments": "^0.5.3", "values.js": "^2.1.1", diff --git a/src/App.vue b/src/App.vue index 2fcbb06b..61a4a162 100644 --- a/src/App.vue +++ b/src/App.vue @@ -313,7 +313,7 @@ function typingKeydown(event: CoordinateKeyboardEvent) { let index = scale.scale.baseMidiNote + scale.scale.size * scale.equaveShift + scale.degreeShift if (scale.keyboardMode === 'isomorphic') { - index += x * state.isomorphicHorizontal + (2 - y) * state.isomorphicVertical + index += x * scale.isomorphicHorizontal + (2 - y) * scale.isomorphicVertical } else { if (scale.qwertyMapping.has(event.code)) { // QWERTY mapping incorporates shifts @@ -356,8 +356,8 @@ onMounted(() => { scale.userBaseFrequency = scaleWorkshopOneData.freq scale.autoFrequency = false scale.baseMidiNote = scaleWorkshopOneData.midi - state.isomorphicHorizontal = scaleWorkshopOneData.horizontal - state.isomorphicVertical = scaleWorkshopOneData.vertical + scale.isomorphicHorizontal = scaleWorkshopOneData.horizontal + scale.isomorphicVertical = scaleWorkshopOneData.vertical if (scaleWorkshopOneData.data !== undefined) { const colors = scaleWorkshopOneData.colors ?? '' @@ -394,8 +394,8 @@ onMounted(() => { scale.userBaseFrequency = decodedState.baseFrequency scale.autoFrequency = false scale.baseMidiNote = decodedState.baseMidiNote - state.isomorphicHorizontal = decodedState.isomorphicHorizontal - state.isomorphicVertical = decodedState.isomorphicVertical + scale.isomorphicHorizontal = decodedState.isomorphicHorizontal + scale.isomorphicVertical = decodedState.isomorphicVertical scale.keyboardMode = decodedState.keyboardMode scale.pianoMode = pianoMode scale.equaveShift = decodedState.equaveShift @@ -505,6 +505,10 @@ function panic() { :typingKeyboard="typingKeyboard" @panic="panic" /> + diff --git a/src/__tests__/util.spec.ts b/src/__tests__/util.spec.ts index 0cd95b97..7869a817 100644 --- a/src/__tests__/util.spec.ts +++ b/src/__tests__/util.spec.ts @@ -1,6 +1,13 @@ import { describe, it, expect } from 'vitest' -import { autoKeyColors, formatExponential, formatHertz, gapKeyColors } from '../utils' +import { + autoKeyColors, + encodeUrlSafe64, + formatExponential, + formatHertz, + gapKeyColors, + randomId +} from '../utils' function naiveExponential(x: number, fractionDigits = 3) { if (Math.abs(x) < 10000) { @@ -92,3 +99,27 @@ describe('Gap key color algorithm', () => { ) }) }) + +describe('URL safe number encoder', () => { + it('encodes the whole range', () => { + const expected = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-~' + for (let i = 0; i < 64; ++i) { + expect(encodeUrlSafe64(i)).toBe(expected[i]) + } + }) +}) + +describe('Unique ID generator', () => { + it('produces a short URL-friendly identifier', () => { + const urlSafe = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-~' + const id = randomId() + expect(id).toHaveLength(9) + for (const char of id) { + expect(urlSafe).toContain(char) + } + }) + + it("won't collide with this particular identifier for 30 years", () => { + expect(randomId()).not.toBe('oKh5gWb04') + }) +}) diff --git a/src/components/ExporterButtons.vue b/src/components/ExporterButtons.vue index 64626b9d..b53902ca 100644 --- a/src/components/ExporterButtons.vue +++ b/src/components/ExporterButtons.vue @@ -1,11 +1,12 @@ diff --git a/src/views/TermsOfService.vue b/src/views/TermsOfService.vue new file mode 100644 index 00000000..ceef7c4d --- /dev/null +++ b/src/views/TermsOfService.vue @@ -0,0 +1,304 @@ + + diff --git a/src/views/VirtualKeyboardView.vue b/src/views/VirtualKeyboardView.vue index 3c2098fa..923c744f 100644 --- a/src/views/VirtualKeyboardView.vue +++ b/src/views/VirtualKeyboardView.vue @@ -38,8 +38,8 @@ type NoteOnCallback = (index: number) => NoteOff