|
@@ -0,0 +1,287 @@
|
|
|
|
+import {createStore, combineReducers, applyMiddleware} from "redux";
|
|
|
|
+import thunk from "redux-thunk";
|
|
|
|
+
|
|
|
|
+const getGQL = function(url) {
|
|
|
|
+ return async function(query, variables) {
|
|
|
|
+ const res = await fetch(url, {
|
|
|
|
+ method: "POST",
|
|
|
|
+ headers: {
|
|
|
|
+ "Content-Type": "application/json",
|
|
|
|
+ ...(localStorage.authToken ? { "Authorization": "Bearer " + localStorage.authToken } : {})
|
|
|
|
+ },
|
|
|
|
+ body: JSON.stringify({ query, variables })
|
|
|
|
+ });
|
|
|
|
+ const data = await res.json();
|
|
|
|
+ if (data.data) {
|
|
|
|
+ return Object.values(data.data)[0];
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ throw new Error(JSON.stringify(data.errors));
|
|
|
|
+ };
|
|
|
|
+ };
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const backendURL = 'http://marketplace.asmer.fs.a-level.com.ua';
|
|
|
|
+
|
|
|
|
+const gql = getGQL(backendURL + '/graphql');
|
|
|
|
+
|
|
|
|
+const promiseReducer = function(state={}, {type, name, status, payload, error}) {
|
|
|
|
+ if (type == 'PROMISE'){
|
|
|
|
+ return {
|
|
|
|
+ ...state,
|
|
|
|
+ [name]:{status, payload, error}
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return state;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const actionPending = name => ({type: "PROMISE", name, status: 'PENDING'});
|
|
|
|
+const actionFulfilled = (name,payload) => ({type: "PROMISE", name, status: 'FULFILLED', payload});
|
|
|
|
+const actionRejected = (name,error) => ({type: "PROMISE", name, status: 'REJECTED', error});
|
|
|
|
+const actionPromise = function(name, promise) {
|
|
|
|
+ return async dispatch => {
|
|
|
|
+ dispatch(actionPending(name));
|
|
|
|
+ try {
|
|
|
|
+ let payload = await promise
|
|
|
|
+ dispatch(actionFulfilled(name, payload))
|
|
|
|
+ return payload
|
|
|
|
+ }
|
|
|
|
+ catch(error){
|
|
|
|
+ dispatch(actionRejected(name, error))
|
|
|
|
+ };
|
|
|
|
+ };
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+let jwtDecode = function(token) {
|
|
|
|
+ let payloadInBase64;
|
|
|
|
+ let payloadInJson;
|
|
|
|
+ let payload;
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ payloadInBase64 = token.split(".")[1];
|
|
|
|
+ payloadInJson = atob(payloadInBase64);
|
|
|
|
+ payload = JSON.parse(payloadInJson);
|
|
|
|
+
|
|
|
|
+ return payload;
|
|
|
|
+ }
|
|
|
|
+ catch(err) {
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const authReducer = function(state, {type, token}) {
|
|
|
|
+ let payload;
|
|
|
|
+
|
|
|
|
+ if (state == undefined) {
|
|
|
|
+ if(localStorage.authToken) {
|
|
|
|
+ type = "AUTH_LOGIN";
|
|
|
|
+ token = localStorage.authToken;
|
|
|
|
+ } else {
|
|
|
|
+ type = "AUTH_LOGOUT";
|
|
|
|
+ };
|
|
|
|
+ };
|
|
|
|
+ if (type == "AUTH_LOGIN") {
|
|
|
|
+ payload = jwtDecode(token);
|
|
|
|
+
|
|
|
|
+ if(payload) {
|
|
|
|
+ localStorage.authToken = token;
|
|
|
|
+ return {
|
|
|
|
+ token: token,
|
|
|
|
+ payload: payload
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ return {
|
|
|
|
+ error: true
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ };
|
|
|
|
+ if (type == "AUTH_LOGOUT") {
|
|
|
|
+ localStorage.removeItem("authToken");
|
|
|
|
+
|
|
|
|
+ return {};
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ return state || {};
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const actionAuthLogin = token => ({type: "AUTH_LOGIN", token});
|
|
|
|
+const actionAuthLogout = () => ({type: "AUTH_LOGOUT"});
|
|
|
|
+const actionFullLogin = function(login, password) {
|
|
|
|
+ return async dispatch => {
|
|
|
|
+ let token = await gql(`query userLogin($login: String!, $password: String!) {
|
|
|
|
+ login(login: $login, password: $password)
|
|
|
|
+ }`, {
|
|
|
|
+ "login": login,
|
|
|
|
+ "password": password
|
|
|
|
+ });
|
|
|
|
+ dispatch(actionAuthLogin(token));
|
|
|
|
+ };
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const actionFullRegister = function(login, password) {
|
|
|
|
+ return async dispatch => {
|
|
|
|
+ await dispatch(actionPromise("userRegister", gql(`mutation userRegister($login: String!, $password: String!) {
|
|
|
|
+ createUser(user: {login: $login, password: $password}) {
|
|
|
|
+ _id login
|
|
|
|
+ }
|
|
|
|
+ }`,
|
|
|
|
+ {
|
|
|
|
+ "login": login,
|
|
|
|
+ "password": password
|
|
|
|
+ })));
|
|
|
|
+ dispatch(actionFullLogin(login, password));
|
|
|
|
+ };
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const actionChangePassword = function(login, password, newPassword) {
|
|
|
|
+ return async dispatch => {
|
|
|
|
+ await dispatch(actionPromise("userChangPassword", gql(`mutation userChangePassword($login: String!, $password: String!, $newPassword: String!) {
|
|
|
|
+ changePassword(login: $login, password: $password, newPassword: $newPassword) {
|
|
|
|
+ _id login
|
|
|
|
+ }
|
|
|
|
+ }`, {
|
|
|
|
+ "login": login,
|
|
|
|
+ "password": password,
|
|
|
|
+ "newPassword": newPassword
|
|
|
|
+ })));
|
|
|
|
+ dispatch(actionFullLogin(login, newPassword));
|
|
|
|
+ };
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const actionAdById = function(_id) {
|
|
|
|
+ return actionPromise("adById", gql(`query adFindById($query: String) {
|
|
|
|
+ AdFindOne(query: $query) {
|
|
|
|
+ _id createdAt title description address price tags
|
|
|
|
+ owner {
|
|
|
|
+ _id login
|
|
|
|
+ avatar {
|
|
|
|
+ _id url
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ images {
|
|
|
|
+ _id url
|
|
|
|
+ }
|
|
|
|
+ comments {
|
|
|
|
+ _id text createdAt
|
|
|
|
+ owner {
|
|
|
|
+ _id login
|
|
|
|
+ avatar {
|
|
|
|
+ _id url
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }`, {
|
|
|
|
+ query: JSON.stringify([{"_id": _id}])
|
|
|
|
+ }));
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const actionNewComment = function(text, adId, answerToId) {
|
|
|
|
+ return async dispatch => {
|
|
|
|
+ await dispatch(actionPromise("newComment", gql(`mutation newComment($text: String, $adId: ID, $answerToId: ID) {
|
|
|
|
+ CommentUpsert(comment: {text: $text, ad: {_id: $adId}, answerTo: {_id: $answerToId}}) {
|
|
|
|
+ _id
|
|
|
|
+ }
|
|
|
|
+ }`, {
|
|
|
|
+ text: text,
|
|
|
|
+ adId: adId,
|
|
|
|
+ answerToId: answerToId
|
|
|
|
+ })));
|
|
|
|
+ dispatch(actionAdById(adId))
|
|
|
|
+ };
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const uploadFile = async function(file) {
|
|
|
|
+ let fd = new FormData();
|
|
|
|
+
|
|
|
|
+ fd.append("photo", file);
|
|
|
|
+
|
|
|
|
+ const res = await fetch(`${backendURL}/upload`, {
|
|
|
|
+ method: "POST",
|
|
|
|
+ headers: localStorage.authToken ? { Authorization: 'Bearer ' + localStorage.authToken } : {},
|
|
|
|
+ body: fd
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ return res.json();
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const actionUploadFiles = function(files) {
|
|
|
|
+ return actionPromise("uploadFiles", Promise.all(files.map(file => uploadFile(file))));
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const actionSaveAdState = function(state) {
|
|
|
|
+ return actionPromise("newAd", gql(`mutation newAd($ad: AdInput) {
|
|
|
|
+ AdUpsert(ad: $ad) {
|
|
|
|
+ _id
|
|
|
|
+ }
|
|
|
|
+ }`, {
|
|
|
|
+ ad: {...state, images: state.images.map(imag => ({_id: imag._id}))}
|
|
|
|
+ }));
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const endlessScrollReducer = function(state={skip: 0, arr: []}, {type, arr, limit}) {
|
|
|
|
+ if(type == "add") {
|
|
|
|
+ return {skip: state.skip + limit,
|
|
|
|
+ arr: [...state.arr, ...arr]}
|
|
|
|
+ };
|
|
|
|
+ if(type == "clear") {
|
|
|
|
+ return {skip: 0, arr: []}
|
|
|
|
+ }
|
|
|
|
+ return state;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const actionEndlessScrollAdd = (arr, limit) => ({type: "add", arr, limit});
|
|
|
|
+const actionEndlessScrollClear = () => ({type: "clear"});
|
|
|
|
+const actionEnndlessScrollAdFind = function(_id, limit) {
|
|
|
|
+ return async (dispatch, getState) => {
|
|
|
|
+ let result = await gql(`query userAdFindById($query: String) {
|
|
|
|
+ AdFind(query: $query) {
|
|
|
|
+ _id title price createdAt images {
|
|
|
|
+ _id url
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }`, {
|
|
|
|
+ query: JSON.stringify([{"___owner": _id},
|
|
|
|
+ {"sort": [{"_id": -1}],
|
|
|
|
+ "skip": [getState().scroll?.skip],
|
|
|
|
+ "limit": [limit]}])
|
|
|
|
+ });
|
|
|
|
+ dispatch(actionEndlessScrollAdd(result, limit));
|
|
|
|
+ };
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const actionAboutUserById = function(_id) {
|
|
|
|
+ return actionPromise("aboutUserById", gql(`query aboutUser($query:String) {
|
|
|
|
+ UserFindOne(query: $query) {
|
|
|
|
+ _id createdAt login nick phones addresses
|
|
|
|
+ avatar {
|
|
|
|
+ _id url
|
|
|
|
+ }
|
|
|
|
+ incomings {
|
|
|
|
+ _id createdAt text
|
|
|
|
+ image {
|
|
|
|
+ _id url
|
|
|
|
+ }
|
|
|
|
+ to {
|
|
|
|
+ _id login
|
|
|
|
+ }
|
|
|
|
+ owner {
|
|
|
|
+ _id login
|
|
|
|
+ avatar {
|
|
|
|
+ _id url
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }`, {
|
|
|
|
+ query: JSON.stringify([{"___owner": _id}])
|
|
|
|
+ }))
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const store = createStore(combineReducers({auth: authReducer, promise: promiseReducer, scroll: endlessScrollReducer}), applyMiddleware(thunk));
|
|
|
|
+
|
|
|
|
+store.subscribe(() => console.log(store.getState()));
|
|
|
|
+
|
|
|
|
+export {store, backendURL, actionFullLogin, actionAuthLogout, actionFullRegister, actionChangePassword, actionEnndlessScrollAdFind, actionEndlessScrollClear, actionAdById, actionNewComment, actionUploadFiles, actionSaveAdState, actionAboutUserById};
|