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;
}
#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;
}
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 w-full;
@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 absolute w-full top-0 left-0 right-0 overflow-y-auto; */
}
button{

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

@ -1,4 +1,5 @@
import { Button } from '../comps/button.jsx';
import { getState } from '../utils/firebase.js';
import { useText } from '../utils/usetext.jsx';
import { useNavigate, useLocation } from 'react-router-dom';
@ -38,7 +39,34 @@ export default function About({showContact, onClose}) {
<Button onClick={()=>{
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')}
</Button>

@ -17,9 +17,9 @@ const State={
const AnimationTime=1000;
export default function Explore() {
export default function Explore({onClose}) {
const { selectedKeyword, getText } = useText();
const { selectedKeyword, getText, globalState } = useText();
const navigate=useNavigate();
if(selectedKeyword.length===0){
@ -43,6 +43,7 @@ export default function Explore() {
}
useEffect(()=>{
if(!lookat) return;
@ -73,10 +74,11 @@ export default function Explore() {
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">
<button className="uppercase flex flex-row" onClick={()=>{
navigate('/keywords');
// navigate('/keywords');
onClose();
}}><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"
@ -128,6 +130,8 @@ export default function Explore() {
</section>
</main>
{showInfo && <About onClose={() => setShowInfo(false)} showContact={true}/>}
</>
);
}

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

@ -1,6 +1,6 @@
// Import the functions you need from the SDKs you need
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
// https://firebase.google.com/docs/web/setup#available-libraries
@ -22,31 +22,38 @@ const app = initializeApp(firebaseConfig);
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 batch = writeBatch(db);
// const db = getFirestore(app);
// const batch = writeBatch(db);
sortedKeywords.forEach((keyword) => {
const keywordRef = doc(db, "keywords", keyword.id.toString());
// sortedKeywords.forEach((keyword) => {
// const keywordRef = doc(db, "keywords", keyword.id.toString());
const keywordDoc = {
...keyword,
updatedTime: new Date(),
};
batch.set(keywordRef, keywordDoc);
});
batch.commit()
.then(() => {
console.log("Keywords added successfully!");
})
.catch((error) => {
console.error("Error adding keywords: ", error);
// const keywordDoc = {
// ...keyword,
// updatedTime: new Date(),
// };
// batch.set(keywordRef, keywordDoc);
// });
// batch.commit()
// .then(() => {
// console.log("Keywords added successfully!");
// })
// .catch((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,
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 useSWR from "swr";
import { onStateChange } from "./firebase";
const TextContent=createContext();
@ -20,9 +21,10 @@ export function TextProvider({children}) {
const [nextLang, setNextLang]=useState('en');
const [selectedKeyword, setSelectedKeyword] = useState([]);
const [globalState, setGlobalState] = useState(null);
function getText(key) {
console.log("getText", key, lang, data?.[key]?.[lang]);
// console.log("getText", key, lang, data?.[key]?.[lang]);
return data?.[key]?.[lang] || "";
}
@ -55,13 +57,56 @@ export function TextProvider({children}) {
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(()=>{
console.log("selectedKeyword changed", selectedKeyword);
}, [selectedKeyword]);
useEffect(()=>{
const unsubscribe=onStateChange(onGlobalKeywordChange);
return () => {
unsubscribe();
};
},[]);
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}
</TextContent.Provider>
)

Loading…
Cancel
Save