diff --git a/src/components/TabBar/popups/DeleteConfirmation.jsx b/src/components/TabBar/popups/DeleteConfirmation.jsx
index 0ead7c8..eaace75 100644
--- a/src/components/TabBar/popups/DeleteConfirmation.jsx
+++ b/src/components/TabBar/popups/DeleteConfirmation.jsx
@@ -1,18 +1,19 @@
import React from 'react';
import { useDispatch } from 'react-redux';
+import { actions } from '../../../data/redux';
+
import './DeleteConfirmation.scss';
-import * as actions from '../../../store/actionIndex';
export const DeleteConfirmation = ({
id,
}) => {
const dispatch = useDispatch();
- const cancelClick = () => dispatch(actions.resetPopup());
+ const cancelClick = () => dispatch(actions.session.resetPopup());
const confirmClick = () => {
- dispatch(actions.destroyView(id));
- dispatch(actions.resetPopup());
+ dispatch(actions.project.destroyTab({ id }));
+ dispatch(actions.session.resetPopup());
};
return (
diff --git a/src/components/ToolMenu/hooks.js b/src/components/ToolMenu/hooks.js
index 12261ac..ebf25b0 100644
--- a/src/components/ToolMenu/hooks.js
+++ b/src/components/ToolMenu/hooks.js
@@ -1,11 +1,11 @@
import { useDispatch, useSelector } from 'react-redux';
-import * as actions from '../../store/actionIndex';
+import { actions } from '../../data/redux';
export const useToolMenuHooks = () => {
const dispatch = useDispatch();
- const activeCard = useSelector(state => state.sessionManager.activeCardId);
- const activeTab = useSelector(state => state.campaignData.present.activeViewId);
+ const activeCard = useSelector(state => state.session.activeCardId);
+ const activeTab = useSelector(state => state.project.present.activeViewId);
const disableNewCard = !activeTab;
const disableCopyCard = !activeCard || !activeCard;
@@ -13,13 +13,13 @@ export const useToolMenuHooks = () => {
return {
onClickNewCard: () => {
if (!disableNewCard) {
- dispatch(actions.createCard());
+ dispatch(actions.project.createCard());
}
},
disableNewCard,
onClickCopyCard: () => {
if (!disableCopyCard) {
- dispatch(actions.copyCard(activeCard));
+ dispatch(actions.project.copyCard({ id: activeCard }));
}
},
disableCopyCard,
diff --git a/src/store/firestoreAPI/authTransactions.js b/src/data/api/auth.js
similarity index 54%
rename from src/store/firestoreAPI/authTransactions.js
rename to src/data/api/auth.js
index cc397f8..b8223fa 100644
--- a/src/store/firestoreAPI/authTransactions.js
+++ b/src/data/api/auth.js
@@ -1,10 +1,10 @@
-import { ActionCreators } from 'redux-undo';
import {
applyActionCode,
checkActionCode,
confirmPasswordReset,
createUserWithEmailAndPassword,
FacebookAuthProvider,
+ getAdditionalUserInfo,
getAuth,
GoogleAuthProvider,
onAuthStateChanged,
@@ -15,112 +15,69 @@ import {
signOut,
verifyPasswordResetCode,
} from '@firebase/auth';
+import { actions, clearHistory } from '../redux';
+import * as api from '../api/database';
-import { auth } from './firebase';
-import * as actions from '../actionIndex';
-import * as fireactions from '../firestoreIndex';
+export const auth = getAuth();
+// export const isNewUser = getAdditionalUserInfo();
+export const googleProvider = new GoogleAuthProvider();
+export const facebookProvider = new FacebookAuthProvider();
+export const getUser = () => auth.currentUser ?? null;
+export const getUserId = () => auth.currentUser ? auth.currentUser.uid : null;
-export const getParameterByName = (name) => {
- // Sample action handle URL:
- // https://example.com/usermgmt?mode=resetPassword&oobCode=ABC123&apiKey=AIzaSy...&lang=fr
-
- let value = window.location.href;
- if (value) {
- value = value.split(name+"=")[1];
- }
- if (value) {
- value = value.split('&')[0];
- }
-
- return value;
-};
-
-// const auth = getAuth();
-const googleProvider = new GoogleAuthProvider();
-const facebookProvider = new FacebookAuthProvider();
-
-const getUser = () => (auth.currentUser ? auth.currentUser : null);
-
-export const manageUser = ({
+export const authListener = ({
dispatch,
- introCampaignEdit,
- campaignData,
+ saveProject,
+ projectId,
+ projectData,
}) => {
- onAuthStateChanged(auth, (user) => {
- console.log('[Status] loading. Triggered by auth listener.');
- dispatch(actions.setStatus('loading'));
- if (user && user.uid) {
- // Signed in
+ onAuthStateChanged(auth, user => {
+ if (user) { // User is signed in
console.log('[authListener] signed in user:', user.uid);
- dispatch(actions.loadUser(user));
- // prompt user to save intro campaign
- if (introCampaignEdit) {
- let save = window.confirm(
- 'Would you like to save your work as a new campaign?'
- );
- if (!save) {
- dispatch(fireactions.fetchCampaignList());
- dispatch(fireactions.fetchActiveCampaignId());
- } else {
- dispatch(
- fireactions.saveIntroProjectData({
- projectData: campaignData,
- callback: () => {
- dispatch(fireactions.fetchCampaignList());
- dispatch(fireactions.fetchActiveCampaignId());
- },
- })
- );
- }
+ const userData = {
+ userId: user.uid,
+ displayName: user.displayName,
+ email: user.email,
+ emailVerified: user.emailVerified,
+ providerId: user.providerId,
+ providerData: user.providerData,
+ };
+ dispatch(actions.user.loadUser({ ...userData }));
+
+ if (saveProject) {
+ dispatch(api.save(projectId, projectData, () => {
+ dispatch(api.fetchActiveProjectId());
+ dispatch(api.fetchProjects());
+ }));
} else {
- dispatch(fireactions.fetchCampaignList());
- dispatch(fireactions.fetchActiveCampaignId());
+ dispatch(api.fetchActiveProjectId());
+ dispatch(api.fetchProjects());
}
- } else {
- // Signed out
+ } else { // User is signed out
console.log('[authListener] signed out');
- dispatch(actions.unloadUser());
- dispatch(actions.resetSessionManager());
- dispatch(actions.loadIntroCampaign());
- dispatch(ActionCreators.clearHistory());
+ dispatch(actions.project.loadIntroProject());
+ dispatch(actions.session.loadIntro());
+ dispatch(actions.user.initialize());
+ dispatch(clearHistory());
}
});
};
-export const updateDisplayName = (displayName) => {
- const user = getUser();
- return (dispatch) => {
- if (user) {
- user
- .updateProfile({ displayName: displayName })
- .then((resp) => {
- console.log('[updateDisplayName] updated displayName:', resp);
- dispatch(actions.updUserDisplayname(displayName));
- })
- .catch((err) =>
- console.log('[updateDisplayName] error updating displayName:', err)
- );
- }
- };
-};
-
export const emailSignIn = ({
email,
password,
callback,
errorCallback,
-}) => (dispatch) => {
+}) => dispatch => {
signInWithEmailAndPassword(auth, email, password)
- .then((response) => {
+ .then(response => {
console.log('[emailSignIn] sign in successful:', response);
- // dispatch(actions.unsetErrorEmailSignIn()); // TODO REMOVE error handled in local state
if (callback) {
callback();
}
})
- .catch((error) => {
+ .catch(error => {
console.log('[emailSignIn] error:', error.message);
- // dispatch(actions.setErrorEmailSignIn(err.code)); // TODO REMOVE error handled in local state
if (errorCallback) {
errorCallback(error.code);
}
@@ -128,119 +85,88 @@ export const emailSignIn = ({
};
export const emailSignOut = () => {
- return (dispatch) => signOut(auth)
- .then(console.log('[emailSignout] sign out successful'))
- .catch((err) => console.log('[emailSignOut] error:', err));
+ signOut(auth)
+ .then(response => console.log('[emailSignout] success'))
+ .catch(error => console.log('[emailSignOut] error', error));
};
-export const googleSignIn = () => {
- return (dispatch) => signInWithPopup(auth, googleProvider)
- .then((resp) => {
- console.log('[googleSignIn] sign in successful');
- dispatch(actions.unsetErrorGoogleSignUp());
+export const googleSignIn = ({
+ callback,
+ errorCallback,
+}) => dispatch => {
+ console.log(callback)
+ signInWithPopup(auth, googleProvider)
+ .then(response => {
+ console.log('[googleSignIn] success');
+ if (callback) {
+ callback();
+ }
})
- .catch((err) => {
- console.log('[googleSignIn] error signing up with google:', err);
- dispatch(actions.setErrorGoogleSignUp(err.code));
+ .catch(error => {
+ console.log('[googleSignIn] error', error);
+ if (errorCallback) {
+ errorCallback(error.code);
+ }
});
};
-export const facebookSignIn = () => {
- return (dispatch) =>
- signInWithPopup(auth, facebookProvider)
- .then((resp) => {
- console.log('[facebookSignIn] sign in successful');
- dispatch(actions.unsetErrorFacebookSignUp());
- })
- .catch((err) => {
- console.log('[facebookSignIn] error signing up with google:', err);
- dispatch(actions.setErrorFacebookSignUp(err.code));
- });
-};
+// export const facebookSignIn = () => dispatch => {
+// signInWithPopup(auth, facebookProvider)
+// .then(response => console.log('[facebookSignIn] success'))
+// .catch(error => console.log('[facebookSignIn] error', error));
+// };
export const emailSignUp = ({
email,
password,
callback,
errorCallback,
-}) => {
- return (dispatch) => createUserWithEmailAndPassword(auth, email, password)
- .then((response) => {
+}) => dispatch => {
+ createUserWithEmailAndPassword(auth, email, password)
+ .then(response => {
console.log('[emailSignUp] sign up successful:', response);
- // dispatch(actions.unsetErrorEmailSignUp());
sendVerificationToEmail();
if (callback) {
callback();
}
})
- .catch((error) => {
+ .catch(error => {
console.log('[emailSignUp] error:', error);
- // dispatch(actions.setErrorEmailSignUp(error.code));
if (errorCallback) {
errorCallback(error.code);
}
});
};
-export const sendVerificationToEmail = () => {
- return (dispatch) =>
- sendEmailVerification(auth.currentUser)
- .then((resp) =>
- console.log('[sendVerificationToEmail] sent email verification:', resp)
- )
- .catch((err) => console.log('[sendVerificationToEmail] error:', err));
+export const sendVerificationToEmail = () => dispatch => {
+ sendEmailVerification(getUser())
+ .then(response => console.log('[sendVerificationToEmail] success', response))
+ .catch(error => console.log('[sendVerificationToEmail] error', error));
};
export const sendPasswordResetToEmail = ({
email,
callback,
errorCallback,
-}) => {
- return (dispatch) => sendPasswordResetEmail(auth, email)
- .then((response) => {
+}) => dispatch => {
+ sendPasswordResetEmail(auth, email)
+ .then(response => {
console.log('[sendPasswordResetToEmail] sent password reset email to:', email, '. ', response);
- // dispatch(actions.unsetErrorPasswordReset());
if (callback) {
callback();
}
})
- .catch((error) => {
+ .catch(error => {
console.log('[sendPasswordResetToEmail] error:', error);
- // dispatch(actions.setErrorPasswordReset(err.code));
if (errorCallback) {
errorCallback(error.code);
}
});
};
-export const emailActionHandler = () => {
- // TODO: does this need to be implemented?
- document.addEventListener(
- 'DOMContentLoaded',
- () => {
- // Sample action handle URL:
- // https://example.com/usermgmt?mode=resetPassword&oobCode=ABC123&apiKey=AIzaSy...&lang=fr
- const mode = getParameterByName('mode');
- const actionCode = getParameterByName('oobCode');
- const continueUrl = getParameterByName('continueUrl');
- switch (mode) {
- case 'resetPassword':
- return handleResetPassword(actionCode, continueUrl);
- case 'recoverEmail':
- return handleRecoverEmail(actionCode);
- case 'verifyEmail':
- return handleVerifyEmail(actionCode, continueUrl);
- default:
- return console.log('[emailActionHandler] invalid mode:', mode);
- }
- },
- false
- );
-};
-
const handleResetPassword = (actionCode, continueUrl) => {
verifyPasswordResetCode(auth, actionCode)
- .then((email) => {
+ .then(email => {
// let accountEmail = email;
// TODO: Show the reset screen with the user's email and ask the user for
@@ -249,7 +175,7 @@ const handleResetPassword = (actionCode, continueUrl) => {
// Save the new password.
confirmPasswordReset(auth, actionCode, newPassword)
- .then((resp) => {
+ .then(response => {
// Password reset has been confirmed and new password updated.
// TODO: Display a link back to the app, or sign-in the user directly
// if the page belongs to the same domain as the app:
@@ -258,12 +184,12 @@ const handleResetPassword = (actionCode, continueUrl) => {
// click redirects the user back to the app via continueUrl with
// additional state determined from that URL's parameters.
})
- .catch((err) => {
+ .catch(error => {
// Error occurred during confirmation. The code might have expired or the
// password is too weak.
});
})
- .catch((err) => {
+ .catch(error => {
// Invalid or expired action code. Ask user to try to reset the password
// again.
});
@@ -275,7 +201,7 @@ const handleRecoverEmail = (actionCode) => {
let restoredEmail = null;
// Confirm the action code is valid.
checkActionCode(auth, actionCode)
- .then((info) => {
+ .then(info => {
// Get the restored email address.
restoredEmail = info['data']['email'];
@@ -293,11 +219,11 @@ const handleRecoverEmail = (actionCode) => {
.then(() => {
// Password reset confirmation sent. Ask user to check their email.
})
- .catch((err) => {
+ .catch(error => {
// Error encountered while sending password reset code.
});
})
- .catch((err) => {
+ .catch(error => {
// Invalid code.
});
};
@@ -305,8 +231,8 @@ const handleRecoverEmail = (actionCode) => {
const handleVerifyEmail = (actionCode, continueUrl) => {
// Try to apply the email verification code.
applyActionCode(auth, actionCode)
- .then((resp) => {
- console.log('[handleVerifyEmail] email verified:', resp);
+ .then(response => {
+ console.log('[handleVerifyEmail] success', response);
// Email address has been verified.
// TODO: Display a confirmation message to the user.
@@ -316,7 +242,39 @@ const handleVerifyEmail = (actionCode, continueUrl) => {
// click redirects the user back to the app via continueUrl with
// additional state determined from that URL's parameters.
})
- .catch((err) =>
- console.log('[handleVerifyEmail] error verifying email:', err)
- );
+ .catch(error => console.log('[handleVerifyEmail] error', error));
+};
+
+export const getParameterByName = (name) => {
+ // Sample action handle URL:
+ // https://example.com/usermgmt?mode=resetPassword&oobCode=ABC123&apiKey=AIzaSy...&lang=fr
+ let value = window.location.href;
+ value = value.split(name+"=")[1] ?? '';
+ value = value.split('&')[0] ?? '';
+ return value;
+};
+
+export const emailActionHandler = () => {
+ // TODO: does this need to be implemented?
+ document.addEventListener(
+ 'DOMContentLoaded',
+ () => {
+ // Sample action handle URL:
+ // https://example.com/usermgmt?mode=resetPassword&oobCode=ABC123&apiKey=AIzaSy...&lang=fr
+ const mode = getParameterByName('mode');
+ const actionCode = getParameterByName('oobCode');
+ const continueUrl = getParameterByName('continueUrl');
+ switch (mode) {
+ case 'resetPassword':
+ return handleResetPassword(actionCode, continueUrl);
+ case 'recoverEmail':
+ return handleRecoverEmail(actionCode);
+ case 'verifyEmail':
+ return handleVerifyEmail(actionCode, continueUrl);
+ default:
+ return console.log('[emailActionHandler] invalid mode:', mode);
+ }
+ },
+ false,
+ );
};
diff --git a/src/data/authCodes.js b/src/data/api/authCodes.js
similarity index 94%
rename from src/data/authCodes.js
rename to src/data/api/authCodes.js
index 498802b..73b8699 100644
--- a/src/data/authCodes.js
+++ b/src/data/api/authCodes.js
@@ -1,4 +1,4 @@
-const convertToMsg = ({ errorCode }) => {
+export const convertToMsg = ({ errorCode }) => {
switch(errorCode) {
// config
case ('auth/auth-domain-config-required'): return 'missing authorization configuration';
@@ -23,7 +23,3 @@ const convertToMsg = ({ errorCode }) => {
default: return 'error code: ' + errorCode;
}
};
-
-export {
- convertToMsg
-};
diff --git a/src/data/api/database.js b/src/data/api/database.js
new file mode 100644
index 0000000..0e5469e
--- /dev/null
+++ b/src/data/api/database.js
@@ -0,0 +1,236 @@
+import {
+ doc,
+ addDoc,
+ setDoc,
+ getDoc,
+ updateDoc,
+ deleteDoc,
+ collection,
+ getDocs,
+ writeBatch,
+} from '@firebase/firestore';
+
+import { db } from './firebase';
+import { getUserId } from './auth';
+
+import { actions } from '../redux';
+import { NETWORK_STATUS, DEFAULT_PROJECT } from '../redux/session/constants';
+import { BLANK_PROJECT } from '../redux/project/constants';
+
+// TODO replace campaign terminology with project terminology
+
+const userDoc = () => doc(db, 'users', getUserId());
+const projectDoc = (project) => doc(db, 'users', getUserId(), 'campaigns', project);
+const cardDoc = (project, card) => doc(db, 'users', getUserId(), 'campaigns', project, 'cards', card);
+const tabDoc = (project, tab) => doc(db, 'users', getUserId(), 'campaigns', project, 'views', tab);
+const projectCollection = () => collection(db, 'users', getUserId(), 'campaigns');
+const cardCollection = (project) => collection(db, 'users', getUserId(), 'campaigns', project, 'cards');
+const tabCollection = (project) => collection(db, 'users', getUserId(), 'campaigns', project, 'views');
+
+const status = {
+ idle: (logging) => dispatch => dispatch(actions.session.setStatus({ status: NETWORK_STATUS.idle, logging })),
+ loading: (logging) => dispatch => dispatch(actions.session.setStatus({ status: NETWORK_STATUS.loading, logging })),
+ saving: (logging) => dispatch => dispatch(actions.session.setStatus({ status: NETWORK_STATUS.saving, logging })),
+};
+
+export const fetchActiveProjectId = () => dispatch => {
+ getDoc(userDoc())
+ .then(userSnapshot => {
+ console.log('[fetchActiveProjectId] success:', userSnapshot.data());
+ const activeProjectId = userSnapshot.data().activeCampaignId ?? null;
+ if (activeProjectId) {
+ dispatch(actions.session.setActiveProject({ id: activeProjectId }));
+ }
+ })
+ .catch(error => console.log('[fetchActiveProjectId] error', error));
+};
+
+export const fetchProjects = () => dispatch => {
+ getDocs(projectCollection())
+ .then(projectsSnapshot => {
+ console.log(`[fetchProjects] success: ${projectsSnapshot.docs?.length} project titles fetched`);
+ let projects = {};
+ projectsSnapshot.forEach(project => {
+ projects = { ...projects, [project.id]: project.data().title ?? '' };
+ });
+ dispatch(actions.session.loadProjects({ projects }));
+ })
+ .catch(error => console.log('[fetchProjects] error', error));
+};
+
+const fetchCards = (project) => dispatch => {
+ getDocs(cardCollection(project))
+ .then(cardsSnapshot => {
+ console.log(`[fetchCards] success: ${cardsSnapshot.docs?.length} cards fetched`);
+ let cards = {};
+ cardsSnapshot.forEach(cardSnapshot => {
+ cards = { ...cards, [cardSnapshot.id]: cardSnapshot.data() };
+ });
+ dispatch(actions.project.loadCards({ cards }));
+ })
+ .catch(error => console.log('[fetchCards] error', error));
+};
+
+const fetchTabs = (project) => dispatch => {
+ getDocs(tabCollection(project))
+ .then(tabsSnapshot => {
+ console.log(`[fetchTabs] success: ${tabsSnapshot.docs?.length} tabs fetched`);
+ let tabs = {};
+ tabsSnapshot.forEach(tabSnapshot => {
+ tabs = { ...tabs, [tabSnapshot.id]: tabSnapshot.data() };
+ });
+ dispatch(actions.project.loadTabs({ tabs }));
+ })
+ .catch(error => console.log('[fetchTabs] error', error));
+};
+
+export const fetchProjectData = (id, callback) => dispatch => {
+ dispatch(status.loading('fetch project'));
+ getDoc(projectDoc(id))
+ .then(projectSnapshot => {
+ console.log(`[fetchProjectData] success: project "${projectSnapshot.data()?.title}" fetched`);
+ dispatch(actions.project.loadProject({ project: projectSnapshot.data() }));
+ dispatch(fetchCards(id));
+ dispatch(fetchTabs(id));
+ dispatch(status.idle('finished fetching project'));
+ if (callback) {
+ callback();
+ }
+ })
+ .catch(error => console.log('[fetchProjectData] error', error));
+};
+
+export const firstTimeSetup = () => dispatch => {
+ setDoc(userDoc(), { activeCampaignId: null })
+ .then(response => console.log('[firstTimeSetup] success', response))
+ .catch(error => console.log('[firstTimeSetup] error', error));
+};
+
+const saveActiveProjectId = (id, callback) => dispatch => {
+ updateDoc(userDoc(), { activeCampaignId: id })
+ .then(response => {
+ console.log('[saveActiveProjectId] success');
+ if (callback) {
+ callback();
+ }
+ })
+ .catch(error => console.log('[saveActiveProjectId] error', error));
+};
+
+export const switchProject = (projectId) => dispatch => {
+ dispatch(saveActiveProjectId(projectId, () => {
+ dispatch(actions.session.setActiveProject({ id: projectId }));
+ }));
+};
+
+const saveExistingProject = (id, data, callback) => dispatch => {
+ const batch = writeBatch(db);
+ let projectBatch = { ...data, lastSavedOn: Date.now() };
+ delete projectBatch.cards;
+ delete projectBatch.views;
+ batch.set(projectDoc(id), projectBatch);
+ for (let cardId in data.cards) {
+ batch.set(cardDoc(id, cardId), data.cards[cardId]);
+ }
+ for (let tabId in data.views) {
+ batch.set(tabDoc(id, tabId), data.views[tabId]);
+ }
+ batch.commit()
+ .then(response => {
+ console.log('[saveExistingProject] success');
+ if (callback) {
+ callback();
+ }
+ })
+ .catch(error => console.log('[saveExistingProject] error', error));
+};
+
+const saveNewProject = (data, callback) => dispatch => {
+ addDoc(projectCollection(), { createdOn: Date.now(), lastSavedOn: Date.now() })
+ .then(response => {
+ console.log('[saveNewProject] success', response);
+ dispatch(saveExistingProject(response.id, data, callback));
+ })
+ .catch(error => console.log('[saveNewProject] error', error));
+};
+
+export const save = (projectId, projectData, callback) => dispatch => {
+ dispatch(status.saving('saving project'));
+ if (DEFAULT_PROJECT[projectId]) {
+ dispatch(saveNewProject(projectData, () => {
+ dispatch(actions.session.setIsProjectEdited(false));
+ if (callback) {
+ callback();
+ }
+ dispatch(status.idle('finished saving new project'));
+ }));
+ } else {
+ dispatch(saveExistingProject(projectId, projectData, () => {
+ dispatch(actions.session.setIsProjectEdited(false));
+ if (callback) {
+ callback();
+ }
+ dispatch(status.idle('finished saving existing project'));
+ }));
+ }
+};
+
+export const createAndSwitchToEmptyProject = (callback) => dispatch => {
+ addDoc(projectCollection(), { ...BLANK_PROJECT })
+ .then(response => {
+ console.log(`[createAndSwitchToEmptyProject] success: created project ${response?.id}`);
+ dispatch(actions.session.addProject({
+ id: response.id,
+ title: BLANK_PROJECT.title,
+ }));
+ dispatch(saveActiveProjectId(response.id, () => {
+ dispatch(actions.session.setActiveProject({ id: response.id }));
+ }));
+ })
+ .catch(error => console.log('[createAndSwitchToEmptyProject] error', error));
+};
+
+export const copyProject = (id, callback) => dispatch => {
+ let projectData = {};
+ getDoc(projectDoc(id))
+ .then(projectSnapshot => {
+ projectData = projectSnapshot.data();
+ projectData = {
+ ...projectData,
+ title: projectData.title + ' (copy)',
+ createdOn: Date.now(),
+ lastSavedOn: Date.now(),
+ };
+ getDocs(cardCollection(id))
+ .then(cardsSnapshot => {
+ let cards = {};
+ cardsSnapshot.forEach(cardSnapshot => {
+ cards = { ...cards, [cardSnapshot.id]: cardSnapshot.data() };
+ });
+ projectData.cards = cards;
+ getDocs(tabCollection(id))
+ .then(tabsSnapshot => {
+ let tabs = {};
+ tabsSnapshot.forEach(tabSnapshot => {
+ tabs = { ...tabs, [tabSnapshot.id]: tabSnapshot.data() };
+ });
+ projectData.views = tabs;
+ dispatch(saveNewProject(projectData, callback));
+ })
+ .catch(error => console.log('[copyProject] error getting tabs', error));
+ })
+ .catch(error => console.log('[copyProject] error getting cards', error));
+ })
+ .catch(error => console.log('[copyProject] error', error));
+};
+
+export const destroyProject = (id, callback) => dispatch => {
+ deleteDoc(projectDoc(id))
+ .then(response => {
+ console.log('[destroyProject] success', response);
+ if (callback) {
+ callback();
+ }
+ })
+ .catch(error => console.log('[destroyProject] error', error));
+};
diff --git a/src/store/firestoreAPI/firebase.js b/src/data/api/firebase.js
similarity index 69%
rename from src/store/firestoreAPI/firebase.js
rename to src/data/api/firebase.js
index ca50e1a..03cff2e 100644
--- a/src/store/firestoreAPI/firebase.js
+++ b/src/data/api/firebase.js
@@ -1,10 +1,5 @@
import { initializeApp } from '@firebase/app';
import { getFirestore } from '@firebase/firestore';
-import {
- getAuth,
- GoogleAuthProvider,
- FacebookAuthProvider,
-} from '@firebase/auth';
const firebaseConfig = {
apiKey: import.meta.env.VITE_APP_API_KEY,
@@ -18,14 +13,8 @@ const firebaseConfig = {
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
-const auth = getAuth();
-const googleProvider = new GoogleAuthProvider();
-const facebookProvider = new FacebookAuthProvider();
export {
app,
- auth,
db,
- facebookProvider,
- googleProvider,
};
diff --git a/src/data/api/user.js b/src/data/api/user.js
new file mode 100644
index 0000000..c0acf02
--- /dev/null
+++ b/src/data/api/user.js
@@ -0,0 +1,12 @@
+import { getAuth, updateProfile } from 'firebase/auth';
+
+export const updateDisplayName = (name) => dispatch => {
+ const auth = getAuth();
+ updateProfile(auth.currentUser, { displayName: name })
+ .then(response => {
+ console.log('[updateDisplayName] success', response);
+ // There is currently a bug where the displayName is updated but we get an error back.
+ // dispatch(actions.user.updateUserDisplayName({ displayName: name }));
+ })
+ .catch(error => console.log('[updateDisplayName] error', error));
+};
diff --git a/src/data/campaign/reducers.js b/src/data/campaign/reducers.js
deleted file mode 100644
index 1e718c9..0000000
--- a/src/data/campaign/reducers.js
+++ /dev/null
@@ -1,419 +0,0 @@
-import { createSlice } from '@reduxjs/toolkit';
-import { GRID } from '../../styles/constants';
-
-// The campaignData reducer mirrors the campaign data structure on firebase
-
-const initialState = {
- activeViewId: '',
- cardCreateCnt: null,
- cards: {},
- title: '',
- viewCreateCnt: null,
- viewOrder: [],
- views: {},
-};
-
-// INTRO CAMPAIGN STATE
-const introCampaign = {
- title: "DM Kit",
- activeCardId: null,
- activeViewId: "view0",
- viewOrder: ["view0", "view1"],
- cardCreateCnt: 4,
- viewCreateCnt: 2,
- cards: {
- card0: {
- views: {
- view0: {
- pos: {x: 5*GRID.size, y: 7*GRID.size},
- size: {width: 16*GRID.size, height: 10*GRID.size},
- cardForm: "card",
- },
- },
- title: "Greetings Traveler!",
- color: "jungle",
- content: {
- text: "Welcome to DM Kit, a tool to help plan your next adventure. Take a look at the READ ME tab for more information on functions. If you would like to save your work, please create an account!",
- },
- },
- card1: {
- views: {
- view1: {
- pos: {x: 0, y: 0},
- size: {width: 8*GRID.size, height: 9*GRID.size},
- cardForm: "card",
- },
- },
- title: "Tools",
- color: "cotton_blue",
- content: {
- text: "Use the buttons to build your project. You can add cards, copy cards, reset the board position. You can also save your progress, but you must first create an account.",
- },
- },
- card2: {
- views: {
- view1: {
- pos: {x: 4*GRID.size, y: 20*GRID.size},
- size: {width: 10*GRID.size, height: 5*GRID.size},
- cardForm: "card",
- },
- },
- title: "Tabs",
- color: "cobalt",
- content: {
- text: "Use the buttons below to add tabs and switch between them.",
- },
- },
- card3: {
- views: {
- view1: {
- pos: {x: 25*GRID.size, y: 3*GRID.size},
- size: {width: 10*GRID.size, height: 10*GRID.size},
- cardForm: "card",
- },
- },
- title: "Library",
- color: "lavender",
- content: {
- text: "All the cards you create are stored in the library, which you can access by clicking the book to the right. The same card can be placed in multiple views and edited from multiple places.",
- },
- },
- },
- views: {
- view0: {
- pos: { x: 0, y: 0 },
- scale: 1,
- lock: true,
- color: "green",
- title: "Welcome!",
- },
- view1: {
- pos: { x: 0, y: 0 },
- scale: 1,
- lock: true,
- color: "blue",
- title: "READ ME",
- },
- },
-};
-
-const campaign = createSlice({
- name: 'campaign',
- initialState,
- reducers: {
- loadCampaignData: ({ campaignData }) => ({ campaignData }),
- unloadCampaignData: () => ({}),
- loadIntroCampaign: () => ({ ...introCampaign }),
- updCampaignTitle: (state, { title }) => ({ ...state, title }),
- updActiveViewId: (state, { activeViewId }) => ({ ...state, activeViewId }),
- shiftViewInViewOrder: (state, { shiftedViewId, posShift }) => {
- let newViewOrder = [ ...state.viewOrder ];
- const newPos = newViewOrder.indexOf(shiftedViewId) + posShift;
- newViewOrder = newViewOrder.filter(id => id !== shiftedViewId);
- newViewOrder = newViewOrder.splice(newPos, 0, shiftedViewId);
- return { ...state, viewOrder: newViewOrder };
- },
- createCard: (state) => {
- if (!state.activeViewId) return state;
- const newCardId = 'card' + state.cardCreateCnt;
- return {
- ...state,
- activeCardId: newCardId,
- cardCreateCnt: state.cardCreateCnt + 1,
- cards: {
- ...state.cards,
- [newCardId]: {
- views: {
- [state.activeViewId]: {
- pos: { x: 3 * GRID.size, y: 3 * GRID.size },
- size: { width: 8 * GRID.size, height: 10 * GRID.size },
- cardType: 'card',
- },
- },
- title: newCardId,
- color: 'gray',
- content: { text: '' },
- },
- },
- };
- },
- copyCard: (state, { cardId }) => {
- if (!state.activeViewId) return state;
- const newCardId = 'card' + state.cardCreateCnt;
- return {
- ...state,
- activeCardId: newCardId,
- cardCreateCnt: state.cardCreateCnt + 1,
- cards: {
- ...state.cards,
- [newCardId]: {
- ...state.cards[cardId],
- views: {
- [state.activeViewId]: {
- ...state.cards[cardId].views[state.activeViewId],
- pos: {
- x: state.cards[cardId].views[state.activeViewId].pos.x + GRID.size,
- y: state.cards[cardId].views[state.activeViewId].pos.y + GRID.size,
- },
- },
- },
- },
- },
- };
- },
- destroyCard: (state, { cardId }) => {
- let newCards = { ...state.cards };
- delete newCards[cardId];
- return {
- ...state,
- activeCardId: (cardId === state.activeCardId) ? null : state.activeCardId,
- cards: newCards,
- };
- },
- linkCardToView: (state, { cardId, pos }) => {
- if (!state.activeViewId) return state;
- return {
- ...state,
- activeCardId: cardId,
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- views: {
- ...state.cards[cardId].views,
- [state.activeViewId]: {
- pos: pos,
- size: {width: 8*GRID.size, height: 10*GRID.size},
- cardType: 'card',
- },
- },
- },
- },
- };
- },
- unlinkCardFromView: (state, { cardId }) => {
- if (!state.activeViewId) return state;
- let newCardViews = { ...state.cards[cardId].views };
- delete newCardViews[state.activeViewId];
- return {
- ...state,
- activeCardId: null,
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- views: newCardViews,
- },
- },
- };
- },
- updCardPos: (state, { cardId, pos }) => {
- if (!state.activeViewId) return state;
- const newPos = {
- x: Math.round(pos.x / GRID.size) * GRID.size,
- y: Math.round(pos.y / GRID.size) * GRID.size,
- };
- return {
- ...state,
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- views: {
- ...state.cards[cardId].views,
- [state.activeViewId]: {
- ...state.cards[cardId].views[state.activeViewId],
- pos: newPos,
- },
- },
- },
- },
- }
- },
- updCardSize: (state, { cardId, size }) => {
- if (!state.activeViewId) return state;
- const newSize = {
- height: (Math.round(size.height.split('px').shift() / GRID.size) * GRID.size) + 'px',
- width: (Math.round(size.width.split('px').shift() / GRID.size) * GRID.size) + 'px',
- };
- return {
- ...state,
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- views: {
- ...state.cards[cardId].views,
- [state.activeViewId]: {
- ...state.cards[cardId].views[state.activeViewId],
- size: newSize,
- },
- },
- },
- },
- };
- },
- updCardTitle: (state, { cardId, title }) => ({
- ...state,
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- title: title,
- },
- },
- }),
- updCardColor: (state, { cardId, color }) => ({
- ...state,
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- color: color,
- },
- },
- }),
- updCardText: (state, { cardId, text }) => ({
- ...state,
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- content: {
- ...state.cards[cardId].content,
- text: text,
- },
- },
- },
- }),
- createView: (state) => {
- const newViewId = 'untitled' + state.viewCreateCnt;
- let newViewOrder = [ ...state.viewOrder ];
- const pos = state.activeViewId ? newViewOrder.indexOf(state.activeViewId) + 1 : 0;
- newViewOrder.splice(pos, 0, newViewId);
- return {
- ...state,
- activeViewId: newViewId,
- viewOrder: newViewOrder,
- viewCreateCnt: state.viewCreateCnt + 1,
- views: {
- ...state.views,
- [newViewId]: {
- pos: { x: 0, y: 0 },
- scale: 1,
- lock: true,
- color: 'gray',
- title: newViewId,
- },
- },
- };
- },
- destroyView: (state, { viewId }) => {
- let newViews = { ...state.views };
- delete newViews[viewId];
- const newViewOrder = [ ...state.viewOrder ].filter(id => id !== viewId);
- return {
- ...state,
- activeViewId: (viewId === state.activeViewId) ? null : state.activeViewId,
- viewOrder: newViewOrder,
- views: newViews,
- };
- },
- lockActiveView: (state) => {
- if (!state.activeViewId) return state;
- return {
- ...state,
- views: {
- ...state.views,
- [state.activeViewId]: {
- ...state.views[state.activeViewId],
- lock: true,
- },
- },
- };
- },
- unlockActiveView: (state) => {
- if (!state.activeViewId) return state;
- return {
- ...state,
- views: {
- ...state.views,
- [state.activeViewId]: {
- ...state.views[state.activeViewId],
- lock: false,
- },
- },
- };
- },
- updActiveViewPos: (state, { pos }) => {
- if (!state.activeViewId) return state;
- return {
- ...state,
- views: {
- ...state.views,
- [state.activeViewId]: {
- ...state.views[state.activeViewId],
- pos: pos,
- },
- },
- };
- },
- updActiveViewScale: (state, { scale }) => {
- if (!state.activeViewId) return state;
- return {
- ...state,
- views: {
- ...state.views,
- [state.activeViewId]: {
- ...state.views[state.activeViewId],
- scale: scale,
- },
- },
- };
- },
- resetActiveView: (state) => {
- if (!state.activeViewId) return state;
- return {
- ...state,
- views: {
- ...state.views,
- [state.activeViewId]: {
- ...state.views[state.activeViewId],
- pos: { x: 0, y: 0 },
- scale: 1,
- },
- },
- };
- },
- updViewTitle: (state, { viewId, title }) => ({
- ...state,
- views: {
- ...state.views,
- [viewId]: {
- ...state.views[viewId],
- title: title,
- },
- },
- }),
- updViewColor: (state, { viewId, color }) => ({
- ...state,
- views: {
- ...state.views,
- [viewId]: {
- ...state.views[viewId],
- color: color,
- },
- },
- }),
- },
-});
-
-const actions = campaign.actions;
-
-const { reducer } = campaign;
-
-export {
- actions,
- initialState,
- reducer,
-};
diff --git a/src/data/redux/index.js b/src/data/redux/index.js
new file mode 100644
index 0000000..806786a
--- /dev/null
+++ b/src/data/redux/index.js
@@ -0,0 +1,63 @@
+import { combineReducers } from 'redux';
+import { configureStore } from '@reduxjs/toolkit';
+import undoable, { ActionCreators, includeAction } from 'redux-undo';
+
+import * as project from './project';
+import * as session from './session';
+import * as user from './user';
+
+const modules = {
+ project,
+ session,
+ user,
+};
+
+let undoableActions = Object.keys(project.actions);
+const actionsToRemove = [
+ 'initialize',
+ 'unloadProject',
+ 'loadProject',
+ 'loadIntroProject',
+ 'loadBlankProject',
+ 'loadCards',
+ 'loadTabs',
+ 'loadTabOrder',
+ 'setActiveCard',
+ 'setActiveTab',
+ 'setActiveTabPosition',
+ 'setActiveTabScale',
+];
+undoableActions = undoableActions.filter(action => !actionsToRemove.includes(action));
+undoableActions = undoableActions.map(action => 'project/' + action);
+
+const rootReducer = combineReducers({
+ project: undoable(
+ project.reducer,
+ {
+ filter: includeAction(undoableActions),
+ limit: 10,
+ }
+ ),
+ session: session.reducer,
+ user: user.reducer,
+});
+
+const store = configureStore({
+ reducer: rootReducer,
+ devTools: process.env.NODE_ENV !== 'production',
+});
+
+const moduleProps = (propName) => Object.keys(modules).reduce(
+ (obj, moduleKey) => ({ ...obj, [moduleKey]: modules[moduleKey][propName] }),
+ {},
+);
+const actions = moduleProps('actions');
+const selectors = moduleProps('selectors');
+
+const undo = () => store.dispatch(ActionCreators.undo());
+const redo = () => store.dispatch(ActionCreators.redo());
+const clearHistory = () => store.dispatch(ActionCreators.clearHistory());
+
+export { actions, selectors };
+export { undo, redo, clearHistory };
+export default store;
diff --git a/src/data/redux/project/constants.js b/src/data/redux/project/constants.js
new file mode 100644
index 0000000..c6ec4eb
--- /dev/null
+++ b/src/data/redux/project/constants.js
@@ -0,0 +1,126 @@
+import { GRID } from '../../../styles/constants';
+
+export const DEFAULT_CARD_POSITION = {
+ x: 3 * GRID.size,
+ y: 3 * GRID.size,
+};
+export const DEFAULT_CARD_SIZE = {
+ width: 8 * GRID.size,
+ height: 10 * GRID.size,
+};
+export const DEFAULT_CARD = {
+ title: 'untitled',
+ color: 'gray',
+ content: { text: '' },
+ views: {},
+ createdOn: Date.now(),
+ editedOn: Date.now(),
+};
+
+export const DEFAULT_TAB_POSITION = {
+ x: 80,
+ y: 50,
+};
+export const DEFAULT_TAB_SCALE = 1;
+export const DEFAULT_TAB = {
+ title: 'untitled',
+ pos: DEFAULT_TAB_POSITION,
+ scale: DEFAULT_TAB_SCALE,
+ createdOn: Date.now(),
+ editedOn: Date.now(),
+};
+
+export const INTRO_CARDS = {
+ 'card0': {
+ title: 'Greetings Traveler!',
+ color: 'jungle',
+ content: {
+ text: 'Welcome to DM Kit, a tool to help plan your next adventure. Take a look at the READ ME tab for more information on functions. If you would like to save your work, please create an account!',
+ },
+ views: {
+ 'tab0': {
+ pos: {x: 5*GRID.size, y: 7*GRID.size},
+ size: {width: 32*GRID.size, height: 20*GRID.size},
+ },
+ },
+ },
+ 'card1': {
+ title: 'Tools',
+ color: 'cotton_blue',
+ content: {
+ text: 'Use the buttons to build your project. You can add cards, copy cards, reset the board position. You can also save your progress, but you must first create an account.',
+ },
+ views: {
+ 'tab1': {
+ pos: {x: 5*GRID.size, y: 7*GRID.size},
+ size: {width: 16*GRID.size, height: 18*GRID.size},
+ },
+ },
+ },
+ 'card2': {
+ title: 'Tabs',
+ color: 'cobalt',
+ content: {
+ text: 'Use the buttons below to add tabs and switch between them.',
+ },
+ views: {
+ 'tab1': {
+ pos: {x: 8*GRID.size, y: 40*GRID.size},
+ size: {width: 20*GRID.size, height: 10*GRID.size},
+ },
+ },
+ },
+ 'card3': {
+ title: 'Library',
+ color: 'lavender',
+ content: {
+ text: 'All the cards you create are stored in the library, which you can access by clicking the book to the right. The same card can be placed in multiple views and edited from multiple places.',
+ },
+ views: {
+ 'tab1': {
+ pos: {x: 50*GRID.size, y: 6*GRID.size},
+ size: {width: 20*GRID.size, height: 20*GRID.size},
+ },
+ },
+ },
+};
+export const INTRO_TABS = {
+ 'tab0': {
+ title: 'Welcome!',
+ pos: DEFAULT_TAB_POSITION,
+ scale: 1,
+ cards: ['card0'],
+ },
+ 'tab1': {
+ title: 'READ ME',
+ pos: DEFAULT_TAB_POSITION,
+ scale: 1,
+ cards: ['card1', 'card2', 'card3'],
+ },
+};
+export const INTRO_PROJECT = {
+ title: 'DM Kit',
+ viewOrder: ['tab0', 'tab1'],
+ activeViewId: 'tab0',
+ cards: INTRO_CARDS,
+ views: INTRO_TABS,
+ createdOn: Date.now(),
+ editedOn: Date.now(),
+};
+
+export const BLANK_PROJECT = {
+ title: 'Title',
+ viewOrder: ['tab0'],
+ activeViewId: 'tab0',
+ cards: {},
+ views: {
+ 'tab0': {
+ title: 'Title',
+ pos: DEFAULT_TAB_POSITION,
+ scale: 1,
+ cards: [],
+ },
+ },
+ createdOn: Date.now(),
+ editedOn: Date.now(),
+};
diff --git a/src/data/campaign/index.js b/src/data/redux/project/index.js
similarity index 100%
rename from src/data/campaign/index.js
rename to src/data/redux/project/index.js
diff --git a/src/data/redux/project/reducers.js b/src/data/redux/project/reducers.js
new file mode 100644
index 0000000..0d39219
--- /dev/null
+++ b/src/data/redux/project/reducers.js
@@ -0,0 +1,345 @@
+import { createSlice } from '@reduxjs/toolkit';
+import {
+ DEFAULT_CARD_POSITION,
+ DEFAULT_CARD_SIZE,
+ DEFAULT_CARD,
+ DEFAULT_TAB,
+ INTRO_PROJECT,
+ BLANK_PROJECT,
+} from './constants';
+import { GRID } from '../../../styles/constants';
+import { v4 as uuidv4 } from 'uuid';
+
+// TODO name refactor
+// view -> tab
+// pos -> position
+
+const initialState = {
+ title: '',
+ viewOrder: [],
+ activeViewId: null,
+ cards: {},
+ views: {},
+};
+
+const generateUID = (prefix) => (prefix + uuidv4().slice(0, 8));
+
+const project = createSlice({
+ name: 'project',
+ initialState,
+ reducers: {
+ // Actions below do not affect undo/redo.
+ initialize: () => ({ ...initialState }),
+ unloadProject: () => ({ ...initialState }),
+ loadProject: (state, { payload }) => ({ ...state, ...payload.project }),
+ loadIntroProject: () => ({ ...INTRO_PROJECT }),
+ loadBlankProject: () => ({ ...BLANK_PROJECT }),
+ loadCards: (state, { payload }) => ({ ...state, cards: payload.cards }),
+ loadTabs: (state, { payload }) => ({ ...state, views: payload.tabs }),
+ setActiveTab: (state, { payload }) => ({ ...state, activeViewId: payload.id }),
+ // Actions above do not affect undo/redo.
+
+ updateProjectTitle: (state, { payload }) => ({ ...state, title: payload.title }),
+ shiftTabBy: (state, { payload }) => {
+ const { id, position } = payload;
+ let newViewOrder = [ ...state.viewOrder ];
+ const newPosition = newViewOrder.indexOf(id) + position;
+ newViewOrder = newViewOrder.filter(tabId => tabId !== id);
+ newViewOrder.splice(newPosition, 0, id);
+ return { ...state, viewOrder: newViewOrder };
+ },
+
+ createCard: (state) => {
+ if (!state.activeViewId) return state;
+ const newCardId = generateUID('card');
+ return {
+ ...state,
+ activeCardId: newCardId,
+ cards: {
+ ...state.cards,
+ [newCardId]: {
+ ...DEFAULT_CARD,
+ views: {
+ [state.activeViewId]: {
+ pos: DEFAULT_CARD_POSITION,
+ size: DEFAULT_CARD_SIZE,
+ }
+ },
+ },
+ },
+ // views: {
+ // ...state.views,
+ // [state.activeViewId]: {
+ // ...state.views[state.activeViewId],
+ // cards: [
+ // ...state.views[state.activeViewId].cards,
+ // newCardId,
+ // ],
+ // },
+ // },
+ };
+ },
+ copyCard: (state, { payload }) => {
+ if (!state.activeViewId) return state;
+ const newCardId = generateUID('card');
+ return {
+ ...state,
+ activeCardId: newCardId,
+ cards: {
+ ...state.cards,
+ [newCardId]: {
+ ...state.cards[payload.id],
+ title: state.cards[payload.id].title + ' (copy)',
+ views: {
+ [state.activeViewId]: {
+ ...state.cards[payload.id].views[state.activeViewId],
+ pos: {
+ x: state.cards[payload.id].views[state.activeViewId].pos.x + 3*GRID.size,
+ y: state.cards[payload.id].views[state.activeViewId].pos.y + 3*GRID.size,
+ },
+ },
+ },
+ createdOn: Date.now(),
+ editedOn: Date.now(),
+ },
+ },
+ // views: {
+ // ...state.views,
+ // [state.activeViewId]: {
+ // ...state.views[state.activeViewId],
+ // cards: [
+ // ...state.views[state.activeViewId].cards,
+ // newCardId,
+ // ],
+ // },
+ // },
+ };
+ },
+ destroyCard: (state, { payload }) => {
+ let newCards = { ...state.cards };
+ delete newCards[payload.id];
+ return {
+ ...state,
+ cards: newCards,
+ };
+ },
+ linkCardToView: (state, { payload }) => {
+ if (!state.activeViewId) return state;
+ const { id, position } = payload;
+ return {
+ ...state,
+ activeCardId: id,
+ cards: {
+ ...state.cards,
+ [id]: {
+ ...state.cards[id],
+ views: {
+ ...state.cards[id].views,
+ [state.activeViewId]: {
+ pos: position,
+ size: DEFAULT_CARD_SIZE,
+ },
+ },
+ },
+ },
+ // views: {
+ // ...state.views,
+ // [state.activeViewId]: {
+ // ...state.views[state.activeViewId],
+ // cards: [
+ // ...state.views[state.activeViewId].cards,
+ // newCardId,
+ // ],
+ // },
+ // },
+ };
+ },
+ unlinkCardFromView: (state, { payload }) => {
+ if (!state.activeViewId) return state;
+ let newCardViews = { ...state.cards[payload.id].views };
+ delete newCardViews[state.activeViewId];
+ return {
+ ...state,
+ activeCardId: null,
+ cards: {
+ ...state.cards,
+ [payload.id]: {
+ ...state.cards[payload.id],
+ views: newCardViews,
+ },
+ },
+ };
+ },
+ updateCardPosition: (state, { payload }) => {
+ if (!state.activeViewId) return state;
+ const { id, position } = payload;
+ const newPos = {
+ x: Math.round(position.x / GRID.size) * GRID.size,
+ y: Math.round(position.y / GRID.size) * GRID.size,
+ };
+ return {
+ ...state,
+ cards: {
+ ...state.cards,
+ [id]: {
+ ...state.cards[id],
+ views: {
+ ...state.cards[id].views,
+ [state.activeViewId]: {
+ ...state.cards[id].views[state.activeViewId],
+ pos: newPos,
+ },
+ },
+ },
+ },
+ }
+ },
+ updateCardSize: (state, { payload }) => {
+ if (!state.activeViewId) return state;
+ const { id, size } = payload;
+ const newSize = {
+ height: (Math.round(size.height.split('px').shift() / GRID.size) * GRID.size) + 'px',
+ width: (Math.round(size.width.split('px').shift() / GRID.size) * GRID.size) + 'px',
+ };
+ return {
+ ...state,
+ cards: {
+ ...state.cards,
+ [id]: {
+ ...state.cards[id],
+ views: {
+ ...state.cards[id].views,
+ [state.activeViewId]: {
+ ...state.cards[id].views[state.activeViewId],
+ size: newSize,
+ },
+ },
+ },
+ },
+ };
+ },
+ updateCardTitle: (state, { payload }) => {
+ const { id, title } = payload;
+ return {
+ ...state,
+ cards: {
+ ...state.cards,
+ [id]: {
+ ...state.cards[id],
+ title: title,
+ editedOn: Date.now(),
+ },
+ },
+ };
+ },
+ updateCardColor: (state, { payload }) => {
+ const { id, color } = payload;
+ return {
+ ...state,
+ cards: {
+ ...state.cards,
+ [id]: {
+ ...state.cards[id],
+ color: color,
+ editedOn: Date.now(),
+ },
+ },
+ };
+ },
+ updateCardText: (state, { payload }) => {
+ const { id, text } = payload;
+ return {
+ ...state,
+ cards: {
+ ...state.cards,
+ [id]: {
+ ...state.cards[id],
+ content: {
+ ...state.cards[id].content,
+ text: text,
+ },
+ editedOn: Date.now(),
+ },
+ },
+ };
+ },
+
+ createTab: (state) => {
+ const newViewId = generateUID('tab');
+ let newViewOrder = [ ...state.viewOrder ];
+ const pos = state.activeViewId ? newViewOrder.indexOf(state.activeViewId) + 1 : 0;
+ newViewOrder.splice(pos, 0, newViewId);
+ return {
+ ...state,
+ activeViewId: newViewId,
+ viewOrder: newViewOrder,
+ views: {
+ ...state.views,
+ [newViewId]: DEFAULT_TAB,
+ },
+ };
+ },
+ destroyTab: (state, { payload }) => {
+ let newViews = { ...state.views };
+ delete newViews[payload.id];
+ const newViewOrder = [ ...state.viewOrder ].filter(tabId => tabId !== payload.id);
+ return {
+ ...state,
+ activeViewId: (payload.id === state.activeViewId) ? null : state.activeViewId,
+ viewOrder: newViewOrder,
+ views: newViews,
+ };
+ },
+ updateTabTitle: (state, { payload }) => {
+ const { id, title } = payload;
+ return {
+ ...state,
+ views: {
+ ...state.views,
+ [id]: {
+ ...state.views[id],
+ title: title,
+ editedOn: Date.now(),
+ },
+ },
+ };
+ },
+ setActiveTabPosition: (state, { payload }) => {
+ if (!state.activeViewId) return state;
+ return {
+ ...state,
+ views: {
+ ...state.views,
+ [state.activeViewId]: {
+ ...state.views[state.activeViewId],
+ pos: payload.position,
+ },
+ },
+ };
+ },
+ setActiveTabScale: (state, { payload }) => {
+ // does not affect undo/redo
+ if (!state.activeViewId) return state;
+ return {
+ ...state,
+ views: {
+ ...state.views,
+ [state.activeViewId]: {
+ ...state.views[state.activeViewId],
+ scale: payload.scale,
+ },
+ },
+ };
+ },
+ },
+});
+
+const actions = project.actions;
+
+const { reducer } = project;
+
+export {
+ actions,
+ initialState,
+ reducer,
+};
diff --git a/src/data/campaign/selectors.js b/src/data/redux/project/selectors.js
similarity index 75%
rename from src/data/campaign/selectors.js
rename to src/data/redux/project/selectors.js
index c449ef2..c4a743d 100644
--- a/src/data/campaign/selectors.js
+++ b/src/data/redux/project/selectors.js
@@ -6,11 +6,8 @@ const mkSimpleSelector = (cb) => createSelector([module.campaignState], cb);
export const simpleSelectors = {
completeState: mkSimpleSelector(campaignData => campaignData),
campaignTitle: mkSimpleSelector(campaignData => campaignData.title),
- activeCardId: mkSimpleSelector(campaignData => campaignData.activeCardId),
activeViewId: mkSimpleSelector(campaignData => campaignData.activeViewId),
viewOrder: mkSimpleSelector(campaignData => campaignData.viewOrder),
- cardCreateCnt: mkSimpleSelector(campaignData => campaignData.cardCreateCnt),
- viewCreateCnt: mkSimpleSelector(campaignData => campaignData.viewCreateCnt),
cards: mkSimpleSelector(campaignData => campaignData.cards),
views: mkSimpleSelector(campaignData => campaignData.views),
};
diff --git a/src/data/redux/session/constants.js b/src/data/redux/session/constants.js
new file mode 100644
index 0000000..1083506
--- /dev/null
+++ b/src/data/redux/session/constants.js
@@ -0,0 +1,12 @@
+import { INTRO_PROJECT, BLANK_PROJECT } from '../project/constants';
+
+export const NETWORK_STATUS = {
+ idle: 'idle',
+ saving: 'saving',
+ loading: 'loading',
+};
+
+export const DEFAULT_PROJECT = {
+ intro_project_id: INTRO_PROJECT.title,
+ blank_project_id: BLANK_PROJECT.title,
+};
diff --git a/src/data/session/index.js b/src/data/redux/session/index.js
similarity index 100%
rename from src/data/session/index.js
rename to src/data/redux/session/index.js
diff --git a/src/data/redux/session/reducers.js b/src/data/redux/session/reducers.js
new file mode 100644
index 0000000..09c7777
--- /dev/null
+++ b/src/data/redux/session/reducers.js
@@ -0,0 +1,82 @@
+import { createSlice } from '@reduxjs/toolkit';
+import { NETWORK_STATUS, DEFAULT_PROJECT } from './constants';
+
+const initialState = {
+ status: NETWORK_STATUS.idle,
+ popup: {
+ id: null,
+ type: null,
+ },
+
+ campaignList: {}, // campaignId: campaignTitle
+ activeCampaignId: null,
+ activeCardId: null,
+
+ isProjectEdited: false, // flag for unsaved changes
+};
+
+const session = createSlice({
+ name: 'session',
+ initialState,
+ reducers: {
+ initialize: () => ({ ...initialState }),
+ loadIntro: () => ({
+ ...initialState,
+ campaignList: {
+ 'intro_project_id': DEFAULT_PROJECT.intro_project_id,
+ },
+ activeCampaignId: 'intro_project_id',
+ }),
+
+ setStatus: (state, { payload }) => {
+ const { status, logging } = payload;
+ console.log(`[Status: ${status}]`, logging);
+ return ({ ...state, status });
+ },
+
+ setPopup: (state, { payload }) => ({ ...state, popup: { ...payload } }),
+ resetPopup: (state) => ({ ...state, popup: { id: null, type: null } }),
+
+ loadProjects: (state, { payload }) => ({
+ ...state,
+ campaignList: payload.projects,
+ }),
+ addProject: (state, { payload }) => ({
+ ...state,
+ campaignList: {
+ ...state.campaignList,
+ [payload.id]: payload.title,
+ },
+ }),
+ removeProject: (state, { payload }) => {
+ let newCampaignList = { ...state.campaignList };
+ delete newCampaignList[payload.id];
+ return {
+ ...state,
+ campaignList: newCampaignList,
+ activeCampaignId: state.activeCampaignId === payload.id ? null : state.activeCampaignId,
+ };
+ },
+ updateProjectTitle: (state, { payload }) => ({
+ ...state,
+ campaignList: {
+ ...state.campaignList,
+ [payload.id]: payload.title,
+ },
+ }),
+ setActiveProject: (state, { payload }) => ({ ...state, activeCampaignId: payload.id }),
+ setActiveCard: (state, { payload }) => ({ ...state, activeCardId: payload.id }),
+
+ setIsProjectEdited: (state, { payload }) => ({ ...state, isProjectEdited: payload }),
+ },
+});
+
+const actions = session.actions;
+
+const { reducer } = session;
+
+export {
+ actions,
+ initialState,
+ reducer,
+};
diff --git a/src/data/session/selectors.js b/src/data/redux/session/selectors.js
similarity index 50%
rename from src/data/session/selectors.js
rename to src/data/redux/session/selectors.js
index ffbdae9..a7ad954 100644
--- a/src/data/session/selectors.js
+++ b/src/data/redux/session/selectors.js
@@ -4,18 +4,13 @@ import * as module from './selectors';
export const sessionState = (state) => state.session;
const mkSimpleSelector = (cb) => createSelector([module.sessionState], cb);
export const simpleSelectors = {
- completeState: mkSimpleSelector(problemData => problemData),
- campaignList: mkSimpleSelector(sessionData => sessionData.campaignList),
- activeCampaign: mkSimpleSelector(sessionData => sessionData.activeCampaign),
- activeCard: mkSimpleSelector(sessionData => sessionData.activeCard),
popup: mkSimpleSelector(sessionData => sessionData.popup),
status: mkSimpleSelector(sessionData => sessionData.status),
- isCampaignEdited: mkSimpleSelector(sessionData => sessionData.isCampaignEdited),
- passwordResetError: mkSimpleSelector(sessionData => sessionData.passwordResetError),
- emailSignInError: mkSimpleSelector(sessionData => sessionData.emailSignInError),
- emailSignUpError: mkSimpleSelector(sessionData => sessionData.emailSignUpError),
- googleSignInError: mkSimpleSelector(sessionData => sessionData.googleSignInError),
- facebookSignInError: mkSimpleSelector(sessionData => sessionData.facebookSignInError),
+ completeState: mkSimpleSelector(problemData => problemData),
+ campaignList: mkSimpleSelector(sessionData => sessionData.campaignList),
+ activeCampaignId: mkSimpleSelector(sessionData => sessionData.activeCampaignId),
+ activeCardId: mkSimpleSelector(sessionData => sessionData.activeCardId),
+ isCampaignEdited: mkSimpleSelector(sessionData => sessionData.isProjectEdited),
};
export default {
diff --git a/src/data/user/index.js b/src/data/redux/user/index.js
similarity index 100%
rename from src/data/user/index.js
rename to src/data/redux/user/index.js
diff --git a/src/data/user/reducers.js b/src/data/redux/user/reducers.js
similarity index 77%
rename from src/data/user/reducers.js
rename to src/data/redux/user/reducers.js
index b5a82d0..616f52c 100644
--- a/src/data/user/reducers.js
+++ b/src/data/redux/user/reducers.js
@@ -1,8 +1,5 @@
import { createSlice } from '@reduxjs/toolkit';
-// TODO make the following name changes
-// userId -> uid
-
const initialState = {
userId: null,
displayName: null,
@@ -17,22 +14,22 @@ const user = createSlice({
name: 'user',
initialState,
reducers: {
- resetUser: () => ({ ...initialState }),
- loadUser: (state, { user }) => {
+ initialize: () => ({ ...initialState }),
+ loadUser: (state, { payload }) => {
const {
- uid,
+ userId,
displayName,
email,
emailVerified,
providerId,
providerData,
- } = user;
+ } = payload;
const emailVerificationRequired = providerData
.map((provider) => provider.providerId)
.includes('password');
return {
...state,
- userId: uid,
+ userId,
displayName,
email,
emailVerified,
@@ -41,6 +38,10 @@ const user = createSlice({
providerData,
};
},
+ updateUserDisplayName: (state, { payload }) => ({
+ ...state,
+ displayName: payload.displayName,
+ }),
},
});
diff --git a/src/data/user/selectors.js b/src/data/redux/user/selectors.js
similarity index 100%
rename from src/data/user/selectors.js
rename to src/data/redux/user/selectors.js
diff --git a/src/data/session/reducers.js b/src/data/session/reducers.js
deleted file mode 100644
index 313ddc2..0000000
--- a/src/data/session/reducers.js
+++ /dev/null
@@ -1,166 +0,0 @@
-import { createSlice } from '@reduxjs/toolkit';
-
-// TODO make the following name changes
-// activeCampaignId -> activeCampaign
-// activeCardId -> activeCard
-// campaignEdit -> isCampaignEdited
-// errorPasswordReset -> passwordResetError
-// errorEmailSignIn -> emailSignInError
-// errorEmailSignUp -> emailSignUpError
-// errorGoogleSignIn -> googleSignInError
-// errorFacebookSignIn -> facebookSignInError
-
-const initialState = {
- campaignList: {
- // campaignId: campaignTitle,
- },
- activeCampaignId: null,
- activeCardId: null,
-
- popup: {
- id: '',
- type: '',
- },
-
- status: 'loading', // idle, loading or saving
- campaignEdit: false, // flag for any unsaved changes
-
- errorPasswordReset: '',
- errorEmailSignIn: '',
- errorEmailSignUp: '',
- errorGoogleSignIn: '',
- errorFacebookSignIn: '',
-};
-
-const session = createSlice({
- name: 'session',
- initialState,
- reducers: {
- resetSession: () => ({ ...initialState }),
-
- // CAMPAIGN LIST
- loadCampaignList: (state, { campaignList }) => ({ ...state, campaignList }),
- addCampaign: (state, { id, title }) => ({
- ...state,
- campaignList: {
- ...state.campaignList,
- [id]: title,
- },
- activeCampaignId: id,
- }),
- removeCampaign: (state, { id }) => {
- let newCampaignList = { ...state.campaignList };
- delete newCampaignList[id];
- return {
- ...state,
- newCampaignList,
- activeCampaignId: state.activeCampaignId === id ? null : state.activeCampaignId,
- };
- },
- updateCampaign: (state, { title }) => ({
- ...state,
- campaignList: {
- ...state.campaignList,
- [state.activeCampaignId]: title,
- },
- }),
-
- // ACTIVE/SELECTED
- updateActiveCampaignId: (state, { id }) => ({ ...state, activeCampaignId: id }),
- updateActiveCardId: (state, { id }) => ({ ...state, activeCardId: id }),
-
- // POPUP STATES
- resetPopup: (state) => ({
- ...state,
- popup: {
- id: '',
- type: '',
- },
- }),
- setPopup: (state, { popup }) => ({ ...state, popup }),
-
- // SAVE LOAD STATES
- setStatus: (state, { status }) => ({ ...state, status: status }),
- setCampaignEdit: (state, { isEdited }) => ({ ...state, campaignEdit: isEdited }),
- // setIsIntroCampaignEdited,
-
- // ERROR HANDLING
- setErrorPasswordReset: (state, { code }) => {
- let msg = '';
- switch (code) {
- case ('auth/invalid-email'): msg = 'email address is not valid';
- case ('auth/user-not-found'): msg = 'user does not exist';
- // other cases: auth/missing-android-pkg-name, auth/missing-continue-uri, auth/missing-ios-bundle-id, auth/invalid-continue-uri, auth/unauthorized-continue-uri
- default: msg = 'could not send password reset email';
- }
- return { ...state, errorPasswordReset: msg };
- },
- resetErrorPasswordReset: (state) => ({ ...state, errorPasswordReset: '' }),
- setErrorEmailSignIn: (state, { code }) => {
- let msg = '';
- switch (code) {
- case ('auth/invalid-email'): msg = 'invalid email';
- case ('auth/user-disabled'): msg = 'user disabled';
- case ('auth/user-not-found'): msg = 'user not found';
- case ('auth/wrong-password'): msg = 'invalid password';
- default: msg = 'sign in unsuccessful';
- }
- return { ...state, errorEmailSignIn: msg };
- },
- resetErrorEmailSignIn: (state => ({ ...state, errorEmailSignIn: '' })),
- setErrorEmailSignUp: (state, { code }) => {
- let msg = '';
- switch (code) {
- case ('auth/email-already-in-use'): msg = 'email already in use';
- case ('auth/invalid-email'): msg = 'invalid email';
- case ('auth/operation-not-allowed'): msg = 'email sign up currently not in service';
- case ('auth/weak-password'): msg = 'password must be at least 6 characters long';
- default: msg = 'email sign up unsuccessful';
- }
- return { ...state, errorEmailSignUp: '' };
- },
- resetErrorEmailSignUp: (state => ({ ...state, errorEmailSignUp: '' })),
- setErrorGoogleSignIn: (state, { code }) => {
- let msg = '';
- switch (code) {
- case ('auth/account-exists-with-different-credential'): msg = 'account for this email already exists';
- case ('auth/auth-domain-config-required'): msg = 'missing authorization configuration';
- case ('auth/cancelled-popup-request'): msg = 'too many sign in popups attempted';
- case ('auth/operation-not-allowed'): msg = 'operation not allowed';
- case ('auth/operation-not-supported-in-this-environment'): msg = 'operation not supported';
- case ('auth/popup-blocked'): msg = 'sign in popup blocked';
- // case ('auth/popup-closed-by-user'): msg = 'sign in popup closed';
- case ('auth/unauthorized-domain'): msg = 'unauthorized domain';
- default: msg = 'google sign in failed';
- }
- return { ...state, errorGoogleSignIn: '' };
- },
- resetErrorGoogleSignIn: (state => ({ ...state, errorGoogleSignIn: '' })),
- setErrorFacebookSignIn: (state, { code }) => {
- let msg = '';
- switch (code) {
- case ('auth/account-exists-with-different-credential'): msg = 'account for this email already exists';
- case ('auth/auth-domain-config-required'): msg = 'missing authorization configuration';
- case ('auth/cancelled-popup-request'): msg = 'too many sign in popups attempted';
- case ('auth/operation-not-allowed'): msg = 'operation not allowed';
- case ('auth/operation-not-supported-in-this-environment'): msg = 'operation not supported';
- case ('auth/popup-blocked'): msg = 'sign in popup blocked';
- case ('auth/popup-closed-by-user'): msg = 'sign in popup closed';
- case ('auth/unauthorized-domain'): msg = 'unauthorized domain';
- default: msg = 'facebook sign in failed';
- }
- return { ...state, errorFacebookSignIn: '' };
- },
- resetErrorFacebookSignIn: (state => ({ ...state, errorFacebookSignIn: '' })),
- },
-});
-
-const actions = session.actions;
-
-const { reducer } = session;
-
-export {
- actions,
- initialState,
- reducer,
-};
diff --git a/src/data/store.js b/src/data/store.js
deleted file mode 100644
index c0ff1a5..0000000
--- a/src/data/store.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import { combineReducers } from 'redux';
-import { configureStore } from '@reduxjs/toolkit';
-import undoable, { includeAction } from 'redux-undo';
-
-import * as campaign from './campaign/reducers';
-import * as session from './session/reducers';
-import * as user from './user/reducers';
-
-/* TODO: redux refactor
- Files under /data will be the future store for dmkit.
- Get these files to work with the app.
- Files under /store to be removed.
-*/
-
-const rootReducer = combineReducers({
- campaignData: undoable(
- campaign.reducer,
- {
- filter: includeAction([
- 'updCampaignTitle',
- 'shiftViewInViewOrder',
- 'createCard',
- 'copyCard',
- 'destroyCard',
- 'linkCardToView',
- 'unlinkCardToView',
- 'updCardPos',
- 'updCardSize',
- 'updCardColor',
- 'updCardTitle',
- 'updCardText',
- 'createView',
- 'destroyView',
- 'updViewTitle',
- ]),
- limit: 10,
- }
- ),
- session: session.reducer,
- user: user.reducer,
-});
-
-const store = configureStore({
- reducer: rootReducer,
- devTools: process.env.NODE_ENV !== 'production',
-});
-
-export default store;
diff --git a/src/hooks.js b/src/hooks.js
new file mode 100644
index 0000000..ba19601
--- /dev/null
+++ b/src/hooks.js
@@ -0,0 +1,80 @@
+import { useEffect, useRef, useState } from "react";
+import { useDispatch, useSelector } from "react-redux";
+
+import { authListener } from './data/api/auth';
+import * as api from './data/api/database';
+import { actions, clearHistory } from './data/redux';
+import { NETWORK_STATUS } from './data/redux/session/constants';
+
+export const useListenerHooks = () => {
+ const dispatch = useDispatch();
+ const userId = useSelector(state => state.user.userId);
+ const status = useSelector(state => state.session.status || NETWORK_STATUS.idle);
+ const activeProject = useSelector(state => state.session.activeCampaignId || '');
+ const isProjectEdited = useSelector(state => state.session.isProjectEdited);
+ const projectData = useSelector(state => state.project.present || {});
+ const projectChanges = useSelector(state => state.project._latestUnfiltered);
+ const isLoggedIn = !!userId;
+
+ // auth listener
+ useEffect(() => {
+ const listener = authListener({
+ dispatch,
+ saveProject: isProjectEdited,
+ projectId: activeProject,
+ projectData,
+ });
+ return () => listener();
+ }, []);
+
+ // load project when activeProject changes
+ useEffect(() => {
+ if (activeProject) {
+ if (activeProject === 'intro_project_id') {
+ dispatch(actions.project.loadIntroProject());
+ } else if (activeProject === 'blank_project_id') {
+ dispatch(actions.project.loadBlankProject());
+ } else {
+ dispatch(api.fetchProjectData(activeProject, () => {
+ dispatch(clearHistory());
+ }));
+ }
+ }
+ }, [activeProject]);
+
+ // set edit when project data changes
+ useEffect(() => {
+ dispatch(actions.session.setIsProjectEdited(true));
+ }, [projectChanges])
+
+ // auto-save every minute
+ useEffect(() => {
+ const autoSave = setInterval(() => {
+ if ((status === NETWORK_STATUS.idle) && isLoggedIn && !!activeProject && isProjectEdited) {
+ console.log('[auto-save] triggered');
+ dispatch(api.save(activeProject, projectData));
+ }
+ }, 60000);
+ return () => clearInterval(autoSave);
+ }, [status, userId, activeProject, isProjectEdited, projectData]);
+};
+
+export const useMenuStateHooks = () => {
+ const activeProject = useSelector(state => state.session.activeCampaignId || '');
+ const [ isToolMenuOpen, setIsToolMenuOpen ] = useState(!!activeProject);
+ const toolMenuRef = useRef();
+
+ useEffect(() => {
+ setIsToolMenuOpen(!!activeProject);
+ }, [activeProject]);
+
+ return {
+ toolMenuRef,
+ isToolMenuOpen,
+ toggleToolMenu: () => {
+ if (activeProject) {
+ setIsToolMenuOpen(!isToolMenuOpen);
+ }
+ },
+ };
+}
diff --git a/src/index.jsx b/src/index.jsx
index 0762910..c8b51fe 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -1,72 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
-import { applyMiddleware, compose, combineReducers } from 'redux';
-import { configureStore } from '@reduxjs/toolkit';
-import thunk from 'redux-thunk';
-import undoable, { includeAction } from 'redux-undo';
import App from './App';
-import sessionManagerReducer from './store/reducer/sessionManager';
-import userReducer from './store/reducer/userData';
-import campaignDataReducer from './store/reducer/campaignData';
import * as serviceWorker from './serviceWorker';
-import * as actionTypes from './store/actionTypes';
-// import store from './data/store';
-
-/* TODO: redux refactor
- Files under /data will be the future store for dmkit.
- Get these files to work with the app.
- Files under /store to be removed.
-*/
-
-const composeEnhancers =
- process.env.NODE_ENV === 'development'
- ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
- : null || compose;
-
-// const editReducer = () =>
-// baseReducer => (state, action) => {
-// return dispatch => {
-// dispatch( { type: actionTypes.SET_CAMPAIGN_EDIT, edit: true } );
-// dispatch(baseReducer(state, action));
-// }
-// };
-
-const rootReducer = combineReducers({
- sessionManager: sessionManagerReducer,
- userData: userReducer,
- campaignData: undoable(campaignDataReducer, {
- filter: includeAction([
- actionTypes.UPD_CAMPAIGN_TITLE,
- actionTypes.SHIFT_VIEW_IN_VIEW_ORDER,
- actionTypes.CREATE_CARD,
- actionTypes.COPY_CARD,
- actionTypes.DESTROY_CARD,
- actionTypes.LINK_CARD_TO_VIEW,
- actionTypes.UNLINK_CARD_FROM_VIEW,
- actionTypes.UPD_CARD_POS,
- actionTypes.UPD_CARD_SIZE,
- actionTypes.UPD_CARD_COLOR,
- actionTypes.UPD_CARD_COLOR_FOR_VIEW,
- actionTypes.UPD_CARD_FORM,
- actionTypes.UPD_CARD_TITLE,
- actionTypes.UPD_CARD_TEXT,
- actionTypes.CREATE_VIEW,
- actionTypes.DESTROY_VIEW,
- actionTypes.UPD_VIEW_COLOR,
- actionTypes.UPD_VIEW_TITLE,
- ]),
- limit: 10,
- // debug: true,
- }),
-});
-
-export const store = configureStore({
- reducer: rootReducer,
- // middleware: composeEnhancers(applyMiddleware(thunk)),
- devTools: process.env.NODE_ENV !== 'production',
-});
+import store from './data/redux';
const app = (
diff --git a/src/store/action/campaign.js b/src/store/action/campaign.js
deleted file mode 100644
index 272c70f..0000000
--- a/src/store/action/campaign.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import * as actionTypes from '../actionTypes';
-
-// actions for sessionManager reducer
-export const resetSessionManager = () => { return { type: actionTypes.RESET_SESSION_MANAGER }; };
-export const loadCampaignList = (campaignList) => { return { type: actionTypes.LOAD_CAMPAIGN_LIST, campaignList: campaignList }; };
-export const addCampaignToList = (campaignId, campaignTitle) => { return { type: actionTypes.ADD_CAMPAIGN_TO_LIST, campaignId: campaignId, campaignTitle: campaignTitle }; };
-export const removeCampaignFromList = (campaignId) => { return { type: actionTypes.REMOVE_CAMPAIGN_FROM_LIST, campaignId: campaignId }; };
-export const updActiveCampaignId = (activeCampaignId) => { return { type: actionTypes.UPD_ACTIVE_CAMPAIGN_ID, activeCampaignId: activeCampaignId }; };
-
-export const resetPopup = () => { return { type: actionTypes.RESET_POPUP }; };
-export const setPopup = (popup) => { return { type: actionTypes.SET_POPUP, popup }; };
-
-export const setStatus = (status) => { return { type: actionTypes.SET_STATUS, status: status }; };
-export const setCampaignEdit = (edit) => { return { type: actionTypes.SET_CAMPAIGN_EDIT, edit: edit }; };
-export const setIntroCampaignEdit = (edit) => { return { type: actionTypes.SET_INTRO_CAMPAIGN_EDIT, edit: edit }; };
-
-// actions for campaignData reducer
-export const loadCampaignData = (campaignData) => { return { type: actionTypes.LOAD_CAMPAIGN_DATA, campaignData: campaignData }; };
-export const unloadCampaignData = () => { return { type: actionTypes.UNLOAD_CAMPAIGN_DATA }; };
-export const loadIntroCampaign = () => { return { type: actionTypes.LOAD_INTRO_CAMPAIGN }; };
-export const updCampaignTitle = (title) => {
- return dispatch => {
- dispatch({ type: actionTypes.UPD_CAMPAIGN_TITLE, title: title });
- dispatch({ type: actionTypes.UPD_CAMPAIGN_ON_LIST, title: title });
- };
-};
\ No newline at end of file
diff --git a/src/store/action/card.js b/src/store/action/card.js
deleted file mode 100644
index ff3201b..0000000
--- a/src/store/action/card.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import * as actionTypes from '../actionTypes';
-
-// actions for sessionManager reducer
-export const updActiveCardId = (activeCardId) => { return { type: actionTypes.UPD_ACTIVE_CARD_ID, activeCardId: activeCardId }; };
-
-// actions for campaingData reducer
-export const createCard = () => { return { type: actionTypes.CREATE_CARD }; };
-export const copyCard = (cardId) => { return { type: actionTypes.COPY_CARD, cardId: cardId }; };
-export const destroyCard = (cardId) => { return { type: actionTypes.DESTROY_CARD, cardId: cardId }; };
-export const linkCardToView = (cardId, pos) => { return { type: actionTypes.LINK_CARD_TO_VIEW, cardId: cardId, pos: pos }; };
-export const unlinkCardFromView = (cardId) => { return { type: actionTypes.UNLINK_CARD_FROM_VIEW, cardId: cardId }; };
-
-export const updCardPos = (cardId, pos) => { return { type: actionTypes.UPD_CARD_POS, cardId: cardId, pos: pos }; };
-export const updCardSize = (cardId, size) => { return { type: actionTypes.UPD_CARD_SIZE, cardId: cardId, size: size }; };
-export const updCardColor = (cardId, color) => { return { type: actionTypes.UPD_CARD_COLOR, cardId: cardId, color: color }; };
-export const updCardColorForView = (cardId, color) => { return { type: actionTypes.UPD_CARD_COLOR_FOR_VIEW, cardId: cardId, color: color }; };
-export const updCardForm = (cardId, cardForm) => { return { type: actionTypes.UPD_CARD_FORM, cardId: cardId, cardForm: cardForm }; };
-
-export const updCardTitle = (cardId, title) => { return { type: actionTypes.UPD_CARD_TITLE, cardId: cardId, title: title }; };
-export const updCardText = (cardId, text) => { return { type: actionTypes.UPD_CARD_TEXT, cardId: cardId, text: text }; };
diff --git a/src/store/action/user.js b/src/store/action/user.js
deleted file mode 100644
index 750fd71..0000000
--- a/src/store/action/user.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import * as actionTypes from '../actionTypes';
-
-// actions for userData reducer
-export const loadUser = (user) => { return { type: actionTypes.LOAD_USER, user: user }; };
-export const unloadUser = () => { return { type: actionTypes.UNLOAD_USER }; };
-export const updUserDisplayname = (displayName) => { return { type: actionTypes.UPD_USER_DISPLAYNAME, displayName: displayName }; };
-
-// actions for auth errors fround in sessionManager reducer
-export const setErrorPasswordReset = (errorCode) => { return { type: actionTypes.SET_ERROR_PASSWORD_RESET, errorCode: errorCode }; };
-export const unsetErrorPasswordReset = () => { return { type: actionTypes.UNSET_ERROR_PASSWORD_RESET }; };
-export const setErrorEmailSignIn = (errorCode) => { return { type: actionTypes.SET_ERROR_EMAIL_SIGN_IN, errorCode: errorCode }; };
-export const unsetErrorEmailSignIn = () => { return { type: actionTypes.UNSET_ERROR_EMAIL_SIGN_IN }; };
-export const setErrorEmailSignUp = (errorCode) => { return { type: actionTypes.SET_ERROR_EMAIL_SIGN_UP, errorCode: errorCode }; };
-export const unsetErrorEmailSignUp = () => { return { type: actionTypes.UNSET_ERROR_EMAIL_SIGN_UP }; };
-export const setErrorGoogleSignUp = (errorCode) => { return { type: actionTypes.SET_ERROR_GOOGLE_SIGN_IN, errorCode: errorCode }; };
-export const unsetErrorGoogleSignUp = () => { return { type: actionTypes.UNSET_ERROR_GOOGLE_SIGN_IN }; };
-export const setErrorFacebookSignUp = (errorCode) => { return { type: actionTypes.SET_ERROR_FACEBOOK_SIGN_IN, errorCode: errorCode }; };
-export const unsetErrorFacebookSignUp = () => { return { type: actionTypes.UNSET_ERROR_FACEBOOK_SIGN_IN }; };
\ No newline at end of file
diff --git a/src/store/action/view.js b/src/store/action/view.js
deleted file mode 100644
index 7ff21c5..0000000
--- a/src/store/action/view.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import * as actionTypes from '../actionTypes';
-
-// actions for sessionManager reducer
-
-// actions for campaingData reducer
-export const updActiveViewId = (activeViewId) => { return { type: actionTypes.UPD_ACTIVE_VIEW_ID, activeViewId: activeViewId }; };
-export const shiftViewInViewOrder = (shiftedViewId, posShift) => { return { type: actionTypes.SHIFT_VIEW_IN_VIEW_ORDER, shiftedViewId: shiftedViewId, posShift: posShift }; };
-export const createView = () => { return { type: actionTypes.CREATE_VIEW }; };
-export const destroyView = (viewId) => { return { type: actionTypes.DESTROY_VIEW, viewId: viewId }; };
-export const lockActiveView = () => { return { type: actionTypes.LOCK_ACTIVE_VIEW }; };
-export const unlockActiveView = () => { return { type: actionTypes.UNLOCK_ACTIVE_VIEW }; };
-export const updActiveViewPos = (pos) => { return { type: actionTypes.UPD_ACTIVE_VIEW_POS, pos: pos }; };
-export const updActiveViewScale = (scale) => { return { type: actionTypes.UPD_ACTIVE_VIEW_SCALE, scale: scale }; };
-export const resetActiveView = () => { return { type: actionTypes.RESET_ACTIVE_VIEW }; };
-export const updViewColor = (viewId, color) => { return { type: actionTypes.UPD_VIEW_COLOR, viewId: viewId, color: color }; };
-export const updViewTitle = (viewId, title) => { return { type: actionTypes.UPD_VIEW_TITLE, viewId: viewId, title: title }; };
\ No newline at end of file
diff --git a/src/store/actionIndex.js b/src/store/actionIndex.js
deleted file mode 100644
index 0c1a481..0000000
--- a/src/store/actionIndex.js
+++ /dev/null
@@ -1,62 +0,0 @@
-/* TODO: redux refactor
- Files under /data will be the future store for dmkit.
- Get these files to work with the app.
- Files under /store to be removed.
-*/
-
-// USER
-export {
- loadUser, unloadUser,
- updUserDisplayname,
-
- setErrorPasswordReset, unsetErrorPasswordReset,
- setErrorEmailSignIn, unsetErrorEmailSignIn,
- setErrorEmailSignUp, unsetErrorEmailSignUp,
- setErrorGoogleSignUp, unsetErrorGoogleSignUp,
- setErrorFacebookSignUp, unsetErrorFacebookSignUp,
-} from './action/user';
-
-// CAMPAIGN
-export {
- resetSessionManager,
- loadCampaignList, addCampaignToList, removeCampaignFromList,
- updActiveCampaignId,
-
- resetPopup,
- setPopup,
-
- setStatus,
- setCampaignEdit, setIntroCampaignEdit,
-
- loadCampaignData, unloadCampaignData, loadIntroCampaign,
- updCampaignTitle,
-} from './action/campaign';
-
-// CARD
-export {
- updActiveCardId,
-
- createCard, copyCard, destroyCard,
- linkCardToView, unlinkCardFromView,
-
- updCardPos, updCardSize,
- updCardColor, updCardColorForView,
- updCardForm,
-
- updCardTitle, updCardText,
-} from './action/card';
-
-// VIEW
-export {
- updActiveViewId,
- shiftViewInViewOrder,
-
- createView, destroyView,
-
- lockActiveView, unlockActiveView,
- updActiveViewPos, updActiveViewScale,
- resetActiveView,
-
- updViewColor,
- updViewTitle,
-} from './action/view'
\ No newline at end of file
diff --git a/src/store/actionTypes.js b/src/store/actionTypes.js
deleted file mode 100644
index 303de5b..0000000
--- a/src/store/actionTypes.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/* TODO: redux refactor
- Files under /data will be the future store for dmkit.
- Get these files to work with the app.
- Files under /store to be removed.
-*/
-
-/*------------------*/
-// userData reducer //
-export const LOAD_USER = 'LOAD_USER';
-export const UNLOAD_USER = 'UNLOAD_USER';
-export const UPD_USER_DISPLAYNAME = 'UPD_USER_DISPLAYNAME';
-
-/*------------------------*/
-// sessionManager reducer //
-export const RESET_SESSION_MANAGER = 'RESET_SESSION_MANAGER';
-// campaign related actions
-export const LOAD_CAMPAIGN_LIST = 'LOAD_CAMPAIGN_LIST';
-export const ADD_CAMPAIGN_TO_LIST = 'ADD_CAMPAIGN_TO_LIST';
-export const REMOVE_CAMPAIGN_FROM_LIST = 'REMOVE_CAMPAIGN_FROM_LIST';
-export const UPD_CAMPAIGN_ON_LIST = 'UPD_CAMPAIGN_ON_LIST';
-export const UPD_ACTIVE_CAMPAIGN_ID = 'UPD_ACTIVE_CAMPAIGN_ID';
-export const UPD_ACTIVE_CARD_ID = 'UPD_ACTIVE_CARD_ID';
-export const SET_STATUS = 'SET_STATUS';
-export const SET_CAMPAIGN_EDIT = 'SET_CAMPAIGN_EDIT';
-export const SET_INTRO_CAMPAIGN_EDIT = 'SET_INTRO_CAMPAIGN_EDIT';
-// popups
-export const RESET_POPUP = 'RESET_POPUP';
-export const SET_POPUP = 'SET_POPUP';
-export const SET_POPUP_TYPE = 'SET_POPUP_TYPE';
-export const SET_POPUP_PROPS = 'SET_POPUP_PROPS';
-// errors
-export const SET_ERROR_PASSWORD_RESET = 'SET_ERROR_PASSWORD_RESET';
-export const UNSET_ERROR_PASSWORD_RESET = 'UNSET_ERROR_PASSWORD_RESET';
-export const SET_ERROR_EMAIL_SIGN_IN = 'SET_ERROR_EMAIL_SIGN_IN';
-export const UNSET_ERROR_EMAIL_SIGN_IN = 'UNSET_ERROR_EMAIL_SIGN_IN';
-export const SET_ERROR_EMAIL_SIGN_UP = 'SET_ERROR_EMAIL_SIGN_UP';
-export const UNSET_ERROR_EMAIL_SIGN_UP = 'UNSET_ERROR_EMAIL_SIGN_UP';
-export const SET_ERROR_GOOGLE_SIGN_IN = 'SET_ERROR_GOOGLE_SIGN_IN';
-export const UNSET_ERROR_GOOGLE_SIGN_IN = 'UNSET_ERROR_GOOGLE_SIGN_IN';
-export const SET_ERROR_FACEBOOK_SIGN_IN = 'SET_ERROR_FACEBOOK_SIGN_IN';
-export const UNSET_ERROR_FACEBOOK_SIGN_IN = 'UNSET_ERROR_FACEBOOK_SIGN_IN';
-
-/*----------------------*/
-// campaignData reducer //
-export const LOAD_INTRO_CAMPAIGN = 'LOAD_INTRO_CAMPAIGN';
-export const LOAD_CAMPAIGN_DATA = 'LOAD_CAMPAIGN_DATA';
-export const UNLOAD_CAMPAIGN_DATA = 'UNLOAD_CAMPAIGN_DATA';
-// activeCampaign actions
-export const UPD_CAMPAIGN_TITLE = 'UPD_CAMPAIGN_TITLE';
-export const UPD_ACTIVE_VIEW_ID = 'UPD_ACTIVE_VIEW_ID';
-export const SHIFT_VIEW_IN_VIEW_ORDER = 'SHIFT_VIEW_IN_VIEW_ORDER';
-// card actions
-export const CREATE_CARD = 'CREATE_CARD';
-export const COPY_CARD = 'COPY_CARD';
-export const DESTROY_CARD = 'DESTROY_CARD';
-export const LINK_CARD_TO_VIEW = 'LINK_CARD_TO_VIEW';
-export const UNLINK_CARD_FROM_VIEW = 'UNLINK_CARD_FROM_VIEW';
-export const UPD_CARD_POS = 'UPD_CARD_POS';
-export const UPD_CARD_SIZE = 'UPD_CARD_SIZE';
-export const UPD_CARD_COLOR = 'UPD_CARD_COLOR';
-export const UPD_CARD_COLOR_FOR_VIEW = 'UPD_CARD_COLOR_FOR_VIEW';
-export const UPD_CARD_FORM = 'UPD_CARD_FORM';
-export const UPD_CARD_TITLE = 'UPD_CARD_TITLE';
-export const UPD_CARD_TEXT = 'UPD_CARD_TEXT';
-// view actions
-export const CREATE_VIEW = 'CREATE_VIEW';
-export const DESTROY_VIEW = 'DESTROY_VIEW';
-export const LOCK_ACTIVE_VIEW = 'LOCK_VIEW';
-export const UNLOCK_ACTIVE_VIEW = 'UNLOCK_VIEW';
-export const UPD_ACTIVE_VIEW_POS = 'UPD_ACTIVE_VIEW_POS';
-export const UPD_ACTIVE_VIEW_SCALE = 'UPD_ACTIVE_VIEW_SCALE';
-export const RESET_ACTIVE_VIEW = 'RESET_ACTIVE_VIEW';
-export const UPD_VIEW_TITLE = 'UPD_VIEW_TITLE';
-export const UPD_VIEW_COLOR = 'UPD_VIEW_COLOR';
\ No newline at end of file
diff --git a/src/store/firestoreAPI/storeTransactions.js b/src/store/firestoreAPI/storeTransactions.js
deleted file mode 100644
index 7451281..0000000
--- a/src/store/firestoreAPI/storeTransactions.js
+++ /dev/null
@@ -1,400 +0,0 @@
-// TODO replace campaign terminology with project terminology
-
-import {
- doc,
- addDoc,
- setDoc,
- getDoc,
- updateDoc,
- deleteDoc,
- collection,
- getDocs,
- writeBatch,
-} from '@firebase/firestore';
-
-import { auth, db } from './firebase';
-import * as actions from '../actionIndex';
-import { updateObject } from '../../utils/updateObject';
-import { GRID } from '../../styles/constants';
-
-// User contains uid, email, emailVerified (check firebase for more)
-const getUser = () => auth.currentUser ? auth.currentUser : null;
-
-const firstTimeSetup = (userId) => {
- return dispatch => {
- if (userId) {
- setDoc(doc(db, "users", userId), { activeCampaignId: null })
- .then(resp => {
- console.log("[firstTimeSetup] success performing first time setup");
- console.log("[Status] idle. Triggered by first time setup completion.");
- dispatch(actions.setStatus('idle'));
- })
- .catch(err => console.log("[firstTimeSetup] error performing first time setup:", err));
- }
- }
-};
-
-export const fetchActiveCampaignId = () => {
- const user = getUser();
- return dispatch => {
- if (user) {
- const userId = user.uid;
- getDoc(doc(db, "users", userId))
- .then(resp => {
- if (resp.exists) {
- dispatch(actions.updActiveCampaignId(resp.data().activeCampaignId));
- if (!resp.data().activeCampaignId) {
- dispatch(actions.unloadCampaignData());
- console.log("[Status] idle. Triggered by lack of server side activeCampaignId.");
- dispatch(actions.setStatus('idle'));
- };
- console.log("[fetchActiveCampaignId] success loading activeCampaignId", resp.data().activeCampaignId);
- } else dispatch(firstTimeSetup(userId));
- })
- .catch(err => {
- console.log("[fetchActiveCampaignId] error loading activeCampaignId:", err);
- // TODO insert case when error is "Failed to get document because the client is offline."
- });
- }
- };
-};
-
-export const fetchCampaignList = () => {
- const user = getUser();
- return dispatch => {
- if (user) {
- const userId = user.uid;
- getDocs(collection(db, "users", userId, "campaigns"))
- .then(campaignSnapshot => {
- let campaignList = {};
- campaignSnapshot.forEach(campaign => {
- campaignList = updateObject(campaignList, {[campaign.id]: campaign.data().title});
- });
- dispatch(actions.loadCampaignList(campaignList));
- console.log("[loadCampaignList] success loading campaignList");
- })
- .catch(err => console.log("[loadCampaignList] error loading campaignList:", err));
- }
- };
-};
-
-export const fetchCampaignData = (campaignId, callback) => {
- const user = getUser();
- return dispatch => {
- if (user && campaignId) {
- const userId = user.uid;
- let campaignData = {};
- // CAMPAIGN data
- getDoc(doc(db, "users", userId, "campaigns", campaignId))
- .then(campaign => {
- if (campaign.exists) {
- campaignData = updateObject(campaignData, campaign.data());
- // CARD data
- getDocs(collection(db, "users", userId, "campaigns", campaignId, "cards"))
- .then(cardSnapshot => {
- let cardCollection = {};
- cardSnapshot.forEach(card => {
- cardCollection = updateObject(cardCollection, {[card.id]: card.data()});
- });
- campaignData = updateObject(campaignData, {cards: cardCollection});
- // VIEW data
- getDocs(collection(db, "users", userId, "campaigns", campaignId, "views"))
- .then(viewSnapshot => {
- let viewCollection = {};
- viewSnapshot.forEach(view => {
- viewCollection = updateObject(viewCollection, {[view.id]: view.data()})
- });
- campaignData = updateObject(campaignData, {views: viewCollection});
- // LOAD DATA
- dispatch(actions.loadCampaignData(campaignData));
- if (callback) callback();
- console.log("[fetchCampaignData] success loading campaign");
- })
- .catch(err => console.log("[fetchCampaignData] error loading views:", err));
- })
- .catch(err => console.log("[fetchCampaignData] error loading cards:", err));
- } else console.log("[fetchCampaignData] campaign", campaignId, "does not exist for this user");
- })
- .catch(err => console.log("[fetchCampaignData] error loading campaign:", err));
- }
- };
-};
-
-export const saveCampaignData = (campaignId, campaignData, callback) => {
- const user = getUser();
- return dispatch => {
- if (user && campaignId) {
- console.log("[Status] saving. Triggered by save.");
- dispatch(actions.setStatus('saving'));
- const userId = user.uid;
- const batch = writeBatch(db);
- // CAMPAIGN data
- let campaignPackage = {...campaignData};
- delete campaignPackage.cards;
- delete campaignPackage.views;
- batch.set(
- doc(db, "users", userId, "campaigns", campaignId),
- campaignPackage
- );
- // CARD data
- for (let cardId in campaignData.cards) {
- batch.set(
- doc(db, "users", userId, "campaigns", campaignId, "cards", cardId),
- campaignData.cards[cardId]
- );
- }
- // VIEW data
- for (let viewId in campaignData.views) {
- batch.set(
- doc(db, "users", userId, "campaigns", campaignId, "views", viewId),
- campaignData.views[viewId]
- );
- }
- // SAVE DATA (BATCH COMMIT)
- batch.commit()
- .then(resp => {
- dispatch(actions.setCampaignEdit(false));
- if (callback) callback();
- console.log("[saveActiveCampaignData] success saving campaign");
- })
- .catch(err => {
- console.log("[saveActiveCampaignData] error saving campaign:", err);
- });
- }
- }
-};
-
-export const saveIntroProjectData = ({
- projectData,
- callback,
-}) => {
- const user = getUser();
- return dispatch => {
- if (user) {
- console.log("[Status] saving. Triggered by intro campaign save.");
- dispatch(actions.setStatus('saving'));
- const userId = user.uid;
- // CAMPAIGN data
- let campaignPackage = {...projectData};
- delete campaignPackage.cards;
- delete campaignPackage.views;
- addDoc(collection(db, "users", userId, "campaigns"), campaignPackage)
- .then(resp => {
- const campaignId = resp.id;
- if (campaignId) {
- const batch = writeBatch(db);
- // CARD data
- for (let cardId in projectData.cards) {
- batch.set(
- doc(db, "users", userId, "campaigns", campaignId, "cards", cardId),
- projectData.cards[cardId]
- );
- }
- // VIEW data
- for (let viewId in projectData.views) {
- batch.set(
- doc(db, "users", userId, "campaigns", campaignId, "views", viewId),
- projectData.views[viewId]
- );
- }
- batch.commit()
- .then(resp => {
- updateDoc(doc(db, "users", userId), {
- activeCampaignId: campaignId,
- })
- .then(resp => {
- dispatch(actions.setCampaignEdit(false));
- if (callback) {
- callback();
- }
- console.log("[saveIntroProjectData] success saving intro campaign");
- })
- .catch(err => {
- console.log("[saveIntroProjectData] error saving intro campaign (setting activeCampaignId):", err)
- });
- })
- .catch(err => {
- console.log("[saveIntroProjectData] error saving intro campaign (batching cards and views):", err)
- });
- }
- })
- .catch(err => {
- console.log("[saveIntroProjectData] error saving intro campaign (creating new campaign):", err)
- });
- }
- };
-};
-
-export const switchProject = ({
- projectId,
- callback,
-}) => {
- // Note: this does not save data to the server
- const user = getUser();
- return dispatch => {
- if (user) {
- const userId = user.uid;
- updateDoc(doc(db, "users", userId), {
- activeCampaignId: projectId,
- })
- .then(resp => {
- dispatch(actions.updActiveCampaignId(projectId));
- if (callback) {
- callback();
- }
- console.log("[switchProject] success loading activeCampaignId", projectId);
- })
- .catch(err => console.log("[switchProject] error loading activeCampaignId:", err))
- }
- };
-};
-
-export const createProject = (callback) => {
- const user = getUser();
- return dispatch => {
- if (user) {
- const userId = user.uid;
- const campaignData = {
- title: "untitled campaign",
- activeCardId: null, activeViewId: "view0",
- viewOrder: ["view0"],
- cardCreateCnt: 1, viewCreateCnt: 1,
- };
- // create the campaign
- addDoc(collection(db, "users", userId, "campaigns"), campaignData)
- .then(resp => {
- const campaignId = resp.id;
- if (campaignId) {
- // create the card
- setDoc(doc(db, "users", userId, "campaigns", campaignId, "cards", "card0"), {
- views: {
- view0: {
- pos: {x: 3*GRID.size, y: 3*GRID.size},
- size: {width: 8*GRID.size, height: 10*GRID.size},
- cardType: "card",
- },
- },
- title: "card0",
- color: "gray",
- content: {text: ""},
- }, {
- merge: true,
- })
- .then(resp => {
- // create the view
- setDoc(doc(db, "users", userId, "campaigns", campaignId, "views", "view0"), {
- title: "view0",
- color: "gray",
- }, {
- merge: true
- })
- .then(resp => {
- dispatch(actions.addCampaignToList(campaignId, "untitled campaign"));
- if (callback) {
- callback();
- }
- console.log("[createProject] added campaign:", campaignId);
- })
- .catch(err => console.log("[createProject] error creating view:", err));
- })
- .catch(err => console.log("[createProject] error creating card:", err));
- }
- })
- .catch(err => console.log("[createProject] error adding campaign:", err));
-
- }
- };
-};
-
-export const copyProject = ({
- projectId,
- callback,
-}) => {
- // should save before copy
- const user = getUser();
- return dispatch => {
- if (user) {
- const userId = user.uid;
- // fetch CAMPAIGN
- getDoc(doc(db, "users", userId, "campaigns", projectId))
- .then(campaign => {
- if (campaign.exists) {
- let campaignData = campaign.data();
- campaignData.title = campaignData.title + " (copy)";
- // copy CAMPAIGN
- addDoc(collection(db, "users", userId, "campaigns"), campaignData)
- .then(resp => {
- console.log("[copyProject] copied campaign level info");
- const copiedCampaignId = resp.id;
- if (copiedCampaignId) {
- // fetch CARDS
- getDocs(collection(db, "users", userId, "campaigns", projectId, "cards"))
- .then(cardSnapshot => {
- // copy CARDS
- const cardBatch = writeBatch(db);
- cardSnapshot.forEach(card => {
- cardBatch.set(
- doc(db, "users", userId, "campaigns", copiedCampaignId, "cards", card.id),
- card.data()
- );
- });
- cardBatch.commit()
- .then(resp => {
- console.log("[copyProject] copied cards");
- // fetch VIEWS
- getDocs(collection(db, "users", userId, "campaigns", projectId, "views"))
- .then(viewSnapshot => {
- // copy VIEWS
- const viewBatch = writeBatch(db);
- viewSnapshot.forEach(view => {
- viewBatch.set(
- doc(db, "users", userId, "campaigns", copiedCampaignId, "views", view.id),
- view.data()
- );
- })
- viewBatch.commit()
- .then(resp => {
- console.log("[copyProject] copied views");
- // CLEANUP
- dispatch(fetchCampaignList());
- if (callback) {
- callback();
- }
- })
- .catch(err => console.log("[copyProject] error copying view data"))
- })
- .catch(err => console.log("[copyProject] error fetching view data"));
- })
- .catch(err => console.log("[copyProject] error copying card data"))
- })
- .catch(err => console.log("[copyProject] error fetching card data"))
- }
- })
- .catch(err => console.log("[copyProject] error copying campaign data"));
- }
- })
- .catch(err => console.log("[copyProject] error fetching campaign data"));
- }
- };
-};
-
-export const destroyProject = ({
- projectId,
- callback,
-}) => {
- const user = getUser();
- return dispatch => {
- if (user && projectId) {
- const userId = user.uid;
- deleteDoc(doc(db, "users", userId, "campaigns", projectId))
- .then(resp => {
- dispatch(actions.removeCampaignFromList(projectId));
- if (callback) {
- callback();
- }
- console.log("[destroyProject] success deleting campaign", projectId);
- })
- .catch(err => console.log("[destroyProject] error deleting campaign:", err));
- }
- };
-};
\ No newline at end of file
diff --git a/src/store/firestoreIndex.js b/src/store/firestoreIndex.js
deleted file mode 100644
index b81e462..0000000
--- a/src/store/firestoreIndex.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/* TODO: redux refactor
- Files under /data will be the future store for dmkit.
- Get these files to work with the app.
- Files under /store to be removed.
-*/
-
-// USER AUTH API
-export {
- updateDisplayName,
-
- emailSignIn, emailSignOut,
- googleSignIn, facebookSignIn,
-
- emailSignUp, sendVerificationToEmail, sendPasswordResetToEmail,
-} from './firestoreAPI/authTransactions';
-
-export {
- fetchActiveCampaignId, fetchCampaignList,
- fetchCampaignData, saveCampaignData, saveIntroProjectData,
- switchProject,
- createProject, copyProject, destroyProject,
-} from './firestoreAPI/storeTransactions';
diff --git a/src/store/reducer/campaignData.js b/src/store/reducer/campaignData.js
deleted file mode 100644
index 1870df4..0000000
--- a/src/store/reducer/campaignData.js
+++ /dev/null
@@ -1,526 +0,0 @@
-import * as actionTypes from '../actionTypes';
-import { GRID } from '../../styles/constants';
-import { updateObject } from '../../utils/updateObject';
-
-// The campaignData reducer mirrors the campaign data structure on firebase
-
-// const exampleReducerStructure =
-// {
-// title: "campaign title",
-// activeViewId: "view0",
-// viewOrder: ["view0"],
-// cardCreateCnt: 1,
-// viewCreateCnt: 1,
-// cards: {
-// card0: {
-// views: {
-// view0: {
-// pos: {x: "card x-position", y: "card y-position"},
-// size: {width: "card width", height: "card height"},
-// cardForm: "card or bubble",
-// },
-// },
-// title: "card title",
-// color: "card color",
-// content: {
-// text: "card text",
-// },
-// },
-// },
-// views: {
-// view0: {
-// pos: {x: "view display x-position", y: "view display x-position"},
-// title: "view title",
-// color: "view color",
-// },
-// },
-// }
-
-const reducer = (state = {}, action) => {
- switch (action.type) {
- case actionTypes.LOAD_CAMPAIGN_DATA: return action.campaignData;
- case actionTypes.UNLOAD_CAMPAIGN_DATA: return {};
- case actionTypes.LOAD_INTRO_CAMPAIGN: return {...introCampaign};
-
- // CAMPAIGN
- case actionTypes.UPD_CAMPAIGN_TITLE: return updateObject(state, {title: action.title});
- case actionTypes.UPD_ACTIVE_VIEW_ID: return updateObject(state, {activeViewId: action.activeViewId});
- case actionTypes.SHIFT_VIEW_IN_VIEW_ORDER: return shiftViewInViewOrder(state, action.shiftedViewId, action.posShift);
-
- // CARD
- case actionTypes.CREATE_CARD: return createCard(state);
- case actionTypes.COPY_CARD: return copyCard(state, action.cardId);
- case actionTypes.DESTROY_CARD: return destroyCard(state, action.cardId);
- case actionTypes.LINK_CARD_TO_VIEW: return linkCardToView(state, action.cardId, action.pos);
- case actionTypes.UNLINK_CARD_FROM_VIEW: return unlinkCardFromView(state, action.cardId);
- case actionTypes.UPD_CARD_POS: return updCardPos(state, action.cardId, action.pos);
- case actionTypes.UPD_CARD_SIZE: return updCardSize(state, action.cardId, action.size);
- case actionTypes.UPD_CARD_FORM: return updCardForm(state, action.cardId, action.cardForm);
- case actionTypes.UPD_CARD_TITLE: return updCardTitle(state, action.cardId, action.title);
- case actionTypes.UPD_CARD_COLOR: return updCardColor(state, action.cardId, action.color);
- case actionTypes.UPD_CARD_COLOR_FOR_VIEW: return updCardColorForView(state, action.cardId, action.color);
- case actionTypes.UPD_CARD_TEXT: return updCardText(state, action.cardId, action.text);
-
- // VIEW
- case actionTypes.CREATE_VIEW: return createView(state);
- case actionTypes.DESTROY_VIEW: return destroyView(state, action.viewId);
- case actionTypes.LOCK_ACTIVE_VIEW: return lockActiveView(state);
- case actionTypes.UNLOCK_ACTIVE_VIEW: return unlockActiveView(state);
- case actionTypes.UPD_ACTIVE_VIEW_POS: return updActiveViewPos(state, action.pos);
- case actionTypes.UPD_ACTIVE_VIEW_SCALE: return updActiveViewScale(state, action.scale);
- case actionTypes.RESET_ACTIVE_VIEW: return resetActiveView(state);
- case actionTypes.UPD_VIEW_TITLE: return updViewTitle(state, action.viewId, action.title);
- case actionTypes.UPD_VIEW_COLOR: return updViewColor(state, action.viewId, action.color);
-
- default: return state;
- }
-};
-
-// CAMPAIGN
-const shiftViewInViewOrder = (state, shiftedViewId, posShift) => {
- let updatedViewOrder = [...state.viewOrder];
- const newPos = updatedViewOrder.indexOf(shiftedViewId) + posShift;
- updatedViewOrder = updatedViewOrder.filter(id => id !== shiftedViewId);
- updatedViewOrder.splice(newPos, 0, shiftedViewId);
- return updateObject(state, {
- viewOrder: updatedViewOrder,
- });
-};
-
-// CARD
-const createCard = (state) => {
- if (!state.activeViewId) return state;
- const newCardId = "card"+state.cardCreateCnt;
- return updateObject(state, {
- activeCardId: newCardId,
- cardCreateCnt: state.cardCreateCnt + 1,
- cards: {
- ...state.cards,
- [newCardId]: {
- views: {
- [state.activeViewId]: {
- pos: {x: 3*GRID.size, y: 3*GRID.size},
- size: {width: 8*GRID.size, height: 10*GRID.size},
- cardType: "card",
- },
- },
- title: newCardId,
- color: "gray",
- content: {text: ""},
- },
- },
- });
-};
-
-const copyCard = (state, cardId) => {
- // this copies everything from the other card except it only appears in the activeView
- if (!state.activeViewId) return state;
- const newCardId = "card"+state.cardCreateCnt;
- return updateObject(state, {
- activeCardId: newCardId,
- cardCreateCnt: state.cardCreateCnt + 1,
- cards: {
- ...state.cards,
- [newCardId]: {
- ...state.cards[cardId],
- views: {
- [state.activeViewId]: {
- ...state.cards[cardId].views[state.activeViewId],
- pos: {
- x: state.cards[cardId].views[state.activeViewId].pos.x + GRID.size,
- y: state.cards[cardId].views[state.activeViewId].pos.y + GRID.size,
- },
- },
- },
- },
- },
- });
-};
-
-const destroyCard = (state, cardId) => {
- let updatedCards = {...state.cards};
- delete updatedCards[cardId];
- return updateObject(state, {
- activeCardId: cardId === state.activeCardId ? null : state.activeCardId,
- cards: updatedCards,
- });
-};
-
-const linkCardToView = (state, cardId, pos) => {
- if (!state.activeViewId) return state;
- return updateObject(state, {
- activeCardId: cardId,
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- views: {
- ...state.cards[cardId].views,
- [state.activeViewId]: {
- pos: pos,
- size: {width: 8*GRID.size, height: 10*GRID.size},
- cardType: "card",
- },
- },
- },
- },
- });
-};
-
-const unlinkCardFromView = (state, cardId) => {
- if (!state.activeViewId) return state;
- let updatedCardViews = {...state.cards[cardId].views};
- delete updatedCardViews[state.activeViewId];
- return updateObject(state, {
- activeCardId: null,
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- views: updatedCardViews,
- },
- },
- });
-};
-
-const updCardPos = (state, cardId, newPos) => {
- if (!state.activeViewId) return state;
- const roundedPos = {
- x: Math.round(newPos.x / GRID.size) * GRID.size,
- y: Math.round(newPos.y / GRID.size) * GRID.size
- };
- return updateObject(state, {
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- views: {
- ...state.cards[cardId].views,
- [state.activeViewId]: {
- ...state.cards[cardId].views[state.activeViewId],
- pos: roundedPos,
- },
- },
- },
- },
- });
-};
-
-const updCardSize = (state, cardId, newSize) => {
- if (!state.activeViewId) return state;
- const roundedSize = {
- width: (Math.round(newSize.width.split("px").shift() / GRID.size) * GRID.size) + "px",
- height: (Math.round(newSize.height.split("px").shift() / GRID.size) * GRID.size) + "px"
- };
- return updateObject(state, {
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- views: {
- ...state.cards[cardId].views,
- [state.activeViewId]: {
- ...state.cards[cardId].views[state.activeViewId],
- size: roundedSize,
- },
- },
- },
- },
- });
-};
-
-const updCardColor = (state, cardId, color) => {
- return updateObject(state, {
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- color: color,
- },
- },
- });
-};
-
-const updCardColorForView = (state, cardId, color) => {
- if (!state.activeViewId) return state;
- return updateObject(state, {
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- views: {
- ...state.cards[cardId].views,
- [state.activeViewId]: {
- ...state.cards[cardId].views[state.activeViewId],
- color: color,
- },
- },
- },
- },
- });
-};
-
-const updCardForm = (state, cardId, cardForm) => {
- if (!state.activeViewId) return state;
- return updateObject(state, {
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- views: {
- ...state.cards[cardId].views,
- [state.activeViewId]: {
- ...state.cards[cardId].views[state.activeViewId],
- cardForm: cardForm,
- },
- },
- },
- },
- });
-};
-
-const updCardTitle = (state, cardId, title) => {
- return updateObject(state, {
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- title: title,
- },
- },
- });
-};
-
-const updCardText = (state, cardId, text) => {
- return updateObject(state, {
- cards: {
- ...state.cards,
- [cardId]: {
- ...state.cards[cardId],
- content: {
- ...state.cards[cardId].content,
- text: text,
- },
- },
- },
- });
-};
-
-// VIEW
-const createView = (state) => {
- const newViewId = "tab"+state.viewCreateCnt;
- let updatedViewOrder = [...state.viewOrder];
- const pos = state.activeViewId ? updatedViewOrder.indexOf(state.activeViewId) + 1 : 0;
- updatedViewOrder.splice(pos, 0, newViewId);
- return updateObject(state, {
- activeViewId: newViewId,
- viewOrder: updatedViewOrder,
- viewCreateCnt: state.viewCreateCnt + 1,
- views: {
- ...state.views,
- [newViewId]: {
- pos: { x: 0, y: 0 },
- scale: 1,
- lock: true,
- color: "gray",
- title: newViewId,
- },
- },
- });
-};
-
-const destroyView = (state, viewId) => {
- let updatedViews = {...state.views};
- delete updatedViews[viewId];
- const updatedViewOrder = [...state.viewOrder].filter(id => id !== viewId);
- let updatedActiveViewId = state.activeViewId;
- if (viewId === updatedActiveViewId) {
- const viewIndex = state.viewOrder.indexOf(viewId)
- if (viewIndex === 0) {
- updatedActiveViewId = state.viewOrder[1];
- } else {
- updatedActiveViewId = state.viewOrder[viewIndex - 1];
- }
- }
- return updateObject(state, {
- activeViewId: updatedActiveViewId,
- viewOrder: updatedViewOrder,
- views: updatedViews,
- });
-};
-
-const lockActiveView = (state) => {
- if (!state.activeViewId) return state;
- return updateObject(state, {
- views: {
- ...state.views,
- [state.activeViewId]: {
- ...state.views[state.activeViewId],
- lock: true,
- },
- },
- });
-};
-
-const unlockActiveView = (state) => {
- if (!state.activeViewId) return state;
- return updateObject(state, {
- views: {
- ...state.views,
- [state.activeViewId]: {
- ...state.views[state.activeViewId],
- lock: false,
- },
- },
- });
-};
-
-const updActiveViewPos = (state, pos) => {
- if (!state.activeViewId) return state;
- return updateObject(state, {
- views: {
- ...state.views,
- [state.activeViewId]: {
- ...state.views[state.activeViewId],
- pos: pos,
- },
- },
- });
-};
-
-const updActiveViewScale = (state, scale) => {
- if (!state.activeViewId) return state;
- return updateObject(state, {
- views: {
- ...state.views,
- [state.activeViewId]: {
- ...state.views[state.activeViewId],
- scale: scale,
- },
- },
- });
-};
-
-const resetActiveView = (state) => {
- if (!state.activeViewId) return state;
- return updateObject(state, {
- views: {
- ...state.views,
- [state.activeViewId]: {
- ...state.views[state.activeViewId],
- pos: { x: 0, y: 0 },
- scale: 1,
- },
- },
- });
-};
-
-const updViewColor = (state, viewId, color) => {
- return updateObject(state, {
- views: {
- ...state.views,
- [viewId]: {
- ...state.views[viewId],
- color: color,
- },
- },
- });
-};
-
-const updViewTitle = (state, viewId, title) => {
- return updateObject(state, {
- views: {
- ...state.views,
- [viewId]: {
- ...state.views[viewId],
- title: title,
- },
- },
- });
-};
-
-// INTRO CAMPAIGN STATE
-const introCampaign = {
- title: "DM Kit",
- activeCardId: null,
- activeViewId: "view0",
- viewOrder: ["view0", "view1"],
- cardCreateCnt: 4,
- viewCreateCnt: 2,
- cards: {
- card0: {
- views: {
- view0: {
- pos: {x: 5*GRID.size, y: 7*GRID.size},
- size: {width: 16*GRID.size, height: 10*GRID.size},
- cardForm: "card",
- },
- },
- title: "Greetings Traveler!",
- color: "jungle",
- content: {
- text: "Welcome to DM Kit, a tool to help plan your next adventure. Take a look at the READ ME tab for more information on functions. If you would like to save your work, please create an account!",
- },
- },
- card1: {
- views: {
- view1: {
- pos: {x: 0, y: 0},
- size: {width: 8*GRID.size, height: 9*GRID.size},
- cardForm: "card",
- },
- },
- title: "Tools",
- color: "cotton_blue",
- content: {
- text: "Use the buttons to build your project. You can add cards, copy cards, reset the board position. You can also save your progress, but you must first create an account.",
- },
- },
- card2: {
- views: {
- view1: {
- pos: {x: 4*GRID.size, y: 20*GRID.size},
- size: {width: 10*GRID.size, height: 5*GRID.size},
- cardForm: "card",
- },
- },
- title: "Tabs",
- color: "cobalt",
- content: {
- text: "Use the buttons below to add tabs and switch between them.",
- },
- },
- card3: {
- views: {
- view1: {
- pos: {x: 25*GRID.size, y: 3*GRID.size},
- size: {width: 10*GRID.size, height: 10*GRID.size},
- cardForm: "card",
- },
- },
- title: "Library",
- color: "lavender",
- content: {
- text: "All the cards you create are stored in the library, which you can access by clicking the book to the right. The same card can be placed in multiple views and edited from multiple places.",
- },
- },
- },
- views: {
- view0: {
- pos: { x: 0, y: 0 },
- scale: 1,
- lock: true,
- color: "green",
- title: "Welcome!",
- },
- view1: {
- pos: { x: 0, y: 0 },
- scale: 1,
- lock: true,
- color: "blue",
- title: "READ ME",
- },
- },
-};
-
-export default reducer;
\ No newline at end of file
diff --git a/src/store/reducer/sessionManager.js b/src/store/reducer/sessionManager.js
deleted file mode 100644
index 3a18d36..0000000
--- a/src/store/reducer/sessionManager.js
+++ /dev/null
@@ -1,155 +0,0 @@
-import * as actionTypes from '../actionTypes';
-import { updateObject } from '../../utils/updateObject';
-
-const initialState = {
- campaignList: {
- // campaignId: campaignTitle,
- },
- activeCampaignId: null,
- activeCardId: null,
-
- popup: {
- id: '',
- type: '',
- },
-
- status: 'loading', // idle, loading or saving
- campaignEdit: false, // flag for any unsaved changes
- introCampaignEdit: false,
-
- errorPasswordReset: '',
- errorEmailSignIn: '',
- errorEmailSignUp: '',
- errorGoogleSignIn: '',
- errorFacebookSignIn: '',
-};
-
-const reducer = (state = initialState, action) => {
- switch (action.type) {
- case actionTypes.RESET_SESSION_MANAGER: return {...initialState};
-
- // CAMPAIGN RELATED
- case actionTypes.LOAD_CAMPAIGN_LIST: return updateObject(state, {campaignList: action.campaignList});
- case actionTypes.ADD_CAMPAIGN_TO_LIST: return addCampaignToList(state, action.campaignId, action.campaignTitle);
- case actionTypes.REMOVE_CAMPAIGN_FROM_LIST: return removeCampaignFromList(state, action.campaignId);
- case actionTypes.UPD_CAMPAIGN_ON_LIST: return updCampaignOnList(state, action.title);
- case actionTypes.UPD_ACTIVE_CAMPAIGN_ID: return updateObject(state, {activeCampaignId: action.activeCampaignId});
- case actionTypes.UPD_ACTIVE_CARD_ID: return updateObject(state, {activeCardId: action.activeCardId});
-
- case actionTypes.SET_STATUS: return updateObject(state, {status: action.status});
- case actionTypes.SET_CAMPAIGN_EDIT: return updateObject(state, {campaignEdit: action.edit});
- case actionTypes.SET_INTRO_CAMPAIGN_EDIT: return updateObject(state, {introCampaignEdit: action.edit});
-
- // POP UP
- case actionTypes.RESET_POPUP: return updateObject(state, {popup: {id: '', type: ''}});
- case actionTypes.SET_POPUP: return updateObject(state, {popup: action.popup});
-
- // ERRORS
- case actionTypes.SET_ERROR_PASSWORD_RESET: return setErrorPasswordReset(state, action.errorCode);
- case actionTypes.UNSET_ERROR_PASSWORD_RESET: return updateObject(state, {errorPasswordReset: ""});
- case actionTypes.SET_ERROR_EMAIL_SIGN_IN: return setErrorEmailSignIn(state, action.errorCode);
- case actionTypes.UNSET_ERROR_EMAIL_SIGN_IN: return updateObject(state, {errorEmailSignIn: ""});
- case actionTypes.SET_ERROR_EMAIL_SIGN_UP: return setErrorEmailSignUp(state, action.errorCode);
- case actionTypes.UNSET_ERROR_EMAIL_SIGN_UP: return updateObject(state, {errorEmailSignUp: ""});
- case actionTypes.SET_ERROR_GOOGLE_SIGN_IN: return setErrorGoogleSignIn(state, action.errorCode);
- case actionTypes.UNSET_ERROR_GOOGLE_SIGN_IN: return updateObject(state, {errorGoogleSignIn: ""});
- case actionTypes.SET_ERROR_FACEBOOK_SIGN_IN: return setErrorFaceboookSignIn(state, action.errorCode);
- case actionTypes.UNSET_ERROR_FACEBOOK_SIGN_IN: return updateObject(state, {errorFacebookSignIn: ""});
-
- default: return state;
- }
-};
-
-const addCampaignToList = (state, campaignId, campaignTitle) => {
- let updatedCampaignList = {...state.campaignList};
- updatedCampaignList = updateObject(updatedCampaignList, {[campaignId]: campaignTitle});
- const updatedState = {
- campaignList: updatedCampaignList,
- activeCampaignId: campaignId,
- };
- return updateObject(state, updatedState);
-};
-
-const removeCampaignFromList = (state, campaignId) => {
- let updatedCampaignList = {...state.campaignList};
- delete updatedCampaignList[campaignId];
- const updatedState = {
- campaignList: updatedCampaignList,
- activeCampaignId: campaignId === state.activeCampaignId ? null : state.activeCampaignId,
- };
- return updateObject(state, updatedState);
-};
-
-const updCampaignOnList = (state, title) => {
- let updatedCampaignList = {...state.campaignList};
- updatedCampaignList[state.activeCampaignId] = title;
- const updatedState = {
- campaignList: updatedCampaignList,
- };
- return updateObject(state, updatedState);
-};
-
-//ERRORS
-const setErrorPasswordReset = (state, errorCode) => {
- // error codes for firebase method Auth.sendPasswordResetToEmail
- switch (errorCode) {
- case ('auth/invalid-email'): return updateObject(state, {errorPasswordReset: "email address is not valid"});
- case ('auth/user-not-found'): return updateObject(state, {errorPasswordReset: "user does not exist"});
- // other cases: auth/missing-android-pkg-name, auth/missing-continue-uri, auth/missing-ios-bundle-id, auth/invalid-continue-uri, auth/unauthorized-continue-uri
- default: return updateObject(state, {errorPasswordReset: "could not send password reset email"});
- }
-};
-
-const setErrorEmailSignIn = (state, errorCode) => {
- // error codes for firebase method Auth.signInWithEmailAndPassword
- switch (errorCode) {
- case ('auth/invalid-email'): return updateObject(state, {errorEmailSignIn: "invalid email"});
- case ('auth/user-disabled'): return updateObject(state, {errorEmailSignIn: "user disabled"});
- case ('auth/user-not-found'): return updateObject(state, {errorEmailSignIn: "user not found"});
- case ('auth/wrong-password'): return updateObject(state, {errorEmailSignIn: "invalid password"});
- default: return updateObject(state, {errorEmailSignIn: "sign in unsuccessful"});
- }
-};
-
-const setErrorEmailSignUp = (state, errorCode) => {
- // error codes for firebase method Auth.createUserWithEmailAndPassword
- switch (errorCode) {
- case ('auth/email-already-in-use'): return updateObject(state, {errorEmailSignUp: "email already in use"});
- case ('auth/invalid-email'): return updateObject(state, {errorEmailSignUp: "invalid email"});
- case ('auth/operation-not-allowed'): return updateObject(state, {errorEmailSignUp: "email sign up currently not in service"});
- case ('auth/weak-password'): return updateObject(state, {errorEmailSignUp: "password must be at least 6 characters long"});
- default: return updateObject(state, {errorEmailSignUp: "email sign up unsuccessful"});
- }
-};
-
-const setErrorGoogleSignIn = (state, errorCode) => {
- // error codes for firebase method Auth.signInWithPopUp
- switch (errorCode) {
- case ('auth/account-exists-with-different-credential'): return updateObject(state, {errorGoogleSignIn: "account for this email already exists"});
- case ('auth/auth-domain-config-required'): return updateObject(state, {errorGoogleSignIn: "missing authorization configuration"});
- case ('auth/cancelled-popup-request'): return updateObject(state, {errorGoogleSignIn: "too many sign in popups attempted"});
- case ('auth/operation-not-allowed'): return updateObject(state, {errorGoogleSignIn: "operation not allowed"});
- case ('auth/operation-not-supported-in-this-environment'): return updateObject(state, {errorGoogleSignIn: "operation not supported"});
- case ('auth/popup-blocked'): return updateObject(state, {errorGoogleSignIn: "sign in popup blocked"});
- // case ('auth/popup-closed-by-user'): return updateObject(state, {errorGoogleSignIn: "sign in popup closed"});
- case ('auth/unauthorized-domain'): return updateObject(state, {errorGoogleSignIn: "unauthorized domain"});
- default: return state;
- }
-};
-
-const setErrorFaceboookSignIn = (state, errorCode) => {
- // error codes for firebase method Auth.signInWithPopUp
- switch (errorCode) {
- case ('auth/account-exists-with-different-credential'): return updateObject(state, {errorFacebookSignIn: "account for this email already exists"});
- case ('auth/auth-domain-config-required'): return updateObject(state, {errorFacebookSignIn: "missing authorization configuration"});
- case ('auth/cancelled-popup-request'): return updateObject(state, {errorFacebookSignIn: "too many sign in popups attempted"});
- case ('auth/operation-not-allowed'): return updateObject(state, {errorFacebookSignIn: "operation not allowed"});
- case ('auth/operation-not-supported-in-this-environment'): return updateObject(state, {errorFacebookSignIn: "operation not supported"});
- case ('auth/popup-blocked'): return updateObject(state, {errorFacebookSignIn: "sign in popup blocked"});
- case ('auth/popup-closed-by-user'): return updateObject(state, {errorFacebookSignIn: "sign in popup closed"});
- case ('auth/unauthorized-domain'): return updateObject(state, {errorFacebookSignIn: "unauthorized domain"});
- default: return state;
- }
-};
-
-export default reducer;
diff --git a/src/store/reducer/userData.js b/src/store/reducer/userData.js
deleted file mode 100644
index 0765f27..0000000
--- a/src/store/reducer/userData.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import * as actionTypes from '../actionTypes';
-import { updateObject } from '../../utils/updateObject';
-
-const initialState = {
- userId: null,
- displayName: null,
- email: null,
- emailVerified: null,
- emailVerificationRequired: null,
-};
-
-const reducer = (state = initialState, action) => {
- switch(action.type) {
- case actionTypes.LOAD_USER: return loadUser(state, action.user);
- case actionTypes.UNLOAD_USER: return {...initialState};
-
- case actionTypes.UPD_USER_DISPLAYNAME: return updateObject(state, {displayName: action.displayName});
-
- default: return state;
- }
-};
-
-const loadUser = (state, user) => {
- return updateObject(state, {
- userId: user.uid,
- displayName: user.displayName,
- email: user.email,
- emailVerified: user.emailVerified,
- emailVerificationRequired: user.providerData.map(provider => provider.providerId).includes('password'),
- providerId: user.providerId,
- providerData: user.providerData,
- });
-};
-
-export default reducer;
\ No newline at end of file
diff --git a/src/utils/useOutsideClick.js b/src/utils/useOutsideClick.js
index 1553d1a..89624fc 100644
--- a/src/utils/useOutsideClick.js
+++ b/src/utils/useOutsideClick.js
@@ -1,7 +1,7 @@
import {useEffect} from 'react';
export const useOutsideClick = (refs, condition, handler) => {
- // refs contains an array of references that the user can click without triggering func
+ // refs contains an array of references that the user can click without logginging func
useEffect(() => {
const handleClickOutside = (event) => {
let runHandler = true;
@@ -25,7 +25,6 @@ export const useOutsideClick = (refs, condition, handler) => {
// adding refs and handler as dependencies below causes an issue with re-renders
// these re-renders keep other useOutsideClicks from completing their run
- // TODO figure out a way around this for exhaustive-deps
}, [condition]);
};