jevstrudel.git / website / src / repl / util.mjs
util.mjsannotatedutil.mjssource205 lines · 6.3 KB · raw
1import { code2hash, errorLogger, evalScope, hash2code, logger } from '@strudel/core';
2import { settingPatterns } from '../settings.mjs';
3import { setVersionDefaults } from '@strudel/webaudio';
4import { getMetadata } from '../metadata_parser';
5import { isTauri } from '../tauri.mjs';
6import './Repl.css';
7import { createClient } from '@supabase/supabase-js';
8import { writeText } from '@tauri-apps/plugin-clipboard-manager';
9import { $featuredPatterns /* , loadDBPatterns */ } from '@src/user_pattern_utils.mjs';

Create a single supabase client for interacting with your database

12export const supabase = createClient(
13  'https://pidxdsxphlhzjnzmifth.supabase.co',
14  'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM',
15);
17let dbLoaded;

if (typeof window !== 'undefined') { dbLoaded = loadDBPatterns(); }

22export async function initCode() {
23  // load code from url hash (either short hash from database or decode long hash)
24  try {
25    const initialUrl = window.location.href;
26    const hash = initialUrl.split('?')[1]?.split('#')?.[0]?.split('&')[0];
27    const codeParam = window.location.href.split('#')[1] || '';
28    if (codeParam) {
29      // looking like https://strudel.cc/#ImMzIGUzIg%3D%3D (hash length depends on code length)
30      return hash2code(codeParam);
31    } else if (hash) {
32      // looking like https://strudel.cc/?J01s5i1J0200 (fixed hash length)
33      return supabase
34        .from('code_v1')
35        .select('code')
36        .eq('hash', hash)
37        .then(({ data, error }) => {
38          if (error) {
39            console.warn('failed to load hash', error);
40          }
41          if (data.length) {
42            //console.log('load hash from database', hash);
43            return data[0].code;
44          }
45        });
46    }
47  } catch (err) {
48    console.warn('failed to decode', err);
49  }
50}
52export const parseJSON = (json) => {
53  json = json != null && json.length ? json : '{}';
54  try {
55    return JSON.parse(json);
56  } catch {
57    return '{}';
58  }
59};
60
61export async function getRandomTune() {
62  await dbLoaded;
63  const featuredTunes = Object.entries($featuredPatterns.get());
64  const randomItem = (arr) => arr[Math.floor(Math.random() * arr.length)];
65  const [_, data] = randomItem(featuredTunes);
66  return data;
67}
68
69export function loadModules() {
70  let modules = [
71    import('@strudel/core'),
72    import('@strudel/draw'),
73    import('@strudel/edo'),
74    import('@strudel/tonal'),
75    import('@strudel/mini'),
76    import('@strudel/xen'),
77    import('@strudel/webaudio'),
78    import('@strudel/codemirror'),
79    import('@strudel/hydra'),
80    import('@strudel/serial'),
81    import('@strudel/soundfonts'),
82    import('@strudel/csound'),
83    import('@strudel/tidal'),
84    import('@strudel/gamepad'),
85    import('@strudel/motion'),
86    import('@strudel/mqtt'),
87    import('@strudel/mondo'),
88    import('@strudel/dough'),
89    // jevstrudel: `jev({...})` and TypeSafe's choice/score/noul
90    import('../jev/jev.mjs'),
91  ];
92  if (isTauri()) {
93    modules = modules.concat([
94      import('@strudel/desktopbridge/loggerbridge.mjs'),
95      import('@strudel/desktopbridge/midibridge.mjs'),
96      import('@strudel/desktopbridge/oscbridge.mjs'),
97    ]);
98  } else {
99    modules = modules.concat([import('@strudel/midi'), import('@strudel/osc')]);
100  }
101
102  return evalScope(settingPatterns, ...modules);
103}

confirm dialog is a promise in webkit and a boolean in other browsers... normalize it to be a promise everywhere

105export function confirmDialog(msg) {
106  const confirmed = confirm(msg);
107  if (confirmed instanceof Promise) {
108    return confirmed;
109  }
110  return new Promise((resolve) => {
111    resolve(confirmed);
112  });
113}
114export const SETTING_CHANGE_RELOAD_MSG = 'Changing this setting requires the window to reload itself. OK?';
116export function confirmAndReloadPage(onSuccess) {
117  confirmDialog(SETTING_CHANGE_RELOAD_MSG).then((r) => {
118    if (r == true) {
119      try {
120        onSuccess();
121        return window.location.reload();
122      } catch (e) {
123        errorLogger(e);
124      }
125    }
126  });
127}

RIP due to SPAM let lastShared; export async function shareCode(codeToShare) { // const codeToShare = activeCode || code; if (lastShared === codeToShare) { logger(Link already generated!, 'error'); return; }

confirmDialog( 'Do you want your pattern to be public? If no, press cancel and you will get just a private link.', ).then(async (isPublic) => { const hash = nanoid(12); const shareUrl = window.location.origin + window.location.pathname + '?' + hash; const { error } = await supabase.from('code_v1').insert([{ code: codeToShare, hash, ['public']: isPublic }]); if (!error) { lastShared = codeToShare; // copy shareUrl to clipboard if (isTauri()) { await writeText(shareUrl); } else { await navigator.clipboard.writeText(shareUrl); } const message = Link copied to clipboard: ${shareUrl}; alert(message); // alert(message); logger(message, 'highlight'); } else { console.log('error', error); const message = Error: ${error.message}; // alert(message); logger(message); } }); }

164export async function shareCode(codeToShare) {
165  try {
166    const hash = '#' + code2hash(codeToShare);
167    const shareUrl = window.location.origin + window.location.pathname + hash;
168    if (isTauri()) {
169      await writeText(shareUrl);
170    } else {
171      await navigator.clipboard.writeText(shareUrl);
172    }
173    const message = `Link copied to clipboard!`;
174    alert(message);
175    logger(message, 'highlight');
176  } catch (e) {
177    console.error(e);
178  }
179}
181export const isIframe = () => window.location !== window.parent.location;
182function isCrossOriginFrame() {
183  try {
184    return !window.top.location.hostname;
185  } catch (e) {
186    return true;
187  }
188}
189
190export const isUdels = () => {
191  if (isCrossOriginFrame()) {
192    return false;
193  }
194  return window.top?.location?.pathname.includes('udels');
195};
196
197export function setVersionDefaultsFrom(code) {
198  try {
199    const metadata = getMetadata(code);
200    setVersionDefaults(metadata.version);
201  } catch (err) {
202    console.error('Error parsing metadata..');
203    console.error(err);
204  }
205}