11import { Button , Input , SnippngCodeArea } from "@/components" ;
2+ import { db } from "@/config/firebase" ;
3+ import { useAuth } from "@/context/AuthContext" ;
24import { useSnippngEditor } from "@/context/SnippngEditorContext" ;
35import { useToast } from "@/context/ToastContext" ;
46import { ColorPicker } from "@/lib/color-picker" ;
57import { defaultCustomTheme , defaultEditorConfig } from "@/lib/constants" ;
68import { SnippngThemeAttributesInterface } from "@/types" ;
7- import { LocalStorage } from "@/utils" ;
9+ import { LocalStorage , deepClone } from "@/utils" ;
810import { ArrowDownOnSquareIcon } from "@heroicons/react/24/outline" ;
11+ import { addDoc , collection } from "firebase/firestore" ;
912import React , { useEffect , useState } from "react" ;
1013
1114const SnippngThemeBuilder : React . FC < {
1215 themeConfig ?: SnippngThemeAttributesInterface ;
1316} > = ( { themeConfig = { ...defaultCustomTheme } } ) => {
1417 const [ theme , setTheme ] = useState < SnippngThemeAttributesInterface > ( {
1518 ...themeConfig ,
19+ isCustom : true ,
1620 } ) ;
21+ const [ saving , setSaving ] = useState ( false ) ;
1722
1823 const { handleConfigChange, setEditorConfig } = useSnippngEditor ( ) ;
1924 const { addToast } = useToast ( ) ;
25+ const { user } = useAuth ( ) ;
2026
2127 const onConfigChange = ( key : keyof typeof theme . config , value : string ) => {
2228 setTheme ( ( prevTheme ) => ( {
@@ -28,30 +34,54 @@ const SnippngThemeBuilder: React.FC<{
2834 } ) ) ;
2935 } ;
3036
31- const saveAndApplyTheme = ( ) => {
32- let previousThemes =
33- ( LocalStorage . get ( "local_themes" ) as SnippngThemeAttributesInterface [ ] ) ||
34- [ ] ;
35- let id = crypto . randomUUID ( ) ;
36- let themeToBeApplied = {
37- id,
38- label : theme . label ,
39- } ;
40- previousThemes . push ( {
41- ...theme ,
42- id,
43- } ) ;
44- LocalStorage . set ( "local_themes" , previousThemes ) ;
45- handleConfigChange ( "selectedTheme" ) ( themeToBeApplied ) ;
46- addToast ( {
47- message : "Theme saved successfully!" ,
48- description : "You can view your custom themes in your profile" ,
49- } ) ;
37+ const saveAndApplyTheme = async ( ) => {
38+ if ( ! db ) return console . log ( Error ( "Firebase is not configured" ) ) ; // This is to handle error when there is no `.env` file. So, that app doesn't crash while developing without `.env` file.
39+ if ( ! user ) return ;
40+ setSaving ( true ) ;
41+ try {
42+ const dataToBeAdded = {
43+ ...deepClone ( theme ) , // deep clone the theme to avoid mutation
44+ ownerUid : user . uid ,
45+ owner : {
46+ displayName : user ?. displayName ,
47+ email : user ?. email ,
48+ photoURL : user ?. photoURL ,
49+ } ,
50+ } ;
51+ const savedDoc = await addDoc ( collection ( db , "themes" ) , {
52+ ...dataToBeAdded ,
53+ } ) ;
54+ if ( savedDoc . id ) {
55+ // get previously saved themes
56+ let previousThemes =
57+ ( LocalStorage . get (
58+ "local_themes"
59+ ) as SnippngThemeAttributesInterface [ ] ) || [ ] ;
60+
61+ // push newly created theme inside the previous themes array
62+ previousThemes . push ( {
63+ ...dataToBeAdded ,
64+ id : savedDoc . id ,
65+ } ) ;
66+ // store the newly created theme inside local storage
67+ LocalStorage . set ( "local_themes" , previousThemes ) ;
68+
69+ addToast ( {
70+ message : "Theme saved successfully!" ,
71+ description : "You can view your custom themes in your profile" ,
72+ } ) ;
73+ }
74+ } catch ( e ) {
75+ console . error ( "Error adding document: " , e ) ;
76+ } finally {
77+ setSaving ( false ) ;
78+ }
5079 } ;
5180
5281 useEffect ( ( ) => {
5382 return ( ) => {
54- // to avoid changing main editor's config state while creating theme we will set the persisted editor state
83+ // to avoid changing main editor's config state while creating theme
84+ // we will set the persisted editor state
5585 let persistedEditorConfig = LocalStorage . get ( "config" ) ;
5686 setEditorConfig ( {
5787 ...( persistedEditorConfig || defaultEditorConfig ) ,
@@ -98,11 +128,12 @@ const SnippngThemeBuilder: React.FC<{
98128 } ) }
99129 </ div >
100130 < Button
131+ disabled = { saving }
101132 StartIcon = { ArrowDownOnSquareIcon }
102133 className = "w-full justify-center"
103134 onClick = { saveAndApplyTheme }
104135 >
105- Save theme
136+ { saving ? "Saving..." : " Save theme" }
106137 </ Button >
107138 </ div >
108139 < div className = "w-full -mb-10 p-4" >
0 commit comments