sync with global state

main
reng 1 month ago
parent b7da60714d
commit d958a3f932
  1. 7
      src/index.css
  2. 8
      src/main.jsx
  3. 30
      src/pages/about.jsx
  4. 12
      src/pages/explore.jsx
  5. 37
      src/pages/keywords.jsx
  6. 95
      src/utils/firebase.js
  7. 49
      src/utils/usetext.jsx

@ -7,12 +7,13 @@ body{
@apply flex justify-center items-center min-h-screen; @apply flex justify-center items-center min-h-screen;
} }
#root{ #root{
@apply flex-1 overflow-x-hidden bg-black text-white flex justify-center items-center w-full max-w-[640px] ; @apply flex-1 overflow-x-hidden bg-black text-white w-full max-w-[640px];
@apply flex justify-center items-center;
@apply overflow-y-auto; @apply overflow-y-auto;
} }
main{ main{
@apply bg-black text-white flex justify-center items-center min-h-screen px-[1rem] py-[3.5rem] flex flex-col justify-end gap-[3rem]; @apply bg-black text-white w-full flex justify-center items-center min-h-screen px-[1rem] py-[3.5rem] flex flex-col justify-end gap-[3rem];
@apply w-full; /* @apply absolute w-full top-0 left-0 right-0 overflow-y-auto; */
} }
button{ button{

@ -25,10 +25,10 @@ const router = createBrowserRouter([
path: '/keywords', path: '/keywords',
element: <Keywords />, element: <Keywords />,
}, },
{ // {
path: '/explore', // path: '/explore',
element: <Explore />, // element: <Explore />,
} // }
]); ]);
createRoot(document.getElementById('root')).render( createRoot(document.getElementById('root')).render(

@ -1,4 +1,5 @@
import { Button } from '../comps/button.jsx'; import { Button } from '../comps/button.jsx';
import { getState } from '../utils/firebase.js';
import { useText } from '../utils/usetext.jsx'; import { useText } from '../utils/usetext.jsx';
import { useNavigate, useLocation } from 'react-router-dom'; import { useNavigate, useLocation } from 'react-router-dom';
@ -38,7 +39,34 @@ export default function About({showContact, onClose}) {
<Button onClick={()=>{ <Button onClick={()=>{
if(typeof onClose === 'function') onClose(); if(typeof onClose === 'function') onClose();
else navigate('/keywords'); else{
navigate('/keywords');
// getState().then((state)=>{
// if(!state || !state.state){
// navigate('/keywords');
// return;
// }
// if(state.state==='keywords'){
// // check time
// const time=new Date(state.updatedTime);
// const now=new Date();
// const diff=(now.getTime()-time.getTime())/1000; // in seconds
// if(diff<30){
// // navigate('/explore');
// navigate('/keywords', {state:{ showExplore:true }});
// }else{
// navigate('/keywords');
// }
// }
// }).catch((error)=>{
// console.error('Error checking state:', error);
// });
}
}}> }}>
{getText('button_next')} {getText('button_next')}
</Button> </Button>

@ -17,9 +17,9 @@ const State={
const AnimationTime=1000; const AnimationTime=1000;
export default function Explore() { export default function Explore({onClose}) {
const { selectedKeyword, getText } = useText(); const { selectedKeyword, getText, globalState } = useText();
const navigate=useNavigate(); const navigate=useNavigate();
if(selectedKeyword.length===0){ if(selectedKeyword.length===0){
@ -43,6 +43,7 @@ export default function Explore() {
} }
useEffect(()=>{ useEffect(()=>{
if(!lookat) return; if(!lookat) return;
@ -73,10 +74,11 @@ export default function Explore() {
return ( return (
<> <>
<main className="!px-0 !gap-0 !justify-start"> <main className="!px-0 !gap-0 !justify-start absolute top-0 left-0 right-0 min-h-[100dvh]">
<section className="z-10 fixed top-[1.5rem] left-[1.5rem] right-[1.5rem] flex flex-row justify-between items-center"> <section className="z-10 fixed top-[1.5rem] left-[1.5rem] right-[1.5rem] flex flex-row justify-between items-center">
<button className="uppercase flex flex-row" onClick={()=>{ <button className="uppercase flex flex-row" onClick={()=>{
navigate('/keywords'); // navigate('/keywords');
onClose();
}}><img src="back.svg" alt="back"/>{getText('button_reselect')}</button> }}><img src="back.svg" alt="back"/>{getText('button_reselect')}</button>
<button className="z-20 mr-[2.5rem] w-[1.875rem] border aspect-square flex justify-center items-center rounded-full" <button className="z-20 mr-[2.5rem] w-[1.875rem] border aspect-square flex justify-center items-center rounded-full"
@ -128,6 +130,8 @@ export default function Explore() {
</section> </section>
</main> </main>
{showInfo && <About onClose={() => setShowInfo(false)} showContact={true}/>} {showInfo && <About onClose={() => setShowInfo(false)} showContact={true}/>}
</> </>
); );
} }

@ -3,10 +3,11 @@ import {Button} from "../comps/button";
import { useText } from "../utils/usetext.jsx"; import { useText } from "../utils/usetext.jsx";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { addKeywords } from '../utils/firebase'; import { addKeywords } from '../utils/firebase';
import Explore from './explore.jsx';
export default function Keywords() { export default function Keywords() {
const { getText, getKeywords, selectedKeyword, select, clearSelectedKeyword } = useText(); const { getText, getKeywords, selectedKeyword, select, clearSelectedKeyword, globalState, globalStateAvailable } = useText();
const keywords = getKeywords(); const keywords = getKeywords();
const rowCount=3; const rowCount=3;
const colCount = Math.ceil(keywords.length / rowCount); const colCount = Math.ceil(keywords.length / rowCount);
@ -14,6 +15,13 @@ export default function Keywords() {
const refContainer=useRef(); const refContainer=useRef();
const navigate=useNavigate(); const navigate=useNavigate();
const [showExplore, setShowExplore] = useState(false);
function onClose(){
setShowExplore(false);
}
useEffect(()=>{ useEffect(()=>{
const container=refContainer.current; const container=refContainer.current;
@ -26,6 +34,21 @@ export default function Keywords() {
}, [keywords]); }, [keywords]);
useEffect(()=>{
// if(globalState?.state=='keywords'){
// setShowExplore(true);
// }
// console.log('Checking global state availability', globalState);
if(globalStateAvailable()){
setShowExplore(true);
}else{
setShowExplore(false);
}
},[globalState])
useEffect(()=>{ useEffect(()=>{
// clear selected keywords when unmount // clear selected keywords when unmount
@ -36,7 +59,9 @@ export default function Keywords() {
return ( return (
<>
<main className="!px-0 !justify-between"> <main className="!px-0 !justify-between">
{/* <p>{JSON.stringify(globalState)}</p> */}
<h1 className="text-[1.125rem]">{getText('title_keywords')}</h1> <h1 className="text-[1.125rem]">{getText('title_keywords')}</h1>
<section className='w-full flex-1 flex items-center justify-center'> <section className='w-full flex-1 flex items-center justify-center'>
@ -67,13 +92,21 @@ export default function Keywords() {
<Button onClick={()=>{ <Button onClick={()=>{
addKeywords(selectedKeyword); addKeywords(selectedKeyword);
navigate('/explore'); // navigate('/explore');
}} }}
disabled={selectedKeyword.length<4} disabled={selectedKeyword.length<4}
> >
{getText('button_explore')} {getText('button_explore')}
</Button> </Button>
</div> </div>
{showExplore && (
<Explore onClose={onClose}/>
)}
</main> </main>
</>
); );
} }

@ -1,6 +1,6 @@
// Import the functions you need from the SDKs you need // Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app"; import { initializeApp } from "firebase/app";
import { getFirestore, collection, doc, writeBatch, setDoc } from "firebase/firestore"; import { getFirestore, collection, doc, writeBatch, setDoc, onSnapshot, getDoc } from "firebase/firestore";
// TODO: Add SDKs for Firebase products that you want to use // TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries // https://firebase.google.com/docs/web/setup#available-libraries
@ -22,31 +22,38 @@ const app = initializeApp(firebaseConfig);
export function addKeywords(keywords) { export function addKeywords(keywords) {
console.log('Adding keywords to Firestore:', keywords); // console.log('Adding keywords to Firestore:', keywords);
const sortedKeywords = [...keywords].sort((a, b) => a.id - b.id); // const sortedKeywords = [...keywords].sort((a, b) => a.id - b.id);
const db = getFirestore(app); // const db = getFirestore(app);
const batch = writeBatch(db); // const batch = writeBatch(db);
sortedKeywords.forEach((keyword) => { // sortedKeywords.forEach((keyword) => {
const keywordRef = doc(db, "keywords", keyword.id.toString()); // const keywordRef = doc(db, "keywords", keyword.id.toString());
const keywordDoc = { // const keywordDoc = {
...keyword, // ...keyword,
updatedTime: new Date(), // updatedTime: new Date(),
}; // };
batch.set(keywordRef, keywordDoc); // batch.set(keywordRef, keywordDoc);
}); // });
batch.commit() // batch.commit()
.then(() => { // .then(() => {
console.log("Keywords added successfully!"); // console.log("Keywords added successfully!");
}) // })
.catch((error) => { // .catch((error) => {
console.error("Error adding keywords: ", error); // console.error("Error adding keywords: ", error);
// });
setState({
state: 'keywords',
keywords: keywords,
updatedTime: new Date(),
}); });
} }
@ -64,4 +71,48 @@ export function lookatKeyword(keyword) {
title: keyword.title, title: keyword.title,
updatedTime: new Date(), updatedTime: new Date(),
}); });
} }
export function setState(state) {
console.log('Setting state in Firestore:', state);
const db = getFirestore(app);
const stateRef = doc(collection(db, "state"), 'latest_page');
setDoc(stateRef, {
...state,
updatedTime: new Date(),
});
}
export function onStateChange(callback) {
const db = getFirestore(app);
const stateRef = doc(collection(db, "state"), 'latest_page');
const unsubscribe = onSnapshot(stateRef, (doc) => {
if (doc.exists()) {
callback(doc.data());
} else {
console.log("No such document!");
}
});
return unsubscribe;
}
export async function getState(){
const db = getFirestore(app);
const stateRef = doc(collection(db, "state"), 'latest_page');
const state_doc=await getDoc(stateRef);
if (state_doc.exists()) {
return state_doc.data();
} else {
console.log("No such document!");
return null;
}
}

@ -1,5 +1,6 @@
import { createContext, useContext, useEffect, useState } from "react"; import { createContext, useContext, useEffect, useState } from "react";
import useSWR from "swr"; import useSWR from "swr";
import { onStateChange } from "./firebase";
const TextContent=createContext(); const TextContent=createContext();
@ -20,9 +21,10 @@ export function TextProvider({children}) {
const [nextLang, setNextLang]=useState('en'); const [nextLang, setNextLang]=useState('en');
const [selectedKeyword, setSelectedKeyword] = useState([]); const [selectedKeyword, setSelectedKeyword] = useState([]);
const [globalState, setGlobalState] = useState(null);
function getText(key) { function getText(key) {
console.log("getText", key, lang, data?.[key]?.[lang]); // console.log("getText", key, lang, data?.[key]?.[lang]);
return data?.[key]?.[lang] || ""; return data?.[key]?.[lang] || "";
} }
@ -55,13 +57,56 @@ export function TextProvider({children}) {
setSelectedKeyword([]); setSelectedKeyword([]);
} }
function onGlobalKeywordChange(new_state){
if(new_state.state!=='keywords') return;
console.log('Global keyword change detected', new_state);
setGlobalState(new_state);
if(globalStateAvailable(new_state)){
const keywords=new_state.keywords || [];
setSelectedKeyword(keywords);
}
}
function globalStateAvailable(new_state){
const state=new_state || globalState;
if(!state || !state.state) return false;
const time=new Date(state?.updatedTime.seconds*1000 + state?.updatedTime.nanoseconds/1000000);
const now=new Date();
const diff=(now.getTime()-time.getTime())/1000; // in seconds
console.log('Checking global state availability', state, diff);
return state && state.state==='keywords' && diff<30;
}
useEffect(()=>{ useEffect(()=>{
console.log("selectedKeyword changed", selectedKeyword); console.log("selectedKeyword changed", selectedKeyword);
}, [selectedKeyword]); }, [selectedKeyword]);
useEffect(()=>{
const unsubscribe=onStateChange(onGlobalKeywordChange);
return () => {
unsubscribe();
};
},[]);
return ( return (
<TextContent.Provider value={{ data, getText, nextLang, toggleLang, getKeywords, selectedKeyword, select, clearSelectedKeyword }}> <TextContent.Provider value={{ data, getText, nextLang, toggleLang, getKeywords,
selectedKeyword, select, clearSelectedKeyword,
globalState, globalStateAvailable }}>
{children} {children}
</TextContent.Provider> </TextContent.Provider>
) )

Loading…
Cancel
Save