Skip to content

Commit

Permalink
kv_oauth wip
Browse files Browse the repository at this point in the history
  • Loading branch information
EthanThatOneKid committed Jan 16, 2024
1 parent a72ad4a commit f0c3cb1
Show file tree
Hide file tree
Showing 15 changed files with 225 additions and 69 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"deno.enable": true,
"deno.lint": true,
"deno.unstable": true,
"editor.formatOnSave": true,
"editor.defaultFormatter": "denoland.vscode-deno",
"[typescriptreact]": {
Expand Down
8 changes: 4 additions & 4 deletions deno.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
"check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx",
"cli": "echo \"import '\\$fresh/src/dev/cli.ts'\" | deno run --unstable -A -",
"manifest": "deno task cli manifest $(pwd)",
"start": "deno run -A --watch=static/,routes/ dev.ts",
"build": "deno run -A dev.ts build",
"preview": "deno run -A main.ts",
"start": "deno run -A --env --unstable --watch=static/,routes/ dev.ts",
"build": "deno run -A --env --unstable dev.ts build",
"preview": "deno run -A --env --unstable main.ts",
"update": "deno run -A -r https://fresh.deno.dev/update ."
},
"lint": { "rules": { "tags": ["fresh", "recommended"] } },
"exclude": ["**/_fresh/*"],
"imports": {
"$fresh/": "https://deno.land/x/fresh@1.6.1/",
"$fresh/": "https://deno.land/x/fresh@1.6.3/",
"preact": "https://esm.sh/preact@10.19.2",
"preact/": "https://esm.sh/preact@10.19.2/",
"@preact/signals": "https://esm.sh/*@preact/signals@1.2.1",
Expand Down
7 changes: 6 additions & 1 deletion fresh.config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { defineConfig } from "$fresh/server.ts";
import { kvOAuthPlugin } from "#/plugins/kv_oauth/mod.ts";
import { kv } from "#/lib/resources/kv.ts";
import { denoBlocksAPIPlugin } from "#/plugins/deno_blocks_api/mod.ts";

export default defineConfig({
plugins: [
kvOAuthPlugin,
kvOAuthPlugin(kv),
denoBlocksAPIPlugin({
kv,
}),
],
});
4 changes: 2 additions & 2 deletions fresh.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import * as $_404 from "./routes/_404.tsx";
import * as $_app from "./routes/_app.tsx";
import * as $index from "./routes/index.tsx";
import * as $blockly_island from "./islands/blockly_island.tsx";
import * as $deno_blocks_ide_island from "./islands/deno_blocks_ide_island.tsx";
import { type Manifest } from "$fresh/server.ts";

const manifest = {
Expand All @@ -15,7 +15,7 @@ const manifest = {
"./routes/index.tsx": $index,
},
islands: {
"./islands/blockly_island.tsx": $blockly_island,
"./islands/deno_blocks_ide_island.tsx": $deno_blocks_ide_island,
},
baseUrl: import.meta.url,
} satisfies Manifest;
Expand Down
14 changes: 12 additions & 2 deletions islands/blockly_island.tsx → islands/deno_blocks_ide_island.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { useEffect, useRef } from "preact/hooks";
import { denoBlockly } from "#/lib/blockly/examples/deno_blockly/mod.ts";
import {
denoBlockly,
type DenoBlocklyOptions,
} from "#/lib/blockly/examples/deno_blockly/mod.ts";

export default function BlocklyIsland() {
export type DenoBlocksIDEIslandProps = Pick<
DenoBlocklyOptions,
"getInitialWorkspace" | "onWorkspaceChange"
>;

export default function DenoBlocksIDEIsland(props: DenoBlocksIDEIslandProps) {
const blocklyRef = useRef<HTMLDivElement>(null);
const codeRef = useRef<HTMLElement>(null);

Expand All @@ -13,6 +21,8 @@ export default function BlocklyIsland() {
denoBlockly({
blocklyElement: blocklyRef.current,
codeElement: codeRef.current,
getInitialWorkspace: props.getInitialWorkspace,
onWorkspaceChange: props.onWorkspaceChange,
});
}, [blocklyRef, codeRef]);

Expand Down
107 changes: 57 additions & 50 deletions lib/blockly/blockly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,47 @@ export interface BlocklyOptions extends Blockly.BlocklyOptions {
// deno-lint-ignore no-explicit-any
blocks: any[];
name: string;
generator(g: Blockly.CodeGenerator): void;
storageKey?: string;
generator: (g: Blockly.CodeGenerator) => void;
getInitialWorkspace?: () => Blockly.Workspace | undefined;
onWorkspaceChange?: (workspace: Blockly.Workspace) => void;
}

/**
* setWorkspace saves the state of the workspace to browser's local storage.
*/
export function setWorkspace(storageKey: string, workspace: Blockly.Workspace) {
const data = Blockly.serialization.workspaces.save(workspace);
window.localStorage?.setItem(storageKey, JSON.stringify(data));
}

/**
* getWorkspace loads saved state from local storage into the given workspace.
*/
export function getWorkspace(
storageKey: string,
): Blockly.Workspace | undefined {
if (!storageKey) {
return;
}

const data = window.localStorage?.getItem(storageKey);
if (!data) {
return;
}

return JSON.parse(data) as Blockly.Workspace;
}

/**
* @see
* https://github.com/google/blockly-samples/blob/master/examples/custom-generator-codelab/src/index.js
*/
export function blockly(options: BlocklyOptions) {
// TODO: Replace JSON definitions with TypeScript definitions.
// Reference: https://developers.google.com/blockly/guides/create-custom-blocks/define-blocks
const blocks = Blockly.common.createBlockDefinitionsFromJsonArray(
options.blocks,
);

/**
* saveWorkspace saves the state of the workspace to browser's local storage.
*/
function saveWorkspace(workspace: Blockly.Workspace) {
if (!options.storageKey) {
return;
}

const data = Blockly.serialization.workspaces.save(workspace);
window.localStorage?.setItem(options.storageKey, JSON.stringify(data));
}

/**
* loadWorkspace loads saved state from local storage into the given workspace.
*/
function loadWorkspace(workspace: Blockly.Workspace) {
if (!options.storageKey) {
return;
}

const data = window.localStorage?.getItem(options.storageKey);
if (!data) return;

// Don't emit events during loading.
Blockly.Events.disable();
Blockly.serialization.workspaces.load(
JSON.parse(data),
workspace,
{ recordUndo: false },
);
Blockly.Events.enable();
}

const generator = new Blockly.Generator(options.name);
options.generator(generator);

Expand All @@ -76,23 +69,35 @@ export function blockly(options: BlocklyOptions) {
options.codeElement.textContent = code;
}

loadWorkspace(workspace);
updateCode();
if (options.getInitialWorkspace) {
const initialWorkspace = options.getInitialWorkspace();

if (initialWorkspace) {
// Don't emit events during loading.
Blockly.Events.disable();
Blockly.serialization.workspaces.load(
initialWorkspace,
workspace,
{ recordUndo: false },
);
Blockly.Events.enable();
}
}

if (options.storageKey) {
// Every time the workspace changes state, save the changes to storage.
workspace.addChangeListener((event) => {
// UI events are things like scrolling, zooming, etc.
// No need to save after one of these.
if (event.isUiEvent) {
return;
}
// Every time the workspace changes state, save the changes to storage.
workspace.addChangeListener((event) => {
// UI events are things like scrolling, zooming, etc.
// No need to save after one of these.
if (event.isUiEvent) {
return;
}

// TODO: Add debounce.
// TODO: Add debounce.

saveWorkspace(workspace);
});
}
if (options.onWorkspaceChange) {
options.onWorkspaceChange(workspace);
}
});

if (options.codeElement) {
// Whenever the workspace changes meaningfully, run the code again.
Expand All @@ -113,4 +118,6 @@ export function blockly(options: BlocklyOptions) {
updateCode();
});
}

updateCode();
}
14 changes: 11 additions & 3 deletions lib/blockly/examples/deno_blockly/deno_blockly.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { default as Blockly } from "blockly";
import { blockly, type BlocklyOptions } from "#/lib/blockly/mod.ts";
import {
blockly,
type BlocklyOptions,
getWorkspace,
setWorkspace,
} from "#/lib/blockly/mod.ts";
import { storageKey } from "./storage_key.ts";

const TOOLBOX: Blockly.utils.toolbox.ToolboxDefinition = {
Expand Down Expand Up @@ -253,7 +258,7 @@ const THEME = Blockly.Theme.defineTheme("deno", {

export type DenoBlocklyOptions = Pick<
BlocklyOptions,
"blocklyElement" | "codeElement"
"blocklyElement" | "codeElement" | "getInitialWorkspace" | "onWorkspaceChange"
>;

export function denoBlockly(options: DenoBlocklyOptions) {
Expand All @@ -264,7 +269,10 @@ export function denoBlockly(options: DenoBlocklyOptions) {
blocks: BLOCKS,
generator: GENERATOR,
theme: THEME,
storageKey,
getInitialWorkspace: options.getInitialWorkspace ??
(() => getWorkspace(storageKey)),
onWorkspaceChange: options.onWorkspaceChange ??
((workspace) => setWorkspace(storageKey, workspace)),
trashcan: true,
});
}
14 changes: 11 additions & 3 deletions lib/blockly/examples/json_blockly/json_blockly.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { default as Blockly } from "blockly";
import { blockly, type BlocklyOptions } from "#/lib/blockly/mod.ts";
import {
blockly,
type BlocklyOptions,
getWorkspace,
setWorkspace,
} from "#/lib/blockly/mod.ts";
import { storageKey } from "./storage_key.ts";

const TOOLBOX: Blockly.utils.toolbox.ToolboxDefinition = {
Expand Down Expand Up @@ -157,7 +162,7 @@ function GENERATOR(g: Blockly.CodeGenerator) {

export type JSONBlocklyOptions = Pick<
BlocklyOptions,
"blocklyElement" | "codeElement"
"blocklyElement" | "codeElement" | "getInitialWorkspace" | "onWorkspaceChange"
>;

export function jsonBlockly(options: JSONBlocklyOptions) {
Expand All @@ -167,6 +172,9 @@ export function jsonBlockly(options: JSONBlocklyOptions) {
toolbox: TOOLBOX,
blocks: BLOCKS,
generator: GENERATOR,
storageKey,
getInitialWorkspace: options.getInitialWorkspace ??
(() => getWorkspace(storageKey)),
onWorkspaceChange: options.onWorkspaceChange ??
((workspace) => setWorkspace(storageKey, workspace)),
});
}
27 changes: 27 additions & 0 deletions lib/github_api/github_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export const GITHUB_API_URL = "https://api.github.com";

export function makeGitHubAPIURL(path: string): string {
return `${GITHUB_API_URL}${path}`;
}

export interface GitHubAPIUser {
login: string;
avatar_url: string;
html_url: string;
}

export async function getGitHubAPIUserByAccessToken(
accessToken: string,
): Promise<GitHubAPIUser> {
const response = await fetch(makeGitHubAPIURL("/user"), {
headers: {
"Authorization": `Bearer ${accessToken}`,
},
});
if (!response.ok) {
await response.body?.cancel();
throw new Error(`Failed to get user: ${response.statusText}`);
}

return await response.json() as GitHubAPIUser;
}
1 change: 1 addition & 0 deletions lib/github_api/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./github_api.ts";
3 changes: 3 additions & 0 deletions lib/resources/kv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/// <reference lib="deno.unstable" />

export const kv = await Deno.openKv();
Loading

0 comments on commit f0c3cb1

Please sign in to comment.