diff --git a/README.md b/README.md index b66205b8..8aeb65ac 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ First of all make sure that your are using version 14 of node. If the problem st This problem generally arises when you have not setuped/started the Firebase emulators. -1. [Setup firebase emulator](#install-configure-and-integrate-firebase-emulator-suite). -2. [Start the Emulators](#to-start-the-emulators-) +1. [Setup firebase emulator](https://github.com/scorelab/Codelabz/blob/master/CONTRIBUTING.md#firebase-setup). +2. [Start the Emulators]( https://github.com/scorelab/Codelabz/blob/master/CONTRIBUTING.md#run-firebase-emulator) 3. Run the app using `npm run dev`. > 📝**NOTE** : Remember to start the emulators before running the app. diff --git a/functions/cloud_functions/onCreateFunctions.js b/functions/cloud_functions/onCreateFunctions.js index 03bf9209..f87a8527 100644 --- a/functions/cloud_functions/onCreateFunctions.js +++ b/functions/cloud_functions/onCreateFunctions.js @@ -76,19 +76,17 @@ exports.createOrganizationHandler = async (snapshot, context) => { }); /** - * create org_users sub-collection => set user uid and respective permissions + * create org_users collection => set user uid and respective permissions * @type {Promise} */ const setOrgUsers = db - .collection("cl_org_general") - .doc(org_handle) - .collection("cl_org_users") - .doc("users") + .collection("org_users") + .doc(`${org_handle}_${user_uid}`) .set({ - [user_uid]: [3], - createdAt: admin.firestore.FieldValue.serverTimestamp(), - updatedAt: admin.firestore.FieldValue.serverTimestamp() - }); + uid: user_uid, + org_handle, + permissions: [3] + }) await Promise.all([registerOrgHandle, setOrgMetrics, setOrgUsers]); return console.log( diff --git a/package.json b/package.json index 8f394d6b..b9a99f18 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "react-responsive": "^8.1.0", "react-reveal": "^1.2.2", "react-router-dom": "^5.2.0", + "react-router-hash-link": "^2.4.3", "react-select": "^5.7.0", "react-social-login-buttons": "^3.4.0", "react-syntax-highlighter": "^15.4.3", @@ -68,6 +69,7 @@ "redux-thunk": "^2.3.0", "simple-peer": "^9.11.1", "start-server-and-test": "^1.15.3", + "swiper": "^10.2.0", "url": "^0.11.0", "validator": "^13.6.0", "y-quill": "^0.1.5", diff --git a/src/App.jsx b/src/App.jsx index 8598d544..ba39b5f1 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,25 +1,26 @@ import React, { useEffect } from "react"; import Routes from "./routes"; import "./App.less"; -import { useFirebase } from "react-redux-firebase"; +import { useFirebase, useFirestore } from "react-redux-firebase"; import { useDispatch, useSelector } from "react-redux"; import { getProfileData } from "./store/actions"; const App = () => { - const firebase = useFirebase(); - const dispatch = useDispatch(); - const organizations = useSelector( - ({ - firebase: { - profile: { organizations } - } - }) => organizations - ); + const firebase = useFirebase(); + const firestore = useFirestore(); + const dispatch = useDispatch(); + const organizations = useSelector( + ({ + firebase: { + profile: { organizations }, + }, + }) => organizations + ); - useEffect(() => { - getProfileData(organizations)(firebase, dispatch); - }, [organizations, firebase, dispatch]); - return ; + useEffect(() => { + getProfileData(organizations)(firebase, firestore, dispatch); + }, [organizations, firebase, dispatch]); + return ; }; -export default App; +export default App; \ No newline at end of file diff --git a/src/components/AuthPage/Login/index.jsx b/src/components/AuthPage/Login/index.jsx index 0aacd7f1..890c3aa2 100644 --- a/src/components/AuthPage/Login/index.jsx +++ b/src/components/AuthPage/Login/index.jsx @@ -197,12 +197,12 @@ const Login = ({ /> - + Forgot password diff --git a/src/components/Card/CardWithPicture.jsx b/src/components/Card/CardWithPicture.jsx index a7604000..e8cd65c2 100644 --- a/src/components/Card/CardWithPicture.jsx +++ b/src/components/Card/CardWithPicture.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { makeStyles } from "@mui/styles"; import Card from "@mui/material/Card"; import CardHeader from "@mui/material/CardHeader"; @@ -18,6 +18,10 @@ import MoreVertOutlinedIcon from "@mui/icons-material/MoreVertOutlined"; import { ToggleButton, ToggleButtonGroup } from "@mui/material"; import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; +import { Link } from "react-router-dom"; +import { useDispatch, useSelector } from "react-redux"; +import { useFirebase, useFirestore } from "react-redux-firebase"; +import { getUserProfileData } from "../../store/actions"; const useStyles = makeStyles(theme => ({ root: { @@ -67,10 +71,13 @@ const useStyles = makeStyles(theme => ({ } })); -export default function CardWithPicture(props) { +export default function CardWithPicture({ tutorial }) { const classes = useStyles(); const [alignment, setAlignment] = React.useState("left"); const [count, setCount] = useState(1); + const dispatch = useDispatch(); + const firebase = useFirebase(); + const firestore = useFirestore(); const handleIncrement = () => { setCount(count + 1); }; @@ -83,22 +90,40 @@ export default function CardWithPicture(props) { setAlignment(newAlignment); }; + useEffect(() => { + getUserProfileData(tutorial?.created_by)(firebase, firestore, dispatch); + }, [tutorial]); + + const user = useSelector( + ({ + profile: { + user: { data } + } + }) => data + ); + + const getTime = timestamp => { + return timestamp.toDate().toDateString(); + }; + return ( - + + + - S + + {user?.photoURL && user?.photoURL.length > 0 ? ( + + ) : ( + user?.displayName[0] + )} } title={ @@ -110,9 +135,9 @@ export default function CardWithPicture(props) { color="textPrimary" data-testId="UserName" > - {props.name} + {user?.displayName} - {props.organizationName && ( + {tutorial?.owner && ( <> {" for "} - {props.organizationName} + {tutorial?.owner} )} } - subheader={props.date} + subheader={tutorial?.createdAt ? getTime(tutorial?.createdAt) : ""} /> - - - {props.title} - - - {props.contentDescription} - - + + + + {tutorial?.title} + + + {tutorial?.summary} + + + - {props.time} + {"10 min"}
({ root: { margin: "0.5rem", @@ -62,10 +65,13 @@ const useStyles = makeStyles(theme => ({ } })); -export default function CardWithoutPicture(props) { +export default function CardWithoutPicture({ tutorial }) { const classes = useStyles(); const [alignment, setAlignment] = React.useState("left"); const [count, setCount] = useState(1); + const dispatch = useDispatch(); + const firebase = useFirebase(); + const firestore = useFirestore(); const handleIncrement = () => { setCount(count + 1); }; @@ -78,16 +84,32 @@ export default function CardWithoutPicture(props) { setAlignment(newAlignment); }; + useEffect(() => { + getUserProfileData(tutorial?.created_by)(firebase, firestore, dispatch); + }, [tutorial]); + + const user = useSelector( + ({ + profile: { + user: { data } + } + }) => data + ); + + const getTime = timestamp => { + return timestamp.toDate().toDateString(); + }; + return ( - S + + {user?.photoURL && user?.photoURL.length > 0 ? ( + + ) : ( + user?.displayName[0] + )} } title={ @@ -99,9 +121,9 @@ export default function CardWithoutPicture(props) { color="textPrimary" data-testId="UserName" > - {props.name} + {user?.displayName} - {props.organizationName && ( + {tutorial?.owner && ( <> {" for "} - {props.organizationName} + {tutorial?.owner} )} } - subheader={props.date} + subheader={tutorial?.createdAt ? getTime(tutorial?.createdAt) : ""} /> - - - {props.title} - - - {props.contentDescription} - - + + + + {tutorial?.title} + + + {tutorial?.summary} + + + - {props.time} + {"10 min"}
>>>>>> 3225631bf90c2b306f0a63e4b5035d7ffef3c770 const [value, setValue] = useState(2); const [selectedTab, setSelectedTab] = useState("1"); const [visibleModal, setVisibleModal] = useState(false); @@ -155,6 +167,7 @@ function HomePage({ background = "white", textColor = "black" }) { } ]); +<<<<<<< HEAD const notification = () => {}; const handleChange = (event, newValue) => { setValue(newValue); @@ -334,6 +347,207 @@ function HomePage({ background = "white", textColor = "black" }) { +======= + const profileData = useSelector(({ firebase: { profile } }) => profile); + useEffect(() => { + const getFeed = async () => { + const tutorialIdArray = await getTutorialFeedIdArray(profileData.uid)( + firebase, + firestore, + dispatch + ); + getTutorialFeedData(tutorialIdArray)(firebase, firestore, dispatch); + }; + getFeed(); + }, []); + const tutorials = useSelector( + ({ + tutorialPage: { + feed: { homepageFeedArray } + } + }) => homepageFeedArray + ); + + const notification = () => {}; + const handleChange = (event, newValue) => { + setValue(newValue); + }; + const handleTabChange = (event, newValue) => { + setSelectedTab(newValue); + }; + const closeModal = () => { + setVisibleModal(prev => !prev); + }; + return ( + + + + {windowSize.width > 750 && ( + + + + + + )} + + + + closeModal(e)} + /> + + + + + + + {tutorials.map(tutorial => { + return !tutorial?.featured_image ? ( + + ) : ( + + ); + })} + + + + } + value="1" + style={{ width: "25%" }} + /> + } + value="2" + style={{ width: "25%" }} + /> + } + value="3" + style={{ width: "25%" }} + /> + } + value="4" + style={{ width: "25%" }} + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>>>>>>> 3225631bf90c2b306f0a63e4b5035d7ffef3c770 ({ + container: { + margin: "20px 0" + }, + root: { + height: "100%", + display: "flex", + alignItems: "center", + justifyContent: "space-evenly", + flexDirection: "column" + }, + media: { + height: "auto", + maxHeight: "180px", + minHeight: "200px", + width: "100%" + }, + heading: { + padding: "10px 20px 0", + fontSize: "1.1rem", + fontWeight: "600" + } +})); + +const CodelabzCarousel = ({ sortBy }) => { + const classes = useStyles(); + + const tutorials = useSelector( + ({ tutorialPage }) => tutorialPage.feed.homepageFeedArray + ) || [0, 0, 0, 0, 0, 0]; + const profileData = useSelector(({ firebase: { profile } }) => profile); + const dispatch = useDispatch(); + const firestore = useFirestore(); + const firebase = useFirebase(); + + useEffect(() => { + const getFeed = async () => { + const tutorialIdArray = await getTutorialFeedIdArray(profileData.uid)( + firebase, + firestore, + dispatch + ); + getTutorialFeedData(tutorialIdArray)(firebase, firestore, dispatch); + }; + getFeed(); + return () => {}; + }, [firestore, dispatch]); + + console.log(tutorials); + + const getTitle = () => { + switch (sortBy) { + case "trending": + return "Trending Now"; + case "featured": + return "Featured on Codelabz"; + case "best": + return "Best of this month"; + } + }; + return ( + <> + + + {getTitle()} + + + {tutorials.map((tutorial, i) => { + return tutorial == 0 ? ( + + + + + + + + ) : ( + + + + + + + + {tutorial?.title} + + + {tutorial?.summary} + + + + + + + ); + })} + + + + ); +}; + +export default CodelabzCarousel; diff --git a/src/components/MyFeed/DiscoverCodelabz/CodelabzExplore.jsx b/src/components/MyFeed/DiscoverCodelabz/CodelabzExplore.jsx new file mode 100644 index 00000000..9eae1a29 --- /dev/null +++ b/src/components/MyFeed/DiscoverCodelabz/CodelabzExplore.jsx @@ -0,0 +1,51 @@ +import React, { useState, useEffect } from "react"; +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import { makeStyles } from "@mui/styles"; +import { useDispatch, useSelector } from "react-redux"; +import { useFirestore } from "react-redux-firebase"; +import CodelabzCarousel from "./CodelabzCarousel"; +const useStyles = makeStyles(theme => ({ + container: { + padding: "30px" + }, + heading: { + fontSize: "1.2rem", + fontWeight: "600", + marginBottom: "10px" + }, + subHeading: { + fontSize: "1rem", + marginBottom: "10px", + fontWeight: "400" + } +})); +const CodelabzExplore = () => { + const [selectedTab, setSelectedTab] = useState(0); + const classes = useStyles(); + const dispatch = useDispatch(); + const firestore = useFirestore(); + + const handleTabChange = (e, value) => { + setSelectedTab(value); + }; + + return ( + <> + + + Discover Codelabz + + + Explore top rated Codelabz and find what you are looking for + + + + + + + + ); +}; + +export default CodelabzExplore; diff --git a/src/components/MyFeed/discoverOrgs/OrgExplore.jsx b/src/components/MyFeed/discoverOrgs/OrgExplore.jsx new file mode 100644 index 00000000..169751bc --- /dev/null +++ b/src/components/MyFeed/discoverOrgs/OrgExplore.jsx @@ -0,0 +1,100 @@ +import React, { useState, useEffect } from "react"; +import Box from "@mui/material/Box"; +import Grid from "@mui/material/Grid"; +import Typography from "@mui/material/Typography"; +import { Tabs, Tab, Paper } from "@mui/material"; +import { makeStyles } from "@mui/styles"; +import OrgsCarousel from "./components/orgsCarousel"; +import TagCard from "../../CardTabs/Tags"; +const useStyles = makeStyles(theme => ({ + container: { + padding: "30px 40px 0" + }, + heading: { + fontSize: "1.2rem", + fontWeight: "600", + marginBottom: "10px" + }, + subHeading: { + fontSize: "1rem", + marginBottom: "10px", + fontWeight: "400" + } +})); +const OrgsExplore = () => { + const [selectedTab, setSelectedTab] = useState(0); + const classes = useStyles(); + + const [tags, setTags] = useState([ + "HTML", + "JavaScript", + "Css", + "Python", + "React", + "Java", + "HTML", + "System Design", + "Cyber Security", + "Python", + "Node", + "Django", + "C", + "C++", + "Python", + "GoLang", + "ML", + "AI/ML", + "Cloud", + "DevOps", + "Figma", + "Angular" + ]); + + const handleTabChange = (e, value) => { + setSelectedTab(value); + }; + + return ( + <> + + + Discover Organizations + + + Explore top rated organization and find what you are looking for + + + + + + + + + + + + + + + + + ); +}; + +export default OrgsExplore; diff --git a/src/components/MyFeed/discoverOrgs/components/orgsCarousel.jsx b/src/components/MyFeed/discoverOrgs/components/orgsCarousel.jsx new file mode 100644 index 00000000..75ee20b5 --- /dev/null +++ b/src/components/MyFeed/discoverOrgs/components/orgsCarousel.jsx @@ -0,0 +1,119 @@ +import React, { useState, useEffect } from "react"; +import { Swiper, SwiperSlide } from "swiper/react"; +import { Navigation, Pagination } from "swiper/modules"; +import "swiper/css/bundle"; +import "swiper/css"; +import "swiper/css/effect-coverflow"; +import "swiper/css/pagination"; +import Skeleton from "@mui/material/Skeleton"; +import CardActionArea from "@mui/material/CardActionArea"; +import CardContent from "@mui/material/CardContent"; +import CardMedia from "@mui/material/CardMedia"; +import Typography from "@mui/material/Typography"; +import { Paper } from "@mui/material"; +import { makeStyles } from "@mui/styles"; +import { useDispatch, useSelector } from "react-redux"; +import { useFirestore } from "react-redux-firebase"; +import Default from "../../../../assets/images/logo.jpeg"; +import { Link } from "react-router-dom"; +import { clearOrgData, getLaunchedOrgsData } from "../../../../store/actions"; + +const useStyles = makeStyles(theme => ({ + container: { + margin: "20px 0" + }, + root: { + height: "100%", + display: "flex", + alignItems: "center", + justifyContent: "space-evenly", + flexDirection: "column" + }, + media: { + height: "auto", + maxHeight: "180px", + minHeight: "200px", + width: "100%" + } +})); + +const OrgsCarousel = () => { + const classes = useStyles(); + const launchedOrgs = useSelector(({ org }) => org.launched.data) || [ + 0, 0, 0, 0, 0 + ]; + const dispatch = useDispatch(); + const firestore = useFirestore(); + useEffect(() => { + getLaunchedOrgsData()(firestore, dispatch); + return () => {}; + }, [firestore, dispatch]); + return ( + <> + + + {launchedOrgs.map((org, i) => { + return org == 0 ? ( + + + + + + + + ) : ( + + + + + + + + {org?.org_handle} + + + {org?.org_description} + + + + + + + ); + })} + + + + ); +}; + +export default OrgsCarousel; diff --git a/src/components/MyFeed/index.jsx b/src/components/MyFeed/index.jsx index b3917ce9..6ef47936 100644 --- a/src/components/MyFeed/index.jsx +++ b/src/components/MyFeed/index.jsx @@ -4,7 +4,10 @@ import Typography from "@mui/material/Typography"; import React from "react"; import PropTypes from "prop-types"; import Carousel from "./Carousel/index"; +import OrgsExplore from "./discoverOrgs/OrgExplore"; +import CodelabzExplore from "./DiscoverCodelabz/CodelabzExplore"; +<<<<<<< HEAD const MyFeed = ({ heading = "Explore Codelabz", title = " Explore top rated Organizations and find the Codelabz you are looking for", @@ -49,6 +52,15 @@ MyFeed.propTypes = { title: PropTypes.string, backgroundcolor: PropTypes.string, textcolor: PropTypes.string +======= +const MyFeed = () => { + return ( + + + + + ); +>>>>>>> 3225631bf90c2b306f0a63e4b5035d7ffef3c770 }; export default MyFeed; diff --git a/src/components/MyFeed/ExploreOrgs.jsx b/src/components/MyFeed/oldExploreOrgs.jsx similarity index 100% rename from src/components/MyFeed/ExploreOrgs.jsx rename to src/components/MyFeed/oldExploreOrgs.jsx diff --git a/src/components/NavBar/new/MainNavbar/RightMenu.jsx b/src/components/NavBar/new/MainNavbar/RightMenu.jsx index 84f07dc6..5a62d2b6 100644 --- a/src/components/NavBar/new/MainNavbar/RightMenu.jsx +++ b/src/components/NavBar/new/MainNavbar/RightMenu.jsx @@ -91,7 +91,7 @@ const RightMenu = ({ mode, onClick }) => { }) => organizations ); - console.log("Nav Orgs", organizations); + // console.log("Nav Orgs", organizations); const allowOrgs = organizations && organizations.length > 0; diff --git a/src/components/Organization/ViewOrganization/index.jsx b/src/components/Organization/ViewOrganization/index.jsx index 14f377db..f4a37100 100644 --- a/src/components/Organization/ViewOrganization/index.jsx +++ b/src/components/Organization/ViewOrganization/index.jsx @@ -7,8 +7,8 @@ import { useParams } from "react-router-dom"; import { clearOrgData, getOrgData, - addFollower, - removeFollower + subscribeOrg, + unSubscribeOrg } from "../../../store/actions"; import Banner from "../../ProfileBanner/Organization"; import { Container } from "@mui/material"; @@ -107,49 +107,29 @@ const ViewOrganization = () => { ]; useEffect(() => { - const unsubscribe = db - .collection("cl_org_general") - .doc(handle) - .onSnapshot(snap => { - const data = snap.data(); - setPeople(data.followers); + db.collection("org_subscribers") + .where("org_handle", "==", handle) + .get() + .then(querySnapshot => { + setPeople(querySnapshot.forEach(doc => doc.data())) }); - - return () => unsubscribe(); }, [db, handle]); useEffect(() => { - const unsubscribe = db - .collection("cl_user") - .doc(profileData.uid) - .onSnapshot(snap => { - const data = snap.data(); - setOrgFollowed(data.orgFollowed); + db.collection("org_subscribers") + .where("uid", "==", profileData.uid) + .get() + .then(querySnapshot => { + setOrgFollowed(querySnapshot.forEach(doc => doc.data())) }); - - return () => unsubscribe(); }, [db, profileData.uid]); - const addfollower = (e, people, handle, orgFollowed) => { - e.preventDefault(); - addFollower( - profileData.handle, - people, - handle, - orgFollowed, - profileData.uid - )(firestore, dispatch); - }; - const removefollower = (e, val, people, handle, orgFollowed) => { - e.preventDefault(); - removeFollower( - val, - people, - handle, - orgFollowed, - profileData.uid - )(firestore, dispatch); - }; + const handleOrgSubscription = async () => { + if (!currentOrgData.userSubscription) + await subscribeOrg(handle)(firebase, firestore, dispatch); + else + await unSubscribeOrg(handle)(firebase, firestore, dispatch); + } const loading = useSelector( ({ @@ -205,16 +185,18 @@ const ViewOrganization = () => { { diff --git a/src/components/Profile/ViewProfile/index.jsx b/src/components/Profile/ViewProfile/index.jsx index 42930a2a..4f62df18 100644 --- a/src/components/Profile/ViewProfile/index.jsx +++ b/src/components/Profile/ViewProfile/index.jsx @@ -33,6 +33,10 @@ const ProfileView = () => { const [followers, setFollowers] = useState([]); const [targetUserFollowing, setTargetUserFollowing] = useState(0); const [following, setFollowing] = useState([]); +<<<<<<< HEAD +======= + const [followDisable, setFollowDisable] = useState(false); +>>>>>>> 3225631bf90c2b306f0a63e4b5035d7ffef3c770 const db = firebase.firestore(); useEffect(() => { @@ -107,16 +111,25 @@ const ProfileView = () => { ); } +<<<<<<< HEAD const addFollower = e => { e.preventDefault(); addUserFollower( currentProfileData, followers, following, +======= + const addFollower = async e => { + e.preventDefault(); + setFollowDisable(true); + await addUserFollower( + currentProfileData, +>>>>>>> 3225631bf90c2b306f0a63e4b5035d7ffef3c770 profileData, firestore, dispatch ); +<<<<<<< HEAD }; const removeFollower = e => { @@ -125,10 +138,21 @@ const ProfileView = () => { followers, currentProfileData, following, +======= + setFollowDisable(false); + }; + + const removeFollower = async e => { + e.preventDefault(); + setFollowDisable(true); + await removeUserFollower( + currentProfileData, +>>>>>>> 3225631bf90c2b306f0a63e4b5035d7ffef3c770 profileData, firestore, dispatch ); +<<<<<<< HEAD }; return ( @@ -319,10 +343,205 @@ const ProfileView = () => {

)} +======= + setFollowDisable(false); + }; + + return ( + + + {profileData && ( +
+ + + + Profile Details + + + + + + + + {profileData.photoURL && profileData.photoURL.length > 0 + ? BasicImage(profileData.photoURL, profileData.displayName) + : BasicImage(NoImage, "Not Available")} + + +

+ + {profileData.displayName} + +

+ {checkAvailable(profileData.description) && ( +

{profileData.description}

+ )} + {checkAvailable(profileData.link_facebook) && ( +

+ +

+ +

+ )} + {checkAvailable(profileData.link_twitter) && ( +

+ +

+ +

+ )} + {checkAvailable(profileData.link_github) && ( +

+ +

+ +

+ )} + {checkAvailable(profileData.link_linkedin) && ( +

+ +

+ +

+ )} + {checkAvailable(profileData.website) && ( +

+ +

+ +

+ )} + {checkAvailable(profileData.country) && ( +

+ +

+ +

+ )} + +>>>>>>> 3225631bf90c2b306f0a63e4b5035d7ffef3c770 +<<<<<<< HEAD Followers : {followers ? followers.length : 0}{" "} Following :{" "} @@ -330,10 +549,20 @@ const ProfileView = () => { {!following ? ( +======= + Followers : {profileData.followerCount ? profileData.followerCount : 0}{" "} + Following :{" "} + + {profileData.followingCount ? profileData.followingCount : 0} + + + {!profileData.isFollowing ? ( +>>>>>>> 3225631bf90c2b306f0a63e4b5035d7ffef3c770 @@ -341,11 +570,21 @@ const ProfileView = () => { +======= + disabled={followDisable} + > + follow + +>>>>>>> 3225631bf90c2b306f0a63e4b5035d7ffef3c770 ) : ( diff --git a/src/components/ProfileBanner/Organization/index.jsx b/src/components/ProfileBanner/Organization/index.jsx index 4d5f5892..82712823 100644 --- a/src/components/ProfileBanner/Organization/index.jsx +++ b/src/components/ProfileBanner/Organization/index.jsx @@ -18,9 +18,12 @@ export default function Banner({ contributors = 402, feed = 40, handle = "apple", - isOrgBelongsToUser = false + isOrgBelongsToUser = false, + isUserSubscribed = false, + handleOrgSubscription, }) { const classes = useStyles(); + const [loading, setLoading] = useState(false); return ( <> - {isOrgBelongsToUser && ( + {isOrgBelongsToUser ? ( + ) : ( + )} -
diff --git a/src/components/TutorialPage/StepList.jsx b/src/components/TutorialPage/StepList.jsx index d51a805b..13316e2d 100644 --- a/src/components/TutorialPage/StepList.jsx +++ b/src/components/TutorialPage/StepList.jsx @@ -11,6 +11,8 @@ import { Button } from "@mui/material"; import { makeStyles } from "@mui/styles"; +import { HashLink } from "react-router-hash-link"; +import { useParams } from "react-router-dom"; const useStyles = makeStyles(theme => ({ icons: { @@ -72,6 +74,7 @@ const StepList = ({ children }) => { const classes = useStyles(); + const { id } = useParams(); /** * * Cases for rendering the menu items @@ -96,7 +99,11 @@ const StepList = ({ data-testId={item?.dataTestId} > {item.id && ( - + { @@ -139,7 +146,7 @@ const StepList = ({ {item.title} - + )} {!item.link && item.onClick && ( ({ container: { margin: "10px 0", @@ -33,6 +39,7 @@ const useStyles = makeStyles(() => ({ fontWeight: "600" }, comments: { + margin: "5px", padding: "10px 15px" }, settings: { @@ -45,17 +52,38 @@ const useStyles = makeStyles(() => ({ } })); -const Comment = () => { +const Comment = ({ id }) => { const classes = useStyles(); - const [commentText, setCommentText] = useState(""); - const [showEmojiPicker, setShowEmojiPicker] = useState(false); const [showReplyfield, setShowReplyfield] = useState(false); - const addEmoji = emoji => { - setCommentText(prev => prev + emoji.emoji); - setShowEmojiPicker(false); - }; const [alignment, setAlignment] = React.useState("left"); const [count, setCount] = useState(1); + const firestore = useFirestore(); + const firebase = useFirebase(); + const dispatch = useDispatch(); + useState(() => { + getCommentData(id)(firebase, firestore, dispatch); + }, [id]); + + const commentsArray = useSelector( + ({ + tutorialPage: { + comment: { data } + } + }) => data + ); + + const [data] = commentsArray.filter(comment => comment.comment_id == id); + + const repliesArray = useSelector( + ({ + tutorialPage: { + comment: { replies } + } + }) => replies + ); + + const [replies] = repliesArray.filter(replies => replies.comment_id == id); + const handleIncrement = () => { setCount(count + 1); }; @@ -68,82 +96,81 @@ const Comment = () => { setAlignment(newAlignment); }; + const handleSubmit = comment => { + const commentData = { + content: comment, + replyTo: data.comment_id, + tutorial_id: data.tutorial_id, + createdAt: firestore.FieldValue.serverTimestamp(), + userId: "codelabzuser" + }; + addComment(commentData)(firebase, firestore, dispatch); + }; + return ( - <> - - - Amazing content keep it up 😃 - - - - - A - - - - Abhishek - - - 19th March,2023 - - - - - {!showReplyfield && ( - - )} - - - - {count} - - + + + {data?.content} + + + + + {!showReplyfield && ( + + )} + - - - - - - - - - - {showReplyfield && ( -
- -
- )} - + + + {count} + + + + +
+ + + + + + + {showReplyfield && ( +
+ + {replies?.replies.map((id, index) => { + return ; + })} +
+ )} + + ) ); }; diff --git a/src/components/TutorialPage/components/Commnets/CommentBox.jsx b/src/components/TutorialPage/components/Commnets/CommentBox.jsx index 71ad2300..373b0acc 100644 --- a/src/components/TutorialPage/components/Commnets/CommentBox.jsx +++ b/src/components/TutorialPage/components/Commnets/CommentBox.jsx @@ -1,28 +1,11 @@ -import { - Card, - Grid, - Box, - Typography, - Avatar, - TextField, - Button, - IconButton, - InputAdornment, - Paper -} from "@mui/material"; -import EmojiPicker from "emoji-picker-react"; -import { InsertEmoticon, Send } from "@mui/icons-material"; -import AccountCircle from "@mui/icons-material/AccountCircle"; +import { Card, Grid, Typography, Button } from "@mui/material"; import { makeStyles } from "@mui/styles"; -import CardActions from "@mui/material/CardActions"; -import MoreVertOutlinedIcon from "@mui/icons-material/MoreVertOutlined"; -import ToggleButton from "@mui/lab/ToggleButton"; -import ToggleButtonGroup from "@mui/lab/ToggleButtonGroup"; -import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; -import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import Textbox from "./Textbox"; import Comment from "./Comment"; +import { addComment } from "../../../../store/actions/tutorialPageActions"; +import { useDispatch, useSelector } from "react-redux"; +import { useFirebase, useFirestore } from "react-redux-firebase"; const useStyles = makeStyles(() => ({ container: { margin: "10px 0", @@ -45,30 +28,57 @@ const useStyles = makeStyles(() => ({ } })); -const CommentBox = () => { +const CommentBox = ({ commentsArray, tutorialId }) => { const classes = useStyles(); + const firestore = useFirestore(); + const firebase = useFirebase(); + const dispatch = useDispatch(); + const [comments, setComments] = useState([]); + const [currCommentCount, setCurrCommentCount] = useState(3); + const handleSubmit = comment => { + const commentData = { + content: comment, + replyTo: tutorialId, + tutorial_id: tutorialId, + createdAt: firestore.FieldValue.serverTimestamp(), + userId: "codelabzuser" + }; + addComment(commentData)(firebase, firestore, dispatch); + }; + + useEffect(() => { + setComments(commentsArray?.slice(0, currCommentCount)); + }, [currCommentCount, commentsArray]); + + console.log(commentsArray, comments, currCommentCount); + + const increaseCommentCount = () => { + setCurrCommentCount(state => state + 3); + }; + return ( - + - Comments(34) + Comments({commentsArray?.length || 0}) - + - - - - - - - - - - - + {comments?.map((id, index) => { + return ( + + + + ); + })} - + {comments?.length != commentsArray?.length && ( + + )} diff --git a/src/components/TutorialPage/components/Commnets/Textbox.jsx b/src/components/TutorialPage/components/Commnets/Textbox.jsx index c452361d..891d9c00 100644 --- a/src/components/TutorialPage/components/Commnets/Textbox.jsx +++ b/src/components/TutorialPage/components/Commnets/Textbox.jsx @@ -10,7 +10,7 @@ import EmojiPicker from "emoji-picker-react"; import { InsertEmoticon, Send } from "@mui/icons-material"; import AccountCircle from "@mui/icons-material/AccountCircle"; -const Textbox = ({ type }) => { +const Textbox = ({ type, handleSubmit }) => { const [commentText, setCommentText] = useState(""); const [showEmojiPicker, setShowEmojiPicker] = useState(false); const addEmoji = emoji => { @@ -67,7 +67,11 @@ const Textbox = ({ type }) => { ) }} /> - diff --git a/src/components/TutorialPage/components/PostDetails.jsx b/src/components/TutorialPage/components/PostDetails.jsx index 3563fcc4..1c46ac63 100644 --- a/src/components/TutorialPage/components/PostDetails.jsx +++ b/src/components/TutorialPage/components/PostDetails.jsx @@ -12,9 +12,12 @@ import ToggleButton from "@mui/lab/ToggleButton"; import ToggleButtonGroup from "@mui/lab/ToggleButtonGroup"; import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; +import User from "./UserDetails"; import { useDispatch, useSelector } from "react-redux"; import { useFirebase, useFirestore } from "react-redux-firebase"; import { getUserProfileData } from "../../../store/actions"; +import { HashLink } from "react-router-hash-link"; +import { useParams } from "react-router-dom"; const useStyles = makeStyles(() => ({ container: { padding: "20px", @@ -43,7 +46,8 @@ const PostDetails = ({ details }) => { const firebase = useFirebase(); const firestore = useFirestore(); const [alignment, setAlignment] = React.useState("left"); - const [count, setCount] = useState(details.upVote - details.downVote); + const [count, setCount] = useState(details.upVote - details.downVote || 0); + const { id } = useParams(); useEffect(() => { getUserProfileData(details.user)(firebase, firestore, dispatch); @@ -96,52 +100,11 @@ const PostDetails = ({ details }) => { - - - - {user?.photoURL && user?.photoURL.length > 0 ? ( - - ) : ( - user?.displayName[0] - )} - - - - - {user?.displayName} - - - {details?.published_on - ? getTime(details?.published_on) - : ""} - - - - + @@ -171,9 +134,11 @@ const PostDetails = ({ details }) => { - - - + + + + + ({ container: { @@ -20,10 +21,9 @@ const Tutorial = ({ steps }) => { {i + 1 + ". " + step.title} - + + + ); })} diff --git a/src/components/TutorialPage/components/UserDetails.jsx b/src/components/TutorialPage/components/UserDetails.jsx new file mode 100644 index 00000000..d028eea8 --- /dev/null +++ b/src/components/TutorialPage/components/UserDetails.jsx @@ -0,0 +1,120 @@ +import React, { useEffect, useState } from "react"; +import { Typography, Button, Grid } from "@mui/material"; +import { makeStyles } from "@mui/styles"; +import Avatar from "@mui/material/Avatar"; +import { useDispatch, useSelector } from "react-redux"; +import { useFirebase, useFirestore } from "react-redux-firebase"; +import { getUserProfileData } from "../../../store/actions"; +import { isUserFollower } from "../../../store/actions/profileActions"; +import { addUserFollower } from "../../../store/actions"; +const useStyles = makeStyles(() => ({ + container: { + padding: "20px", + boxSizing: "border-box" + }, + small: { + padding: "2px" + }, + bold: { + fontWeight: "600" + } +})); +const User = ({ id, timestamp, showFollowButton, size }) => { + const classes = useStyles(); + const dispatch = useDispatch(); + const firebase = useFirebase(); + const firestore = useFirestore(); + const [isFollowed, setIsFollowed] = useState(true); + useEffect(() => { + getUserProfileData(id)(firebase, firestore, dispatch); + }, [id]); + + const profileData = useSelector(({ firebase: { profile } }) => profile); + + const user = useSelector( + ({ + profile: { + user: { data } + } + }) => data + ); + + useEffect(() => { + const checkIsFollowed = async () => { + const status = await isUserFollower(profileData.uid, user.uid, firestore); + setIsFollowed(status); + console.log(status); + }; + checkIsFollowed(); + }, [profileData, user]); + + const followUser = () => { + addUserFollower(profileData, user, firestore); + }; + + const getTime = timestamp => { + return timestamp.toDate().toDateString(); + }; + return ( + <> + + + + {user?.photoURL && user?.photoURL.length > 0 ? ( + + ) : ( + user?.displayName[0] + )} + + + + + {user?.displayName} + + + {timestamp ? getTime(timestamp) : ""} + + {showFollowButton && ( + + )} + + + + ); +}; + +export default User; diff --git a/src/components/TutorialPage/index.jsx b/src/components/TutorialPage/index.jsx index f1d16f14..bef8ef54 100644 --- a/src/components/TutorialPage/index.jsx +++ b/src/components/TutorialPage/index.jsx @@ -9,7 +9,10 @@ import Grid from "@mui/material/Grid"; import useStyles from "./styles"; import StepsBar from "./StepBar"; import useWindowSize from "../../helpers/customHooks/useWindowSize"; -import { getTutorialData } from "../../store/actions/tutorialPageActions"; +import { + getTutorialData, + getTutorialSteps +} from "../../store/actions/tutorialPageActions"; import { getUserProfileData } from "../../store/actions"; import { useDispatch, useSelector } from "react-redux"; import { useFirebase, useFirestore } from "react-redux-firebase"; @@ -28,6 +31,7 @@ function TutorialPage({ background = "white", textColor = "black" }) { const firestore = useFirestore(); useEffect(() => { getTutorialData(id)(firebase, firestore, dispatch); + getTutorialSteps(id)(firebase, firestore, dispatch); }, []); const tutorial = useSelector( ({ @@ -38,15 +42,23 @@ function TutorialPage({ background = "white", textColor = "black" }) { ); const postDetails = { - title: tutorial?.tut_title, - org: tutorial?.org_handle, - user: tutorial?.user_handle, + title: tutorial?.title, + org: tutorial?.owner, + user: tutorial?.created_by, upVote: tutorial?.upVotes, downVote: tutorial?.downVotes, - published_on: tutorial?.published_on, + published_on: tutorial?.createdAt, tag: tutorial?.tut_tags }; + const steps = useSelector( + ({ + tutorialPage: { + post: { steps } + } + }) => steps + ); + return ( @@ -86,8 +98,8 @@ function TutorialPage({ background = "white", textColor = "black" }) { xs={6} > - - + + diff --git a/src/components/Tutorials/MyTutorials/BaseTutorialsComponent/index.jsx b/src/components/Tutorials/MyTutorials/BaseTutorialsComponent/index.jsx index e1e7aae4..f77b00c2 100644 --- a/src/components/Tutorials/MyTutorials/BaseTutorialsComponent/index.jsx +++ b/src/components/Tutorials/MyTutorials/BaseTutorialsComponent/index.jsx @@ -32,11 +32,19 @@ const BaseTutorialsComponent = ({ owner = "", ownerName = "", users = [] }) => { const index = [...user, ...org]; const index_array = index.filter(e => e.owner === owner); +<<<<<<< HEAD return (
{index_array.map((tutorial, index) => ( +======= + console.log(index, index_array); + return ( +
+ + {index_array[0]?.tutorials?.map((tutorial, index) => ( +>>>>>>> 3225631bf90c2b306f0a63e4b5035d7ffef3c770 { ))} {index_array.length === 0 && ( +<<<<<<< HEAD +======= + + + +>>>>>>> 3225631bf90c2b306f0a63e4b5035d7ffef3c770 )}
diff --git a/src/components/Tutorials/MyTutorials/OrgTutorials/index.jsx b/src/components/Tutorials/MyTutorials/OrgTutorials/index.jsx index f4499a6b..827baf42 100644 --- a/src/components/Tutorials/MyTutorials/OrgTutorials/index.jsx +++ b/src/components/Tutorials/MyTutorials/OrgTutorials/index.jsx @@ -16,7 +16,6 @@ const OrgTabPanel = ({ orgList, user }) => { }); const onSelectTab = index => setSelectedTab(index); - useEffect(() => { setListData([ { @@ -59,15 +58,7 @@ const OrgTabPanel = ({ orgList, user }) => { - - Not Implemented Yet - + diff --git a/src/components/Tutorials/subComps/RemoveStepModal.jsx b/src/components/Tutorials/subComps/RemoveStepModal.jsx index 22deaf18..52c9abb6 100644 --- a/src/components/Tutorials/subComps/RemoveStepModal.jsx +++ b/src/components/Tutorials/subComps/RemoveStepModal.jsx @@ -24,7 +24,11 @@ const RemoveStepModal = ({ setVisible(viewModal); }, [viewModal]); +<<<<<<< HEAD const handleOnOk = () => { +======= + const handleOnOk = event => { +>>>>>>> 3225631bf90c2b306f0a63e4b5035d7ffef3c770 ; if (step_length > 1) { +<<<<<<< HEAD +======= + event.preventDefault(); +>>>>>>> 3225631bf90c2b306f0a63e4b5035d7ffef3c770 removeStep( owner, tutorial_id, @@ -82,7 +90,11 @@ const RemoveStepModal = ({ +<<<<<<< HEAD diff --git a/src/store/actions/actionTypes.js b/src/store/actions/actionTypes.js index 990aee45..34c8d586 100644 --- a/src/store/actions/actionTypes.js +++ b/src/store/actions/actionTypes.js @@ -135,3 +135,23 @@ export const GET_LAUNCHED_ORGS_FAIL = "GET_LAUNCHED_ORGS_FAIL"; export const GET_POST_DATA_START = "GET_POST_DETAILS_START"; export const GET_POST_DATA_SUCCESS = "GET_POST_DETAILS_SUCCESS"; export const GET_POST_DATA_FAIL = "GET_POST_DETAILS_FAILED"; + +export const GET_COMMENT_DATA_START = "GET_COMMENT_DATA_START"; +export const GET_COMMENT_DATA_SUCCESS = "GET_COMMENT_DATA_SUCCESS"; +export const GET_COMMENT_DATA_FAIL = "GET_COMMENT_DATA_FAIL"; + +export const GET_REPLIES_START = "GET_REPLIES_START"; +export const GET_REPLIES_SUCCESS = "GET_REPLIES_SUCCESS"; +export const GET_REPLIES_FAIL = "GET_REPLIES_FAIL"; + +export const ADD_COMMENT_START = "ADD_COMMENT_START"; +export const ADD_COMMENT_SUCCESS = "ADD_COMMENT_SUCCESS"; +export const ADD_COMMENT_FAILED = "ADD_COMMENT_FAILED"; + +export const GET_STEPS_DATA_START = "GET_STEPS_DETAILS_START"; +export const GET_STEPS_DATA_SUCCESS = "GET_STEPS_DETAILS_SUCCESS"; +export const GET_STEPS_DATA_FAIL = "GET_STEPS_DETAILS_FAILED"; + +export const GET_TUTORIAL_FEED_START = "GET_TUTORIAL_FEED_START"; +export const GET_TUTORIAL_FEED_SUCCESS = "GET_TUTORIAL_FEED_SUCCESS"; +export const GET_TUTORIAL_FEED_FAILED = "GET_TUTORIAL_FEED_FAILED"; diff --git a/src/store/actions/index.js b/src/store/actions/index.js index 53775b7f..76520c41 100644 --- a/src/store/actions/index.js +++ b/src/store/actions/index.js @@ -28,7 +28,9 @@ export { unPublishOrganization, uploadOrgProfileImage, addFollower, - removeFollower + removeFollower, + subscribeOrg, + unSubscribeOrg } from "./orgActions"; export { clearProfileEditError, diff --git a/src/store/actions/orgActions.js b/src/store/actions/orgActions.js index 10fab6cd..917e077f 100644 --- a/src/store/actions/orgActions.js +++ b/src/store/actions/orgActions.js @@ -13,29 +13,30 @@ export const searchFromIndex = query => { export const getOrgUserData = org_handle => async (firestore, dispatch) => { try { dispatch({ type: actions.GET_ORG_USER_DATA_START }); - const usersDoc = await firestore - .collection("cl_org_general") - .doc(org_handle) - .collection("cl_org_users") - .doc("users") + const orgUsersSnap = await firestore + .collection("org_users") + .where("org_handle", "==", org_handle) .get(); - const usersData = usersDoc.data(); - const users = _.omit(usersData, ["createdAt", "updatedAt"]); - const promises = Object.keys(users).map(async uid => { - let userDoc = await firestore.collection("cl_user").doc(uid).get(); + + const orgUsersDocs = orgUsersSnap.docs.map((doc) => doc.data()); + + const orgPromises = orgUsersDocs.map(async (user) => { + let userDoc = await firestore.collection("cl_user").doc(user.uid).get(); return { name: userDoc.get("displayName"), handle: userDoc.get("handle"), image: userDoc.get("photoURL"), - permission_level: users[uid] + permission_level: user.permissions }; }); - const userData = await Promise.all(promises); + const orgUserData = await Promise.all(orgPromises); + + dispatch({ type: actions.GET_ORG_USER_DATA_SUCCESS, - payload: _.orderBy(userData, ["permission_level"], ["desc"]) + payload: _.orderBy(orgUserData, ["permission_level"], ["desc"]) }); - userData.forEach(doc => { + orgUserData.forEach(doc => { elasticlunr.addDocToIndex(doc); }); } catch (e) { @@ -43,6 +44,7 @@ export const getOrgUserData = org_handle => async (firestore, dispatch) => { } }; +// adds a user to organization's users list with a set of permissions export const addOrgUser = ({ org_handle, handle, permissions }) => async (firestore, dispatch) => { @@ -55,14 +57,13 @@ export const addOrgUser = if (userDoc.docs.length === 1) { const uid = userDoc.docs[0].get("uid"); await firestore - .collection("cl_org_general") - .doc(org_handle) - .collection("cl_org_users") - .doc("users") - .update({ - [uid]: [permissions], - updatedAt: firestore.FieldValue.serverTimestamp() - }); + .collection("org_users") + .doc(`${org_handle}_${uid}`) + .set({ + uid: uid, + org_handle: org_handle, + permissions: permissions + }) await getOrgUserData(org_handle)(firestore, dispatch); dispatch({ type: actions.ADD_ORG_USER_SUCCESS }); @@ -73,10 +74,12 @@ export const addOrgUser = }); } } catch (e) { + console.log(e) dispatch({ type: actions.ADD_ORG_USER_FAIL, payload: e.message }); } }; +// removes all permissions of a user from an organization export const removeOrgUser = ({ org_handle, handle }) => async (firestore, dispatch) => { @@ -89,14 +92,9 @@ export const removeOrgUser = if (userDoc.docs.length === 1) { const uid = userDoc.docs[0].get("uid"); await firestore - .collection("cl_org_general") - .doc(org_handle) - .collection("cl_org_users") - .doc("users") - .update({ - [uid]: firestore.FieldValue.delete(), - updatedAt: firestore.FieldValue.serverTimestamp() - }); + .collection("org_users") + .doc(`${org_handle}_${uid}`) + .delete(); await getOrgUserData(org_handle)(firestore, dispatch); dispatch({ type: actions.ADD_ORG_USER_SUCCESS }); @@ -107,6 +105,7 @@ export const removeOrgUser = }); } } catch (e) { + console.log(e); dispatch({ type: actions.ADD_ORG_USER_FAIL, payload: e.message }); } }; @@ -124,45 +123,25 @@ export const getOrgBasicData = org_handle => async firebase => { if (!orgDoc.exists) return null; - const org_name = orgDoc.get("org_name"); - const org_image = orgDoc.get("org_image"); - const org_link_facebook = orgDoc.get("org_link_facebook"); - const org_link_github = orgDoc.get("org_link_github"); - const org_link_linkedin = orgDoc.get("org_link_linkedin"); - const org_link_twitter = orgDoc.get("org_link_twitter"); - const org_website = orgDoc.get("org_website"); - const org_published = orgDoc.get("org_published"); - const org_description = orgDoc.get("org_description"); - const org_country = orgDoc.get("org_country"); - const orgPermissionDoc = await firestore - .collection("cl_org_general") - .doc(org_handle) - .collection("cl_org_users") - .doc("users") + .collection("org_users") + .doc(`${org_handle}_${uid}`) .get(); if (!orgPermissionDoc.exists) return null; - const user_permissions = orgPermissionDoc.get(uid); + const user_permissions = orgPermissionDoc.get("permissions"); if (!user_permissions) return null; + const orgData = orgDoc.data(); return { - org_handle, - org_name, - org_link_facebook, - org_link_github, - org_link_linkedin, - org_link_twitter, - org_website, - org_published, - org_description, - org_country, - org_image: org_image ? org_image : "", + ...orgData, + org_image: orgData.org_image ? orgData.org_image : "", permissions: user_permissions - }; + } } catch (e) { + console.log(e); throw e; } }; @@ -251,7 +230,7 @@ export const getOrgData = try { dispatch({ type: actions.GET_ORG_DATA_START }); - const isOrgExists = await checkOrgHandleExists(org_handle)(firebase); + const isOrgExists = await checkOrgHandleExists(org_handle)(firestore); if (isOrgExists) { const doc = await firestore @@ -262,7 +241,13 @@ export const getOrgData = organizations.includes(org_handle) || doc.get("org_published"); if (isPublished) { - dispatch({ type: actions.GET_ORG_DATA_SUCCESS, payload: doc.data() }); + dispatch({ + type: actions.GET_ORG_DATA_SUCCESS, + payload: { + ...doc.data(), + userSubscription: await isUserSubscribed(org_handle, firebase, firestore) + } + }); } else { dispatch({ type: actions.GET_ORG_DATA_SUCCESS, payload: false }); } @@ -270,6 +255,7 @@ export const getOrgData = dispatch({ type: actions.GET_ORG_DATA_SUCCESS, payload: false }); } } catch (e) { + console.log(e) dispatch({ type: actions.GET_ORG_DATA_FAIL, payload: e.message }); } }; @@ -293,6 +279,65 @@ export const getLaunchedOrgsData = () => async (firestore, dispatch) => { dispatch({ type: actions.GET_LAUNCHED_ORGS_FAIL, payload: e.message }); } }; + +const isUserSubscribed = async (org_handle, firebase, firestore) => { + const auth = firebase.auth().currentUser; + + const subscription = await firestore + .collection("org_subscribers") + .doc(`${org_handle}_${auth.uid}`) + .get() + + return subscription.exists; +} + +export const subscribeOrg = + (org_handle) => async (firebase, firestore, dispatch) => { + try { + const auth = firebase.auth().currentUser; + + await firestore + .collection("org_subscribers") + .doc(`${org_handle}_${auth.uid}`) + .set({ + uid: auth.uid, + org_handle, + }) + + await firestore + .collection("cl_org_general") + .doc(org_handle) + .update({ + followerCount: firebase.firestore.FieldValue.increment(1) + }) + + getOrgData(org_handle, [org_handle])(firebase, firestore, dispatch); + } catch (e) { + console.log(e); + } + } + +export const unSubscribeOrg = + (org_handle) => async (firebase, firestore, dispatch) => { + try { + const auth = firebase.auth().currentUser; + await firestore + .collection("org_subscribers") + .doc(`${org_handle}_${auth.uid}`) + .delete() + + await firestore + .collection("cl_org_general") + .doc(org_handle) + .update({ + followerCount: firebase.firestore.FieldValue.increment(-1) + }) + + getOrgData(org_handle, [org_handle])(firebase, firestore, dispatch); + } catch (e) { + console.log(e); + } + } export const removeFollower = (val, people, handle, orgFollowed, profileId) => (firestore, dispatch) => { console.log("test"); diff --git a/src/store/actions/profileActions.js b/src/store/actions/profileActions.js index 548fe5fb..216314ff 100644 --- a/src/store/actions/profileActions.js +++ b/src/store/actions/profileActions.js @@ -24,22 +24,24 @@ export const setCurrentOrgUserPermissions = } }; -export const getProfileData = organizations => async (firebase, dispatch) => { +export const getProfileData = () => async (firebase, firestore, dispatch) => { try { - let orgs = []; + dispatch({ type: actions.GET_PROFILE_DATA_START }); + const userOrgs = await getAllOrgsOfCurrentUser()(firebase, firestore, dispatch); + const organizations = userOrgs?.map(org => org.org_handle); + // console.log(organizations); if (organizations && organizations.length > 0) { - dispatch({ type: actions.GET_PROFILE_DATA_START }); const promises = organizations.map(org_handle => getOrgBasicData(org_handle)(firebase) ); - orgs = await Promise.all(promises); + const orgs = await Promise.all(promises); setCurrentOrgUserPermissions( orgs[0].org_handle, orgs[0].permissions )(dispatch); dispatch({ type: actions.GET_PROFILE_DATA_SUCCESS, - payload: { organizations: _.orderBy(orgs, ["org_handle"], ["asc"]) } + payload: { organizations: _.orderBy(orgs, ["permissions"], ["desc"]) } }); } else { dispatch({ type: actions.GET_PROFILE_DATA_END }); @@ -155,14 +157,23 @@ export const getUserProfileData = handle => async (firebase, firestore, dispatch) => { try { dispatch({ type: actions.GET_USER_DATA_START }); - const isUserExists = await checkUserHandleExists(handle)(firebase); + const isUserExists = await checkUserHandleExists(handle)(firestore); if (isUserExists) { const docs = await firestore .collection("cl_user") .where("handle", "==", handle) .get(); const doc = docs.docs[0].data(); - dispatch({ type: actions.GET_USER_DATA_SUCCESS, payload: doc }); + const currentUserId = firebase.auth().currentUser.uid; + const followingStatus = await isUserFollower( + currentUserId, + doc.uid, + firestore + ); + dispatch({ + type: actions.GET_USER_DATA_SUCCESS, + payload: { ...doc, isFollowing: followingStatus } + }); } else { dispatch({ type: actions.GET_USER_DATA_SUCCESS, payload: false }); } @@ -175,41 +186,51 @@ export const clearUserProfile = () => dispatch => { dispatch({ type: actions.CLEAR_USER_PROFILE_DATA_STATE }); }; -export const addUserFollower = ( +export const isUserFollower = async (followerId, followingId, firestore) => { + const followerDoc = await firestore + .collection("user_followers") + .doc(`${followingId}_${followerId}`) + .get(); + return followerDoc.exists; +}; + +export const addUserFollower = async ( currentProfileData, - followers, - following, profileData, firestore, dispatch ) => { try { - if (followers && followers.includes(currentProfileData.handle)) { - } else if (followers) { - const arr = [...followers]; - arr.push(currentProfileData.handle); - firestore.collection("cl_user").doc(profileData.uid).update({ - followers: arr - }); - var arr2 = []; - if (following) arr2 = [...following]; - - arr2.push(profileData.handle); - firestore.collection("cl_user").doc(currentProfileData.uid).update({ - following: arr2 - }); - } else { + const followStatus = await isUserFollower( + currentProfileData.uid, + profileData.uid, firestore + ); + if (followStatus === false) { + await firestore + .collection("user_followers") + .doc(`${profileData.uid}_${currentProfileData.uid}`) + .set({ + followingId: profileData.uid, + followerId: currentProfileData.uid + }); + + await firestore .collection("cl_user") - .doc(currentProfileData.uid) + .doc(profileData.uid) .update({ - following: [profileData.handle] + followerCount: firestore.FieldValue + ? firestore.FieldValue.increment(1) + : 1 }); - firestore + + await firestore .collection("cl_user") - .doc(profileData.uid) + .doc(currentProfileData.uid) .update({ - followers: [currentProfileData.handle] + followingCount: firestore.FieldValue + ? firestore.FieldValue.increment(1) + : 1 }); } } catch (e) { @@ -217,26 +238,62 @@ export const addUserFollower = ( } }; -export const removeUserFollower = ( - followers, +export const removeUserFollower = async ( currentProfileData, - following, profileData, firestore, dispatch ) => { try { - var filteredFollowers = followers.filter(function (value, index, arr) { - return value !== currentProfileData.handle; - }); - firestore.collection("cl_user").doc(profileData.uid).update({ - followers: filteredFollowers - }); - var currFollowing = following.filter(function (value, index, arr) { - return profileData.handle !== value; - }); - firestore.collection("cl_user").doc(currentProfileData.uid).update({ - following: currFollowing - }); - } catch (e) {} + const followStatus = await isUserFollower( + currentProfileData.uid, + profileData.uid, + firestore + ); + if (followStatus === true) { + await firestore + .collection("user_followers") + .doc(`${profileData.uid}_${currentProfileData.uid}`) + .delete(); + + await firestore + .collection("cl_user") + .doc(profileData.uid) + .update({ + followerCount: firestore.FieldValue + ? firestore.FieldValue.increment(-1) + : 0 + }); + + await firestore + .collection("cl_user") + .doc(currentProfileData.uid) + .update({ + followingCount: firestore.FieldValue + ? firestore.FieldValue.increment(-1) + : 0 + }); + } + } catch (e) { + console.log(e); + } }; + +const getAllOrgsOfCurrentUser = (uid) => async (firebase, firestore, dispatch) => { + try { + const auth = firebase.auth().currentUser; + if (auth === null) return []; + const orgUsersDocs = await firestore + .collection("org_users") + .where("uid", "==", auth.uid) + .get() + + const userOrgs = orgUsersDocs.docs.map( + orgUserDoc => orgUserDoc.data() + ); + + return userOrgs; + } catch (e) { + console.log(e) + } +} \ No newline at end of file diff --git a/src/store/actions/tutorialPageActions.js b/src/store/actions/tutorialPageActions.js index 986806c3..470aceb3 100644 --- a/src/store/actions/tutorialPageActions.js +++ b/src/store/actions/tutorialPageActions.js @@ -1,15 +1,222 @@ import * as actions from "./actionTypes"; + +export const getTutorialFeedIdArray = + uid => async (firebase, firestore, dispatch) => { + try { + let followings = []; + if (uid) { + followings = await firestore + .collection("user_followers") + .where("followerId", "==", uid) + .where("isPublished", "==", true) + .get() + .then(async docs => { + const result = []; + for (const doc of docs.docs) { + const handle = await firestore + .collection("cl_user") + .doc(doc.data().followingId) + .get() + .then(doc => doc.data().handle); + + result.push(handle); + } + return result; + }); + } + let followingUsersTutorials = []; + if (followings.length > 0) { + followingUsersTutorials = await firestore + .collection("tutorials") + .where("created_by", "in", followings) + .where("isPublished", "==", true) + .limit(50) + .get() + .then(docs => { + const tutorialsArray = []; + docs.docs.map(doc => { + const tutorialId = doc.id; + tutorialsArray.push(tutorialId); + }); + return tutorialsArray; + }); + } + followings.push(uid); + let newTutorials = []; + if (uid) { + newTutorials = await firestore + .collection("tutorials") + .where("created_by", "not-in", followings) + .where("isPublished", "==", true) + .limit(50) + .get() + .then(docs => { + const tutorialsArray = []; + docs.docs.map(doc => { + const tutorialId = doc.id; + tutorialsArray.push(tutorialId); + }); + return tutorialsArray; + }); + } else { + newTutorials = await firestore + .collection("tutorials") + .where("isPublished", "==", true) + .limit(50) + .get() + .then(docs => { + const tutorialsArray = []; + docs.docs.map(doc => { + const tutorialId = doc.id; + tutorialsArray.push(tutorialId); + }); + return tutorialsArray; + }); + } + + const tutorials = followingUsersTutorials.concat(newTutorials); + + return tutorials; + } catch (e) { + console.log(e); + } + }; + +export const getTutorialFeedData = + tutorialIdArray => async (firebase, firestore, dispatch) => { + try { + dispatch({ type: actions.GET_TUTORIAL_FEED_START }); + const tutorials = await firestore + .collection("tutorials") + .where("tutorial_id", "in", tutorialIdArray) + .get(); + if (tutorials.empty) { + dispatch({ type: actions.GET_TUTORIAL_FEED_SUCCESS, payload: [] }); + } else { + const feed = tutorials.docs.map(doc => { + const tutorial = doc.data(); + const tutorialData = { + tutorial_id: tutorial?.tutorial_id, + title: tutorial?.title, + summary: tutorial?.summary, + owner: tutorial?.owner, + created_by: tutorial?.created_by, + createdAt: tutorial?.createdAt, + featured_image: tutorial?.featured_image + }; + return tutorialData; + }); + dispatch({ type: actions.GET_TUTORIAL_FEED_SUCCESS, payload: feed }); + } + } catch (e) { + dispatch({ type: actions.GET_TUTORIAL_FEED_FAILED, payload: e }); + } + }; + export const getTutorialData = tutorialID => async (firebase, firestore, dispatch) => { try { dispatch({ type: actions.GET_POST_DATA_START }); const data = await firestore - .collection("cl_tutorials") + .collection("tutorials") .doc(tutorialID) .get(); const tutorial = data.data(); dispatch({ type: actions.GET_POST_DATA_SUCCESS, payload: tutorial }); + } catch (e) { + dispatch({ type: actions.GET_POST_DATA_FAIL }); + console.log(e); + } + }; + +export const getTutorialSteps = + tutorialID => async (firebase, firestore, dispatch) => { + try { + dispatch({ type: actions.GET_STEPS_DATA_START }); + const data = await firestore + .collection("tutorials") + .doc(tutorialID) + .collection("steps") + .get() + .then(querySnapshot => { + let steps = []; + querySnapshot.forEach(doc => { + steps.push(doc.data()); + }); + return steps; + }); + dispatch({ type: actions.GET_STEPS_DATA_SUCCESS, payload: data }); + } catch (e) { + dispatch({ type: actions.GET_STEPS_DATA_FAIL, payload: e }); + console.log(e); + } + }; + +export const getCommentData = + commentId => async (firebase, firestore, dispatch) => { + try { + dispatch({ type: actions.GET_COMMENT_DATA_START }); + const data = await firestore + .collection("cl_comments") + .doc(commentId) + .get(); + const comment = data.data(); + dispatch({ type: actions.GET_COMMENT_DATA_SUCCESS, payload: comment }); + } catch (e) { + dispatch({ type: actions.GET_COMMENT_DATA_FAIL }); + console.log(e); + } + }; + +export const getCommentReply = + commentId => async (firebase, firestore, dispatch) => { + try { + console.log("commentId", commentId); + dispatch({ type: actions.GET_REPLIES_START }); + console.log("Get replies"); + const replies = await firestore + .collection("cl_comments") + .where("replyTo", "==", commentId) + .get() + .then(querySnapshot => { + let data = []; + querySnapshot.forEach(doc => { + data.push(doc.data().comment_id); + }); + return data; + }); + dispatch({ + type: actions.GET_REPLIES_SUCCESS, + payload: { replies, comment_id: commentId } + }); } catch (e) { console.log(e); } }; + +export const addComment = comment => async (firebase, firestore, dispatch) => { + try { + dispatch({ type: actions.ADD_COMMENT_START }); + await firestore + .collection("cl_comments") + .add(comment) + .then(docref => { + firestore.collection("cl_comments").doc(docref.id).update({ + comment_id: docref.id + }); + if (comment.replyTo == comment.tutorial_id) { + firestore + .collection("tutorials") + .doc(comment.tutorial_id) + .update({ + comments: firebase.firestore.FieldValue.arrayUnion(docref.id) + }); + } + }) + .then(() => { + dispatch({ type: actions.ADD_COMMENT_SUCCESS }); + }); + } catch (e) { + dispatch({ type: actions.ADD_COMMENT_FAILED, payload: e.message }); + } +}; diff --git a/src/store/actions/tutorialsActions.js b/src/store/actions/tutorialsActions.js index eb82f57d..7ab56599 100644 --- a/src/store/actions/tutorialsActions.js +++ b/src/store/actions/tutorialsActions.js @@ -20,7 +20,7 @@ export const getUserTutorialsBasicData = user_handle => async (firestore, dispatch) => { try { dispatch({ type: actions.GET_USER_TUTORIALS_BASIC_START }); - let index; + let index = []; const userTutorialsQuerySnapshot = await firestore .collection("tutorials") .where("editors", "array-contains", user_handle) @@ -44,9 +44,10 @@ export const getUserTutorialsBasicData = return new_doc; }); } + dispatch({ type: actions.GET_USER_TUTORIALS_BASIC_SUCCESS, - payload: index + payload: { owner: user_handle, tutorials: index } }); } catch (e) { dispatch({ @@ -136,6 +137,7 @@ export const createTutorial = owner, summary, title, + tutorial_id: documentID, featured_image: "", icon: "", url: "", @@ -171,9 +173,9 @@ export const createTutorial = } }; -const checkUserOrOrgHandle = handle => async firebase => { - const userHandleExists = await checkUserHandleExists(handle)(firebase); - const orgHandleExists = await checkOrgHandleExists(handle)(firebase); +const checkUserOrOrgHandle = handle => async firestore => { + const userHandleExists = await checkUserHandleExists(handle)(firestore); + const orgHandleExists = await checkOrgHandleExists(handle)(firestore); if (userHandleExists && !orgHandleExists) { return "user"; @@ -347,7 +349,6 @@ export const removeStep = (owner, tutorial_id, step_id, current_step_no) => async (firebase, firestore, dispatch) => { try { - const type = await checkUserOrOrgHandle(owner)(firebase); await firestore .collection("tutorials") .doc(tutorial_id) @@ -389,7 +390,7 @@ export const uploadTutorialImages = (owner, tutorial_id, files) => async (firebase, firestore, dispatch) => { try { dispatch({ type: actions.TUTORIAL_IMAGE_UPLOAD_START }); - const type = await checkUserOrOrgHandle(owner)(firebase); + const type = await checkUserOrOrgHandle(owner)(firestore); const storagePath = `tutorials/${type}/${owner}/${tutorial_id}`; const dbPath = `tutorials`; @@ -431,7 +432,7 @@ export const remoteTutorialImages = dispatch({ type: actions.TUTORIAL_IMAGE_DELETE_START }); - const type = await checkUserOrOrgHandle(owner)(firebase); + const type = await checkUserOrOrgHandle(owner)(firestore); const storagePath = `tutorials/${type}/${owner}/${tutorial_id}/${name}`; const dbPath = `tutorials`; @@ -468,10 +469,7 @@ export const updateStepTitle = (owner, tutorial_id, step_id, step_title) => async (firebase, firestore, dispatch) => { try { - const type = await checkUserOrOrgHandle(owner)(firebase); - const dbPath = `tutorials/${tutorial_id}/steps`; - await firestore .collection(dbPath) .doc(step_id) @@ -486,7 +484,7 @@ export const updateStepTitle = dispatch ); } catch (e) { - console.log(e.message); + console.log(e); } }; @@ -494,8 +492,6 @@ export const updateStepTime = (owner, tutorial_id, step_id, step_time) => async (firebase, firestore, dispatch) => { try { - const type = await checkUserOrOrgHandle(owner)(firebase); - const dbPath = `tutorials/${tutorial_id}/steps`; await firestore @@ -520,8 +516,6 @@ export const setTutorialTheme = ({ tutorial_id, owner, bgColor, textColor }) => async (firebase, firestore, dispatch) => { try { - const type = await checkUserOrOrgHandle(owner)(firebase); - const dbPath = `tutorials`; await firestore.collection(dbPath).doc(tutorial_id).update({ diff --git a/src/store/reducers/tutorialPageReducers/commentReducer.js b/src/store/reducers/tutorialPageReducers/commentReducer.js new file mode 100644 index 00000000..7d1780ac --- /dev/null +++ b/src/store/reducers/tutorialPageReducers/commentReducer.js @@ -0,0 +1,70 @@ +import * as actions from "../../actions/actionTypes"; + +const initialState = { + loading: false, + error: null, + data: [], + replies: [] +}; + +const CommentReducer = (state = initialState, { type, payload }) => { + switch (type) { + case actions.GET_COMMENT_DATA_START: + return { + ...state, + loading: true + }; + + case actions.GET_COMMENT_DATA_SUCCESS: + return { + ...state, + loading: false, + error: false, + data: [...state.data, payload] + }; + + case actions.GET_COMMENT_DATA_FAIL: + return { + ...state, + loading: false, + error: payload + }; + + case actions.GET_REPLIES_START: + return { + ...state, + loading: true + }; + + case actions.GET_REPLIES_SUCCESS: + return { + ...state, + loading: false, + replies: [...state.replies, payload] + }; + + case actions.ADD_COMMENT_START: + return { + ...state, + loading: true + }; + + case actions.ADD_COMMENT_SUCCESS: + return { + ...state, + loading: false + }; + + case actions.ADD_COMMENT_FAILED: + return { + ...state, + loading: false, + error: payload + }; + + default: + return state; + } +}; + +export default CommentReducer; diff --git a/src/store/reducers/tutorialPageReducers/feedReducer.js b/src/store/reducers/tutorialPageReducers/feedReducer.js new file mode 100644 index 00000000..aacd3c67 --- /dev/null +++ b/src/store/reducers/tutorialPageReducers/feedReducer.js @@ -0,0 +1,36 @@ +import * as actions from "../../actions/actionTypes"; + +const initialState = { + loading: false, + error: null, + homepageFeedArray: [] +}; + +const FeedReducer = (state = initialState, { type, payload }) => { + switch (type) { + case actions.GET_TUTORIAL_FEED_START: + return { + ...state, + loading: true + }; + + case actions.GET_TUTORIAL_FEED_SUCCESS: + return { + ...state, + loading: false, + homepageFeedArray: payload + }; + + case actions.GET_TUTORIAL_FEED_FAILED: + return { + ...state, + loading: false, + error: payload + }; + + default: + return state; + } +}; + +export default FeedReducer; diff --git a/src/store/reducers/tutorialPageReducers/index.js b/src/store/reducers/tutorialPageReducers/index.js index 1c7a7aed..7b6c5aee 100644 --- a/src/store/reducers/tutorialPageReducers/index.js +++ b/src/store/reducers/tutorialPageReducers/index.js @@ -1,6 +1,10 @@ import { combineReducers } from "redux"; import PostReducer from "./postReducer"; +import CommentReducer from "./commentReducer"; +import FeedReducer from "./feedReducer"; export default combineReducers({ - post: PostReducer + post: PostReducer, + comment: CommentReducer, + feed: FeedReducer }); diff --git a/src/store/reducers/tutorialPageReducers/postReducer.js b/src/store/reducers/tutorialPageReducers/postReducer.js index c8e85d0c..ea297570 100644 --- a/src/store/reducers/tutorialPageReducers/postReducer.js +++ b/src/store/reducers/tutorialPageReducers/postReducer.js @@ -3,7 +3,8 @@ import * as actions from "../../actions/actionTypes"; const initialState = { loading: false, error: null, - data: null + data: null, + steps: [] }; const PostReducer = (state = initialState, { type, payload }) => { @@ -32,6 +33,27 @@ const PostReducer = (state = initialState, { type, payload }) => { error: payload }; + case actions.GET_STEPS_DATA_START: + return { + ...state, + loading: true + }; + + case actions.GET_STEPS_DATA_SUCCESS: + return { + ...state, + loading: false, + error: false, + steps: payload + }; + + case actions.GET_STEPS_DATA_FAIL: + return { + ...state, + loading: false, + error: payload + }; + default: return state; } diff --git a/src/store/reducers/tutorialsReducer/dataReducer.js b/src/store/reducers/tutorialsReducer/dataReducer.js index 740714ac..8e4c8355 100644 --- a/src/store/reducers/tutorialsReducer/dataReducer.js +++ b/src/store/reducers/tutorialsReducer/dataReducer.js @@ -1,7 +1,7 @@ import * as actions from "../../actions/actionTypes"; const initialState = { - user: null, + user: [], org: [], loading: false, error: null @@ -23,7 +23,7 @@ const TutorialsDataReducer = (state = initialState, { type, payload }) => { case actions.GET_USER_TUTORIALS_BASIC_SUCCESS: return { ...state, - user: payload, + user: [payload], loading: false, error: false }; diff --git a/testdata/auth_export/accounts.json b/testdata/auth_export/accounts.json index 84d8088e..33ce796f 100644 --- a/testdata/auth_export/accounts.json +++ b/testdata/auth_export/accounts.json @@ -1,103 +1 @@ -{ - "kind": "identitytoolkit#DownloadAccountResponse", - "users": [ - { - "localId": "850pPlsLaCg6JjmrKjOu6JqRPwhb", - "createdAt": "1660370247248", - "lastLoginAt": "1675083329436", - "passwordHash": "fakeHash:salt=fakeSaltMqDFaDJIdniCZZ8fssxq:password=123456", - "salt": "fakeSaltMqDFaDJIdniCZZ8fssxq", - "passwordUpdatedAt": 1675545265101, - "providerUserInfo": [ - { - "providerId": "password", - "email": "sougatariju13@gmail.com", - "federatedId": "sougatariju13@gmail.com", - "rawId": "sougatariju13@gmail.com" - } - ], - "validSince": "1675545265", - "email": "sougatariju13@gmail.com", - "emailVerified": true, - "disabled": false - }, - { - "localId": "FdUlui4yIE5AN2P38EZOGq2ajLm4", - "createdAt": "1667115208920", - "lastLoginAt": "1667115295605", - "passwordHash": "fakeHash:salt=fakeSaltRXnfZX61lDd6IRpMkJDJ:password=codelabz", - "salt": "fakeSaltRXnfZX61lDd6IRpMkJDJ", - "passwordUpdatedAt": 1675545265101, - "providerUserInfo": [ - { - "providerId": "password", - "email": "shivananda@gmail.com", - "federatedId": "shivananda@gmail.com", - "rawId": "shivananda@gmail.com" - } - ], - "validSince": "1675545265", - "email": "shivananda@gmail.com", - "emailVerified": true, - "disabled": false - }, - { - "localId": "qrEH9fQUqUe7kONHtmC0GncTKYBP", - "createdAt": "1667114862864", - "lastLoginAt": "1660370113336", - "displayName": "Codelabz User", - "providerUserInfo": [ - { - "providerId": "google.com", - "rawId": "5223858290493228440457232631160468415768", - "federatedId": "5223858290493228440457232631160468415768", - "displayName": "Codelabz User", - "email": "codelabzuser@example.com", - "screenName": "Codelabz User" - } - ], - "validSince": "1675545265", - "email": "codelabzuser@example.com", - "emailVerified": true, - "disabled": false - }, - { - "localId": "ufeINR1inzJu5SjBByaCYLXn3BG6", - "createdAt": "1675082998395", - "lastLoginAt": "1667115034380", - "displayName": "Mahendar Goud", - "providerUserInfo": [ - { - "providerId": "google.com", - "rawId": "1826579163248857660672940763512718407937", - "federatedId": "1826579163248857660672940763512718407937", - "displayName": "Mahendar Goud", - "email": "mahender@gmail.com" - } - ], - "validSince": "1675545265", - "email": "mahender@gmail.com", - "emailVerified": true, - "disabled": false - }, - { - "localId": "wdskLNE5S604JdZI8e9lAPC8VWzw", - "createdAt": "1675082998395", - "lastLoginAt": "1667120457241", - "displayName": "Sarfaraz Alam", - "providerUserInfo": [ - { - "providerId": "google.com", - "rawId": "9076000195242093402071893601634599548874", - "federatedId": "9076000195242093402071893601634599548874", - "displayName": "Sarfaraz Alam", - "email": "sarfaraz@gmail.com" - } - ], - "validSince": "1675545265", - "email": "sarfaraz@gmail.com", - "emailVerified": true, - "disabled": false - } - ] -} +{"kind":"identitytoolkit#DownloadAccountResponse","users":[{"localId":"850pPlsLaCg6JjmrKjOu6JqRPwhb","createdAt":"1660370247248","lastLoginAt":"1691601627634","passwordHash":"fakeHash:salt=fakeSaltMqDFaDJIdniCZZ8fssxq:password=123456","salt":"fakeSaltMqDFaDJIdniCZZ8fssxq","passwordUpdatedAt":1692522760909,"providerUserInfo":[{"providerId":"password","email":"sougatariju13@gmail.com","federatedId":"sougatariju13@gmail.com","rawId":"sougatariju13@gmail.com"}],"validSince":"1692522760","email":"sougatariju13@gmail.com","emailVerified":true,"disabled":false},{"localId":"FdUlui4yIE5AN2P38EZOGq2ajLm4","createdAt":"1667115208920","lastLoginAt":"1667115295605","passwordHash":"fakeHash:salt=fakeSaltRXnfZX61lDd6IRpMkJDJ:password=codelabz","salt":"fakeSaltRXnfZX61lDd6IRpMkJDJ","passwordUpdatedAt":1692522760911,"providerUserInfo":[{"providerId":"password","email":"shivananda@gmail.com","federatedId":"shivananda@gmail.com","rawId":"shivananda@gmail.com"}],"validSince":"1692522760","email":"shivananda@gmail.com","emailVerified":true,"disabled":false},{"localId":"JFxIMgWVfdbhPPSgpCDNv49gcbpy","createdAt":"1691601729962","lastLoginAt":"1691601729962","passwordHash":"fakeHash:salt=fakeSaltm6ZTnQcSSTnhoQbpBMjD:password=VJ_iJWedNZ_50LT","salt":"fakeSaltm6ZTnQcSSTnhoQbpBMjD","passwordUpdatedAt":1692522760911,"providerUserInfo":[{"providerId":"password","email":"candace.armstrong@hotmail.com","federatedId":"candace.armstrong@hotmail.com","rawId":"candace.armstrong@hotmail.com"}],"validSince":"1692522760","email":"candace.armstrong@hotmail.com","emailVerified":false,"disabled":false},{"localId":"qrEH9fQUqUe7kONHtmC0GncTKYBP","createdAt":"1667114862864","lastLoginAt":"1692522850006","displayName":"Codelabz User","providerUserInfo":[{"providerId":"google.com","rawId":"5223858290493228440457232631160468415768","federatedId":"5223858290493228440457232631160468415768","displayName":"Codelabz User","email":"codelabzuser@example.com","screenName":"Codelabz User"}],"validSince":"1692522760","email":"codelabzuser@example.com","emailVerified":true,"disabled":false,"lastRefreshAt":"2023-08-20T09:14:10.007Z"},{"localId":"ufeINR1inzJu5SjBByaCYLXn3BG6","createdAt":"1675082998395","lastLoginAt":"1691594556259","displayName":"Mahendar Goud","providerUserInfo":[{"providerId":"google.com","rawId":"1826579163248857660672940763512718407937","federatedId":"1826579163248857660672940763512718407937","displayName":"Mahendar Goud","email":"mahender@gmail.com"}],"validSince":"1692522760","email":"mahender@gmail.com","emailVerified":true,"disabled":false},{"localId":"wdskLNE5S604JdZI8e9lAPC8VWzw","createdAt":"1675082998395","lastLoginAt":"1667120457241","displayName":"Sarfaraz Alam","providerUserInfo":[{"providerId":"google.com","rawId":"9076000195242093402071893601634599548874","federatedId":"9076000195242093402071893601634599548874","displayName":"Sarfaraz Alam","email":"sarfaraz@gmail.com"}],"validSince":"1692522760","email":"sarfaraz@gmail.com","emailVerified":true,"disabled":false}]} diff --git a/testdata/database_export/codelabz-eb407.json b/testdata/database_export/codelabz-eb407.json index e01b24f1..59bb2d58 100644 --- a/testdata/database_export/codelabz-eb407.json +++ b/testdata/database_export/codelabz-eb407.json @@ -1,46 +1 @@ -{ - "cl_user_presence": { "wdskLNE5S604JdZI8e9lAPC8VWzw": true }, - "cl_user_sessions": { - "-NFbn9i5oqF7DyLXZkau": { - ".priority": "ufeINR1inzJu5SjBByaCYLXn3BG6", - "endedAt": 1667115125242, - "startedAt": 1667115036202, - "user": "ufeINR1inzJu5SjBByaCYLXn3BG6" - }, - "-NFbnWCS53xDUBkypF1F": { - ".priority": "ufeINR1inzJu5SjBByaCYLXn3BG6", - "endedAt": 1667115173065, - "startedAt": 1667115127671, - "user": "ufeINR1inzJu5SjBByaCYLXn3BG6" - }, - "-NFbnpBm5JNhvmb8yw1Q": { - ".priority": "FdUlui4yIE5AN2P38EZOGq2ajLm4", - "endedAt": 1667115365962, - "startedAt": 1667115209544, - "user": "FdUlui4yIE5AN2P38EZOGq2ajLm4" - }, - "-NFbo9DmZmQgkqPoHgGy": { - ".priority": "FdUlui4yIE5AN2P38EZOGq2ajLm4", - "endedAt": 1667115365962, - "startedAt": 1667115295689, - "user": "FdUlui4yIE5AN2P38EZOGq2ajLm4" - }, - "-NFboR1rvD_OsJjpBbW5": { - ".priority": "FdUlui4yIE5AN2P38EZOGq2ajLm4", - "endedAt": 1667120396588, - "startedAt": 1667115368668, - "user": "FdUlui4yIE5AN2P38EZOGq2ajLm4" - }, - "-NFc6q_6xJZj5Lbp9qUz": { - ".priority": "wdskLNE5S604JdZI8e9lAPC8VWzw", - "endedAt": 1667120535744, - "startedAt": 1667120458090, - "user": "wdskLNE5S604JdZI8e9lAPC8VWzw" - }, - "-NFc79BpQ0RZpAGXdpB6": { - ".priority": "wdskLNE5S604JdZI8e9lAPC8VWzw", - "startedAt": 1667120538446, - "user": "wdskLNE5S604JdZI8e9lAPC8VWzw" - } - } -} +{"cl_user_presence":{"qrEH9fQUqUe7kONHtmC0GncTKYBP":true,"wdskLNE5S604JdZI8e9lAPC8VWzw":true},"cl_user_sessions":{"-NFbn9i5oqF7DyLXZkau":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1667115125242,"startedAt":1667115036202,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NFbnWCS53xDUBkypF1F":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1667115173065,"startedAt":1667115127671,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NFbnpBm5JNhvmb8yw1Q":{".priority":"FdUlui4yIE5AN2P38EZOGq2ajLm4","endedAt":1667115365962,"startedAt":1667115209544,"user":"FdUlui4yIE5AN2P38EZOGq2ajLm4"},"-NFbo9DmZmQgkqPoHgGy":{".priority":"FdUlui4yIE5AN2P38EZOGq2ajLm4","endedAt":1667115365962,"startedAt":1667115295689,"user":"FdUlui4yIE5AN2P38EZOGq2ajLm4"},"-NFboR1rvD_OsJjpBbW5":{".priority":"FdUlui4yIE5AN2P38EZOGq2ajLm4","endedAt":1667120396588,"startedAt":1667115368668,"user":"FdUlui4yIE5AN2P38EZOGq2ajLm4"},"-NFc6q_6xJZj5Lbp9qUz":{".priority":"wdskLNE5S604JdZI8e9lAPC8VWzw","endedAt":1667120535744,"startedAt":1667120458090,"user":"wdskLNE5S604JdZI8e9lAPC8VWzw"},"-NFc79BpQ0RZpAGXdpB6":{".priority":"wdskLNE5S604JdZI8e9lAPC8VWzw","startedAt":1667120538446,"user":"wdskLNE5S604JdZI8e9lAPC8VWzw"},"-NbFm3HaTEceFgkthkSM":{".priority":"J8KbB0twUQBBqIfb3hOTOpWgDsvi","startedAt":1691424934088,"user":"J8KbB0twUQBBqIfb3hOTOpWgDsvi"},"-NbFm3Hf3467fZtsmhp6":{".priority":"J8KbB0twUQBBqIfb3hOTOpWgDsvi","startedAt":1691424934112,"user":"J8KbB0twUQBBqIfb3hOTOpWgDsvi"},"-NbFm3HnvrZIbTqu0I6j":{".priority":"J8KbB0twUQBBqIfb3hOTOpWgDsvi","startedAt":1691424934128,"user":"J8KbB0twUQBBqIfb3hOTOpWgDsvi"},"-NbFm3Hpe0SXLkYpG_0P":{".priority":"J8KbB0twUQBBqIfb3hOTOpWgDsvi","startedAt":1691424934150,"user":"J8KbB0twUQBBqIfb3hOTOpWgDsvi"},"-NbFm3WwxiKZrjJeCUT-":{".priority":"J8KbB0twUQBBqIfb3hOTOpWgDsvi","endedAt":1691424936200,"startedAt":1691424934168,"user":"J8KbB0twUQBBqIfb3hOTOpWgDsvi"},"-NbFm3WzHPS1RhWOS0o-":{".priority":"J8KbB0twUQBBqIfb3hOTOpWgDsvi","endedAt":1691424936200,"startedAt":1691424934211,"user":"J8KbB0twUQBBqIfb3hOTOpWgDsvi"},"-NbFm3X0S5kzIDv6Ptqo":{".priority":"J8KbB0twUQBBqIfb3hOTOpWgDsvi","endedAt":1691424936200,"startedAt":1691424934232,"user":"J8KbB0twUQBBqIfb3hOTOpWgDsvi"},"-NbFm3X1kJP_aDfDglAO":{".priority":"J8KbB0twUQBBqIfb3hOTOpWgDsvi","endedAt":1691424936200,"startedAt":1691424934253,"user":"J8KbB0twUQBBqIfb3hOTOpWgDsvi"},"-NbFm7jliVSAvjS6KJU2":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691424959517,"startedAt":1691424951336,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbFm7k2NOUkuXiChcDl":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691424959517,"startedAt":1691424951368,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbFmB7Hqg7NXPggMEDG":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691425003561,"startedAt":1691424965160,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbFmB7TDuFPDH6yEpBV":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691425003561,"startedAt":1691424965188,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbFmEolGtg9o_yS2beB":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","startedAt":1691424980611,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbFmEotoquV0tpek2xR":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","startedAt":1691424980628,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbFmJiCb1IwGPv7lsjF":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691425003561,"startedAt":1691425000343,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbFmJiFEaVg4Sw2Qy7J":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691425003561,"startedAt":1691425000363,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbFmJiRvwDPugy0vo-C":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691425003561,"startedAt":1691425000378,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbFmJicjj9lXM0SwjZU":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691425003561,"startedAt":1691425000390,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbFmM-4yn9aiRURVcUI":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691425035444,"startedAt":1691425009703,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbFmM-YnsZqTuUkKrP0":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691425035444,"startedAt":1691425009725,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbFmTcT0Z9GEpb31ksA":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691425061888,"startedAt":1691425040951,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbFmTchzvju-d2geiue":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691425061888,"startedAt":1691425040971,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbFm_CFYBZ1OXQoSwYY":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","startedAt":1691425067880,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbFm_CXIG2gCrDVTm64":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","startedAt":1691425067910,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbPHu18qaag8CqBZF13":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691584782327,"startedAt":1691584537327,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbPHu1Br9Eg7oQRZfMB":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691584782327,"startedAt":1691584537399,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbPIrfb63NjeZ5vQ3QW":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691584795694,"startedAt":1691584789274,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbPIrfjv4hyHmEFmGvw":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691584795694,"startedAt":1691584789304,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbPIurYqvNHj7wNOBKi":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691584813375,"startedAt":1691584802311,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbPIurqHmMNw67C0Glt":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","endedAt":1691584813375,"startedAt":1691584802333,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NbPJ1b_LkCrzUthyNsL":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691588011738,"startedAt":1691584834049,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPJ1c-EAW69vB4lGjs":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691588011738,"startedAt":1691584834107,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPVAqrynYu4UNcH2Rb":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691594539438,"startedAt":1691588017629,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPVAqzm-fLQJ1m62Ex":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691594539438,"startedAt":1691588017648,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPt7LPYmOJ-nLgAbkF":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691594567335,"startedAt":1691594556853,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPt7Lj_NO_OdL0nD5Y":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691594567335,"startedAt":1691594556900,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPtCcPIYRXiNyiczBm":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691594639119,"startedAt":1691594578489,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPtCcljM2rb6U48jjL":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691594639119,"startedAt":1691594578513,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPtSwnFHE8UtslHS4x":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691594948378,"startedAt":1691594645311,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPtSwwy4wGbWoFLUMm":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691594948378,"startedAt":1691594645336,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPudlRhgt2unqgMLZh":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691595198653,"startedAt":1691594955881,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPudlgLJWtNLfz61k7":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691595198653,"startedAt":1691594955901,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPvaIcMqwkdpQU2bRP":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691595226212,"startedAt":1691595203827,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPvaImtfRe6mMZhQj9":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691595226212,"startedAt":1691595203854,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPvhBceZzUwOA_tGuo":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691595729804,"startedAt":1691595232051,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPvhBtsF5EC4beh0fz":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691595729804,"startedAt":1691595232076,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPxeJT7CURfzVpo5cG":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691596637255,"startedAt":1691595744561,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbPxeJge8Rqt7SCbI2v":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691596637255,"startedAt":1691595744587,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQ05k2gBJPFHC0U2ed":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691596660063,"startedAt":1691596647433,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQ05kJe14TrgXVLXjE":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691596660063,"startedAt":1691596647456,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQ0B0R7Y6PcpucMw7B":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691596900830,"startedAt":1691596669065,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQ0B0omMgYcM08VLrV":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691596900830,"startedAt":1691596669081,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQ15mORMzoMRB5YYrF":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691597176574,"startedAt":1691596909735,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQ15mj40vXw__1ac55":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691597176574,"startedAt":1691596909760,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQ27GKL96xq3_NTF59":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691597965115,"startedAt":1691597177960,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQ27GOmfDG3w4vpvLS":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691597965115,"startedAt":1691597177978,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQ58v4iIkmpMVstbMp":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691598121976,"startedAt":1691597971150,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQ58vDAdxj_sR4n1Fo":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691598121976,"startedAt":1691597971176,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQ5kosBxq3kJnPLa4D":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691598682750,"startedAt":1691598130515,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQ5kpDvLF1DpxySP7b":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691598682750,"startedAt":1691598130541,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQ7thJIiQSeqDglxzJ":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691599669355,"startedAt":1691598691166,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQ7thUDZbTcfUGGCyI":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691599669355,"startedAt":1691598691184,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQBehVXgYKtKjt8sHd":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691599889934,"startedAt":1691599678334,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQBeht2wSli3YuYMvZ":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691599889934,"startedAt":1691599678373,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQCV1askMjQpSejGQL":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691599967868,"startedAt":1691599896766,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQCV207E7bgEtIqgjX":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691599967868,"startedAt":1691599896794,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQCmdbMW0BgPz8Jbkx":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691600706056,"startedAt":1691599972993,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQCmdu4JXUGazmMIOV":{".priority":"ufeINR1inzJu5SjBByaCYLXn3BG6","endedAt":1691600706056,"startedAt":1691599973023,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NbQDAo8OzpnWqida9QP":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600080727,"startedAt":1691600076131,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDAoNAPx7BsPDamPs":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600080727,"startedAt":1691600076147,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDEJf7AubZ44aL8c8":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600093814,"startedAt":1691600090450,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDEJj7dP8oceoaPqO":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600093814,"startedAt":1691600090484,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDHShy5IQ6DTfeUWN":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600106557,"startedAt":1691600103294,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDHSsSRS7kxzz92Gl":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600106557,"startedAt":1691600103320,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDKQ2hudP5lYDHOU7":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600119111,"startedAt":1691600115423,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDKQ6v99YA-x6asGN":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600119111,"startedAt":1691600115447,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDNckHa-Vs9jzmf01":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600132142,"startedAt":1691600128578,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDNcy5Q4U02r3R0lU":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600132142,"startedAt":1691600128600,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDR0C9CjEl2d38n7n":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600146188,"startedAt":1691600142438,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDR0FtiWhamcXuMdE":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600146188,"startedAt":1691600142459,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDU0pXSGDbfl5Xraz":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600158462,"startedAt":1691600154788,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDU0u0fYc5zxmOfP1":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600158462,"startedAt":1691600154832,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDXW7UHl9zJhnaH5N":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600172817,"startedAt":1691600169073,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDXWA7JKdZ3cL7qxh":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600172817,"startedAt":1691600169099,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDa-Nm9QATxADOilu":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600186995,"startedAt":1691600183337,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDa-STx2mtTxApEO0":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600186995,"startedAt":1691600183355,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDd8ity9Qp_j249Ck":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600200127,"startedAt":1691600196233,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDd8sVTLOQu0zYJ29":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600200127,"startedAt":1691600196262,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDgYxmwBVhNocs6Uy":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600215200,"startedAt":1691600210194,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDgZ0Ovww3YmVWv1t":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600215200,"startedAt":1691600210210,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDuw9R3QtiFzLBdF0":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600273484,"startedAt":1691600269130,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDuweHftPtBEE_ODO":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600273484,"startedAt":1691600269156,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDydH32U_zvsdd9y5":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600286584,"startedAt":1691600284265,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQDydLMnkPdbl8ua3P":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600286584,"startedAt":1691600284282,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQEohIc9XO6QmOwL0a":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600509912,"startedAt":1691600505699,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQEohYx-x6ETgFbq-a":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600509912,"startedAt":1691600505719,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQEyYXzx_K-rFKM92S":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600549534,"startedAt":1691600546046,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQEyYcy8suID4Yy5JO":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600549534,"startedAt":1691600546076,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQFAEISczTCWTbyo89":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600602431,"startedAt":1691600598005,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQFAEadgjoIuHoD_Ck":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600602431,"startedAt":1691600598027,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQFEslYTX0CyNvgbwO":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600620458,"startedAt":1691600617035,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQFEsoADIegOl6VPA2":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600620458,"startedAt":1691600617048,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQFVuXbhNKkxfQhpeS":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600691449,"startedAt":1691600686788,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQFVufZBHdAvvJTe0v":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600691449,"startedAt":1691600686805,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQFZSe-RJ1y9YC0kbF":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600732188,"startedAt":1691600701305,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQFZShMuhG6fcwKOXI":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600732188,"startedAt":1691600701325,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQG5zlkhvzFop-6lKA":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600845422,"startedAt":1691600842772,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQG5ztwIQZ6x2k7VDl":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600845422,"startedAt":1691600842788,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQGB5Rq5nGUazb4J72":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600867546,"startedAt":1691600863695,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQGB5ZxltLS62oKSEf":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600867546,"startedAt":1691600863719,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQGGNZV0euo7DLTGUJ":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600890222,"startedAt":1691600885327,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQGGNgHoRL29Sl_kE6":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600890222,"startedAt":1691600885357,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQG_Aji1BLUy1oPt8a":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600970769,"startedAt":1691600966412,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQG_AwY-ieYeAf9HwN":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691600970769,"startedAt":1691600966431,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQGilNFQXKtFVLCO5h":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601008827,"startedAt":1691601005679,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQGilQvdZlFTE1ne7q":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601008827,"startedAt":1691601005693,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQGnXd5mWjCeW2fgh6":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601031930,"startedAt":1691601025223,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQGnXkLM5x3M8QTYyj":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601031930,"startedAt":1691601025236,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQGtRySgqcjd9ppin4":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601057100,"startedAt":1691601049451,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQGtS2bUjrJZ4i-GHT":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601057100,"startedAt":1691601049479,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQGzoStn2Oxz5ii39W":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601082553,"startedAt":1691601075514,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQGzoWfRvHcvJCpSXR":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601082553,"startedAt":1691601075529,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQH4wVh6gk1ae8t890":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601109126,"startedAt":1691601100612,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQH4whVnYisx1MCjdY":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601109126,"startedAt":1691601100627,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHMYpnzGoJyA2vjBf":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601177268,"startedAt":1691601172758,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHMZ-dOX8FPmddWia":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601177268,"startedAt":1691601172778,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHQI7xY7h6KbWrqYD":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601191395,"startedAt":1691601188059,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHQI9ynunW83ufjCq":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601191395,"startedAt":1691601188074,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHYVCs2P-76XG6Q-X":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601225784,"startedAt":1691601221685,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHYVPpGnAGugbjmJe":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601225784,"startedAt":1691601221703,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHb8ooYDBHGSVmXSm":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601240912,"startedAt":1691601236608,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHb8s9lHpqKsa79HA":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601240912,"startedAt":1691601236622,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHlZXcV_2JQcVVYEz":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601283595,"startedAt":1691601279306,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHlZp7p3cSTLqsuib":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601283595,"startedAt":1691601279318,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHrJiiqhdiQAr8Fvj":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601306059,"startedAt":1691601302859,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHrJrtYuDR5AJ6Ssc":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601306059,"startedAt":1691601302870,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHuUGDw62ysmHHyBH":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601319773,"startedAt":1691601315853,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHuUJpkun6N25NDKL":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601319773,"startedAt":1691601315873,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHxwqmjOouhP_8wbr":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601333408,"startedAt":1691601330073,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQHxwtrf-KXqLYKzHk":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601333408,"startedAt":1691601330093,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQI01Wo1P7OZ2ahRtL":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601346598,"startedAt":1691601342668,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQI01Z9X_xRJMBdp4x":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601346598,"startedAt":1691601342681,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQI3CYffTMtUlrgICb":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601362408,"startedAt":1691601355656,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQI3Ch8H1UyTrWOV0z":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601362408,"startedAt":1691601355670,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQI7D1zAaC3ti9-Z-z":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601377797,"startedAt":1691601372085,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQI7D5Vn-sOFRP_TzJ":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601377797,"startedAt":1691601372096,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQIKCB4QHNKLqbmw46":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601429631,"startedAt":1691601425249,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQIKCGqrZdoE_UEYMn":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601429631,"startedAt":1691601425257,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQINUipq-fmcAqc6rT":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601442001,"startedAt":1691601438719,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQINUk3ZqNrghPuTNE":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601442001,"startedAt":1691601438727,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQIoxxwHv37oYw6oZJ":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601560824,"startedAt":1691601555352,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQIoy2uEOKg4lrZxOj":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601560824,"startedAt":1691601555358,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQIxAZHdF8nmEiBnH_":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601593640,"startedAt":1691601588975,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQIxAeWE8_0r3-yZuV":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601593640,"startedAt":1691601588982,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQIzYFeGqnZtZQbnEB":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601601849,"startedAt":1691601598677,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQIzYHpXz8Lr5lVx0w":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601601849,"startedAt":1691601598684,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQJ5kDkl8pcVJCsCo6":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601632776,"startedAt":1691601628194,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQJ5kFA7LcSNRrk9Mu":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601632776,"startedAt":1691601628207,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQJ8gfRHxVRGnPCcoX":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601643542,"startedAt":1691601640245,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQJ8giOyVTsFKlmqFm":{".priority":"850pPlsLaCg6JjmrKjOu6JqRPwhb","endedAt":1691601643542,"startedAt":1691601640257,"user":"850pPlsLaCg6JjmrKjOu6JqRPwhb"},"-NbQJUhuXxwepbO75zej":{".priority":"JFxIMgWVfdbhPPSgpCDNv49gcbpy","endedAt":1691601730760,"startedAt":1691601730442,"user":"JFxIMgWVfdbhPPSgpCDNv49gcbpy"},"-NbQJUi1sY0pZLEk-8eV":{".priority":"JFxIMgWVfdbhPPSgpCDNv49gcbpy","endedAt":1691601730760,"startedAt":1691601730452,"user":"JFxIMgWVfdbhPPSgpCDNv49gcbpy"},"-NcHD6OyQkqT7sfr9CUF":{"startedAt":1692522806022,"user":"ufeINR1inzJu5SjBByaCYLXn3BG6"},"-NcHDHbUR0ZpkwVlMyaf":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","startedAt":1692522851990,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"},"-NcHDHbodDK0h80SMGZv":{".priority":"qrEH9fQUqUe7kONHtmC0GncTKYBP","startedAt":1692522852125,"user":"qrEH9fQUqUe7kONHtmC0GncTKYBP"}}} \ No newline at end of file diff --git a/testdata/firebase-export-metadata.json b/testdata/firebase-export-metadata.json index 8570192f..8b4d5a71 100644 --- a/testdata/firebase-export-metadata.json +++ b/testdata/firebase-export-metadata.json @@ -1,7 +1,7 @@ { - "version": "11.21.0", + "version": "11.24.0", "firestore": { - "version": "1.15.1", + "version": "1.16.0", "path": "firestore_export", "metadata_file": "firestore_export/firestore_export.overall_export_metadata" }, @@ -10,11 +10,11 @@ "path": "database_export" }, "auth": { - "version": "11.21.0", + "version": "11.24.0", "path": "auth_export" }, "storage": { - "version": "11.21.0", + "version": "11.24.0", "path": "storage_export" } } diff --git a/testdata/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata b/testdata/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata index 9eff44ae..5635b0bd 100644 Binary files a/testdata/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata and b/testdata/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata differ diff --git a/testdata/firestore_export/all_namespaces/all_kinds/output-0 b/testdata/firestore_export/all_namespaces/all_kinds/output-0 index db7952b4..6bc6a695 100644 Binary files a/testdata/firestore_export/all_namespaces/all_kinds/output-0 and b/testdata/firestore_export/all_namespaces/all_kinds/output-0 differ diff --git a/testdata/firestore_export/firestore_export.overall_export_metadata b/testdata/firestore_export/firestore_export.overall_export_metadata index ca0d6351..b9b4d9c2 100644 Binary files a/testdata/firestore_export/firestore_export.overall_export_metadata and b/testdata/firestore_export/firestore_export.overall_export_metadata differ