Skip to content

Commit b9d2692

Browse files
committed
feat: Add aboud and license notice modals.
* Update NOTICE file to be a bit clearer/complete * Add simple about dialog that acknowledges contributors and sponsors * Add License NOTICE link in the footer, to be sure it's present and remains for any derivates.
1 parent ba15adc commit b9d2692

35 files changed

+337
-34
lines changed

NOTICE

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
ZMK Studio
12
Copyright 2024 The ZMK Contributors
23

3-
Licensed under the Apache License, Version 2.0 (the "License");
4-
you may not use this file except in compliance with the License.
4+
This product includes software developend by the ZMK Project (https://zmk.dev/),
5+
licensed under the Apache License, Version 2.0 (the "License").
56
You may obtain a copy of the License at
67

78
http://www.apache.org/licenses/LICENSE-2.0

src/AboutModal.tsx

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
import { useModalRef } from "./misc/useModalRef";
2+
3+
import cannonKeys from "./assets/cannonkeys.png";
4+
import cannonKeysDarkMode from "./assets/cannonkeys-dark-mode.png";
5+
6+
import niceAndTyperactive from "./assets/niceandtyperactive.png";
7+
import niceAndTyperactiveDarkMode from "./assets/niceandtyperactive-dark-mode.png";
8+
9+
import kinesis from "./assets/kinesis.png";
10+
import kinesisDarkMode from "./assets/kinesis-dark-mode.png";
11+
12+
import keychron from "./assets/keychron.png";
13+
import keychronDarkMode from "./assets/keychron-dark-mode.png";
14+
15+
import littleKeyboards from "./assets/littlekeyboards.avif";
16+
import littleKeyboardsDarkMode from "./assets/littlekeyboards-dark-mode.avif";
17+
18+
import keebmaker from "./assets/keebmaker.png";
19+
import keebmakerDarkMode from "./assets/keebmaker-dark-mode.png";
20+
21+
import keebio from "./assets/keebio.avif";
22+
23+
import deskHero from "./assets/deskhero.webp";
24+
import deskHeroDarkMode from "./assets/deskhero-dark-mode.webp";
25+
26+
import mode from "./assets/mode.png";
27+
import modeDarkMode from "./assets/mode-dark-mode.png";
28+
29+
import mechlovin from "./assets/mechloving.png";
30+
import mechlovinDarkMode from "./assets/mechlovin-dark-mode.png";
31+
32+
import phaseByte from "./assets/phasebyte.png";
33+
34+
import keycapsss from "./assets/keycapsss.png";
35+
import keycapsssDarkMode from "./assets/keycapsss-dark-mode.png";
36+
37+
import mekibo from "./assets/mekibo.png";
38+
import mekiboDarkMode from "./assets/mekibo-dark-mode.png";
39+
40+
import splitkb from "./assets/splitkb.png";
41+
import splitkbDarkMode from "./assets/splitkb-dark-mode.png";
42+
43+
export interface AboutModalProps {
44+
open: boolean;
45+
onClose: () => void;
46+
}
47+
48+
enum SponsorSize {
49+
Large,
50+
Medium,
51+
Small,
52+
}
53+
54+
const sponsors = [
55+
{
56+
level: "Platinum",
57+
size: SponsorSize.Large,
58+
vendors: [
59+
{
60+
name: "nice!keyboards / typeractive",
61+
img: niceAndTyperactive,
62+
darkModeImg: niceAndTyperactiveDarkMode,
63+
url: "https://typeractive.xyz/",
64+
},
65+
{
66+
name: "Kineses",
67+
img: kinesis,
68+
darkModeImg: kinesisDarkMode,
69+
url: "https://kinesis-ergo.com/",
70+
},
71+
],
72+
},
73+
{
74+
level: "Gold+",
75+
size: SponsorSize.Large,
76+
vendors: [
77+
{
78+
name: "CannonKeys",
79+
img: cannonKeys,
80+
darkModeImg: cannonKeysDarkMode,
81+
url: "https://cannonkeys.com/",
82+
},
83+
{
84+
name: "Keychron",
85+
img: keychron,
86+
darkModeImg: keychronDarkMode,
87+
url: "https://keychron.com/",
88+
},
89+
],
90+
},
91+
{
92+
level: "Gold",
93+
size: SponsorSize.Medium,
94+
vendors: [
95+
{
96+
name: "Little Keyboards",
97+
img: littleKeyboards,
98+
darkModeImg: littleKeyboardsDarkMode,
99+
url: "https://littlekeyboards.com/",
100+
},
101+
{
102+
name: "Keebmaker",
103+
img: keebmaker,
104+
darkModeImg: keebmakerDarkMode,
105+
url: "https://keebmaker.com/",
106+
},
107+
],
108+
},
109+
{
110+
level: "Silver",
111+
size: SponsorSize.Medium,
112+
vendors: [
113+
{
114+
name: "keeb.io",
115+
img: keebio,
116+
url: "https://keeb.io/",
117+
},
118+
{
119+
name: "Mode Designs",
120+
img: mode,
121+
darkModeImg: modeDarkMode,
122+
url: "https://modedesigns.com/",
123+
},
124+
],
125+
},
126+
{
127+
level: "Bronze",
128+
size: SponsorSize.Small,
129+
vendors: [
130+
{
131+
name: "deskhero",
132+
img: deskHero,
133+
darkModeImg: deskHeroDarkMode,
134+
url: "https://deskhero.ca/",
135+
},
136+
{
137+
name: "PhaseByte",
138+
img: phaseByte,
139+
url: "https://phasebyte.com/",
140+
},
141+
{
142+
name: "Mechlovin'",
143+
img: mechlovin,
144+
darkModeImg: mechlovinDarkMode,
145+
url: "https://mechlovin.studio/",
146+
},
147+
],
148+
},
149+
{
150+
level: "Additional",
151+
size: SponsorSize.Small,
152+
vendors: [
153+
{
154+
name: "splitkb.com",
155+
img: splitkb,
156+
darkModeImg: splitkbDarkMode,
157+
url: "https://splitkb.com/",
158+
},
159+
{
160+
name: "keycapsss",
161+
img: keycapsss,
162+
darkModeImg: keycapsssDarkMode,
163+
url: "https://keycapsss.com/",
164+
},
165+
{
166+
name: "mekibo",
167+
img: mekibo,
168+
darkModeImg: mekiboDarkMode,
169+
url: "https://mekibo.com/",
170+
},
171+
],
172+
},
173+
];
174+
175+
export const AboutModal = ({ open, onClose }: AboutModalProps) => {
176+
const ref = useModalRef(open);
177+
178+
return (
179+
<dialog
180+
ref={ref}
181+
onClose={onClose}
182+
className="p-5 rounded-lg border-text-base border min-w-min w-[70vw]"
183+
>
184+
<p className="py-1">
185+
ZMK Studio is made possible thanks to the generous donation of time from
186+
our contributors, as well as the financial sponsorship from the
187+
following vendors:
188+
</p>
189+
<div className="grid gap-2 auto-rows-auto grid-cols-[auto_minmax(min-content,1fr)] justify-items-center items-center">
190+
{sponsors.map((s) => {
191+
const heightVariants = {
192+
[SponsorSize.Large]: "h-16",
193+
[SponsorSize.Medium]: "h-12",
194+
[SponsorSize.Small]: "h-8",
195+
};
196+
197+
return (
198+
<>
199+
<label>{s.level}</label>
200+
<div
201+
className={`grid grid-rows-1 gap-x-1 auto-cols-fr grid-flow-col justify-items-center items-center ${
202+
heightVariants[s.size]
203+
}`}
204+
>
205+
{s.vendors.map((v) => {
206+
const maxSizeVariants = {
207+
[SponsorSize.Large]: "max-h-16",
208+
[SponsorSize.Medium]: "max-h-12",
209+
[SponsorSize.Small]: "max-h-8",
210+
};
211+
212+
return (
213+
<a href={v.url} target="_blank">
214+
<picture aria-label={v.name}>
215+
{v.darkModeImg && (
216+
<source
217+
className={maxSizeVariants[s.size]}
218+
srcSet={v.darkModeImg}
219+
media="(prefers-color-scheme: dark)"
220+
/>
221+
)}
222+
<img className={maxSizeVariants[s.size]} src={v.img} />
223+
</picture>
224+
</a>
225+
);
226+
})}
227+
</div>
228+
</>
229+
);
230+
})}
231+
</div>
232+
</dialog>
233+
);
234+
};

src/App.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ import { LockState } from "@zmkfirmware/zmk-studio-ts-client/core";
2828
import { LockStateContext } from "./rpc/LockStateContext";
2929
import { UnlockModal } from "./UnlockModal";
3030
import { valueAfter } from "./misc/async";
31+
import { AppFooter } from "./AppFooter";
32+
import { AboutModal } from "./AboutModal";
33+
import { LicenseNoticeModal } from "./misc/LicenseNoticeModal";
3134

3235
declare global {
3336
interface Window {
@@ -153,6 +156,8 @@ function App() {
153156
string | undefined
154157
>(undefined);
155158
const [doIt, undo, redo, canUndo, canRedo, reset] = useUndoRedo();
159+
const [showAbout, setShowAbout] = useState(false);
160+
const [showLicenseNotice, setShowLicenseNotice] = useState(false);
156161

157162
const [lockState, setLockState] = useState<LockState>(
158163
LockState.ZMK_STUDIO_CORE_LOCK_STATE_LOCKED
@@ -196,7 +201,12 @@ function App() {
196201
connect(t, setConn, setConnectedDeviceName)
197202
}
198203
/>
199-
<div className="bg-bg-base text-text-base h-full w-full min-w-min inline-grid grid-cols-[auto] grid-rows-[auto_1fr]">
204+
<AboutModal open={showAbout} onClose={() => setShowAbout(false)} />
205+
<LicenseNoticeModal
206+
open={showLicenseNotice}
207+
onClose={() => setShowLicenseNotice(false)}
208+
/>
209+
<div className="bg-bg-base text-text-base h-full w-full min-w-min inline-grid grid-cols-[auto] grid-rows-[auto_1fr_auto]">
200210
<AppHeader
201211
connectedDeviceLabel={connectedDeviceName}
202212
canUndo={canUndo}
@@ -205,6 +215,10 @@ function App() {
205215
onRedo={redo}
206216
/>
207217
<Keyboard />
218+
<AppFooter
219+
onShowAbout={() => setShowAbout(true)}
220+
onShowLicenseNotice={() => setShowLicenseNotice(true)}
221+
/>
208222
</div>
209223
</UndoRedoContext.Provider>
210224
</LockStateContext.Provider>

src/AppFooter.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export interface AppFooterProps {
2+
onShowAbout: () => void;
3+
onShowLicenseNotice: () => void;
4+
}
5+
6+
export const AppFooter = ({
7+
onShowAbout,
8+
onShowLicenseNotice,
9+
}: AppFooterProps) => {
10+
return (
11+
<div className="grid justify-center m-1">
12+
<div>
13+
<span>&copy; 2024 - The ZMK Contributors</span> -{" "}
14+
<a href="#" onClick={onShowAbout}>
15+
About ZMK Studio
16+
</a>{" "}
17+
-{" "}
18+
<a href="#" onClick={onShowLicenseNotice}>
19+
License NOTICE
20+
</a>
21+
</div>
22+
</div>
23+
);
24+
};

src/ConnectModal.tsx

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
1+
import { useCallback, useEffect, useMemo, useState } from "react";
22

33
import type { RpcTransport } from "@zmkfirmware/zmk-studio-ts-client/transport/index";
44
import type { AvailableDevice } from "./tauri/index";
55
import { SignalIcon } from "@heroicons/react/24/outline";
66
import { Key, ListBox, ListBoxItem, Selection } from "react-aria-components";
7+
import { useModalRef } from "./misc/useModalRef";
78

89
export type TransportFactory = {
910
label: string;
@@ -199,19 +200,7 @@ export const ConnectModal = ({
199200
transports,
200201
onTransportCreated,
201202
}: ConnectModalProps) => {
202-
const dialog = useRef<HTMLDialogElement | null>(null);
203-
204-
useEffect(() => {
205-
if (dialog.current) {
206-
if (open) {
207-
if (!dialog.current.open) {
208-
dialog.current.showModal();
209-
}
210-
} else {
211-
dialog.current.close();
212-
}
213-
}
214-
}, [open]);
203+
const dialog = useModalRef(open || false);
215204

216205
const useSimplePicker = useMemo(
217206
() => transports.every((t) => !t.pick_and_connect),

src/UnlockModal.tsx

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { useContext, useEffect, useRef } from "react";
1+
import { useContext, useMemo } from "react";
22

33
import type { RpcTransport } from "@zmkfirmware/zmk-studio-ts-client/transport/index";
44
import type { AvailableDevice } from "./tauri/index";
55
import { LockStateContext } from "./rpc/LockStateContext";
66
import { LockState } from "@zmkfirmware/zmk-studio-ts-client/core";
77
import { ConnectionContext } from "./rpc/ConnectionContext";
8+
import { useModalRef } from "./misc/useModalRef";
89

910
export type TransportFactory = {
1011
label: string;
@@ -18,25 +19,14 @@ export type TransportFactory = {
1819
export interface UnlockModalProps {}
1920

2021
export const UnlockModal = ({}: UnlockModalProps) => {
21-
const dialog = useRef<HTMLDialogElement | null>(null);
22-
2322
let conn = useContext(ConnectionContext);
2423
let lockState = useContext(LockStateContext);
2524

26-
useEffect(() => {
27-
let open =
28-
!!conn && lockState != LockState.ZMK_STUDIO_CORE_LOCK_STATE_UNLOCKED;
29-
30-
if (dialog.current) {
31-
if (open) {
32-
if (!dialog.current.open) {
33-
dialog.current.showModal();
34-
}
35-
} else {
36-
dialog.current.close();
37-
}
38-
}
39-
}, [lockState, conn]);
25+
let open = useMemo(
26+
() => !!conn && lockState != LockState.ZMK_STUDIO_CORE_LOCK_STATE_UNLOCKED,
27+
[conn, lockState]
28+
);
29+
const dialog = useModalRef(open);
4030

4131
return (
4232
<dialog ref={dialog} className="p-5 rounded-lg border-text-base border">
34.6 KB
Loading

src/assets/cannonkeys.png

38.6 KB
Loading

src/assets/deskhero-dark-mode.webp

13.4 KB
Loading

src/assets/deskhero.webp

13.1 KB
Loading

0 commit comments

Comments
 (0)