Skip to content

Commit 06f6e62

Browse files
feat: shareable presets (#637)
* feat: shareable presets * docs(changeset): Add shareable presets * feat: shareable presets
1 parent 7cd65b2 commit 06f6e62

File tree

9 files changed

+88
-4
lines changed

9 files changed

+88
-4
lines changed

.changeset/shaggy-dancers-bow.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@codeimage/app': minor
3+
---
4+
5+
Add shareable presets
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import {mdxComponents} from '../src/mdx/components';
2+
import shareablePresets from './data/1-7-0/shareable_presets.png';
3+
4+
# v1.7.0
5+
6+
<mdxComponents.h3>🎨 Shareable presets</mdxComponents.h3>
7+
8+
This new CodeImage update bring a way to share your presets via link.
9+
10+
Everyone that paste the link into their browser will automatically get your preset into their workspace.
11+
12+
<mdxComponents.image src={shareablePresets} style={{'object-position': 'left'}} />
Loading

apps/codeimage/src/components/Presets/PresetSwitcher/PresetSwitcher.tsx

+12-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import {getTerminalState} from '@codeimage/store/editor/terminal';
55
import {getPresetsStore} from '@codeimage/store/presets/presets';
66
import {PresetData} from '@codeimage/store/presets/types';
77
import {getUiStore} from '@codeimage/store/ui';
8-
import {Box, HStack, Text} from '@codeimage/ui';
8+
import {Box, HStack, Text, toast} from '@codeimage/ui';
99
import {
10+
As,
1011
Button,
1112
DropdownMenu,
1213
DropdownMenuContent,
@@ -15,7 +16,6 @@ import {
1516
DropdownMenuTrigger,
1617
IconButton,
1718
Tooltip,
18-
As,
1919
} from '@codeui/kit';
2020
import {getUmami} from '@core/constants/umami';
2121
import {formatDistanceToNow} from '@core/helpers/date';
@@ -214,6 +214,16 @@ export const PresetSwitcher: ParentComponent<
214214
>
215215
{t('presets.renamePreset.label')}
216216
</DropdownMenuItem>
217+
<DropdownMenuItem
218+
onClick={() => {
219+
presetsStore.actions.copyLink(theme);
220+
toast.success(t('presets.share.confirm'), {
221+
position: 'bottom-center',
222+
});
223+
}}
224+
>
225+
{t('presets.share.label')}
226+
</DropdownMenuItem>
217227
<DropdownMenuItem
218228
onClick={() => {
219229
openDialog(ConfirmDialog, {

apps/codeimage/src/i18n/presets.ts

+8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ export const presets = {
1212
old: 'Vecchio',
1313
new: 'Nuovo',
1414
},
15+
share: {
16+
label: 'Condividi',
17+
confirm: 'Link preset copiato',
18+
},
1519
renamePreset: {
1620
label: 'Rinomina',
1721
confirmTitle: 'Rinomina preset',
@@ -55,6 +59,10 @@ export const presets = {
5559
old: 'Old',
5660
new: 'New',
5761
},
62+
share: {
63+
label: 'Share',
64+
confirm: 'Preset has been copied to clipboard',
65+
},
5866
renamePreset: {
5967
label: 'Rename',
6068
confirmTitle: 'Rename preset',

apps/codeimage/src/mdx/components.css.ts

+4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ export const code = style({
5454

5555
export const img = style({
5656
width: '100%',
57+
borderRadius: themeTokens.radii.lg,
58+
overflow: 'hidden',
59+
objectFit: 'contain',
60+
aspectRatio: '16/9',
5761
});
5862

5963
export const ul = style({

apps/codeimage/src/mdx/components.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,7 @@ export const mdxComponents: MDXComponents = {
3131
ref={el => setTimeout(() => el.play())}
3232
/>
3333
),
34+
image: (props: JSX.IntrinsicElements['img']) => (
35+
<img {...props} class={styles.img} />
36+
),
3437
};

apps/codeimage/src/state/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ import {Container} from 'statebuilder';
22
import {createRoot} from 'solid-js';
33

44
const container = createRoot(() => Container.create());
5+
56
export const provideAppState: typeof container.get = state =>
67
container.get(state);

apps/codeimage/src/state/presets/presets.ts

+43-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
import {provideAppState} from '@codeimage/store/index';
12
import {withEntityPlugin} from '@codeimage/store/plugins/withEntityPlugin';
23
import {withIndexedDbPlugin} from '@codeimage/store/plugins/withIndexedDbPlugin';
34
import {toast} from '@codeimage/ui';
4-
import {untrack} from 'solid-js';
5+
import {createEffect, on, untrack} from 'solid-js';
56
import {withAsyncAction} from 'statebuilder/asyncAction';
6-
import {provideAppState} from '..';
77
import * as api from '../../data-access/preset';
88
import {useIdb} from '../../hooks/use-indexed-db';
99
import {getAuth0State} from '../auth/auth0';
@@ -50,6 +50,42 @@ const PresetStoreDefinition = experimental__defineResource(fetchInitialState)
5050
.extend(withIndexedDbPlugin<PresetsArray>(idbKey, []))
5151
.extend(withPresetBridge(idbKey))
5252
.extend(withAsyncAction())
53+
.extend(store => {
54+
const sharePresetKey = 'share_preset';
55+
createEffect(
56+
on(
57+
() => store.state === 'ready',
58+
isReady => {
59+
if (isReady) {
60+
const params = new URLSearchParams(window.location.search);
61+
if (params.has(sharePresetKey)) {
62+
const sharePreset = params.get(sharePresetKey) as string;
63+
try {
64+
const preset = JSON.parse(window.atob(sharePreset));
65+
store.bridge
66+
.addNewPreset(preset.name, preset.data)
67+
.then(preset => store.set(_ => [preset, ...(_ ?? [])]))
68+
.then(() => {
69+
const url =
70+
window.location.origin + window.location.pathname;
71+
window.history.replaceState(undefined, '', url);
72+
})
73+
.then(() =>
74+
toast.success('Preset imported successfully', {
75+
position: 'bottom-center',
76+
}),
77+
);
78+
} catch (e) {
79+
toast.error('Invalid preset', {
80+
position: 'bottom-center',
81+
});
82+
}
83+
}
84+
}
85+
},
86+
),
87+
);
88+
})
5389
.extend(store => {
5490
return {
5591
sortedPresets() {
@@ -104,6 +140,11 @@ const PresetStoreDefinition = experimental__defineResource(fetchInitialState)
104140
});
105141
});
106142
}),
143+
copyLink: store.asyncAction((payload: Preset) => {
144+
const data = window.btoa(JSON.stringify(payload));
145+
const link = `${window.location.origin}${window.location.pathname}?share_preset=${data}`;
146+
return navigator.clipboard.writeText(link);
147+
}),
107148
syncPreset: store.asyncAction((payload: Preset) => {
108149
return untrack(() => {
109150
const currentState = store();

0 commit comments

Comments
 (0)