reza added vod component

temp
Reza_ashrafi 3 years ago
parent eaef7ac8b8
commit 5ee952357e
  1. 3
      package.json
  2. 5
      src/App.js
  3. 3
      src/App.scss
  4. 2
      src/components/FloatWidthMouse/index.js
  5. 35
      src/components/JolPlayer/index.js
  6. 9
      src/constants/defaultValues.js
  7. 27
      src/redux/actions/blog.js
  8. 39
      src/redux/actions/book.js
  9. 14
      src/redux/actions/content.js
  10. 20
      src/redux/actions/faq.js
  11. 22
      src/redux/actions/file.js
  12. 16
      src/redux/actions/index.js
  13. 26
      src/redux/actions/product.js
  14. 50
      src/redux/actions/public.js
  15. 63
      src/redux/actions/user.js
  16. 31
      src/redux/actions/userFactor.js
  17. 9
      src/redux/actions/userProduct.js
  18. 7
      src/redux/index.js
  19. 120
      src/redux/proxy.js
  20. 28
      src/redux/reducers/blog.js
  21. 166
      src/redux/reducers/book.js
  22. 57
      src/redux/reducers/faq.js
  23. 26
      src/redux/reducers/file.js
  24. 13
      src/redux/reducers/index.js
  25. 28
      src/redux/reducers/product.js
  26. 243
      src/redux/reducers/public.js
  27. 48
      src/redux/reducers/user.js
  28. 39
      src/redux/reducers/userFactor.js
  29. 19
      src/redux/reducers/userProduct.js
  30. 14
      src/redux/store.js
  31. 11
      src/utils/location.js
  32. 6
      src/utils/scroll.js
  33. 8
      src/views/Home/Page2/Mobile/index.js
  34. 11
      src/views/Home/Page2/index.js
  35. 18
      src/views/Home/VideoTube/AddComment/index.js
  36. 13
      src/views/Home/VideoTube/Attachments/index.js
  37. 70
      src/views/Home/VideoTube/Attachments/index.scss
  38. 20
      src/views/Home/VideoTube/Book/index.js
  39. 82
      src/views/Home/VideoTube/Book/index.scss
  40. 20
      src/views/Home/VideoTube/Comment/index.js
  41. 92
      src/views/Home/VideoTube/Comment/index.scss
  42. 145
      src/views/Home/VideoTube/LessonPlayList/index.js
  43. 265
      src/views/Home/VideoTube/LessonPlayList/index.scss
  44. 184
      src/views/Home/VideoTube/index.js
  45. 672
      src/views/Home/VideoTube/index.scss
  46. 3
      src/views/Home/index.js
  47. 47
      yarn.lock

@ -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",

@ -1,12 +1,13 @@
import './App.scss';
import { Provider } from 'react-redux';
import store from './redux/store';
import Router from './router';
function App() {
return (
<div className="App">
<Provider store={store} >
<Router />
</div>
</Provider>
);
}

@ -7,9 +7,6 @@
font-family: Regular;
}
.App {
direction: rtl;
}
.ltr{
direction: ltr;

@ -12,7 +12,7 @@ export default function FloatWithMouse() {
});
return (
<div
className="absolute rounded-full"
className="absolute rounded-full transition-a;; duration-1000 ease-out"
style={{
left: left - 500,
top: top - 500,

@ -0,0 +1,35 @@
import JoLPlayer from "jol-player";
const Video = ({ fileIds, posterId }) => {
return (
<div style={{ direction: "ltr", textAlign: "left", marginBottom: 10 }}>
<JoLPlayer
option={{
videoSrc: `https://dnvn.ir/api/v1/file/${fileIds}`,
width: "100%",
height: window.innerWidth > 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,
}}
/>
</div>
);
};
export default Video;

@ -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',
};

@ -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;

@ -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;

@ -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;

@ -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;

@ -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;

@ -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";

@ -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;

@ -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;

@ -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;

@ -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;

@ -0,0 +1,9 @@
import proxy from "../proxy";
const userProduct = {
addVOD:
(data = {}) =>
async (dispatch) =>
await proxy.post("userProduct/addVOD", data, { dispatch }),
};
export default userProduct;

@ -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';

@ -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;

@ -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;
}
}

@ -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;
}
}

@ -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;
}
}

@ -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;
}
}

@ -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";

@ -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;
}
}

@ -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;
}
}

@ -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;
}
}

@ -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;
}
}

@ -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;
}
}

@ -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;

@ -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;

@ -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);
};
}

