Skip to content

Commit e7a25c0

Browse files
committed
feat: Properly implement Discard.
* Updated connection context to allow re-assigning a new object with the same connection to force a reload of data over the connection. * Tweaks for Discard to re-assign connection and reset undo/redo as needed.
1 parent 1ae525f commit e7a25c0

File tree

6 files changed

+100
-82
lines changed

6 files changed

+100
-82
lines changed

src/App.tsx

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ import { AppHeader } from "./AppHeader";
33
import {
44
call_rpc,
55
create_rpc_connection,
6-
RpcConnection,
76
} from "@zmkfirmware/zmk-studio-ts-client";
87
import type { Notification } from "@zmkfirmware/zmk-studio-ts-client/studio";
9-
import { ConnectionContext } from "./rpc/ConnectionContext";
10-
import { Dispatch, useEffect, useState } from "react";
8+
import { ConnectionState, ConnectionContext } from "./rpc/ConnectionContext";
9+
import { Dispatch, useCallback, useEffect, useState } from "react";
1110
import { ConnectModal, TransportFactory } from "./ConnectModal";
1211

1312
import type { RpcTransport } from "@zmkfirmware/zmk-studio-ts-client/transport/index";
@@ -115,13 +114,13 @@ async function listen_for_notifications(
115114

116115
async function connect(
117116
transport: RpcTransport,
118-
setConn: Dispatch<RpcConnection | null>,
117+
setConn: Dispatch<ConnectionState>,
119118
setConnectedDeviceName: Dispatch<string | undefined>
120119
) {
121-
let rpc_conn = await create_rpc_connection(transport);
120+
let conn = await create_rpc_connection(transport);
122121

123122
let details = await Promise.race([
124-
call_rpc(rpc_conn, { core: { getDeviceInfo: true } })
123+
call_rpc(conn, { core: { getDeviceInfo: true } })
125124
.then((r) => r?.core?.getDeviceInfo)
126125
.catch((e) => {
127126
console.error("Failed first RPC call", e);
@@ -136,22 +135,22 @@ async function connect(
136135
return;
137136
}
138137

139-
listen_for_notifications(rpc_conn.notification_readable)
138+
listen_for_notifications(conn.notification_readable)
140139
.then(() => {
141140
setConnectedDeviceName(undefined);
142-
setConn(null);
141+
setConn({ conn: null });
143142
})
144143
.catch((_e) => {
145144
setConnectedDeviceName(undefined);
146-
setConn(null);
145+
setConn({ conn: null });
147146
});
148147

149148
setConnectedDeviceName(details.name);
150-
setConn(rpc_conn);
149+
setConn({ conn });
151150
}
152151

153152
function App() {
154-
const [conn, setConn] = useState<RpcConnection | null>(null);
153+
const [conn, setConn] = useState<ConnectionState>({ conn: null });
155154
const [connectedDeviceName, setConnectedDeviceName] = useState<
156155
string | undefined
157156
>(undefined);
@@ -174,11 +173,13 @@ function App() {
174173
}
175174

176175
async function updateLockState() {
177-
if (!conn) {
176+
if (!conn.conn) {
178177
return;
179178
}
180179

181-
let locked_resp = await call_rpc(conn, { core: { getLockState: true } });
180+
let locked_resp = await call_rpc(conn.conn, {
181+
core: { getLockState: true },
182+
});
182183

183184
setLockState(
184185
locked_resp.core?.getLockState ||
@@ -189,13 +190,48 @@ function App() {
189190
updateLockState();
190191
}, [conn, setLockState]);
191192

193+
const save = useCallback(() => {
194+
async function doSave() {
195+
if (!conn.conn) {
196+
return;
197+
}
198+
199+
let resp = await call_rpc(conn.conn, { keymap: { saveChanges: true } });
200+
if (!resp.keymap?.saveChanges) {
201+
console.error("Failed to save changes", resp);
202+
}
203+
}
204+
205+
doSave();
206+
}, [conn]);
207+
208+
const discard = useCallback(() => {
209+
async function doDiscard() {
210+
if (!conn.conn) {
211+
return;
212+
}
213+
214+
let resp = await call_rpc(conn.conn, {
215+
keymap: { discardChanges: true },
216+
});
217+
if (!resp.keymap?.discardChanges) {
218+
console.error("Failed to discard changes", resp);
219+
}
220+
221+
reset();
222+
setConn({ conn: conn.conn });
223+
}
224+
225+
doDiscard();
226+
}, [conn]);
227+
192228
return (
193229
<ConnectionContext.Provider value={conn}>
194230
<LockStateContext.Provider value={lockState}>
195231
<UndoRedoContext.Provider value={doIt}>
196232
<UnlockModal />
197233
<ConnectModal
198-
open={!conn}
234+
open={!conn.conn}
199235
transports={TRANSPORTS}
200236
onTransportCreated={(t) =>
201237
connect(t, setConn, setConnectedDeviceName)
@@ -213,6 +249,8 @@ function App() {
213249
canRedo={canRedo}
214250
onUndo={undo}
215251
onRedo={redo}
252+
onSave={save}
253+
onDiscard={discard}
216254
/>
217255
<Keyboard />
218256
<AppFooter

src/AppHeader.tsx

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
import { useCallback, useContext } from "react";
2-
3-
import { call_rpc } from "@zmkfirmware/zmk-studio-ts-client";
41
import { useConnectedDeviceData } from "./rpc/useConnectedDeviceData";
52
import { useSub } from "./usePubSub";
6-
import { ConnectionContext } from "./rpc/ConnectionContext";
73
import {
84
ArrowUturnLeftIcon,
95
ArrowUturnRightIcon,
106
} from "@heroicons/react/24/solid";
117

128
export interface AppHeaderProps {
139
connectedDeviceLabel?: string;
10+
onSave?: () => void | Promise<void>;
11+
onDiscard?: () => void | Promise<void>;
1412
onUndo?: () => Promise<void>;
1513
onRedo?: () => Promise<void>;
1614
canUndo?: boolean;
@@ -23,48 +21,18 @@ export const AppHeader = ({
2321
canUndo,
2422
onRedo,
2523
onUndo,
24+
onSave,
25+
onDiscard,
2626
}: AppHeaderProps) => {
2727
const [unsaved, setUnsaved] = useConnectedDeviceData<boolean>(
2828
{ keymap: { checkUnsavedChanges: true } },
2929
(r) => r.keymap?.checkUnsavedChanges
3030
);
3131

32-
const conn = useContext(ConnectionContext);
33-
3432
useSub("rpc_notification.keymap.unsavedChangesStatusChanged", (unsaved) =>
3533
setUnsaved(unsaved)
3634
);
3735

38-
const save = useCallback(() => {
39-
async function doSave() {
40-
if (!conn) {
41-
return;
42-
}
43-
44-
let resp = await call_rpc(conn, { keymap: { saveChanges: true } });
45-
if (!resp.keymap?.saveChanges) {
46-
console.error("Failed to save changes", resp);
47-
}
48-
}
49-
50-
doSave();
51-
}, [conn]);
52-
53-
const discard = useCallback(() => {
54-
async function doDiscard() {
55-
if (!conn) {
56-
return;
57-
}
58-
59-
let resp = await call_rpc(conn, { keymap: { discardChanges: true } });
60-
if (!resp.keymap?.discardChanges) {
61-
console.error("Failed to discard changes", resp);
62-
}
63-
}
64-
65-
doDiscard();
66-
}, [conn]);
67-
6836
return (
6937
<header className="top-0 left-0 right-0 grid grid-cols-[1fr_auto_1fr] items-center justify-between border-b border-text-base">
7038
<p className="px-3">ZMK Studio</p>
@@ -101,14 +69,14 @@ export const AppHeader = ({
10169
type="button"
10270
className="rounded border-solid border-transparent px-3 py-1.5 border enabled:hover:border-text-base disabled:text-gray-500"
10371
disabled={!unsaved}
104-
onClick={save}
72+
onClick={onSave}
10573
>
10674
Save
10775
</button>
10876
<button
10977
type="button"
11078
className="rounded border-solid border-transparent px-3 py-1.5 border enabled:hover:border-text-base disabled:text-gray-500"
111-
onClick={discard}
79+
onClick={onDiscard}
11280
disabled={!unsaved}
11381
>
11482
Discard

src/UnlockModal.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ export const UnlockModal = ({}: UnlockModalProps) => {
2323
let lockState = useContext(LockStateContext);
2424

2525
let open = useMemo(
26-
() => !!conn && lockState != LockState.ZMK_STUDIO_CORE_LOCK_STATE_UNLOCKED,
26+
() =>
27+
!!conn.conn && lockState != LockState.ZMK_STUDIO_CORE_LOCK_STATE_UNLOCKED,
2728
[conn, lockState]
2829
);
2930
const dialog = useModalRef(open);

0 commit comments

Comments
 (0)