Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save last format and document in localStorage #1895

Merged
merged 7 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions apps/repl/app/routes/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Route from '@ember/routing/route';
import { service } from '@ember/service';

import { DEFAULT_SNIPPET } from 'limber/snippets';
import { formatFrom } from 'limber/utils/messaging';

import type RouterService from '@ember/routing/router-service';
import type Transition from '@ember/routing/transition';
Expand Down Expand Up @@ -35,6 +36,22 @@ export default class EditRoute extends Route {
let hasFormat = qps.format !== undefined;

if (!hasCode) {
/**
* Default starting doc is
* user-configurable.
* (whatever they did last)
*/
let format = localStorage.getItem('format');
let doc = localStorage.getItem('document');

if (format && doc) {
console.info(`Found format and document in localStorage. Using those.`);
transition.abort();
this.editor.fileURIComponent.set(doc, formatFrom(format));

return;
}

console.warn(
'URL contained no document information in the SearchParams. ' +
'Assuming glimdown and using the default sample snippet.'
Expand Down
44 changes: 35 additions & 9 deletions apps/repl/app/utils/editor-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,22 @@ export class FileURIComponent {
return base ?? window.location.toString();
};

#updateWaiter: unknown;
#frame?: number;
#qps: URLSearchParams | undefined;
#tokens: unknown[] = [];
#updateQPs = async (rawText: string, format: Format) => {
this.#tokens.push(queueWaiter.beginAsync());
this.#_updateQPs(rawText, format);
};

#cleanup = () => {
this.#tokens.forEach((token) => {
queueWaiter.endAsync(token);
});
};

#_updateQPs = async (rawText: string, format: Format) => {
if (this.#frame) cancelAnimationFrame(this.#frame);
if (!this.#updateWaiter) this.#updateWaiter = queueWaiter.beginAsync();

let encoded = compressToEncodedURIComponent(rawText);
let qps = new URLSearchParams(location.search);
Expand All @@ -172,15 +182,27 @@ export class FileURIComponent {
qps.delete('t');
qps.set('format', formatFrom(format));

// @ts-expect-error this works
if (this.#qps?.c === qps.get('c') && this.#qps?.format === qps.get('format')) {
// no-op, we should not have gotten here
// it's a mistake to have tried to have update QPs.
// Someone should debug this.
this.#cleanup();

return;
}

localStorage.setItem('format', formatFrom(format));
localStorage.setItem('document', rawText);

this.#qps = {
...this.#qps,
...qps,
};

this.#frame = requestAnimationFrame(async () => {
if (isDestroyed(this) || isDestroying(this)) {
queueWaiter.endAsync(this.#updateWaiter);
this.#updateWaiter = null;
this.#cleanup();

return;
}
Expand All @@ -191,30 +213,34 @@ export class FileURIComponent {
await new Promise((resolve) => setTimeout(resolve, DEBOUNCE_MS));

if (isDestroyed(this) || isDestroying(this)) {
queueWaiter.endAsync(this.#updateWaiter);
this.#updateWaiter = null;
this.#cleanup();

return;
}

queueWaiter.endAsync(this.#updateWaiter);
this.#updateWaiter = null;

// On initial load, if we call #updateQPs,
// we may not have a currentURL, because the first transition has yet to complete
let base = this.router.currentURL?.split('?')[0];

if (macroCondition(isTesting())) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
base ??= (this.router as any) /* private API? */?.location?.path;
// @ts-expect-error private api
base = base.split('?')[0];
} else {
base ??= window.location.pathname;
}

/**
* At some point this added qps
* we don't want them though, so we'll strip them
*/

let next = `${base}?${qps}`;

this.router.replaceWith(next);
this.#text = rawText;
this.#cleanup();
});
};
}
22 changes: 18 additions & 4 deletions apps/repl/tests/application/-page/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,19 @@ export class Page extends PageObject {

async expectRedirectToContent(
to: string,
{ c, t, format }: { t?: string; c?: string; format?: string } = {}
{
c,
t,
format,
checks,
}: { t?: string; c?: string; format?: string; checks?: { aborted?: boolean } } = {}
) {
let sawExpectedError = false;

let _checks = {
aborted: checks?.aborted ?? true,
};

try {
await visit(to);
} catch (e) {
Expand All @@ -45,14 +54,19 @@ export class Page extends PageObject {
sawExpectedError = true;
}

assert(`Expected to see a TransitionAborted error, but it did not occur.`, sawExpectedError);
if (_checks.aborted) {
assert(
`Expected to see a TransitionAborted error, but it did not occur. currentURL: ${currentURL()}`,
sawExpectedError
);
}

// Allow time for transitions to settle
await settled();

let url = currentURL();

assert(`Expected an URL, got ${url}`, url);
assert(`Expected an URL -- via currentURL(), got ${url}`, url);

let [, search] = url.split('?');
let query = new URLSearchParams(search);
Expand All @@ -66,7 +80,7 @@ export class Page extends PageObject {
if (c) {
let lzString = query.get('c');

assert(`Missing c query param`, lzString);
assert(`Missing c query param. currentURL: ${url}`, lzString);

let value = decompressFromEncodedURIComponent(lzString);

Expand Down
16 changes: 16 additions & 0 deletions apps/repl/tests/application/editor-format-test.gts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ module('Editor > Format', function (hooks) {
assert.true(page.editor.hasText('hi'), 'has passed text as well');
});

test('after selecting text, it loads again when visiting /', async function (assert) {
await visit(`/edit?format=gjs&t=${defaultText}`);
await page.editor.load();

assert.strictEqual(page.editor.format, 'gjs');
assert.true(page.editor.hasText('hi'), 'has passed text as well');

await page.expectRedirectToContent(`application`, {
format: 'gjs',
t: defaultText,
checks: { aborted: false },
});
assert.strictEqual(page.editor.format, 'gjs');
assert.true(page.editor.hasText('hi'), 'has passed text as well');
});

test('can start with glimdown, and change to gjs', async function (assert) {
await page.expectRedirectToContent(`/edit`, {
format: 'glimdown',
Expand Down
3 changes: 3 additions & 0 deletions apps/repl/tests/test-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ Object.assign(window, { getSettledState, getPendingWaiterState, currentURL, curr

setup(QUnit.assert);

QUnit.testStart(() => {
localStorage.clear();
});
QUnit.testDone(resetOnerror);

start();
Loading