diff --git a/package.json b/package.json index f131a85..945ea25 100644 --- a/package.json +++ b/package.json @@ -8,12 +8,15 @@ "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", "@trendyol-js/react-carousel": "^2.0.0", + "axios": "^0.24.0", + "jol-player": "^2.5.0", "lodash": "^4.17.21", "react": "^17.0.2", "react-dom": "^17.0.2", "react-redux": "^7.2.6", "react-router-dom": "^6.0.2", "react-scripts": "4.0.3", + "react-toastify": "^8.1.0", "redux": "^4.1.2", "redux-devtools-extension": "^2.13.9", "redux-thunk": "^2.4.0", diff --git a/src/App.js b/src/App.js index 0898b1f..178e80b 100644 --- a/src/App.js +++ b/src/App.js @@ -1,12 +1,13 @@ import './App.scss'; import { Provider } from 'react-redux'; +import store from './redux/store'; import Router from './router'; function App() { return ( -
+ -
+ ); } diff --git a/src/App.scss b/src/App.scss index 0c0dbc1..14acafa 100644 --- a/src/App.scss +++ b/src/App.scss @@ -7,9 +7,6 @@ font-family: Regular; } -.App { - direction: rtl; -} .ltr{ direction: ltr; diff --git a/src/components/FloatWidthMouse/index.js b/src/components/FloatWidthMouse/index.js index 7e95dd0..8fbb9da 100644 --- a/src/components/FloatWidthMouse/index.js +++ b/src/components/FloatWidthMouse/index.js @@ -12,7 +12,7 @@ export default function FloatWithMouse() { }); return (
{ + return ( +
+ 1000 ? window.innerHeight - 300 : 250, + theme: "#4dd35b", + language: "en", + isShowMultiple: window.innerWidth > 700, + hideMouseTime: 10000, + isShowPicture: true, + isShowFullScreen: false, + isShowSet: false, + isShowScreenshot: window.innerWidth > 700, + // isShowSet : false, + isShowPauseButton: false, + poster: `https://dnvn.ir/api/v1/file/${posterId}`, + quality: [ + { + name: "SD", + url: `https://dnvn.ir/api/v1/file/${fileIds}`, + }, + ], + isShowWebFullScreen: false, + }} + /> +
+ ); +}; + +export default Video; diff --git a/src/constants/defaultValues.js b/src/constants/defaultValues.js new file mode 100644 index 0000000..2591544 --- /dev/null +++ b/src/constants/defaultValues.js @@ -0,0 +1,9 @@ +export const ApiConfig = { + apiKey: 'AIzaSyBBksq-Asxq2M4Ot-75X19IyrEYJqNBPcg', + authDomain: 'dnvn.ir', + baseUrl: 'https://dnvn.ir/api/v1', + //baseUrl: 'http://localhost:3030/api/v1', + loginURL: 'user/login', + otploginURL: 'user/otp/login', + logoutURL: 'user/logout', +}; \ No newline at end of file diff --git a/src/redux/actions/blog.js b/src/redux/actions/blog.js new file mode 100644 index 0000000..d873065 --- /dev/null +++ b/src/redux/actions/blog.js @@ -0,0 +1,27 @@ +import proxy from "../proxy"; +const blog = { + list: + (data = {}) => + async (dispatch) => + await proxy.get("public/blogList", data, { dispatch }), + // update: + // (data = {}, data2 = {}) => + // async (dispatch : Dispatch) => { + // await proxy.put("blog/update", data); + // await proxy.get("blog/list", data2, { dispatch }); + // }, + // add: + // (data = {}, data2 = {}) => + // async (dispatch : Dispatch) => { + // await proxy.post("blog/add", data); + // await proxy.get("blog/list", data2, { dispatch }); + // }, + // del: + // (data = {}, data2 = {}) => + // async (dispatch : Dispatch) => { + // await proxy.delete("blog/delete", data); + // await proxy.get("blog/list", data2, { dispatch }); + // }, +}; + +export default blog; diff --git a/src/redux/actions/book.js b/src/redux/actions/book.js new file mode 100644 index 0000000..baa730c --- /dev/null +++ b/src/redux/actions/book.js @@ -0,0 +1,39 @@ +import proxy from "../proxy"; +const book = { + list: + (data = {}) => + async (dispatch) => + await proxy.get("book/list", data, { dispatch }), + setLessonActive: (data) => async (dispatch) => + await dispatch({ type: "book/lesson/active", data: data }), + gradeFilter: (data) => async (dispatch) => + await dispatch({ type: "book/gradeFilter", data: data }), + sortFilter: (data) => async (dispatch) => + await dispatch({ type: "book/sortFilter", data: data }), + // update: + // (data = {}, data2 = {}) => + // async (dispatch : Dispatch) => { + // await proxy.put("book/update", data); + // await proxy.get("book/list", data2, { dispatch }); + // }, + // add: + // (data = {}, data2 = {}) => + // async (dispatch) => { + // await proxy.post("book/add", data); + // await proxy.get("book/list", data2, { dispatch }); + // }, + // del: + // (data = {}, data2 = {}) => + // async (dispatch) => { + // await proxy.delete("book/delete", data); + // await proxy.get("book/list", data2, { dispatch }); + // }, + // searchBook: (data) => async (dispatch) => { + // dispatch({ type: "book/search", data: data }); + // }, + // filterBook: (data) => async (dispatch) => { + // dispatch({ type: "book/filter", data: data }); + // }, +}; + +export default book; diff --git a/src/redux/actions/content.js b/src/redux/actions/content.js new file mode 100644 index 0000000..e09c8c8 --- /dev/null +++ b/src/redux/actions/content.js @@ -0,0 +1,14 @@ +import proxy from "../proxy"; +import { Dispatch } from "redux"; +const content = { + listVOD: + (data = {}) => + async (dispatch) => + await proxy.get("content/listVOD", data, { dispatch }), + infoVOD: + (data = {}) => + async (dispatch) => + await proxy.get("content/infoVOD", data, { dispatch }), +}; + +export default content; diff --git a/src/redux/actions/faq.js b/src/redux/actions/faq.js new file mode 100644 index 0000000..aaf031b --- /dev/null +++ b/src/redux/actions/faq.js @@ -0,0 +1,20 @@ +import proxy from "../proxy"; +import { Dispatch } from "redux"; +const faq = { + list: + (data = {}) => + async (dispatch) => + await proxy.get("faq/list", data, { dispatch }), + add: + (data = {}) => + async (dispatch) => + await proxy.post("faq/add", data, { dispatch }), + selectFaq: + (data = {}) => + async (dispatch) => + await dispatch({ type: "faq/selectFaq", data: data }), + searchFaq: (data) => async (dispatch) => + await dispatch({ type: "faq/search", data: data }), +}; + +export default faq; diff --git a/src/redux/actions/file.js b/src/redux/actions/file.js new file mode 100644 index 0000000..02090a1 --- /dev/null +++ b/src/redux/actions/file.js @@ -0,0 +1,22 @@ +import proxy from "../proxy"; +const file = { + upload: + (data = {}) => + async (dispatch) => { + var formData = new FormData(); + for (let key in data) formData.append(key, data[key]); + return await proxy.post( + "file/upload", + formData, + { + dispatch, + headers: { + "Content-Type": "multipart/form-data", + }, + }, + data + ); + }, +}; + +export default file; diff --git a/src/redux/actions/index.js b/src/redux/actions/index.js new file mode 100644 index 0000000..9fd9385 --- /dev/null +++ b/src/redux/actions/index.js @@ -0,0 +1,16 @@ +// @create-index +export { default as publicApi } from "./public"; +export { default as user } from "./user"; +export { default as book } from "./book"; +export { default as blog } from "./blog"; +export { default as product } from "./product"; +export { default as userFactor } from "./userFactor"; +export { default as userProduct } from "./userProduct"; +export { default as content } from "./content"; +export { default as faq } from "./faq"; +export { default as file } from "./file"; + + + + + diff --git a/src/redux/actions/product.js b/src/redux/actions/product.js new file mode 100644 index 0000000..7a770ad --- /dev/null +++ b/src/redux/actions/product.js @@ -0,0 +1,26 @@ +import proxy from "../proxy"; +const product = { + listVOD: + (data = {}) => + async (dispatch) => + await proxy.get("product/listVOD", data, { dispatch }), + addVOD: + (data = {}, data2 = {}) => + async (dispatch) => { + await proxy.post("product/addVOD", data); + await proxy.get("product/listVOD", data2, { dispatch }); + }, + deleteVOD: + (data = {}, data2 = {}) => + async (dispatch) => { + await proxy.delete("product/deleteVOD", data); + await proxy.get("product/listVOD", data2, { dispatch }); + }, + selectVOD: + (data = {}) => + async (dispatch) => { + await dispatch({ type: "product/selectVOD", data }); + }, +}; + +export default product; diff --git a/src/redux/actions/public.js b/src/redux/actions/public.js new file mode 100644 index 0000000..94344e3 --- /dev/null +++ b/src/redux/actions/public.js @@ -0,0 +1,50 @@ +import proxy from "../proxy"; +const publicApi = { + setTheme: (data) => async (dispatch) => + await dispatch({ type: "public/setTheme", data: data }), + getHomeData: (data) => async (dispatch) => + await proxy.get("public/homePage", data, { dispatch }), + + setFaqActive: (data) => async (dispatch) => + await dispatch({ type: "public/faq/activate", data: data }), + bookInfo: + (data = {}) => + async (dispatch) => + await proxy.get("public/vod/bookCourse", data, { dispatch }), + bookSection: + (data = {}) => + async (dispatch) => + await proxy.get("public/vod/bookSection", data, { dispatch }), + getContactData: + (data = {}) => + async (dispatch) => + await proxy.get("public/contact", data, { dispatch }), + + getBlogList: (data) => async (dispatch) => + await proxy.get("public/blogList", data, { dispatch }), + + getBlogInfo: (data) => async (dispatch) => + await proxy.get("public/blogInfo", data, { dispatch }), + setSubscribe: (data) => async (dispatch) => + await dispatch({ type: "public/setSubscribe", data: data }), + getProvince: + (data = {}) => + async (dispatch) => + await proxy.get("public/province", data, { dispatch }), + getCity: + (data = {}) => + async (dispatch) => + await proxy.get("public/city", data, { dispatch }), + selectMenu: (data) => async (dispatch) => + await dispatch({ type: "public/selectMenu", data: data }), + getHeader: + (data = {}) => + async (dispatch) => + await proxy.get("public/VOD/homePage", data, { dispatch }), + activeCategory: (data) => async (dispatch) => + await dispatch({ type: "active/category", data: data }), + setVideoActive: (data) => async (dispatch) => + await dispatch({ type: "active/video", data: data }), +}; + +export default publicApi; diff --git a/src/redux/actions/user.js b/src/redux/actions/user.js new file mode 100644 index 0000000..31fe949 --- /dev/null +++ b/src/redux/actions/user.js @@ -0,0 +1,63 @@ +import proxy from "../proxy"; + +const user = { + otp: (data) => async (dispatch) => + await proxy.post("public/sendOTP", data, { dispatch }), + + otp_login: (data) => async (dispatch) => + await proxy.login("user/otp/login", data, { dispatch }), + + login: (data) => async (dispatch) => + await proxy.login("user/login", data, { dispatch }), + + // register: (data : undefined) => async (dispatch : Dispatch) => + // await proxy.login("user/register", data, { dispatch }), + + // switchRole: (data : String) => async (dispatch : Dispatch) => + // await proxy.login("user/switchRole", data, { dispatch }), + + logout: (data) => async (dispatch) => + await proxy.logout("user/logout", data, { dispatch }), + + getUserRole: (data) => async (dispatch) => + await proxy.get("user/getUserRole", data, { dispatch }), + + // list: + // (data = {}) => + // async (dispatch : Dispatch) => + // await proxy.get("user/list", data, { dispatch }), + // domains: + // (data = {}) => + // async (dispatch : Dispatch) => + // await proxy.get("user/domains", data, { dispatch }), + // getmothers: + // (data = {}) => + // async (dispatch : Dispatch) => + // await proxy.get("mothers/list", data, { + // dispatch, + // }), + getProfile: + (data = {}) => + async (dispatch) => + await proxy.get("user/getProfile", data, { dispatch }), + setProfile: + (data = {}, data2 = {}) => + async (dispatch) => { + await proxy.put("user/setProfile", data, { dispatch }); + await proxy.get("user/getProfile", data2, { dispatch }); + }, + add: + (data = {}, data2 = {}) => + async (dispatch) => { + await proxy.post("user/add", data); + await proxy.get("user/list", data2, { dispatch }); + }, + del: + (data = {}, data2 = {}) => + async (dispatch) => { + await proxy.delete("user/delete", data); + await proxy.get("user/list", data2, { dispatch }); + }, +}; + +export default user; diff --git a/src/redux/actions/userFactor.js b/src/redux/actions/userFactor.js new file mode 100644 index 0000000..16424e9 --- /dev/null +++ b/src/redux/actions/userFactor.js @@ -0,0 +1,31 @@ +import proxy from "../proxy"; +const userFactor = { + payment: + (data1 = {}, data2 = {}) => + async (dispatch) => { + await proxy.post("userProduct/addVOD", data1, { dispatch }); + await proxy.post("userFactor/paymentVOD", data2, { dispatch }); + }, + verify: + (data = {}) => + async (dispatch) => + await proxy.post("userFactor/verifyVOD", data, { dispatch }), + info: + (data = {}) => + async (dispatch) => + await proxy.get("userFactor/info", data, { dispatch }), + update: + (data = {}, data2 = {}) => + async (dispatch) => { + await proxy.put("userFactor/update", data, { dispatch }); + await proxy.get("userFactor/info", data2, { dispatch }); + }, + deleteUserOffCode: + (data = {}, data2 = {}) => + async (dispatch) => { + await proxy.delete("userFactor/deleteUserOffCode", data, { dispatch }); + await proxy.get("userFactor/info", data2, { dispatch }); + }, +}; + +export default userFactor; diff --git a/src/redux/actions/userProduct.js b/src/redux/actions/userProduct.js new file mode 100644 index 0000000..677c191 --- /dev/null +++ b/src/redux/actions/userProduct.js @@ -0,0 +1,9 @@ +import proxy from "../proxy"; +const userProduct = { + addVOD: + (data = {}) => + async (dispatch) => + await proxy.post("userProduct/addVOD", data, { dispatch }), +}; + +export default userProduct; \ No newline at end of file diff --git a/src/redux/index.js b/src/redux/index.js new file mode 100644 index 0000000..94cfa95 --- /dev/null +++ b/src/redux/index.js @@ -0,0 +1,7 @@ +// @create-index + +export { default as actions } from './actions'; +export { default as proxy } from './proxy.js'; +export { default as reducers } from './reducers'; +export { default as store } from './store.js'; + diff --git a/src/redux/proxy.js b/src/redux/proxy.js new file mode 100644 index 0000000..23bc497 --- /dev/null +++ b/src/redux/proxy.js @@ -0,0 +1,120 @@ +import { ApiConfig } from "../constants/defaultValues"; +import axios from "axios"; + +let { baseUrl } = ApiConfig; +let access = window.localStorage.getItem("access"); +baseUrl = baseUrl + "/"; + +const Axios = axios.create({ + withCredentials: true, + validateStatus: null, + baseURL: baseUrl, + //headers: access ? { Authorization: `Bearer ${access}` } : {}, +}); +class Proxy { + get = async (url, params, opt, data) => + await this.check( + url, + opt, + async () => await Axios.get(url, { params, ...opt }), + data || params + ); + post = async (url, params, opt, data ) => + await this.check( + url, + opt, + async () => await Axios.post(url, params, opt), + data || params + ); + put = async (url , params , opt , data ) => + await this.check( + url, + opt, + async () => await Axios.put(url, params, opt), + data || params + ); + delete = async (url , params , opt , data ) => { + await this.check( + url, + opt, + async () => await Axios.delete(url, { ...opt, data: params }), + data || params + ); + }; + + check = async (url , { dispatch } , fetch , params ) => { + dispatch = dispatch || (() => {}); + dispatch({ type: url.split("/")[0] + "/" + "loading" }); + let response = await fetch(); + switch (response.status) { + case 200: + dispatch({ type: url, data: response.data.data, params }); + return response.data.data; + case 401: + if (await this.refresh()) { + let response = await fetch(); + dispatch({ type: url, data: response.data.data, params }); + return response.data.data; + } + break; + default: + dispatch({ + type: url.split("/")[0] + "/" + "error", + data: response.data, + params, + }); + } + return false; + }; + + refresh = async () => { + let refresh = localStorage.getItem("refresh"); + if (!refresh) window.location.href = "/"; + let login = await this.login( + "user/login", + {}, + { headers: { Authorization: `Bearer ${refresh}` } } + ); + return login ? true : false; + }; + login = async (url , params , { dispatch } ) => + this.post(url, params, { + dispatch: (obj ) => { + let login = obj.data; + if (!login || !login.refreshToken) return false; + localStorage.setItem("refresh", login.refreshToken); + localStorage.setItem("access", login.accessToken); + delete login.refreshToken; + delete login.accessToken; + localStorage.setItem("userData", JSON.stringify(login.profile)); + dispatch(obj); + }, + }); + + logout = async (url , params , { dispatch } ) => { + this.post(url, params, { + dispatch: (obj ) => { + localStorage.removeItem("refresh"); + localStorage.removeItem("access"); + localStorage.removeItem("userData"); + dispatch(obj); + }, + }); + }; + status = () => { + let refresh = localStorage.getItem("refresh"); + let userData = localStorage.getItem("userData"); + if (!refresh) return false; + if (refresh == "undefined") { + localStorage.removeItem("refresh"); + localStorage.removeItem("access"); + localStorage.removeItem("userData"); + return false; + } + + return JSON.parse(userData || ''); + }; +} +const _proxy = new Proxy(); + +export default _proxy; diff --git a/src/redux/reducers/blog.js b/src/redux/reducers/blog.js new file mode 100644 index 0000000..f7fafc3 --- /dev/null +++ b/src/redux/reducers/blog.js @@ -0,0 +1,28 @@ +import { toast } from "react-toastify"; + +const initialState = { + loading: false, + error: null, + list: [], +}; +export default function blog(state = initialState, action) { + let { type, data } = action; + switch (type) { + case "public/blogList": + return { ...state, loading: false, list: data, error: null }; + // case "blog/update": + // return { ...state, loading: false, error: null }; + // case "blog/add": + // return { ...state, loading: false, error: null }; + // case "blog/delete": + // return { ...state, loading: false, error: null }; + ///////////// + case "blog/loading": + return { ...state, loading: true }; + case "blog/error": + toast.error(data.message); + return { ...state, loading: false, error: data.message }; + default: + return state; + } +} diff --git a/src/redux/reducers/book.js b/src/redux/reducers/book.js new file mode 100644 index 0000000..df57c87 --- /dev/null +++ b/src/redux/reducers/book.js @@ -0,0 +1,166 @@ +import { toast } from "react-toastify"; + +const initialState = { + loading: false, + error: null, + list: [], + info: null, + searchResult: [], + filterResult: [], + subjects: [], + levels: [], + sortFilters: ["مرتب سازی", "محبوب ترین", "جدیدترین"], + selectedVideo: null, + gradeFilterBooks: null, + sortFilterCourse: null, + selectedGrade: null, + selectedSort: "پربازدید ترین ها", +}; + +const grades = [ + "پیش دبستان", + "اول", + "دوم", + "سوم", + "چهارم", + "پنجم", + "ششم", + "هفتم", + "هشتم", + "نهم", + "دهم", + "یازدهم", + "دوازدهم", +]; + +export default function book(state = initialState, action) { + let { type, data } = action; + switch (type) { + case "book/list": + let itemsSubject = (Array.isArray(data) ? data : []).map( + (item, index) => item.name + ); + if (window.innerWidth < 1000) { + itemsSubject.unshift("نام کتاب"); + } + let itemsLevel = (Array.isArray(data) ? data : []) + .map((item) => item.gradeId * 1) + .sort((a, b) => a - b) + .map((e) => grades[e]); + if (window.innerWidth < 1000) { + itemsLevel.unshift("پایه تحصیلی"); + } + return { + ...state, + loading: false, + list: data, + subjects: Array.from(new Set(itemsSubject)), + levels: Array.from(new Set(itemsLevel)), + filterResult: data, + error: null, + }; + case "book/lesson/active": + return { + ...state, + loading: false, + error: null, + }; + case "book/gradeFilter": + if (data === state.selectedGrade) { + return { ...state, gradeFilterBooks: null, selectedGrade: null }; + } else { + return { + ...state, + gradeFilterBooks: state.list.filter( + (item, i) => item.gradeId === data + ), + selectedGrade: data, + }; + } + case "book/sortFilter": + const search = state.list.filter( + (item) => item.name.indexOf(data.search) !== -1 + ); + if (data.sort === "تمامی دوره‌ها") { + return { ...state, sortFilterCourse: search, selectedSort: data.sort }; + } else if (data.sort === "جدیدترین‌ها") { + return { + ...state, + sortFilterCourse: search.sort((item1, item2) => { + return item1.publishYear - item2.publishYear; + }), + selectedSort: data.sort, + }; + } + return { ...state, sortFilterCourse: search, selectedSort: data.sort }; + + // case "book/update": + // return { ...state, loading: false, error: null }; + // case "book/add": + // return { ...state, loading: false, error: null }; + // case "book/delete": + // return { ...state, loading: false, error: null }; + // case "book/search": + // return { ...state }; + // case "book/filter": + // const realList = state.list; + // const level = data.level; + // const subject = data.subject; + // const sort = data.sort; + // const search = data.search; + // if (level.length > 0 || subject.length > 0 || sort || search) { + // localStorage.setItem("filter", "ON"); + // } + // let finalSort; + // let byLevelFilter = []; + // if (level.length > 0) { + // level.map((item1) => { + // for (let i = 0; i < realList.length; i++) { + // if (grades[realList[i].gradeId] === item1) { + // byLevelFilter.push(realList[i]); + // } + // } + // }); + // } else { + // byLevelFilter = realList; + // } + + // let bySubjectFilter = []; + // if (subject.length > 0) { + // subject.map((item1) => { + // for (let i = 0; i < byLevelFilter.length; i++) { + // if (byLevelFilter[i].name === item1) { + // bySubjectFilter.push(byLevelFilter[i]); + // } + // } + // }); + // } else { + // bySubjectFilter = byLevelFilter; + // } + + // const bySearchFilter = search + // ? bySubjectFilter.filter((item1) => item1.name.indexOf(search) !== -1) + // : bySubjectFilter; + + // if (sort === "گران ترین") { + // finalSort = bySearchFilter.sort((item1, item2) => { + // return item1.product[0].price - item2.product[0].price; + // }); + // } else { + // finalSort = bySearchFilter; + // } + // return { + // ...state, + // loading: false, + // error: null, + // filterResult: finalSort, + // }; + case "book/loading": + return { ...state, loading: true }; + case "book/error": + toast.error(data.message); + return { ...state, loading: false, error: data.message }; + default: + return state; + } +} diff --git a/src/redux/reducers/faq.js b/src/redux/reducers/faq.js new file mode 100644 index 0000000..c89b078 --- /dev/null +++ b/src/redux/reducers/faq.js @@ -0,0 +1,57 @@ +import { toast } from "react-toastify"; + +const initialState = { + loading: false, + error: null, + list: [], + searchResult: [], + search: null, +}; +export default function faq(state = initialState, action) { + let { type, data } = action; + switch (type) { + case "faq/list": + return { ...state, loading: false, list: data, error: null }; + case "faq/add": + return { ...state, loading: false, error: null }; + case "faq/selectFaq": + return { + ...state, + loading: false, + error: null, + list: !state.search + ? state.list.map((item) => + item.id === data + ? { ...item, active: !item.active } + : { ...item, active: false } + ) + : state.list, + searchResult: state.search + ? state.searchResult.map((item) => + item.id === data + ? { ...item, active: !item.active } + : { ...item, active: false } + ) + : state.searchResult, + }; + case "faq/search": + return { + ...state, + loading: false, + error: null, + search: data, + searchResult: state.list.filter( + (item) => + item.title.indexOf(data) !== -1 || + item.description.indexOf(data) !== -1 + ), + }; + case "faq/loading": + return { ...state, loading: true }; + case "faq/error": + toast.error(data.message); + return { ...state, loading: false, error: data.message }; + default: + return state; + } +} diff --git a/src/redux/reducers/file.js b/src/redux/reducers/file.js new file mode 100644 index 0000000..164494c --- /dev/null +++ b/src/redux/reducers/file.js @@ -0,0 +1,26 @@ +const initialState = { + loading: false, + error: null, + uploads: {}, + lastUpload: null, +}; + +export default function file(state = initialState, action) { + let { type, data, params } = action; + switch (type) { + case "file/upload": + return { + ...state, + loading: false, + error: null, + uploads: { ...state.uploads, [params?.target || "last"]: data.id }, + lastUpload: data.id, + }; + case "file/loading": + return { ...state, loading: true }; + case "file/error": + return { ...state, loading: false, error: data.message }; + default: + return state; + } +} diff --git a/src/redux/reducers/index.js b/src/redux/reducers/index.js new file mode 100644 index 0000000..e5ec6b1 --- /dev/null +++ b/src/redux/reducers/index.js @@ -0,0 +1,13 @@ +// @create-index +export { default as publicApi } from "./public"; +export { default as user } from "./user"; +export { default as book } from "./book"; +export { default as blog } from "./blog"; +export { default as product } from "./product"; +export { default as faq } from "./faq"; +export { default as file } from "./file"; +export { default as userFactor } from "./userFactor"; +export { default as userProduct } from "./userProduct"; + + + diff --git a/src/redux/reducers/product.js b/src/redux/reducers/product.js new file mode 100644 index 0000000..411a256 --- /dev/null +++ b/src/redux/reducers/product.js @@ -0,0 +1,28 @@ +import { toast } from "react-toastify"; + +const initialState = { + loading: false, + error: null, + listVOD: [], + selectedVOD: null, +}; +export default function product(state = initialState, action) { + let { type, data } = action; + switch (type) { + case "product/listVOD": + return { ...state, loading: false, listVOD: data, error: null }; + case "product/addVOD": + return { ...state, loading: false, error: null }; + case "product/deleteVOD": + return { ...state, loading: false, error: null }; + case "product/selectVOD": + return {...state, selectedVOD : data}; + case "product/loading": + return { ...state, loading: true }; + case "product/error": + toast.error(data.message); + return { ...state, loading: false, error: data.message }; + default: + return state; + } +} diff --git a/src/redux/reducers/public.js b/src/redux/reducers/public.js new file mode 100644 index 0000000..c896aeb --- /dev/null +++ b/src/redux/reducers/public.js @@ -0,0 +1,243 @@ +import location from '../../utils/location'; + +const initialState = { + theme : localStorage.getItem('theme') || 'light', + homeData: [], + newProducts: [], + levels: [], + loading: false, + error: null, + searchResult: [], + arList: [], + blogList: [], + blog: null, + province: null, + city: null, + departmans: [ + { + id: 0, + name: "محتوا", + }, + { + id: 0, + name: "دیزاین", + }, + { + id: 0, + name: "برنامه نویسی", + }, + ], + header: [], + selectedMenu: null, + contactData: null, + selectedVideo: null, + bookInfo: null, + activeCategory: null, + categories: null, + bookSection: null, +}; + +const grades = [ + "پیش دبستان", + "اول", + "دوم", + "سوم", + "چهارم", + "پنجم", + "ششم", + "هفتم", + "هشتم", + "نهم", + "دهم", + "یازدهم", + "دوازدهم", +]; +export default function publicApi(state = initialState, action) { + let { type, data } = action; + switch (type) { + case "public/setTheme": + localStorage.setItem("theme", data); + return { ...state, location: false, error : null, theme: data }; + case "active/video": + return { + ...state, + loading: false, + error: null, + selectedVideo: data, + }; + case "active/category": + return { + ...state, + loading: false, + activeCategory: state.activeCategory === data ? null : data, + error: null, + }; + case "public/vod/bookSection": + const videoId = location.getId(); + return { + ...state, + loading: false, + bookSection: data, + error: null, + selectedVideo: data.vods?.filter((item) => item.id !== videoId)[0] + }; + + case "public/vod/bookCourse": + let allCats; + allCats = Array.from( + new Set( + data.vods.map((item) => item.categoryId && item.categoryId) + ) + ); + return { + ...state, + loading: false, + bookInfo: { + ...data, + vods: data.vods.map((item) => { + return { ...item, active: false }; + }), + }, + error: null, + categories: allCats.map((item) => + data.vods.filter((item1) => item1.categoryId === item) + ), + selectedVideo: data.vods.filter((item ) => item.id !== location.getId())[0], + }; + case "public/homePage": + let itemsLevel; + if (window.innerWidth > 1000) { + const jadidTarin = data.filter( + (item) => item.title === "جدیدترین کتاب‌های دانوین" + ); + itemsLevel = jadidTarin[0].data + .map((item) => item.book.gradeId * 1) + .sort((a, b) => a - b) + .map((e) => grades[e]); + itemsLevel.unshift("همه"); + } + + return { + ...state, + loading: false, + levels: window.innerWidth > 1000 ? Array.from(new Set(itemsLevel)) : [], + error: null, + homeData: data, + }; + // case "public/filter": + // const realList = + // state.newProducts.length === 0 + // ? state.homeData.filter( + // (item) => item.title === "جدیدترین کتاب‌های دانوین" + // )[0].data + // : state.newProducts; + // if (state.newProducts.length === 0) { + // state = { + // ...state, + // newProducts: realList, + // }; + // console.log("here1"); + // } + // const level = data.levels; + // const search = data.search; + // if (level.length > 0 || search) { + // localStorage.setItem("filter", "ON"); + // } else { + // return { + // ...state, + // loading: false, + // error: null, + // homeData: state.homeData.map((item) => + // item.title === "جدیدترین کتاب‌های دانوین" + // ? { ...item, data: state.newProducts } + // : { ...item } + // ), + // newProducts: [], + // }; + // } + // let byLevelFilter = []; + // if (level.length > 0) { + // level.map((item1) => { + // for (let i = 0; i < realList.length; i++) { + // if (grades[realList[i].book.gradeId] === item1) { + // byLevelFilter.push(realList[i]); + // } + // } + // }); + // } else { + // byLevelFilter = realList; + // } + + // // let bySubjectFilter = []; + // // if (subject.length > 0) { + // // subject.map((item1) => { + // // for (let i = 0; i < byLevelFilter.length; i++) { + // // if (byLevelFilter[i].name === item1) { + // // bySubjectFilter.push(byLevelFilter[i]); + // // } + // // } + // // }); + // // } else { + // // bySubjectFilter = byLevelFilter; + // // } + + // const bySearchFilter = search + // ? byLevelFilter.filter((item1) => item1.name.indexOf(search) !== -1) + // : byLevelFilter; + // // if (sort === "گران ترین") { + // // finalSort = bySearchFilter.sort((item1, item2) => { + // // return item1.product[0].price - item2.product[0].price; + // // }); + // // } else { + // // finalSort = bySearchFilter; + // // } + + // return { + // ...state, + // loading: false, + // error: null, + // homeData: state.homeData.map((item) => + // item.title === "جدیدترین کتاب‌های دانوین" + // ? { ...item, data: bySearchFilter } + // : { ...item } + // ), + // }; + // case "faq/list": + // return { ...state, loading: false, error: null, faqList: data }; + // case "public/faq/search": + // return { + // ...state, + // loading: false, + // error: null, + // searchResult: state.faqList.filter( + // (item) => + // item.title.indexOf(data) !== -1 || + // item.description.indexOf(data) !== -1 + // ), + // }; + case "public/contact": + return { ...state, loading: false, error: null, contactData: data }; + // case "public/ar": + // return { ...state, loading: false, error: null, arList: data }; + case "public/blogList": + return { ...state, loading: false, error: null, blogList: data }; + case "public/blogInfo": + return { ...state, loading: false, error: null, blog: data }; + case "public/setSubscribe": + return { ...state, loading: false, error: null, selectedSubscribe: data }; + case "public/province": + return { ...state, loading: false, error: null, province: data }; + case "public/city": + return { ...state, loading: false, error: null, city: data }; + case "public/selectMenu": + return { ...state, loading: false, error: null, selectedMenu: data }; + case "public/VOD/homePage": + return { ...state, loading: false, error: null, header: data }; + case "loading": + return { ...state, loading: true }; + case "error": + return { ...state, loading: false, error: data.message }; + default: + return state; + } +} diff --git a/src/redux/reducers/user.js b/src/redux/reducers/user.js new file mode 100644 index 0000000..d856d84 --- /dev/null +++ b/src/redux/reducers/user.js @@ -0,0 +1,48 @@ +import proxy from "../proxy"; +import { toast } from "react-toastify"; +const initialState = { + status: proxy.status(), + loading: false, + error: null, + list: null, + domains: [], + mothers: [], + profileStatus: false, + setDone: false, + setLogin : false, +}; +export default function user(state = initialState, action) { + let { type, data } = action; + switch (type) { + // case "user/login": + // case "user/register": + // case "user/switchRole": + case "public/sendOTP": + return { ...state, loading: false, status: data.profile, error: null }; + case "user/login": + case "user/otp/login": + return { + ...state, + loading: false, + status: data.profile, + error: null, + setLogin: !state.setLogin, + }; + case "user/getProfile": + localStorage.setItem("userData", JSON.stringify(data)); + return { ...state, loading: false, status: data, error: null, setDone : false, }; + case "user/logout": + return { ...state, loading: false, status: proxy.status(), error: null }; + case "user/getUserRole": + return { ...state, loading: false, userRoles: data, error: null }; + case "user/setProfile": + toast.success("تغییرات اعمال شد."); + return { ...state, loading: false, error: null, setDone : true }; + case "loading": + return { ...state, loading: true }; + case "error": + return { ...state, loading: false, error: data.message }; + default: + return state; + } +} diff --git a/src/redux/reducers/userFactor.js b/src/redux/reducers/userFactor.js new file mode 100644 index 0000000..88db2ef --- /dev/null +++ b/src/redux/reducers/userFactor.js @@ -0,0 +1,39 @@ +import { toast } from "react-toastify"; +const initialState = { + loading: false, + error: null, + list: null, + info: null, + sum: null, + checkEmpty: false, + isVerified: null, +}; +export default function userFactor(state = initialState, action) { + let { type, data } = action; + switch (type) { + case "userFactor/myList": + return { ...state, loading: false, list: data, error: null }; + case "userFactor/info": + return { ...state, loading: false, info: data, error: null }; + case "userFactor/update": + toast.success("درخواست شما با موفقیت انجام شد."); + return { ...state, loading: false, error: null }; + // case "userFactor/add": + // toast.success("درخواست شما با موفقیت انجام شد."); + // return { ...state, loading: false, error: null, sum: data.payPrice }; + case "userFactor/deleteUserOffCode": + return { ...state, loading: false, error: null }; + case "userFactor/paymentVOD": + window.location.replace(data); + return { ...state, loading: false, error: null, isVerified: false }; + case "userFactor/verifyVOD": + return { ...state, loading: false, error: null, isVerified: true }; + case "userFactor/loading": + return { ...state, loading: true }; + case "userFactor/error": + toast.error(data.message); + return { ...state, loading: false, error: data.message }; + default: + return state; + } +} diff --git a/src/redux/reducers/userProduct.js b/src/redux/reducers/userProduct.js new file mode 100644 index 0000000..23cfd01 --- /dev/null +++ b/src/redux/reducers/userProduct.js @@ -0,0 +1,19 @@ +import { toast } from "react-toastify"; +const initialState = { + loading: false, + error: null, +}; +export default function userFactor(state = initialState, action) { + let { type, data } = action; + switch (type) { + case "userProduct/addVOD": + return { ...state, loading: false, error: null }; + case "userProduct/loading": + return { ...state, loading: true }; + case "userProduct/error": + toast.error(data.message); + return { ...state, loading: false, error: data.message }; + default: + return state; + } +} diff --git a/src/redux/store.js b/src/redux/store.js new file mode 100644 index 0000000..ecaf6db --- /dev/null +++ b/src/redux/store.js @@ -0,0 +1,14 @@ +import { createStore, applyMiddleware } from 'redux'; +import { composeWithDevTools } from 'redux-devtools-extension'; +import { combineReducers } from 'redux'; +import * as reducers from './reducers'; +import thunk from 'redux-thunk'; +const initialState = {}; +const middleware = [thunk]; +const store = createStore( + combineReducers(reducers), + initialState, + composeWithDevTools(applyMiddleware(...middleware)) +); +export default store; + diff --git a/src/utils/location.js b/src/utils/location.js new file mode 100644 index 0000000..aa083ce --- /dev/null +++ b/src/utils/location.js @@ -0,0 +1,11 @@ + +class location { + getId = () => { + const pathname = window.location.pathname; + return pathname.slice(pathname.lastIndexOf('/') + 1, pathname.length); + } +} + +const _location = new location(); + +export default _location; \ No newline at end of file diff --git a/src/utils/scroll.js b/src/utils/scroll.js index 4ebf1f5..481d921 100644 --- a/src/utils/scroll.js +++ b/src/utils/scroll.js @@ -94,10 +94,8 @@ class scroll { }; doubleLinearFormula = (currentScroll) => { - const rect = document.getElementsById("video").getClientRects(); - console.log(rect); - - return 1; + const rect = document.getElementById("video").getBoundingClientRect(); + console.log(rect.x / window.innerWidth * 100); }; } diff --git a/src/views/Home/Page2/Mobile/index.js b/src/views/Home/Page2/Mobile/index.js index f36bd78..eeda611 100644 --- a/src/views/Home/Page2/Mobile/index.js +++ b/src/views/Home/Page2/Mobile/index.js @@ -12,7 +12,9 @@ export default function Mobile({ mobilePosition }) { setChecked(false); } }, [mobilePosition]); - + useEffect(async () => { + console.log(document.getElementById("video")); + },[]); return (
diff --git a/src/views/Home/Page2/index.js b/src/views/Home/Page2/index.js index c9ed948..7bf6af4 100644 --- a/src/views/Home/Page2/index.js +++ b/src/views/Home/Page2/index.js @@ -7,6 +7,7 @@ import qrImage from "../../../assets/images/qrImage.png"; export default function Page2({ list, page }) { const height = window.innerHeight; const [top, setTop] = useState(window.innerHeight); + const [videoPosition, setVideoPosition] = useState(null); const [boxPosition, setBoxPosition] = useState({ translateY: null, translateX: null, @@ -78,6 +79,7 @@ export default function Page2({ list, page }) { setTop(scroll.homeScrollHandler(page)); boxPositionHandler(window.scrollY); mobilePositionHandler(window.scrollY); + videoPositionHandler(window.scrollY); }); }, []); @@ -138,6 +140,15 @@ export default function Page2({ list, page }) { } }; + const videoPositionHandler = (scrollY) => { + if(scrollY <= 3 * height){ + setVideoPosition(() => null); + }else if(scrollY > 3 * height && scrollY < 4 * height){ + scroll.doubleLinearFormula(scrollY); + }else{ + + } + } return (
+ {/* */} +
+