@ -12,7 +12,9 @@ export default function Mobile({ mobilePosition }) {
setChecked(false);
}
}, [mobilePosition]);
useEffect(async () => {
console.log(document.getElementById("video"));
},[]);
return (
<div
className={_.join(
@ -90,8 +92,8 @@ export default function Mobile({ mobilePosition }) {
// transform : scroll.doubleLinearFormula(window.scrollY)
}}
controls
autoPlay
autoPlay={true}
muted
>
<source src={codVideo} type={"video/mp4"} />
</video>

@ -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 (
<div
className={_.join(

@ -0,0 +1,18 @@
import React from "react";
// import Avatar from "@mui/material/Avatar";
import user3 from "../../../assets/images/user3.png";
export default function AddComment(props) {
const { theme } = props;
return (
<>
{/* <Avatar style={{ width: 60, height: 60, marginLeft: 10 }} /> */}
<div className="d-flex flex-column" style={{width : '100%'}}>
<textarea rows={5} placeholder="نظر خود را در این قسمت بنویسید " />
<div className="d-flex justify-content-end">
<button className="submit submit-small mt-2">ارسال نظر</button>
</div>
</div>
</>
);
}

@ -0,0 +1,13 @@
import React from 'react';
export default function Attachments({file,num}) {
return (
<div className="download-attachments d-flex justify-content-between align-items-center">
<div className="d-flex">
<span>{num}</span>
<h4>{file.name}</h4>
</div>
<a href={file.file} download>دریافت</a>
</div>
)
}

@ -0,0 +1,70 @@
.download-attachments{
color: white;
margin: 0px auto;
border-bottom: 1px solid $gray8;
span{
}
h4{
margin: 0px;
}
a{
padding: 5px 15px;
border: 1px solid $gray7;
color: white;
text-decoration: none;
border-radius: 8px;
}
}
@media screen and (min-width: 1441px) {
.download-attachments{
width: calc(100% - 20px);
padding: 20px 0px;
a{
font-size: 13px;
}
h4{
font-size: 15px;
}
}
}
@media screen and (min-width: 1008px) and (max-width: 1440px) {
.download-attachments{
width: calc(100% - 20px);
padding: 10px 0px;
a{
font-size: calc(100vw / 100);
}
h4{
font-size: calc(100vw / 100);
}
}
}
@media screen and (min-width: 641px) and (max-width: 1007px) {
.download-attachments{
width: calc(100% - 20px);
padding: 10px 0px;
a{
font-size: calc(100vw / 50);
}
h4{
font-size: calc(100vw / 50);
}
}
}
@media screen and (max-width: 640px) {
.download-attachments{
width: calc(100% - 20px);
padding: 10px 0px;
a{
font-size: calc(100vw / 40);
}
h4{
font-size: calc(100vw / 40);
}
}
}

@ -0,0 +1,20 @@
import React from 'react';
import { Link } from 'react-router-dom';
export default function Book({ data }) {
const desc = {
"ریاضی": 'ریاضی از ریاضت میاد ولی دانوین آسون و لذت بخشش کرده',
'علوم': "علوم تجربی تو دانوین واقعا تجربی و محسوس میشه",
'زیست شناسی': "زیست را در دانوین باید زیست",
'شیمی': 'شیمی اینجا می فهمه کیمیا یعنی چی',
'فیزیک': "با دانوین فیزیکو با یه لنز جدید ببنید"
}
return (
<Link to={'/courses/' + data.id} className="video-tube-suggest d-flex flex-column">
<img src={`https://dnvn.ir/api/v1/file/${data.fileIds}`} alt="" />
<h4>{data.name}</h4>
<p>{desc[data.name] || ""}</p>
</Link>
)
}

@ -0,0 +1,82 @@
.video-tube-suggest{
color: white;
img{
border-top-right-radius: 10px;
border-top-left-radius: 10px;
border-bottom-right-radius: 10px;
border-bottom-left-radius: 24px;
margin-bottom: 10px;
transition: all 0.5s;
}
&:hover{
color: white;
img{
transform: translateY(-10px);
}
}
}
@media screen and (min-width: 1441px) {
.video-tube-suggest{
width: 30%;
img{
width: 130px;
height: 180px;
}
h4{
font-size: 15px;
}
p{
font-size: 11px;
}
}
}
@media screen and (min-width: 1008px) and (max-width: 1440px) {
.video-tube-suggest{
width: 30%;
img{
width: 100%;
// height: 180px;
}
h4{
font-size: calc(100vw / 90);
}
p{
font-size: calc(100vw / 110);
}
}
}
@media screen and (min-width: 641px) and (max-width: 1007px) {
.video-tube-suggest{
width: 30%;
img{
width: 200px;
// height: 180px;
}
h4{
font-size: calc(100vw / 43);
}
p{
font-size: calc(100vw / 52);
}
}
}
@media screen and (max-width: 640px) {
.video-tube-suggest{
width: 30%;
img{
width: 100px;
// height: 180px;
}
h4{
font-size: calc(100vw / 36);
}
p{
font-size: calc(100vw / 43);
}
}
}

@ -0,0 +1,20 @@
import React from "react";
// import Avatar from "@mui/material/Avatar";
export default function Comment({ comment }) {
return (
<div className="video-tube-comment d-flex">
{/* <Avatar src={comment.imageUrl} style={{ width: 65, height: 65, marginLeft: 10 }} /> */}
<div className="d-flex flex-column">
<p>
<strong>{comment.name}</strong> {comment.text}
</p>
<div className="d-flex justify-content-end align-items-center">
<button>پاسخ</button>
<span>{comment.date}</span>
</div>
</div>
</div>
);
}

@ -0,0 +1,92 @@
.video-tube-comment{
padding: 10px 0px;
color: white;
div{
}
button{
background-color: inherit;
color: $green;
border: none;
margin-left: 20px;
}
p{
line-height: 1.5;
margin-bottom: 15px;
}
}
@media screen and (min-width: 1441px){
.video-tube-comment{
width: 90%;
p{
font-size: 13px;
strong{
font-size: 15px;
margin-left: 5px;
}
}
button{
font-size: 13px;
}
span{
font-size: 15px;
}
}
}
@media screen and (min-width: 1008px) and (max-width: 1440px) {
.video-tube-comment{
width: 90%;
p{
font-size: calc(100vw / 105);
strong{
font-size: calc(100vw / 90);
margin-left: 5px;
}
}
button{
font-size: calc(100vw / 100) !important;
}
span{
font-size: calc(100vw / 90);
}
}
}
@media screen and (min-width: 641px) and (max-width: 1007px) {
.video-tube-comment{
width: 95%;
p{
font-size: calc(100vw / 50);
strong{
font-size: calc(100vw / 45);
margin-left: 5px;
}
}
button{
font-size: calc(100vw / 50) !important;
}
span{
font-size: calc(100vw / 45);
}
}
}
@media screen and (max-width: 640px) {
.video-tube-comment{
width: 95%;
p{
font-size: calc(100vw / 38);
strong{
font-size: calc(100vw / 35);
margin-left: 5px;
}
}
button{
font-size: calc(100vw / 40) !important;
}
span{
font-size: calc(100vw / 35);
}
}
}

@ -0,0 +1,145 @@
import React, { useEffect } from "react";
import { connect } from "react-redux";
// import { publicApi } from "../../../redux/actions";
import { Link } from "react-router-dom";
import _ from 'lodash';
function LessonPlayList({
category,
selectedVideo,
setVideoActive,
catergories,
realCategories,
vods,
theme,
}) {
const bookId = localStorage.getItem("bookId");
useEffect(() => {
if (localStorage.getItem("scroll-list") && vods) {
const list = document.getElementById("list");
const listItem = document.getElementById(
"listItem" + selectedVideo?.id
);
const elementHeight = (listItem)?.offsetHeight;
const listHeight = (list).offsetHeight;
const index = vods?.findIndex(
(item) => item?.name === selectedVideo?.name
);
console.log(index);
console.log(listHeight, elementHeight * (Number(index) + 1));
if (elementHeight * (Number(index) + 1) > listHeight) {
list.scrollTo(0, elementHeight * (Number(index) + 1) + elementHeight);
}
localStorage.removeItem("scroll-list");
}
}, [vods]);
const light = theme === "light";
return (
<>
{
<div
className={_.join(
[
"lesson-playlist d-flex flex-column",
light ? "lesson-playlist-light" : "lesson-playlist-dark",
],
" "
)}
>
{bookId && (
<header>
<h3>
{
realCategories.filter(
(item) => item.id === category[0].categoryId
)[0].name
}
</h3>
<span></span>
</header>
)}
{bookId ? (
category.map((video, i) => (
<Link
to={"/video/" + video.id}
className={
"lesson-playlist__item d-flex align-items-center" +
" " +
(video?.id === selectedVideo?.id &&
"lesson-playlist__item--active")
}
key={i}
id={"listItem" + video.id}
onClick={() => setVideoActive(video)}
>
<span className="lesson-playlist__item--count">
{Number(i) + 1}
</span>
<div className="lesson-playlist__item--video">
{/* <video>
<source
src={`https://dnvn.ir/api/v1/file/${video.fileIds}`}
/>
</video> */}
<img
src={`https://dnvn.ir/api/v1/file/${video.posterId}`}
alt=""
/>
<span>22:05</span>
</div>
<div className="lesson-playlist__item--text d-flex flex-column">
<h4>{video.name}</h4>
{/* <p>{file.text}</p> */}
</div>
</Link>
))
) : (
<Link
to={"/video/" + category.id}
className={
"lesson-playlist__item d-flex align-items-center" +
" " +
(category?.id === selectedVideo?.id &&
"lesson-playlist__item--active")
}
onClick={() => setVideoActive(category)}
id={"listItem" + category.id}
>
<span className="lesson-playlist__item--count"></span>
<div className="lesson-playlist__item--video">
{/* <video>
<source
src={`https://dnvn.ir/api/v1/file/${video.fileIds}`}
/>
</video> */}
<img
src={`https://dnvn.ir/api/v1/file/${category.posterId}`}
alt=""
/>
<span>22:05</span>
</div>
<div className="lesson-playlist__item--text d-flex flex-column">
<h4>{category.name}</h4>
{/* <p>{file.text}</p> */}
</div>
</Link>
)}
</div>
}
</>
);
}
export default connect(
(state) => ({
selectedVideo: state.publicApi.selectedVideo,
realCategories: state.publicApi.bookInfo?.categories,
vods: state.publicApi.bookSection?.vods,
}),
{
// setVideoActive: publicApi.setVideoActive,
}
)(LessonPlayList);

@ -0,0 +1,265 @@
.lesson-playlist {
width: 100%;
padding-bottom: 10px;
&-light {
header {
background-color: inherit !important;
h3 {
background-color: $gray12;
}
}
.lesson-playlist {
&__item {
* {
color: black !important;
}
&--active{
background-color: $gray13;
}
}
}
}
&-dark {
header {
background-color: inherit !important;
h3 {
background-color: $gray7;
}
span {
background-color: $gray7;
}
}
.lesson-playlist {
&__item {
&--count {
color: white;
}
&--active {
background: rgba($gray12, 0.21);
}
&--text {
h4 {
color: white;
}
p {
color: $gray7;
}
}
&--video {
span {
background-color: #0000006d;
color: white;
}
}
}
}
}
header {
margin-bottom: 5px;
h3 {
padding: 5px 10px;
margin: 0px;
}
span {
display: flex;
flex: 1;
height: 1px;
}
}
&__item {
&--text {
padding: 15px 10px;
h4 {
margin: 0px;
}
p {
margin: 0px;
}
}
&--video {
span {
padding: 0px 5px;
position: absolute;
left: 5px;
bottom: 10px;
}
}
}
}
@media screen and (min-width: 1441px) {
.lesson-playlist {
width: 100%;
header {
padding: 10px 40px 0px 0px !important;
h3 {
font-size: 16px;
}
span {
}
}
&__item {
padding: 10px 0px 5px 0px;
cursor: pointer;
&--count {
font-size: 18px;
width: 40px;
text-align: center;
}
&--text {
max-width: 55%;
h4 {
font-size: 17px;
}
p {
font-size: 14px;
}
}
&--video {
width: 142px;
height: 90px;
position: relative;
img {
width: 100%;
height: 100%;
}
span {
font-size: 18px;
}
}
}
}
}
@media screen and (min-width: 1008px) and (max-width: 1440px) {
.lesson-playlist {
width: 100%;
header {
padding: 10px 40px 0px 0px !important;
h3 {
font-size: calc(100vw / 95);
}
span {
}
}
&__item {
padding: 10px 0px 5px 0px;
cursor: pointer;
&--count {
font-size: calc(100vw / 72);
width: 40px;
text-align: center;
}
&--text {
max-width: 55%;
h4 {
font-size: calc(100vw / 90);
}
p {
font-size: calc(100vw / 100);
}
}
&--video {
width: 132px;
height: 90px;
position: relative;
img {
width: 100%;
height: 100%;
}
span {
font-size: calc(100vw / 100);
}
}
}
}
}
@media screen and (min-width: 641px) and (max-width: 1007px) {
.lesson-playlist {
width: 100%;
header {
padding: 10px 50px 0px 0px !important;
h3 {
font-size: calc(100vw / 45);
}
span {
}
}
&__item {
padding: 10px 0px 5px 0px;
cursor: pointer;
&--count {
font-size: calc(100vw / 35);
width: 50px;
text-align: center;
}
&--text {
max-width: 55%;
h4 {
font-size: calc(100vw / 45);
}
p {
font-size: calc(100vw / 50);
}
}
&--video {
width: 182px;
// height: 90px;
position: relative;
img {
width: 100%;
height: 100%;
}
span {
font-size: calc(100vw / 45);
}
}
}
}
}
@media screen and (max-width: 640px) {
.lesson-playlist {
width: 100%;
header {
padding: 10px 40px 0px 0px !important;
h3 {
font-size: calc(100vw / 38);
}
span {
}
}
&__item {
padding: 10px 0px 5px 0px;
cursor: pointer;
&--count {
font-size: calc(100vw / 25);
width: 40px;
text-align: center;
}
&--text {
max-width: 55%;
h4 {
font-size: calc(100vw / 35);
}
p {
font-size: calc(100vw / 40);
}
}
&--video {
width: 132px;
height: 90px;
position: relative;
img {
width: 100%;
height: 100%;
}
span {
font-size: calc(100vw / 40);
}
}
}
}
}

@ -0,0 +1,184 @@
import React, { useState, useEffect, useRef } from "react";
import LessonPlayList from "./LessonPlayList";
import Attachments from "./Attachments";
import Book from "./Book";
import Comment from "./Comment";
import AddComment from "./AddComment";
import Video from "../../../components/JolPlayer";
import listIcon from "../../assets/icons/list.png";
import { connect } from "react-redux";
import _ from "lodash";
import scroll from "../../../utils/scroll.js";
import location from "../../../utils/location.js";
import { book, publicApi } from "../../redux/actions";
import Loader from "../../components/Loader";
function VodTube({
selectedVideo,
info,
bookSection,
loading,
getInfo,
categories,
setVideoActive,
allBooks,
theme,
}) {
const videoListTag = useRef(null);
useEffect(() => {
localStorage.setItem("scroll-list", "on");
scroll.goToTop();
getInfo({
bookId: localStorage.getItem("bookId")
? localStorage.getItem("bookId")
: localStorage.getItem("category")?.split(",")[0],
});
}, []);
useEffect(() => {
if (allBooks && localStorage.getItem("category")) {
const cat = localStorage.getItem("category")?.split(",");
bookSection({ bookId: cat[0], categoryId: cat[1] });
}
}, [allBooks]);
const light = theme === "light";
return (
<>
<div
className={_.join(
[
"video-tube main d-flex flex-column",
light ? "video-tube-light bg-white" : "video-tube-dark bg-black",
],
" "
)}
style={{ filter: loading ? "blur(8px)" : "" }}
>
<main className="d-flex">
<div className="video-tube__content d-flex flex-column">
<section className="video-tube__content--display d-flex flex-column">
{/* <video controls>
<source src={selectedVideo.src} />
</video> */}
<Video
fileIds={selectedVideo?.fileIds}
posterId={selectedVideo?.posterId}
/>
{/* <span className="desktop">درس دوم / بیست و یکم</span> */}
<h1 className="desktop">{selectedVideo?.name}</h1>
{/* <p className="desktop">{selectedVideo.text}</p> */}
</section>
<section className="desktop video-tube__content--comments flex-column">
<header className="d-flex justify-content-between align-items-center">
<h2>نظرات</h2>
<button>بستن نظرات</button>
</header>
<div className="video-tube__content-comments___list d-flex flex-column">
{info?.comments?.map((comment, i) => (
<Comment comment={comment} key={i} />
))}
</div>
</section>
<section className="desktop video-tube__content--addComment">
<AddComment theme={theme} />
</section>
</div>
<div className="video-tube__sidebar d-flex flex-column">
<section className="video-tube__sidebar--playlist d-flex flex-column">
<header className="d-flex justify-content-between align-items-center">
<div className="d-flex flex-column">
<h2>
{localStorage.getItem("category")?.split(",")[1] ||
"لیست فیلمها"}
</h2>
{/* <p>در این قسمت توضیحی کوتاه قرار می گیرد</p> */}
</div>
<img src={listIcon} alt="" />
</header>
<div
ref={videoListTag}
className="video-tube__sidebar--playlist___list d-flex flex-column"
id="list"
>
{localStorage.getItem("bookId")
? categories?.map((video, i) => (
<LessonPlayList theme={theme} category={video} key={i} />
))
: info?.vods.map((video, i) => (
<LessonPlayList theme={theme} category={video} key={i} />
))}
</div>
</section>
{info?.attachments.length > 0 && (
<section className="video-tube__sidebar--attachments d-flex flex-column">
<header className="d-flex align-items-center">
<div className="d-flex flex-column">
<h2>فایل پیوست تمارین این فصل</h2>
{/* <p>در این قسمت توضیحی کوتاه قرار می گیرد</p> */}
</div>
</header>
<div className="video-tube__sidebar--attachments___list d-flex flex-column">
{info?.attachments?.map((file, i) => (
<Attachments file={file} key={i} />
))}
</div>
</section>
)}
{info?.related && (
<section className="video-tube__sidebar--suggests d-flex flex-column">
<header className="d-flex justify-content-between align-items-center">
<h2>پیشنهاد دانوین برای شما</h2>
<span>تا 50% تخفیف </span>
</header>
<div className="video-tube__sidebar--suggests___list d-flex justify-content-between align-items-center">
{info?.related.slice(0, 3).map((item, i) => (
<Book data={item} key={i} />
))}
</div>
</section>
)}
<section className="mobile video-tube__content--comments flex-column">
<header className="d-flex justify-content-between align-items-center">
<h2>نظرات</h2>
<button>بستن نظرات</button>
</header>
<div className="video-tube__content-comments___list d-flex flex-column">
{info?.comments?.map((comment, i) => (
<Comment comment={comment} key={i} />
))}
</div>
</section>
</div>
<section className="mobile video-tube__content--addComment">
<AddComment />
</section>
</main>
</div>
{loading && (
<div className="full-loader">
<Loader size={50} />
</div>
)}
</>
);
}
export default connect(
(state) => ({
selectedVideo: state.publicApi.selectedVideo,
info: state.publicApi.bookSection,
loading: state.publicApi.loading,
categories: state.publicApi.categories,
allBooks: state.publicApi.bookInfo?.vods,
theme: state.publicApi.theme,
}),
{
getInfo: publicApi.bookInfo,
getBookList: book.list,
bookSection: publicApi.bookSection,
setVideoActive: publicApi.setVideoActive,
}
)(VodTube);

@ -0,0 +1,672 @@
@import "./Attachments/index.scss";
@import "./Book/index.scss";
@import "./Comment/index.scss";
@import "./LessonPlayList/index.scss";
.video-tube {
&-light {
background-color: white;
h1,
p {
color: black !important;
}
.video-tube {
&__content {
&--comments {
color: black !important;
header{
background-color: inherit !important;
h2{
color: $green !important;
}
}
button{
color: $green !important;
background-color: inherit;
}
}
&--addComment {
textarea {
background-color: $gray13;
color: black !important;
border: none;
}
}
}
&__sidebar {
header {
background-color: $gray12;
* {
color: black !important;
}
}
&--playlist{
border: 1px solid $gray7;
border-top: none;
}
&--suggests{
border : none;
&__list{
background-color: inherit;
}
}
&--attachments{
border: none;
}
}
}
}
&-dark {
.video-tube{
&__content {
&--display {
border-bottom: 1px solid $gray8;
video {
border: 1px solid $gray8;
}
span {
color: $green4;
}
h1 {
color: white;
}
p {
color: white;
}
}
&--comments {
color: white;
border-bottom: 1px solid $gray8;
header {
h2 {
color: white;
}
}
button {
background-color: inherit;
color: $green;
}
}
&--addComment {
textarea {
background-color: $gray8;
color: white;
&::placeholder {
color: $gray4;
}
}
}
}
&__sidebar {
&--playlist {
border: 1px solid $gray8;
header {
color: white;
}
&___list {
/* Track */
&::-webkit-scrollbar-track {
box-shadow: inset 0 0 5px inherit;
}
/* Handle */
&::-webkit-scrollbar-thumb {
background: $gray;
}
/* Handle on hover */
&::-webkit-scrollbar-thumb:hover {
background: white;
}
}
}
&--attachments {
border: 1px solid $gray8;
header {
color: white;
}
&___list {
/* Track */
&::-webkit-scrollbar-track {
box-shadow: inset 0 0 5px inherit;
}
/* Handle */
&::-webkit-scrollbar-thumb {
background: $gray10;
}
/* Handle on hover */
&::-webkit-scrollbar-thumb:hover {
background: white;
}
}
}
&--suggests {
border: 1px solid $gray8;
header {
color: white;
span {
background-color: #00fff74f;
color: $blue2;
}
}
}
}
}
}
&__content {
margin-left: 15px;
&--display {
padding-bottom: 30px;
video {
}
span {
padding: 15px 0px;
}
h1 {
margin-bottom: 20px;
}
p {
}
}
&--comments {
padding-bottom: 30px;
header {
padding: 20px 0px;
h2 {
margin: 0px;
}
}
button {
border: none;
}
}
&--addComment {
padding: 15px 0px;
textarea {
width: 100%;
border-radius: 12px;
padding: 10px;
&:focus {
outline: none;
}
}
}
}
&__sidebar {
&--playlist {
margin-bottom: 30px;
header {
width: 100%;
h2,
p {
margin: 0px;
}
p {
margin-top: 5px;
}
}
&___list {
padding-top: 10px;
overflow-y: scroll;
&::-webkit-scrollbar {
width: 8px;
}
/* Track */
&::-webkit-scrollbar-track {
border-radius: 10px;
}
/* Handle */
&::-webkit-scrollbar-thumb {
border-radius: 10px;
}
/* Handle on hover */
&::-webkit-scrollbar-thumb:hover {
}
}
}
&--attachments {
margin-bottom: 30px;
header {
width: 100%;
h2,
p {
margin: 0px;
}
p {
margin-top: 5px;
}
}
&___list {
overflow-y: scroll;
&::-webkit-scrollbar {
width: 8px;
}
/* Track */
&::-webkit-scrollbar-track {
border-radius: 10px;
}
/* Handle */
&::-webkit-scrollbar-thumb {
border-radius: 10px;
}
/* Handle on hover */
&::-webkit-scrollbar-thumb:hover {
}
}
}
&--suggests {
background-color: inherit;
header {
width: 100%;
h2 {
margin: 0px;
}
span {
padding: 5px 8px;
}
}
}
}
}
@media screen and (min-width: 1441px) {
.video-tube {
main {
max-width: $maxWidth;
margin-top: 10px;
}
&__content {
flex: 8;
&--display {
span {
font-size: 16px;
}
h1 {
font-size: 24px;
}
p {
font-size: 16px;
}
}
&--comments {
header {
h2 {
font-size: 20px;
}
}
button {
font-size: 14px;
}
}
&--addComment {
width: 60%;
textarea {
font-size: 14px;
&::placeholder {
font-size: 14px;
}
}
}
}
&__sidebar {
flex: 4;
&--playlist {
max-height: calc(100vh - 140px);
header {
padding: 12px;
background-color: #2c2c2c;
img {
transform: translateY(-5px);
}
h2 {
font-size: 16px;
}
p {
font-size: 10px;
}
}
}
&--attachments {
max-height: 320px;
header {
padding: 14px 12px;
background-color: rgba(#ebebeb, 0.21);
h2 {
font-size: 16px;
}
p {
font-size: 10px;
}
}
}
&--suggests {
header {
padding: 20px 12px;
// background-color: #8bd09746;
h2 {
font-size: 16px;
}
span {
font-size: 11px;
}
}
&___list {
padding: 15px 12px;
// background-color: #08793d58;
}
}
}
}
}
@media screen and (min-width: 1008px) and (max-width: 1440px) {
.video-tube {
main {
max-width: 1250px;
margin-top: 10px;
padding: 0px 10px;
}
&__content {
flex: 8;
&--display {
video {
width: 100%;
}
span {
font-size: calc(100vw / 100);
}
h1 {
font-size: calc(100vw / 60);
}
p {
font-size: calc(100vw / 100);
}
}
&--comments {
header {
h2 {
font-size: calc(100vw / 80);
}
}
button {
font-size: calc(100vw / 90);
}
}
&--addComment {
width: 60%;
button {
font-size: calc(100vw / 110);
}
textarea {
font-size: calc(100vw / 90);
&::placeholder {
font-size: calc(100vw / 90);
}
}
}
}
&__sidebar {
flex: 4;
&--playlist {
max-height: calc(100vh - 140px);
header {
padding: 14px 12px;
background-color: #2c2c2c;
img {
transform: translateY(-5px);
}
h2 {
font-size: calc(100vw / 85);
}
p {
font-size: calc(100vw / 120);
}
}
}
&--attachments {
max-height: 250px;
header {
padding: 14px 12px;
background-color: rgba(#ebebeb, 0.21);
h2 {
font-size: calc(100vw / 85);
}
p {
font-size: calc(100vw / 120);
}
}
}
&--suggests {
background-color: #001702;
header {
padding: 26px 12px;
background-color: rgba(#ebebeb, 0.21);
h2 {
font-size: calc(100vw / 85);
}
span {
font-size: calc(100vw / 120);
}
}
&___list {
padding: 15px 12px;
background-color: #ffffff00;
}
}
}
}
}
@media screen and (min-width: 641px) and (max-width: 1007px) {
.video-tube {
background: black;
main {
max-width: 900px;
margin-top: 10px;
padding: 0px 10px;
flex-direction: column;
}
&__content {
flex: 8;
margin: 0px;
&--display {
padding: 0px;
video {
width: 100%;
margin-bottom: 10px;
}
span {
font-size: calc(100vw / 40);
}
h1 {
font-size: calc(100vw / 30);
}
p {
font-size: calc(100vw / 40);
}
}
&--comments {
header {
h2 {
font-size: calc(100vw / 40);
}
}
button {
font-size: calc(100vw / 50);
}
}
&--addComment {
width: 100%;
button {
font-size: calc(100vw / 45);
}
textarea {
font-size: calc(100vw / 45);
&::placeholder {
font-size: calc(100vw / 45);
}
}
}
}
&__sidebar {
flex: 4;
&--playlist {
max-height: 450px;
header {
padding: 12px 12px;
background-color: #2c2c2c;
img {
transform: translateY(-5px);
width: 40px;
}
h2 {
font-size: calc(100vw / 40);
}
p {
font-size: calc(100vw / 55);
}
}
}
&--attachments {
max-height: 250px;
header {
padding: 14px 12px;
background-color: #2c2c2c;
h2 {
font-size: calc(100vw / 40);
}
p {
font-size: calc(100vw / 55);
}
}
}
&--suggests {
header {
padding: 14px 12px;
background-color: #8bd09746;
h2 {
font-size: calc(100vw / 40);
}
span {
font-size: calc(100vw / 55);
}
}
&___list {
padding: 15px 12px;
background-color: #ffffff00;
}
}
}
}
}
@media screen and (max-width: 640px) {
.video-tube {
background: black;
margin-top: 20px;
main {
max-width: 500px;
margin-top: 0px;
padding: 0px 10px;
flex-direction: column;
}
&__content {
flex: 8;
margin: 0px;
&--display {
padding: 0px;
video {
width: 100%;
margin-bottom: 10px;
}
span {
font-size: calc(100vw / 40);
}
h1 {
font-size: calc(100vw / 30);
}
p {
font-size: calc(100vw / 40);
}
}
&--comments {
header {
h2 {
font-size: calc(100vw / 30);
}
}
button {
font-size: calc(100vw / 37);
}
}
&--addComment {
width: 100%;
button {
font-size: calc(100vw / 40);
}
textarea {
font-size: calc(100vw / 35);
&::placeholder {
font-size: calc(100vw / 35);
}
}
}
}
&__sidebar {
flex: 4;
&--playlist {
max-height: calc(60vh);
header {
padding: 8px 12px;
background-color: #2c2c2c;
img {
transform: translateY(-5px);
width: 30px;
}
h2 {
font-size: calc(100vw / 30);
}
p {
font-size: calc(100vw / 45);
}
}
}
&--attachments {
max-height: 200px;
header {
padding: 14px 12px;
background-color: #2c2c2c;
h2 {
font-size: calc(100vw / 30);
}
p {
font-size: calc(100vw / 45);
}
}
}
&--suggests {
header {
padding: 10px 12px;
background-color: #8bd09746;
h2 {
font-size: calc(100vw / 30);
}
span {
font-size: calc(100vw / 45);
}
}
&___list {
padding: 15px 12px;
}
}
}
}
}

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React from "react";
import Navbar from "../../components/Navbar";
import Page1 from "./Page1";
@ -9,7 +9,6 @@ import Page5 from "./Page5";
import Page6 from "./Page6";
import Page7 from "./Page7";
import Dots from "../../components/Dots";
import { set } from "lodash";
export default function Home() {
return (

@ -2613,6 +2613,13 @@ axe-core@^4.0.2:
resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.1.2.tgz"
integrity sha512-V+Nq70NxKhYt89ArVcaNL9FDryB3vQOd+BFXZIfO3RP6rwtj+2yqqqdHEkacutglPaZLkJeuXKCjCJDMGPtPqg==
axios@^0.24.0:
version "0.24.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6"
integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==
dependencies:
follow-redirects "^1.14.4"
axobject-query@^2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz"
@ -3376,6 +3383,11 @@ cliui@^6.0.0:
strip-ansi "^6.0.0"
wrap-ansi "^6.2.0"
clsx@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
co@^4.6.0:
version "4.6.0"
resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz"
@ -3624,7 +3636,7 @@ core-js-pure@^3.0.0:
resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.9.0.tgz"
integrity sha512-3pEcmMZC9Cq0D4ZBh3pe2HLtqxpGNJBLXF/kZ2YzK17RbKp94w0HFbdbSx8H8kAlZG5k76hvLrkPm57Uyef+kg==
core-js@^2.4.0:
core-js@^2.4.0, core-js@^2.6.10:
version "2.6.12"
resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz"
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
@ -5202,6 +5214,11 @@ follow-redirects@^1.0.0:
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.2.tgz"
integrity sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA==
follow-redirects@^1.14.4:
version "1.14.5"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.5.tgz#f09a5848981d3c772b5392309778523f8d85c381"
integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==
for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz"
@ -5621,6 +5638,11 @@ history@^5.1.0:
dependencies:
"@babel/runtime" "^7.7.6"
hls.js@^1.0.11:
version "1.1.1"
resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-1.1.1.tgz#d8ff641f764741f13259a2740aa33336810a10a5"
integrity sha512-2VEVzO/gr5LOD6K/DEmBkKEary6hr4YZ/SLb0PV81jOAM/Tl9DviL0sFMoUHDq05/j4OZxIj691Zo0p40u3Gtw==
hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz"
@ -6844,6 +6866,17 @@ jest@26.6.0:
import-local "^3.0.2"
jest-cli "^26.6.0"
jol-player@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/jol-player/-/jol-player-2.5.0.tgz#a1cc95746066e759d036a7a64a5690ca36f48430"
integrity sha512-zAGERLbo9i3e+gLv+CC7Viv0HU851PhE94F3c8umqT1rUXUty6Gx+z8GDUFt3VtjGsHWx4BbBTnLogSmQt97vQ==
dependencies:
core-js "^2.6.10"
hls.js "^1.0.11"
react "^17.0.2"
react-dom "^17.0.2"
screenfull "^5.0.0"
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
@ -9477,6 +9510,13 @@ react-scripts@4.0.3:
optionalDependencies:
fsevents "^2.1.3"
react-toastify@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-8.1.0.tgz#acaca4e8c4415c8474562dd84a14e6f390ed7f17"
integrity sha512-M+Q3rTmEw/53Csr7NsV/YnldJe4c7uERcY7Tma9mvLU98QT2VhIkKwjBzzxZkJRk/oBKyUAtkyMjMgO00hx6gQ==
dependencies:
clsx "^1.1.1"
react@^17.0.2:
version "17.0.2"
resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
@ -10065,6 +10105,11 @@ schema-utils@^3.0.0:
ajv "^6.12.5"
ajv-keywords "^3.5.2"
screenfull@^5.0.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba"
integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==
select-hose@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz"

Loading…
Cancel
Save