create-tinybase 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -18
- package/cli.js +2 -2
- package/package.json +3 -3
- package/screenshots/chat.png +0 -0
- package/screenshots/todos.png +0 -0
- package/templates/README.md.hbs +13 -12
- package/templates/client/eslint.config.js.hbs +7 -0
- package/templates/client/package.json.hbs +28 -8
- package/templates/client/src/chat/App.tsx.hbs +28 -22
- package/templates/client/src/chat/ChatStore.tsx.hbs +108 -27
- package/templates/client/src/chat/Message.tsx.hbs +8 -9
- package/templates/client/src/chat/MessageInput.tsx.hbs +7 -7
- package/templates/client/src/chat/Messages.tsx.hbs +5 -15
- package/templates/client/src/chat/SettingsStore.tsx.hbs +86 -9
- package/templates/client/src/chat/UsernameInput.tsx.hbs +6 -11
- package/templates/client/src/chat/app.ts.hbs +18 -18
- package/templates/client/src/chat/chatStore.ts.hbs +97 -35
- package/templates/client/src/chat/message.ts.hbs +4 -3
- package/templates/client/src/chat/messageInput.css.hbs +1 -1
- package/templates/client/src/chat/messageInput.ts.hbs +7 -8
- package/templates/client/src/chat/messages.ts.hbs +7 -12
- package/templates/client/src/chat/settingsStore.ts.hbs +65 -6
- package/templates/client/src/chat/usernameInput.css.hbs +1 -1
- package/templates/client/src/chat/usernameInput.ts.hbs +6 -6
- package/templates/client/src/chat/utils.ts.hbs +26 -0
- package/templates/client/src/drawing/App.tsx.hbs +26 -20
- package/templates/client/src/drawing/BrushSize.tsx.hbs +8 -11
- package/templates/client/src/drawing/Canvas.tsx.hbs +65 -73
- package/templates/client/src/drawing/CanvasStore.tsx.hbs +104 -18
- package/templates/client/src/drawing/ColorPicker.tsx.hbs +4 -11
- package/templates/client/src/drawing/DrawingControls.tsx.hbs +7 -7
- package/templates/client/src/drawing/SettingsStore.tsx.hbs +81 -8
- package/templates/client/src/drawing/app.ts.hbs +18 -8
- package/templates/client/src/drawing/brushSize.ts.hbs +12 -5
- package/templates/client/src/drawing/canvas.ts.hbs +84 -86
- package/templates/client/src/drawing/canvasStore.ts.hbs +93 -26
- package/templates/client/src/drawing/colorPicker.ts.hbs +3 -3
- package/templates/client/src/drawing/drawingControls.ts.hbs +7 -7
- package/templates/client/src/drawing/settingsStore.ts.hbs +63 -8
- package/templates/client/src/game/App.tsx.hbs +20 -16
- package/templates/client/src/game/Board.tsx.hbs +8 -8
- package/templates/client/src/game/Game.tsx.hbs +14 -21
- package/templates/client/src/game/GameStatus.tsx.hbs +5 -5
- package/templates/client/src/game/Square.tsx.hbs +6 -11
- package/templates/client/src/game/Store.tsx.hbs +106 -16
- package/templates/client/src/game/app.ts.hbs +17 -6
- package/templates/client/src/game/board.ts.hbs +7 -7
- package/templates/client/src/game/game.ts.hbs +12 -18
- package/templates/client/src/game/gameStatus.ts.hbs +3 -3
- package/templates/client/src/game/square.ts.hbs +4 -4
- package/templates/client/src/game/store.ts.hbs +95 -23
- package/templates/client/src/index.tsx.hbs +5 -7
- package/templates/client/src/shared/Button.tsx.hbs +3 -3
- package/templates/client/src/shared/Input.tsx.hbs +2 -2
- package/templates/client/src/shared/Loading.tsx.hbs +5 -0
- package/templates/client/src/shared/button.ts.hbs +2 -2
- package/templates/client/src/shared/config.ts.hbs +4 -6
- package/templates/client/src/shared/input.ts.hbs +2 -2
- package/templates/client/src/shared/loading.css.hbs +21 -0
- package/templates/client/src/shared/loading.ts.hbs +13 -0
- package/templates/client/src/shared/pglite.ts.hbs +10 -0
- package/templates/client/src/shared/sqlite.ts.hbs +17 -0
- package/templates/client/src/todos/App.tsx.hbs +22 -22
- package/templates/client/src/todos/Store.tsx.hbs +106 -23
- package/templates/client/src/todos/TodoInput.tsx.hbs +6 -8
- package/templates/client/src/todos/TodoItem.tsx.hbs +5 -6
- package/templates/client/src/todos/TodoList.tsx.hbs +5 -6
- package/templates/client/src/todos/app.ts.hbs +16 -10
- package/templates/client/src/todos/store.ts.hbs +94 -30
- package/templates/client/src/todos/todoInput.ts.hbs +5 -8
- package/templates/client/src/todos/todoItem.ts.hbs +3 -4
- package/templates/client/src/todos/todoList.ts.hbs +6 -8
- package/templates/client/vite-env.d.ts.hbs +4 -0
- package/templates/client/vite.config.js.hbs +53 -3
- package/templates/server/index-do.ts.hbs +10 -14
- package/templates/server/index-node.ts.hbs +3 -3
- package/templates/server/package.json.hbs +5 -5
- package/templates/server/wrangler.toml.hbs +1 -1
- /package/templates/client/{.prettierrc.hbs → .prettierrc.json.hbs} +0 -0
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{{#if schemas}}
|
|
2
|
-
import {createStore} from 'tinybase/with-schemas';
|
|
3
|
-
import * as UiReact from 'tinybase/ui-react/with-schemas';
|
|
4
|
-
import {type NoTablesSchema} from 'tinybase/with-schemas';
|
|
2
|
+
{{addImport "import {createStore} from 'tinybase/with-schemas';"}}
|
|
3
|
+
{{addImport "import * as UiReact from 'tinybase/ui-react/with-schemas';"}}
|
|
4
|
+
{{addImport "import {type NoTablesSchema} from 'tinybase/with-schemas';"}}
|
|
5
5
|
{{else}}
|
|
6
|
-
import {createStore} from 'tinybase';
|
|
7
|
-
import {useCreateStore, useProvideStore, useValue,
|
|
6
|
+
{{addImport "import {createStore} from 'tinybase';"}}
|
|
7
|
+
{{addImport "import {useCreateStore, useProvideStore, useValue, useValueState} from 'tinybase/ui-react';"}}
|
|
8
8
|
{{/if}}
|
|
9
9
|
|
|
10
|
+
{{includeFile template="client/src/chat/utils.ts.hbs" output="client/src/utils.{{ext}}"}}
|
|
11
|
+
{{addImport "import {getRandomName} from './utils';"}}
|
|
12
|
+
|
|
10
13
|
export const STORE_ID = 'settings';
|
|
11
14
|
|
|
12
15
|
{{#if schemas}}
|
|
@@ -16,19 +19,93 @@ export const STORE_ID = 'settings';
|
|
|
16
19
|
|
|
17
20
|
type Schemas = [NoTablesSchema, typeof VALUES_SCHEMA];
|
|
18
21
|
|
|
19
|
-
const {useCreateStore, useProvideStore, useValue,
|
|
22
|
+
const {useCreateStore, {{#if persist}}useCreatePersister, {{/if}}useProvideStore, useValue, useValueState} = UiReact as UiReact.WithSchemas<Schemas>;
|
|
20
23
|
{{/if}}
|
|
21
24
|
|
|
22
|
-
export {useValue,
|
|
25
|
+
export {useValue, useValueState};
|
|
23
26
|
|
|
24
|
-
export const SettingsStore = () => {
|
|
27
|
+
export const SettingsStore = ({onReady}: {onReady?: () => void}) => {
|
|
25
28
|
const store = useCreateStore(() =>
|
|
26
29
|
createStore(){{#if schemas}}
|
|
27
30
|
.setValuesSchema(VALUES_SCHEMA){{/if}}
|
|
28
|
-
.setValue('username',
|
|
31
|
+
.setValue('username', getRandomName()),
|
|
29
32
|
);
|
|
30
33
|
|
|
31
34
|
useProvideStore(STORE_ID, store);
|
|
32
35
|
|
|
36
|
+
{{#if persist}}
|
|
37
|
+
{{#if persistLocalStorage}}
|
|
38
|
+
{{#if schemas}}
|
|
39
|
+
{{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser/with-schemas';"}}
|
|
40
|
+
{{else}}
|
|
41
|
+
{{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser';"}}
|
|
42
|
+
{{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
|
|
43
|
+
{{/if}}
|
|
44
|
+
|
|
45
|
+
useCreatePersister(
|
|
46
|
+
store,
|
|
47
|
+
(store) => createLocalPersister(store, STORE_ID),
|
|
48
|
+
[],
|
|
49
|
+
async (persister) => {
|
|
50
|
+
await persister.load();
|
|
51
|
+
await persister.startAutoSave();
|
|
52
|
+
onReady?.();
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
{{/if}}
|
|
56
|
+
{{#if persistSqlite}}
|
|
57
|
+
{{#if schemas}}
|
|
58
|
+
{{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm/with-schemas';"}}
|
|
59
|
+
{{else}}
|
|
60
|
+
{{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';"}}
|
|
61
|
+
{{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
|
|
62
|
+
{{/if}}
|
|
63
|
+
{{includeFile template="client/src/shared/sqlite.ts.hbs" output="client/src/sqlite.{{ext}}"}}
|
|
64
|
+
{{addImport "import {getDb} from './sqlite';"}}
|
|
65
|
+
|
|
66
|
+
useCreatePersister(
|
|
67
|
+
store,
|
|
68
|
+
async (store) => {
|
|
69
|
+
const {sqlite3, db} = await getDb();
|
|
70
|
+
return createSqliteWasmPersister(store, sqlite3, db, STORE_ID);
|
|
71
|
+
},
|
|
72
|
+
[],
|
|
73
|
+
async (persister) => {
|
|
74
|
+
await persister.load();
|
|
75
|
+
await persister.startAutoSave();
|
|
76
|
+
onReady?.();
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
{{/if}}
|
|
80
|
+
{{#if persistPglite}}
|
|
81
|
+
{{#if schemas}}
|
|
82
|
+
{{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite/with-schemas';"}}
|
|
83
|
+
{{else}}
|
|
84
|
+
{{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite';"}}
|
|
85
|
+
{{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
|
|
86
|
+
{{/if}}
|
|
87
|
+
{{includeFile template="client/src/shared/pglite.ts.hbs" output="client/src/pglite.{{ext}}"}}
|
|
88
|
+
{{addImport "import {getPgLite} from './pglite';"}}
|
|
89
|
+
|
|
90
|
+
useCreatePersister(
|
|
91
|
+
store,
|
|
92
|
+
async (store) => {
|
|
93
|
+
const pgLite = await getPgLite();
|
|
94
|
+
return await createPglitePersister(store, pgLite, STORE_ID);
|
|
95
|
+
},
|
|
96
|
+
[],
|
|
97
|
+
async (persister) => {
|
|
98
|
+
await persister.load();
|
|
99
|
+
await persister.startAutoSave();
|
|
100
|
+
onReady?.();
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
{{/if}}
|
|
104
|
+
{{/if}}
|
|
105
|
+
|
|
106
|
+
{{#unless persist}}{{#unless sync}}
|
|
107
|
+
onReady?.();
|
|
108
|
+
{{/unless}}{{/unless}}
|
|
109
|
+
|
|
33
110
|
return null;
|
|
34
111
|
};
|
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
{{includeFile template="client/src/chat/usernameInput.css.hbs" output="client/src/usernameInput.css"}}
|
|
2
|
-
import {
|
|
3
|
-
import './usernameInput.css';
|
|
2
|
+
{{addImport "import {useValueState, STORE_ID} from './SettingsStore';"}}
|
|
3
|
+
{{addImport "import './usernameInput.css';"}}
|
|
4
4
|
|
|
5
5
|
{{includeFile template="client/src/shared/Input.tsx.hbs" output="client/src/Input.{{ext}}"}}
|
|
6
|
-
import {Input} from './Input';
|
|
6
|
+
{{addImport "import {Input} from './Input';"}}
|
|
7
7
|
|
|
8
8
|
export const UsernameInput = () => {
|
|
9
|
-
const username =
|
|
10
|
-
const setUsername = useSetValueCallback(
|
|
11
|
-
'username',
|
|
12
|
-
(_e) => _e,[],
|
|
13
|
-
STORE_ID,
|
|
14
|
-
);
|
|
9
|
+
const [username, setUsername] = useValueState('username', STORE_ID);
|
|
15
10
|
|
|
16
11
|
return (
|
|
17
12
|
<div id="usernameInput">
|
|
18
13
|
<label>Your name:</label>
|
|
19
|
-
<Input value={username} onChange={setUsername} placeholder="Enter your name" />
|
|
14
|
+
<Input value={username + '' } onChange={setUsername} placeholder="Enter your name" />
|
|
20
15
|
</div>
|
|
21
16
|
);
|
|
22
|
-
};
|
|
17
|
+
};
|
|
@@ -1,39 +1,39 @@
|
|
|
1
1
|
{{includeFile template="client/src/chat/settingsStore.ts.hbs" output="client/src/settingsStore.ts"}}
|
|
2
|
-
import {settingsStore} from './settingsStore';
|
|
2
|
+
{{addImport "import {settingsStore, settingsStoreReady} from './settingsStore';"}}
|
|
3
3
|
|
|
4
4
|
{{includeFile template="client/src/chat/chatStore.ts.hbs" output="client/src/chatStore.ts"}}
|
|
5
|
-
import {chatStore} from './chatStore';
|
|
5
|
+
{{addImport "import {chatStore, chatStoreReady} from './chatStore';"}}
|
|
6
6
|
|
|
7
|
-
{{includeFile template="client/src/shared/
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
{{includeFile template="client/src/shared/input.ts.hbs" output="client/src/input.{{ext}}"}}
|
|
11
|
-
import {createInput} from './input';
|
|
12
|
-
|
|
13
|
-
{{includeFile template="client/src/chat/message.ts.hbs" output="client/src/message.{{ext}}"}}
|
|
14
|
-
import {createMessage} from './message';
|
|
7
|
+
{{includeFile template="client/src/shared/loading.ts.hbs" output="client/src/loading.{{ext}}"}}
|
|
8
|
+
{{addImport "import {showLoading, hideLoading} from './loading';"}}
|
|
15
9
|
|
|
16
10
|
{{includeFile template="client/src/chat/messages.ts.hbs" output="client/src/messages.{{ext}}"}}
|
|
17
|
-
import {createMessages} from './messages';
|
|
11
|
+
{{addImport "import {createMessages} from './messages';"}}
|
|
18
12
|
|
|
19
13
|
{{includeFile template="client/src/chat/usernameInput.ts.hbs" output="client/src/usernameInput.{{ext}}"}}
|
|
20
|
-
import {createUsernameInput} from './usernameInput';
|
|
14
|
+
{{addImport "import {createUsernameInput} from './usernameInput';"}}
|
|
21
15
|
|
|
22
16
|
{{includeFile template="client/src/chat/messageInput.ts.hbs" output="client/src/messageInput.{{ext}}"}}
|
|
23
|
-
import {createMessageInput} from './messageInput';
|
|
17
|
+
{{addImport "import {createMessageInput} from './messageInput';"}}
|
|
24
18
|
|
|
25
|
-
const app = () => {
|
|
19
|
+
export const app = async () => {
|
|
26
20
|
const appContainer = document.getElementById('app')!;
|
|
27
21
|
|
|
22
|
+
// Show loading spinner
|
|
23
|
+
const loadingDiv = showLoading(appContainer);
|
|
24
|
+
|
|
25
|
+
// Wait for stores to be ready
|
|
26
|
+
await Promise.all([settingsStoreReady, chatStoreReady]);
|
|
27
|
+
|
|
28
|
+
// Remove loading spinner
|
|
29
|
+
hideLoading(loadingDiv);
|
|
30
|
+
|
|
28
31
|
appContainer.appendChild(createUsernameInput(settingsStore, chatStore));
|
|
29
32
|
appContainer.appendChild(createMessages(chatStore));
|
|
30
33
|
|
|
31
34
|
const messageInputContainer = createMessageInput(settingsStore, chatStore);
|
|
32
35
|
appContainer.appendChild(messageInputContainer);
|
|
33
36
|
|
|
34
|
-
// Focus the input after it's in the DOM
|
|
35
37
|
const messageInput = messageInputContainer.querySelector('input')!;
|
|
36
38
|
messageInput.focus();
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export {app};
|
|
39
|
+
};
|
|
@@ -1,50 +1,112 @@
|
|
|
1
1
|
{{#if schemas}}
|
|
2
|
-
import {createMergeableStore} from 'tinybase/with-schemas';
|
|
2
|
+
{{addImport "import {createMergeableStore, type Row} from 'tinybase/with-schemas';"}}
|
|
3
3
|
{{else}}
|
|
4
|
-
import {createMergeableStore} from 'tinybase';
|
|
4
|
+
{{addImport "import {createMergeableStore} from 'tinybase';"}}
|
|
5
5
|
{{/if}}
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
{{includeFile template="client/src/chat/utils.ts.hbs" output="client/src/utils.ts"}}
|
|
8
|
+
{{addImport "import {getDefaultContent} from './utils';"}}
|
|
8
9
|
|
|
9
10
|
{{#if schemas}}
|
|
10
11
|
const TABLES_SCHEMA = {
|
|
11
12
|
messages: {
|
|
12
|
-
username: {type: 'string'},
|
|
13
|
-
text: {type: 'string'},
|
|
14
|
-
timestamp: {type: 'number'},
|
|
13
|
+
username: {type: 'string', default: ''},
|
|
14
|
+
text: {type: 'string', default: ''},
|
|
15
|
+
timestamp: {type: 'number', default: 0},
|
|
15
16
|
},
|
|
16
17
|
} as const;
|
|
17
18
|
|
|
18
19
|
{{/if}}
|
|
19
|
-
export const chatStore = createMergeableStore(
|
|
20
|
+
export const chatStore = createMergeableStore(){{#if schemas}}
|
|
20
21
|
.setTablesSchema(TABLES_SCHEMA){{/if}}
|
|
21
|
-
.setDefaultContent(
|
|
22
|
-
|
|
23
|
-
messages: {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
{},
|
|
29
|
-
]);
|
|
30
|
-
|
|
31
|
-
{{#if sync}}
|
|
32
|
-
{{includeFile template="client/src/shared/config.ts.hbs" output="client/src/config.ts"}}
|
|
33
|
-
{{addImport "import {SERVER} from './config';"}}
|
|
34
|
-
{{addImport "import ReconnectingWebSocket from 'reconnecting-websocket';"}}
|
|
35
|
-
{{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';"}}
|
|
36
|
-
|
|
37
|
-
const serverPathId = location.pathname;
|
|
38
|
-
createWsSynchronizer(
|
|
39
|
-
chatStore,
|
|
40
|
-
new ReconnectingWebSocket(SERVER + serverPathId),
|
|
41
|
-
).then(async (synchronizer) => {
|
|
42
|
-
await synchronizer.startSync();
|
|
43
|
-
|
|
44
|
-
synchronizer.getWebSocket().addEventListener('open', () => {
|
|
45
|
-
synchronizer.load().then(() => synchronizer.save());
|
|
46
|
-
});
|
|
22
|
+
.setDefaultContent(getDefaultContent());
|
|
23
|
+
|
|
24
|
+
export type MessageRow = {{#if schemas}}Row<typeof TABLES_SCHEMA, 'messages'>{{else}}{username: string; text: string; timestamp: number}{{/if}};
|
|
25
|
+
|
|
26
|
+
let resolveChatReady: () => void;
|
|
27
|
+
export const chatStoreReady = new Promise<void>((resolve) => {
|
|
28
|
+
resolveChatReady = resolve;
|
|
47
29
|
});
|
|
48
|
-
{{/if}}
|
|
49
30
|
|
|
50
|
-
|
|
31
|
+
{{#if persist}}
|
|
32
|
+
{{#if persistLocalStorage}}
|
|
33
|
+
{{#if schemas}}
|
|
34
|
+
{{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser/with-schemas';"}}
|
|
35
|
+
{{else}}
|
|
36
|
+
{{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser';"}}
|
|
37
|
+
{{/if}}
|
|
38
|
+
|
|
39
|
+
const chatPersister = createLocalPersister(chatStore, 'chat');
|
|
40
|
+
chatPersister.load().then(() => {
|
|
41
|
+
chatPersister.startAutoSave();
|
|
42
|
+
resolveChatReady();
|
|
43
|
+
});
|
|
44
|
+
{{/if}}
|
|
45
|
+
{{#if persistSqlite}}
|
|
46
|
+
{{#if schemas}}
|
|
47
|
+
{{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm/with-schemas';"}}
|
|
48
|
+
{{else}}
|
|
49
|
+
{{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';"}}
|
|
50
|
+
{{/if}}
|
|
51
|
+
{{includeFile template="client/src/shared/sqlite.ts.hbs" output="client/src/sqlite.ts"}}
|
|
52
|
+
{{addImport "import {getDb} from './sqlite';"}}
|
|
53
|
+
|
|
54
|
+
getDb().then(({sqlite3, db}) => {
|
|
55
|
+
const chatPersister = createSqliteWasmPersister(chatStore, sqlite3, db, 'chat');
|
|
56
|
+
chatPersister.load().then(() => {
|
|
57
|
+
chatPersister.startAutoSave();
|
|
58
|
+
resolveChatReady();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
{{/if}}
|
|
62
|
+
{{#if persistPglite}}
|
|
63
|
+
{{#if schemas}}
|
|
64
|
+
{{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite/with-schemas';"}}
|
|
65
|
+
{{else}}
|
|
66
|
+
{{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite';"}}
|
|
67
|
+
{{/if}}
|
|
68
|
+
{{includeFile template="client/src/shared/pglite.ts.hbs" output="client/src/pglite.{{ext}}"}}
|
|
69
|
+
{{addImport "import {getPgLite} from './pglite';"}}
|
|
70
|
+
|
|
71
|
+
getPgLite().then((pgLite) => {
|
|
72
|
+
createPglitePersister(chatStore, pgLite, 'chat').then((chatPersister) => {
|
|
73
|
+
chatPersister.load().then(() => {
|
|
74
|
+
chatPersister.startAutoSave();
|
|
75
|
+
resolveChatReady();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
{{/if}}
|
|
80
|
+
{{/if}}
|
|
81
|
+
|
|
82
|
+
{{#if sync}}
|
|
83
|
+
{{includeFile template="client/src/shared/config.ts.hbs" output="client/src/config.ts"}}
|
|
84
|
+
{{addImport "import {SERVER} from './config';"}}
|
|
85
|
+
{{addImport "import ReconnectingWebSocket from 'reconnecting-websocket';"}}
|
|
86
|
+
{{#if schemas}}
|
|
87
|
+
{{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client/with-schemas';"}}
|
|
88
|
+
{{else}}
|
|
89
|
+
{{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';"}}
|
|
90
|
+
{{/if}}
|
|
91
|
+
|
|
92
|
+
const serverPathId = location.pathname;
|
|
93
|
+
createWsSynchronizer(
|
|
94
|
+
chatStore,
|
|
95
|
+
new ReconnectingWebSocket(SERVER + serverPathId),
|
|
96
|
+
).then(async (synchronizer) => {
|
|
97
|
+
await synchronizer.startSync();
|
|
98
|
+
|
|
99
|
+
synchronizer.getWebSocket().addEventListener('open', () => {
|
|
100
|
+
synchronizer.load().then(() => {
|
|
101
|
+
synchronizer.save();
|
|
102
|
+
{{#unless persist}}resolveChatReady();{{/unless}}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
{{/if}}
|
|
107
|
+
|
|
108
|
+
{{#unless persist}}{{#unless sync}}
|
|
109
|
+
resolveChatReady();
|
|
110
|
+
{{/unless}}{{/unless}}
|
|
111
|
+
|
|
112
|
+
export type ChatStore = typeof chatStore;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{{includeFile template="client/src/chat/message.css.hbs" output="client/src/message.css"}}
|
|
2
|
-
import './message.css';
|
|
2
|
+
{{addImport "import './message.css';"}}
|
|
3
|
+
{{addImport "import type {MessageRow} from './chatStore';"}}
|
|
3
4
|
|
|
4
|
-
export const createMessage = (username
|
|
5
|
+
export const createMessage = ({username, text, timestamp}: MessageRow): HTMLDivElement => {
|
|
5
6
|
const time = new Date(timestamp).toLocaleTimeString();
|
|
6
7
|
|
|
7
8
|
const messageDiv = document.createElement('div');
|
|
@@ -24,4 +25,4 @@ messageDiv.appendChild(textSpan);
|
|
|
24
25
|
messageDiv.appendChild(timeSpan);
|
|
25
26
|
|
|
26
27
|
return messageDiv;
|
|
27
|
-
};
|
|
28
|
+
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{{includeFile template="client/src/chat/messageInput.css.hbs" output="client/src/messageInput.css"}}
|
|
2
|
-
import './messageInput.css';
|
|
3
|
-
import {type SettingsStore} from './settingsStore';
|
|
4
|
-
import {type ChatStore} from './chatStore';
|
|
2
|
+
{{addImport "import './messageInput.css';"}}
|
|
3
|
+
{{addImport "import {type SettingsStore} from './settingsStore';"}}
|
|
4
|
+
{{addImport "import {type ChatStore} from './chatStore';"}}
|
|
5
5
|
|
|
6
6
|
{{includeFile template="client/src/shared/button.ts.hbs" output="client/src/button.{{ext}}"}}
|
|
7
|
-
import {createButton} from './button';
|
|
7
|
+
{{addImport "import {createButton} from './button';"}}
|
|
8
8
|
|
|
9
9
|
{{includeFile template="client/src/shared/input.ts.hbs" output="client/src/input.{{ext}}"}}
|
|
10
|
-
import {createInput} from './input';
|
|
10
|
+
{{addImport "import {createInput} from './input';"}}
|
|
11
11
|
|
|
12
12
|
export const createMessageInput = (settingsStore: SettingsStore, chatStore: ChatStore): HTMLDivElement => {
|
|
13
13
|
const messageInputContainer = document.createElement('div');
|
|
@@ -20,12 +20,11 @@ const sendMessage = () => {
|
|
|
20
20
|
const text = messageInput.value.trim();
|
|
21
21
|
if (text) {
|
|
22
22
|
chatStore.addRow('messages', {
|
|
23
|
-
username: settingsStore.getValue('username'),
|
|
23
|
+
username: settingsStore.getValue('username') || 'Anonymous',
|
|
24
24
|
text,
|
|
25
25
|
timestamp: Date.now(),
|
|
26
26
|
});
|
|
27
27
|
messageInput.value = '';
|
|
28
|
-
messageInput.focus();
|
|
29
28
|
}
|
|
30
29
|
};
|
|
31
30
|
|
|
@@ -39,4 +38,4 @@ const sendButton = createButton('Send', sendMessage, 'primary');
|
|
|
39
38
|
messageInputContainer.appendChild(sendButton);
|
|
40
39
|
|
|
41
40
|
return messageInputContainer;
|
|
42
|
-
};
|
|
41
|
+
};
|
|
@@ -1,33 +1,28 @@
|
|
|
1
1
|
{{includeFile template="client/src/chat/messages.css.hbs" output="client/src/messages.css"}}
|
|
2
|
-
import './messages.css';
|
|
3
|
-
import {type ChatStore} from './chatStore';
|
|
2
|
+
{{addImport "import './messages.css';"}}
|
|
3
|
+
{{addImport "import {type ChatStore, type MessageRow} from './chatStore';"}}
|
|
4
4
|
|
|
5
5
|
{{includeFile template="client/src/chat/message.ts.hbs" output="client/src/message.{{ext}}"}}
|
|
6
|
-
import {createMessage} from './message';
|
|
6
|
+
{{addImport "import {createMessage} from './message';"}}
|
|
7
7
|
|
|
8
8
|
export const createMessages = (store: ChatStore): HTMLDivElement => {
|
|
9
9
|
const messagesContainer = document.createElement('div');
|
|
10
10
|
messagesContainer.id = 'messages';
|
|
11
11
|
|
|
12
12
|
const updateMessages = () => {
|
|
13
|
-
const
|
|
14
|
-
const sortedIds = Object.keys(messageRows).sort(
|
|
15
|
-
(a, b) => messageRows[a].timestamp - messageRows[b].timestamp,
|
|
16
|
-
);
|
|
13
|
+
const sortedIds = store.getSortedRowIds('messages', 'timestamp');
|
|
17
14
|
|
|
18
15
|
messagesContainer.innerHTML = '';
|
|
19
16
|
sortedIds.forEach((id) => {
|
|
20
|
-
const
|
|
21
|
-
const messageElement = createMessage(
|
|
17
|
+
const messageRow = store.getRow('messages', id) as MessageRow;
|
|
18
|
+
const messageElement = createMessage(messageRow);
|
|
22
19
|
messagesContainer.appendChild(messageElement);
|
|
23
20
|
});
|
|
24
21
|
};
|
|
25
22
|
|
|
26
|
-
// Initial render
|
|
27
23
|
updateMessages();
|
|
28
24
|
|
|
29
|
-
// Listen for changes
|
|
30
25
|
store.addTablesListener(updateMessages);
|
|
31
26
|
|
|
32
27
|
return messagesContainer;
|
|
33
|
-
};
|
|
28
|
+
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{{#if schemas}}
|
|
2
|
-
import {createStore} from 'tinybase/with-schemas';
|
|
2
|
+
{{addImport "import {createStore} from 'tinybase/with-schemas';"}}
|
|
3
3
|
{{else}}
|
|
4
|
-
import {createStore} from 'tinybase';
|
|
4
|
+
{{addImport "import {createStore} from 'tinybase';"}}
|
|
5
5
|
{{/if}}
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
{{includeFile template="client/src/chat/utils.ts.hbs" output="client/src/utils.ts"}}
|
|
8
|
+
{{addImport "import {getRandomName} from './utils';"}}
|
|
8
9
|
|
|
9
10
|
{{#if schemas}}
|
|
10
11
|
const VALUES_SCHEMA = {
|
|
@@ -13,7 +14,65 @@ const STORE_ID = 'settings';
|
|
|
13
14
|
|
|
14
15
|
{{/if}}
|
|
15
16
|
export const settingsStore = createStore(){{#if schemas}}
|
|
16
|
-
.setValuesSchema(VALUES_SCHEMA){{/if}}
|
|
17
|
-
.setValue('username', 'Carol');
|
|
17
|
+
.setValuesSchema(VALUES_SCHEMA){{/if}};
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
let resolveSettingsReady: () => void;
|
|
20
|
+
export const settingsStoreReady = new Promise<void>((resolve) => {
|
|
21
|
+
resolveSettingsReady = resolve;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
{{#if persist}}
|
|
25
|
+
{{#if persistLocalStorage}}
|
|
26
|
+
{{#if schemas}}
|
|
27
|
+
{{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser/with-schemas';"}}
|
|
28
|
+
{{else}}
|
|
29
|
+
{{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser';"}}
|
|
30
|
+
{{/if}}
|
|
31
|
+
|
|
32
|
+
const settingsPersister = createLocalPersister(settingsStore, 'settings');
|
|
33
|
+
settingsPersister.load([{}, {username: getRandomName()}]).then(() => {
|
|
34
|
+
settingsPersister.startAutoSave();
|
|
35
|
+
resolveSettingsReady();
|
|
36
|
+
});
|
|
37
|
+
{{/if}}
|
|
38
|
+
{{#if persistSqlite}}
|
|
39
|
+
{{#if schemas}}
|
|
40
|
+
{{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm/with-schemas';"}}
|
|
41
|
+
{{else}}
|
|
42
|
+
{{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';"}}
|
|
43
|
+
{{/if}}
|
|
44
|
+
{{includeFile template="client/src/shared/sqlite.ts.hbs" output="client/src/sqlite.ts"}}
|
|
45
|
+
{{addImport "import {getDb} from './sqlite';"}}
|
|
46
|
+
|
|
47
|
+
getDb().then(({sqlite3, db}) => {
|
|
48
|
+
const settingsPersister = createSqliteWasmPersister(settingsStore, sqlite3, db, 'settings');
|
|
49
|
+
settingsPersister.load([{}, {username: getRandomName()}]).then(() => {
|
|
50
|
+
settingsPersister.startAutoSave();
|
|
51
|
+
resolveSettingsReady();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
{{/if}}
|
|
55
|
+
{{#if persistPglite}}
|
|
56
|
+
{{#if schemas}}
|
|
57
|
+
{{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite/with-schemas';"}}
|
|
58
|
+
{{else}}
|
|
59
|
+
{{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite';"}}
|
|
60
|
+
{{/if}}
|
|
61
|
+
{{includeFile template="client/src/shared/pglite.ts.hbs" output="client/src/pglite.{{ext}}"}}
|
|
62
|
+
{{addImport "import {getPgLite} from './pglite';"}}
|
|
63
|
+
|
|
64
|
+
getPgLite().then((pgLite) => {
|
|
65
|
+
createPglitePersister(settingsStore, pgLite, 'settings').then((settingsPersister) => {
|
|
66
|
+
settingsPersister.load([{}, {username: getRandomName()}]).then(() => {
|
|
67
|
+
settingsPersister.startAutoSave();
|
|
68
|
+
resolveSettingsReady();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
{{/if}}
|
|
73
|
+
{{else}}
|
|
74
|
+
settingsStore.setValue('username', getRandomName());
|
|
75
|
+
resolveSettingsReady();
|
|
76
|
+
{{/if}}
|
|
77
|
+
|
|
78
|
+
export type SettingsStore = typeof settingsStore;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{{includeFile template="client/src/chat/usernameInput.css.hbs" output="client/src/usernameInput.css"}}
|
|
2
|
-
import './usernameInput.css';
|
|
3
|
-
import {type SettingsStore} from './settingsStore';
|
|
4
|
-
import {type ChatStore} from './chatStore';
|
|
2
|
+
{{addImport "import './usernameInput.css';"}}
|
|
3
|
+
{{addImport "import {type SettingsStore} from './settingsStore';"}}
|
|
4
|
+
{{addImport "import {type ChatStore} from './chatStore';"}}
|
|
5
5
|
|
|
6
6
|
{{includeFile template="client/src/shared/input.ts.hbs" output="client/src/input.{{ext}}"}}
|
|
7
|
-
import {createInput} from './input';
|
|
7
|
+
{{addImport "import {createInput} from './input';"}}
|
|
8
8
|
|
|
9
|
-
export const createUsernameInput = (settingsStore: SettingsStore,
|
|
9
|
+
export const createUsernameInput = (settingsStore: SettingsStore, _chatStore: ChatStore): HTMLDivElement => {
|
|
10
10
|
const container = document.createElement('div');
|
|
11
11
|
container.id = 'usernameInput';
|
|
12
12
|
|
|
@@ -27,4 +27,4 @@ usernameInput.value = settingsStore.getValue('username') as string;
|
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
return container;
|
|
30
|
-
};
|
|
30
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const NAMES = [
|
|
2
|
+
'Carol',
|
|
3
|
+
'Charlie',
|
|
4
|
+
'Casey',
|
|
5
|
+
'Cameron',
|
|
6
|
+
'Chris',
|
|
7
|
+
'Dana',
|
|
8
|
+
'Dakota',
|
|
9
|
+
'Drew',
|
|
10
|
+
'Devon',
|
|
11
|
+
'Dylan',
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
export const getRandomName = (): string =>
|
|
15
|
+
NAMES[Math.floor(Math.random() * NAMES.length)];
|
|
16
|
+
|
|
17
|
+
export const getDefaultContent = (): [any, any] =>
|
|
18
|
+
[
|
|
19
|
+
{
|
|
20
|
+
messages: {
|
|
21
|
+
'1': {username: 'Alice', text: 'Hello!', timestamp: Date.now() - 60000},
|
|
22
|
+
'2': {username: 'Bob', text: 'Hi there!', timestamp: Date.now() - 30000},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{},
|
|
26
|
+
];
|
|
@@ -1,36 +1,42 @@
|
|
|
1
|
-
import {StrictMode} from 'react';
|
|
1
|
+
{{addImport "import {StrictMode, useState} from 'react';"}}
|
|
2
|
+
{{includeFile template="client/src/shared/Loading.tsx.hbs" output="client/src/Loading.{{ext}}"}}
|
|
3
|
+
{{addImport "import {Loading} from './Loading';"}}
|
|
2
4
|
|
|
3
|
-
{{
|
|
4
|
-
|
|
5
|
-
{{else}}
|
|
6
|
-
import {Provider} from 'tinybase/ui-react';
|
|
7
|
-
{{/if}}
|
|
8
|
-
import {Inspector} from 'tinybase/ui-react-inspector';
|
|
5
|
+
{{addImport "import {Provider} from 'tinybase/ui-react';"}}
|
|
6
|
+
{{addImport "import {Inspector} from 'tinybase/ui-react-inspector';"}}
|
|
9
7
|
|
|
10
8
|
{{includeFile template="client/src/drawing/SettingsStore.tsx.hbs" output="client/src/SettingsStore.{{ext}}"}}
|
|
11
|
-
import {SettingsStore} from './SettingsStore';
|
|
9
|
+
{{addImport "import {SettingsStore} from './SettingsStore';"}}
|
|
12
10
|
|
|
13
11
|
{{includeFile template="client/src/drawing/CanvasStore.tsx.hbs" output="client/src/CanvasStore.{{ext}}"}}
|
|
14
|
-
import {CanvasStore} from './CanvasStore';
|
|
12
|
+
{{addImport "import {CanvasStore} from './CanvasStore';"}}
|
|
15
13
|
|
|
16
14
|
{{includeFile template="client/src/drawing/DrawingControls.tsx.hbs" output="client/src/DrawingControls.{{ext}}"}}
|
|
17
|
-
import {DrawingControls} from './DrawingControls';
|
|
15
|
+
{{addImport "import {DrawingControls} from './DrawingControls';"}}
|
|
18
16
|
|
|
19
17
|
{{includeFile template="client/src/drawing/Canvas.tsx.hbs" output="client/src/Canvas.{{ext}}"}}
|
|
20
|
-
import {Canvas} from './Canvas';
|
|
18
|
+
{{addImport "import {Canvas} from './Canvas';"}}
|
|
19
|
+
|
|
20
|
+
export const App = () => {
|
|
21
|
+
const [settingsReady, setSettingsReady] = useState(false);
|
|
22
|
+
const [canvasReady, setCanvasReady] = useState(false);
|
|
23
|
+
const loading = !settingsReady || !canvasReady;
|
|
21
24
|
|
|
22
|
-
const App = () => {
|
|
23
25
|
return (
|
|
24
26
|
<StrictMode>
|
|
25
27
|
<Provider>
|
|
26
|
-
<SettingsStore />
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
<SettingsStore onReady={()=> setSettingsReady(true)} />
|
|
29
|
+
<CanvasStore onReady={()=> setCanvasReady(true)} />
|
|
30
|
+
{loading ? (
|
|
31
|
+
<Loading />
|
|
32
|
+
) : (
|
|
33
|
+
<>
|
|
34
|
+
<DrawingControls />
|
|
35
|
+
<Canvas />
|
|
36
|
+
<Inspector />
|
|
37
|
+
</>
|
|
38
|
+
)}
|
|
31
39
|
</Provider>
|
|
32
40
|
</StrictMode>
|
|
33
41
|
);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export {App};
|
|
42
|
+
};
|