11import { useEffect , useRef , useState } from "react" ;
22
3- import { DEFAULT_BASE_SETUP } from "@/lib/constants" ;
3+ import { DEFAULT_BASE_SETUP , THEMES } from "@/lib/constants" ;
44import {
55 clsx ,
6+ constructTheme ,
67 deepClone ,
78 getEditorWrapperBg ,
89 getLanguage ,
@@ -25,6 +26,7 @@ import SnippngWindowControls from "./SnippngWindowControls";
2526import { db } from "@/config/firebase" ;
2627import { useAuth } from "@/context/AuthContext" ;
2728import { useToast } from "@/context/ToastContext" ;
29+ import { SnippngThemeAttributesInterface } from "@/types" ;
2830import {
2931 ArrowDownOnSquareStackIcon ,
3032 ArrowPathIcon ,
@@ -33,7 +35,11 @@ import {
3335import { addDoc , collection , doc , updateDoc } from "firebase/firestore" ;
3436import Logo from "../Logo" ;
3537
36- const SnippngCodeArea = ( ) => {
38+ interface Props {
39+ underConstructionTheme ?: SnippngThemeAttributesInterface ;
40+ }
41+
42+ const SnippngCodeArea : React . FC < Props > = ( { underConstructionTheme } ) => {
3743 const editorRef = useRef < HTMLDivElement > ( null ) ; // useRef to persist existing ref. Might be useful when dealing with background image in future
3844 const wrapperRef = useRef < HTMLDivElement > ( null ) ;
3945 const [ saving , setSaving ] = useState ( false ) ;
@@ -110,16 +116,40 @@ const SnippngCodeArea = () => {
110116 }
111117 } ;
112118
119+ /**
120+ *
121+ * @returns editor compatible theme object
122+ * @description Function is responsible for constructing theme based on if it is a
123+ * - `predefined` - theme in the `@uiw/codemirror-themes-all` library
124+ * - `localCustom` - Build by user and saved locally
125+ * - `underConstructionTheme` - user is currently constructing/configuring new theme
126+ */
127+ const getSelectedTheme = ( ) => {
128+ // If user is configuring the custom theme (SnippngCustomThemeContextProvider modal is mounded)
129+ if ( underConstructionTheme ) return constructTheme ( underConstructionTheme ) ;
130+ else {
131+ // check if selected theme is predefined or locally created
132+ let [ isPredefined , isLocalCustom ] = getTheme ( selectedTheme . id ) ;
133+
134+ if ( isPredefined ) return themes [ isPredefined ] ; // if it is predefined return the theme configuration from the library
135+
136+ if ( isLocalCustom ) return constructTheme ( isLocalCustom ) ; // else construct and return the code mirror compatible theme
137+
138+ return themes [ THEMES [ 0 ] . id as keyof typeof themes ] ; // this will be returned if theme is not predefined as well as not available locally
139+ }
140+ } ;
141+
113142 useEffect ( ( ) => {
114143 // if there is a uid means we are on edit page where we want to avoid persisting the editor config
115- if ( uid ) return ;
144+ // underConstructionTheme means user is on the theme page which we don't want to persist
145+ if ( uid || underConstructionTheme ) return ;
116146 // persist the editor config changes only when user is creating new snippet
117147 LocalStorage . set ( "config" , {
118148 ...editorConfig ,
119149 uid : undefined ,
120150 ownerUid : undefined ,
121151 } ) ;
122- } , [ editorConfig , uid ] ) ;
152+ } , [ editorConfig , uid , underConstructionTheme ] ) ;
123153
124154 return (
125155 < >
@@ -131,7 +161,10 @@ const SnippngCodeArea = () => {
131161 < NoSSRWrapper >
132162 < div className = "rounded-md bg-white dark:bg-zinc-900 md:p-8 p-4 flex justify-center border-[1px] flex-col items-center dark:border-zinc-500 border-zinc-200 shadow-md w-full" >
133163 < div className = "w-full" >
134- < SnippngControlHeader wrapperRef = { wrapperRef } />
164+ < SnippngControlHeader
165+ creatingTheme = { ! ! underConstructionTheme }
166+ wrapperRef = { wrapperRef }
167+ />
135168 </ div >
136169 { bgImageVisiblePatch ? (
137170 < button
@@ -200,7 +233,7 @@ const SnippngCodeArea = () => {
200233 fontSize : `${ editorFontSize } px` ,
201234 } }
202235 // @ts -ignore
203- theme = { themes [ getTheme ( selectedTheme . id ) ] }
236+ theme = { getSelectedTheme ( ) }
204237 indentWithTab
205238 onChange = { ( value ) => handleConfigChange ( "code" ) ( value ) }
206239 >
@@ -232,64 +265,65 @@ const SnippngCodeArea = () => {
232265 ) : null }
233266 </ div >
234267 </ div >
235- < div className = "w-full mt-8 flex md:flex-row flex-col gap-4 justify-start items-center" >
236- < div className = "w-full" >
237- < Input
238- value = { snippetsName }
239- onChange = { ( e ) =>
240- handleConfigChange ( "snippetsName" ) ( e . target . value )
241- }
242- placeholder = "Snippet name..."
243- />
244- </ div >
245- < div className = "flex flex-shrink-0 gap-4 md:flex-row flex-col md:w-fit w-full" >
246- < Button
247- id = "save-snippet-btn"
248- StartIcon = { ArrowDownOnSquareStackIcon }
249- disabled = { saving }
250- onClick = { ( e ) => {
251- e . stopPropagation ( ) ;
252- if ( ! user )
253- return addToast ( {
254- message : "Please login first" ,
255- type : "error" ,
256- description :
257- "You need to login before saving the snippet" ,
258- } ) ;
259- if ( ! snippetsName )
260- return addToast ( {
261- message : "Snippet name is required" ,
262- type : "error" ,
263- } ) ;
264- else saveSnippet ( ) ;
265- } }
266- >
267- { saving
268- ? "Saving..."
269- : uid // if there is a uid, we are on snippet details page where user can copy the snippet
270- ? "Fork snippet"
271- : "Save snippet" }
272- </ Button >
273- { uid && user && user . uid === ownerUid ? (
268+ { ! underConstructionTheme ? (
269+ < div className = "w-full mt-8 flex md:flex-row flex-col gap-4 justify-start items-center" >
270+ < div className = "w-full" >
271+ < Input
272+ value = { snippetsName }
273+ onChange = { ( e ) =>
274+ handleConfigChange ( "snippetsName" ) ( e . target . value )
275+ }
276+ placeholder = "Snippet name..."
277+ />
278+ </ div >
279+ < div className = "flex flex-shrink-0 gap-4 md:flex-row flex-col md:w-fit w-full" >
274280 < Button
275- StartIcon = { ArrowPathIcon }
276- disabled = { updating }
281+ id = "save-snippet-btn"
282+ StartIcon = { ArrowDownOnSquareStackIcon }
283+ disabled = { saving }
277284 onClick = { ( e ) => {
278285 e . stopPropagation ( ) ;
286+ if ( ! user )
287+ return addToast ( {
288+ message : "Please login first" ,
289+ type : "error" ,
290+ description :
291+ "You need to login before saving the snippet" ,
292+ } ) ;
279293 if ( ! snippetsName )
280294 return addToast ( {
281295 message : "Snippet name is required" ,
282296 type : "error" ,
283297 } ) ;
284- updateSnippet ( ) ;
298+ else saveSnippet ( ) ;
285299 } }
286300 >
287- { updating ? "Updating..." : "Update snippet" }
301+ { saving
302+ ? "Saving..."
303+ : uid // if there is a uid, we are on snippet details page where user can copy the snippet
304+ ? "Fork snippet"
305+ : "Save snippet" }
288306 </ Button >
289- ) : null }
307+ { uid && user && user . uid === ownerUid ? (
308+ < Button
309+ StartIcon = { ArrowPathIcon }
310+ disabled = { updating }
311+ onClick = { ( e ) => {
312+ e . stopPropagation ( ) ;
313+ if ( ! snippetsName )
314+ return addToast ( {
315+ message : "Snippet name is required" ,
316+ type : "error" ,
317+ } ) ;
318+ updateSnippet ( ) ;
319+ } }
320+ >
321+ { updating ? "Updating..." : "Update snippet" }
322+ </ Button >
323+ ) : null }
324+ </ div >
290325 </ div >
291- </ div >
292- { /* TODO: Add CTA to remove background image */ }
326+ ) : null }
293327 </ div >
294328 { uid ? (
295329 < small className = "dark:text-zinc-300 text-left text-zinc-600 py-2 inline-block" >
0 commit comments