Frontent dev env (#247)

* Added frontend development files/environment

* More items-categories related removals

* Improvements in pages templates (inc. static pages)

* Improvements in video player

* Added empty home page message + cta

* Updates in media, playlist and management pages

* Improvements in material icons font loading

* Replaced media & playlists links in frontend dev-env

* frontend package version update

* chnaged frontend dev url port

* static files update

* Changed default position of theme switcher

* enabled frontend docker container
This commit is contained in:
Yiannis Stergiou
2021-07-11 18:01:34 +03:00
committed by GitHub
parent 060bb45725
commit aa6520daac
555 changed files with 201927 additions and 66002 deletions

View File

@@ -0,0 +1,657 @@
import React, { useRef, useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import urlParse from 'url-parse';
import { deleteRequest, csrfToken } from '../../../utils/helpers/';
import { usePopup } from '../../../utils/hooks/';
import { PopupMain } from '../../_shared';
import { PendingItemsList } from '../../item-list/PendingItemsList.jsx';
import { renderManageItems } from './includes/functions';
import initManageItemsList from './includes/initManageItemsList';
import { ManageItemsListHandler } from './includes/ManageItemsListHandler';
import './ManageItemList.scss';
function useManageItemList(props, itemsListRef) {
let previousItemsLength = 0;
let itemsListInstance = null;
const [items, setItems] = useState([]);
const [countedItems, setCountedItems] = useState(false);
const [listHandler, setListHandler] = useState(null);
function onItemsLoad(itemsArray) {
setItems([...itemsArray]);
}
function onItemsCount(totalItems) {
setCountedItems(true);
if (void 0 !== props.itemsCountCallback) {
props.itemsCountCallback(totalItems);
}
}
function addListItems() {
if (previousItemsLength < items.length) {
if (null === itemsListInstance) {
itemsListInstance = initManageItemsList([itemsListRef.current])[0];
}
// FIXME: Should get item elements from children components.
const itemsElem = itemsListRef.current.querySelectorAll('.item');
if (!itemsElem || !itemsElem.length) {
return;
}
let i = previousItemsLength;
while (i < items.length) {
itemsListInstance.appendItems(itemsElem[i]);
i += 1;
}
previousItemsLength = items.length;
}
}
useEffect(() => {
if (void 0 !== props.itemsLoadCallback) {
props.itemsLoadCallback();
}
}, [items]);
return [items, countedItems, listHandler, setListHandler, onItemsLoad, onItemsCount, addListItems];
}
function useManageItemListSync(props) {
const itemsListRef = useRef(null);
const itemsListWrapperRef = useRef(null);
const [items, countedItems, listHandler, setListHandler, onItemsLoad, onItemsCount, addListItems] = useManageItemList(
{ ...props, itemsCountCallback },
itemsListRef
);
const [totalItems, setTotalItems] = useState(null);
let classname = {
list: 'manage-items-list',
listOuter: 'items-list-outer' + ('string' === typeof props.className ? ' ' + props.className.trim() : ''),
};
function onClickLoadMore() {
listHandler.loadItems();
}
function itemsCountCallback(itemsSumm) {
setTotalItems(itemsSumm);
}
function afterItemsLoad() {}
function renderBeforeListWrap() {
return null;
}
function renderAfterListWrap() {
if (!listHandler) {
return null;
}
return 1 > listHandler.totalPages() || listHandler.loadedAllItems() ? null : (
<button className="load-more" onClick={onClickLoadMore}>
SHOW MORE
</button>
);
}
useEffect(() => {
addListItems();
afterItemsLoad();
}, [items]);
return [
countedItems,
totalItems,
items,
listHandler,
setListHandler,
classname,
itemsListWrapperRef,
itemsListRef,
onItemsCount,
onItemsLoad,
renderBeforeListWrap,
renderAfterListWrap,
];
}
function pageUrlQuery(baseQuery, pageNumber) {
let queryParams = [];
let pos = 0;
if ('' !== baseQuery) {
queryParams = baseQuery.split('?')[1].split('&');
let param;
let i = 0;
while (i < queryParams.length) {
param = queryParams[i].split('=');
if ('page' === param[0]) {
pos = i;
break;
}
i += 1;
}
}
queryParams[pos] = 'page=' + pageNumber;
return '?' + queryParams.join('&');
}
function pageUrl(parsedUrl, query) {
return parsedUrl.set('query', query).href;
}
function BulkActions(props) {
const [popupContentRef, PopupContent, PopupTrigger] = usePopup();
const [selectedBulkAction, setSelectedBulkAction] = useState('');
const [selectedItemsSize, setSelectedItemsSize] = useState(props.selectedItemsSize);
function onBulkActionSelect(ev) {
setSelectedBulkAction(ev.currentTarget.value);
}
function onClickProceed() {
if ('function' === typeof props.onProceedRemoval) {
props.onProceedRemoval();
}
popupContentRef.current.tryToHide();
}
function onClickCancel() {
popupContentRef.current.tryToHide();
}
useEffect(() => {
setSelectedItemsSize(props.selectedItemsSize);
}, [props.selectedItemsSize]);
return (
<div className="manage-items-bulk-action">
<select value={selectedBulkAction} onChange={onBulkActionSelect}>
<option value="">Bulk actions</option>
<option value="delete">Delete selected</option>
</select>
{!selectedItemsSize || !selectedBulkAction ? null : (
<PopupTrigger contentRef={popupContentRef}>
<button>Apply</button>
</PopupTrigger>
)}
<PopupContent contentRef={popupContentRef}>
<PopupMain>
<div className="popup-message">
<span className="popup-message-title">Bulk removal</span>
<span className="popup-message-main">You're willing to remove selected items permanently?</span>
</div>
<hr />
<span className="popup-message-bottom">
<button className="button-link cancel-profile-removal" onClick={onClickCancel}>
CANCEL
</button>
<button className="button-link proceed-profile-removal" onClick={onClickProceed}>
PROCEED
</button>
</span>
</PopupMain>
</PopupContent>
</div>
);
}
function ManageItemsOptions(props) {
return (
<div className={props.className}>
<BulkActions selectedItemsSize={props.items.length} onProceedRemoval={props.onProceedRemoval} />
{1 === props.pagesSize ? null : (
<div className="manage-items-pagination">
<PaginationButtons
totalItems={props.totalItems}
pageItems={props.pageItems}
onPageButtonClick={props.onPageButtonClick}
query={props.query}
/>
</div>
)}
</div>
);
}
function PaginationButtons(props) {
const buttons = [];
let i;
let maxPagin = 11;
const newPagesNumber = {
last: Math.ceil(props.totalItems / props.pageItems),
current: 1,
};
if ('' !== props.query) {
const queryParams = props.query.split('?')[1].split('&');
let param;
let i = 0;
while (i < queryParams.length) {
param = queryParams[i].split('=');
if ('page' === param[0]) {
newPagesNumber.current = parseInt(param[1], 10);
break;
}
i += 1;
}
}
const paginButtonsData = paginationButtonsList(maxPagin, newPagesNumber);
i = 0;
while (i < paginButtonsData.length) {
if ('button' === paginButtonsData[i].type) {
buttons.push(
<button
key={i + '[button]'}
onClick={props.onPageButtonClick}
page={paginButtonsData[i].number}
className={newPagesNumber.current === paginButtonsData[i].number ? 'active' : ''}
>
{paginButtonsData[i].number}
</button>
);
} else if ('dots' === paginButtonsData[i].type) {
buttons.push(
<span key={i + '[dots]'} className="pagination-dots">
...
</span>
);
}
i += 1;
}
return buttons;
}
function paginationButtonsList(maxPagin, pagesNumber) {
if (3 > maxPagin) {
maxPagin = 3;
}
let i;
let maxCurr;
let maxEdge = 1;
if (maxPagin >= pagesNumber.last) {
maxPagin = pagesNumber.last;
maxCurr = pagesNumber.last;
maxEdge = 0;
} else {
if (5 < maxPagin) {
if (7 >= maxPagin) {
maxEdge = 2;
} else {
maxEdge = Math.floor(maxPagin / 4);
}
}
maxCurr = maxPagin - 2 * maxEdge;
}
const currentArr = [];
const firstArr = [];
const lastArr = [];
if (pagesNumber.current <= maxCurr + maxEdge - pagesNumber.current) {
i = 1;
while (i <= maxCurr + maxEdge) {
currentArr.push(i);
i += 1;
}
i = pagesNumber.last - maxPagin + currentArr.length + 1;
while (i <= pagesNumber.last) {
lastArr.push(i);
i += 1;
}
} else if (pagesNumber.current > pagesNumber.last - (maxCurr + maxEdge - 1)) {
i = pagesNumber.last - (maxCurr + maxEdge - 1);
while (i <= pagesNumber.last) {
currentArr.push(i);
i += 1;
}
i = 1;
while (i <= maxPagin - currentArr.length) {
firstArr.push(i);
i += 1;
}
} else {
currentArr.push(pagesNumber.current);
i = 1;
while (maxCurr > currentArr.length) {
currentArr.push(pagesNumber.current + i);
if (maxCurr === currentArr.length) {
break;
}
currentArr.unshift(pagesNumber.current - i);
i += 1;
}
i = 1;
while (i <= maxEdge) {
firstArr.push(i);
i += 1;
}
i = pagesNumber.last - (maxPagin - (firstArr.length + currentArr.length) - 1);
while (i <= pagesNumber.last) {
lastArr.push(i);
i += 1;
}
}
const ret = [];
i = 0;
while (i < firstArr.length) {
ret.push({
type: 'button',
number: firstArr[i],
});
i += 1;
}
if (firstArr.length && currentArr.length && firstArr[firstArr.length - 1] + 1 < currentArr[0]) {
ret.push({
type: 'dots',
});
}
i = 0;
while (i < currentArr.length) {
ret.push({
type: 'button',
number: currentArr[i],
});
i += 1;
}
if (currentArr.length && lastArr.length && currentArr[currentArr.length - 1] + 1 < lastArr[0]) {
ret.push({
type: 'dots',
});
}
i = 0;
while (i < lastArr.length) {
ret.push({
type: 'button',
number: lastArr[i],
});
i += 1;
}
return ret;
}
export function ManageItemList(props) {
const [
countedItems,
totalItems,
items,
listHandler,
setListHandler,
classname,
itemsListWrapperRef,
itemsListRef,
onItemsCount,
onItemsLoad,
] = useManageItemListSync(props);
const [selectedItems, setSelectedItems] = useState([]);
const [selectedAllItems, setSelectedAllItems] = useState(false);
const [parsedRequestUrl, setParsedRequestUrl] = useState(null);
const [parsedRequestUrlQuery, setParsedRequestUrlQuery] = useState(null);
function onPageButtonClick(ev) {
const clickedPageUrl = pageUrl(
parsedRequestUrl,
pageUrlQuery(parsedRequestUrlQuery, ev.currentTarget.getAttribute('page'))
);
if ('function' === typeof props.onPageChange) {
props.onPageChange(clickedPageUrl, ev.currentTarget.getAttribute('page'));
}
}
function onBulkItemsRemoval() {
deleteSelectedItems();
}
function onAllRowsCheck(selectedAllRows, tableType) {
const newSelected = [];
if (selectedAllRows) {
if (items.length !== selectedItems.length) {
let entry;
if ('media' === tableType) {
for (entry of items) {
newSelected.push(entry.friendly_token);
}
} else if ('users' === tableType) {
for (entry of items) {
newSelected.push(entry.username);
}
} else if ('comments' === tableType) {
for (entry of items) {
newSelected.push(entry.uid);
}
}
}
}
setSelectedItems(newSelected);
setSelectedAllItems(newSelected.length === items.length);
}
function onRowCheck(token, isSelected) {
if (void 0 !== token) {
let newSelected;
if (-1 === selectedItems.indexOf(token)) {
if (isSelected) {
newSelected = [...selectedItems, token];
setSelectedItems(newSelected);
setSelectedAllItems(newSelected.length === items.length);
}
} else {
if (!isSelected) {
newSelected = [];
let entry;
for (entry of selectedItems) {
if (token !== entry) {
newSelected.push(entry);
}
}
setSelectedItems(newSelected);
setSelectedAllItems(newSelected.length === items.length);
}
}
}
}
function removeBulkMediaResponse(response) {
if (response && 204 === response.status) {
setSelectedItems([]);
setSelectedAllItems(false);
if ('function' === typeof props.onRowsDelete) {
props.onRowsDelete(true);
}
}
}
function removeBulkMediaFail() {
if ('function' === typeof props.onRowsDeleteFail) {
props.onRowsDeleteFail(true);
}
}
function deleteItem(token, isManageComments) {
deleteRequest(
props.requestUrl.split('?')[0] + ('comments' === props.manageType ? '?comment_ids=' : '?tokens=') + token,
{
headers: {
'X-CSRFToken': csrfToken(),
},
tokens: token,
},
false,
removeMediaResponse,
removeMediaFail
);
}
function deleteSelectedItems() {
deleteRequest(
props.requestUrl.split('?')[0] +
('comments' === props.manageType ? '?comment_ids=' : '?tokens=') +
selectedItems.join(','),
{
headers: {
'X-CSRFToken': csrfToken(),
},
},
false,
removeBulkMediaResponse,
removeBulkMediaFail
);
}
function removeMediaResponse(response) {
if (response && 204 === response.status) {
props.onRowsDelete(false);
}
}
function removeMediaFail() {
props.onRowsDeleteFail(false);
}
useEffect(() => {
if (parsedRequestUrl) {
setParsedRequestUrlQuery(parsedRequestUrl.query);
}
}, [parsedRequestUrl]);
useEffect(() => {
setParsedRequestUrl(urlParse(props.requestUrl));
}, [props.requestUrl]);
useEffect(() => {
setListHandler(
new ManageItemsListHandler(props.pageItems, props.maxItems, props.requestUrl, onItemsCount, onItemsLoad)
);
return () => {
if (listHandler) {
listHandler.cancelAll();
setListHandler(null);
}
};
}, []);
return !countedItems ? (
<PendingItemsList className={classname.listOuter} />
) : !items.length ? null : (
<div className={classname.listOuter}>
<ManageItemsOptions
totalItems={totalItems}
pageItems={props.pageItems}
onPageButtonClick={onPageButtonClick}
query={parsedRequestUrlQuery || ''}
className="manage-items-options"
items={selectedItems}
pagesSize={listHandler.totalPages()}
onProceedRemoval={onBulkItemsRemoval}
/>
<div ref={itemsListWrapperRef} className="items-list-wrap">
<div ref={itemsListRef} className={classname.list}>
{renderManageItems(items, {
...props,
onAllRowsCheck: onAllRowsCheck,
onRowCheck: onRowCheck,
selectedItems: selectedItems,
selectedAllItems: selectedAllItems,
onDelete: deleteItem,
})}
</div>
</div>
<ManageItemsOptions
totalItems={totalItems}
pageItems={props.pageItems}
onPageButtonClick={onPageButtonClick}
query={parsedRequestUrlQuery || ''}
className="manage-items-options popup-on-top"
items={selectedItems}
pagesSize={listHandler.totalPages()}
onProceedRemoval={onBulkItemsRemoval}
/>
</div>
);
}
ManageItemList.defaultProps = {
itemsCountCallback: PropTypes.func,
maxItems: PropTypes.number.isRequired,
pageItems: PropTypes.number.isRequired,
requestUrl: PropTypes.string.isRequired,
onPageChange: PropTypes.func,
onRowsDelete: PropTypes.func,
onRowsDeleteFail: PropTypes.func,
pageItems: 24,
};
ManageItemList.defaultProps = {
maxItems: 99999,
pageItems: 24,
requestUrl: null,
};

View File

@@ -0,0 +1,666 @@
@use "sass:math";
@import '../../../../css/includes/_variables.scss';
@import '../../../../css/includes/_variables_dimensions.scss';
@import '../../../../css/config/index.scss';
#page-manage-media,
#page-manage-users,
#page-manage-comments {
.media-list-wrapper {
padding: 0 16px;
@media (min-width: 710px) {
padding: 0 24px;
}
max-width: calc(48px + calc(var(--default-item-width) * var(--default-max-row-items)));
}
.manage-items-list {
overflow: auto;
}
.items-list-outer {
position: relative;
display: block;
}
.items-list-wrap {
position: relative;
display: inline-block;
width: 100%;
min-height: 0;
}
}
.media-list-header {
display: block;
padding: 12px 0;
h2,
h3 {
display: inline-block;
margin: 12px 0;
font-weight: 500;
}
h2 {
font-size: 16px;
line-height: 1.25;
}
h3 {
font-size: 14px;
a {
margin: 10px 16px;
text-decoration: none;
color: var(--media-list-header-title-link-text-color);
}
}
}
.manage-items-list {
display: block;
width: 100%;
margin-bottom: 24px;
word-break: break-word;
border-radius: 1px;
box-shadow: 0px 4px 8px 0 rgba(17, 17, 17, 0.06);
overflow: auto;
a {
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
.media-list-header {
display: block;
padding: 12px 0;
h2,
h3 {
display: inline-block;
margin: 12px 0;
font-weight: 500;
}
h2 {
font-size: 16px;
line-height: 1.25;
}
h3 {
font-size: 14px;
a {
margin: 10px 16px;
text-decoration: none;
color: var(--media-list-header-title-link-text-color);
}
}
}
.manage-item,
.item.manage-item {
position: relative;
display: table;
width: 100%;
max-width: 100%;
margin: 0;
border-style: solid;
border-width: 0 0 1px;
border-color: #f0f0f0;
background-color: var(--user-action-form-inner-bg-color);
.dark_theme & {
border-color: #2d2d2d;
}
&:last-child {
border-width: 0;
}
&:nth-child(2n + 1) {
background-color: #f5f5f5;
.dark_theme & {
background-color: #202020;
}
}
&:hover {
background-color: #eaeaea;
box-shadow: 0px 1px 2px 0 rgba(#000, 0.12);
.dark_theme & {
background-color: #181818;
box-shadow: 0px 1px 2px 0 rgba(#000, 0.12);
}
}
> * {
display: table-cell;
border-right: 1px solid #f0f0f0;
.dark_theme & {
border-color: #2d2d2d;
}
&:last-child {
border-right: 0;
}
}
&:hover {
> * {
border-color: #eaeaea;
.dark_theme & {
border-color: #181818;
}
}
}
.material-icons[data-icon='check_circle'],
.material-icons[data-icon='check_circle_outline'] {
color: var(--success-color);
}
.material-icons[data-icon='cancel'],
.material-icons[data-icon='highlight_off'],
.reported-number {
color: var(--danger-color);
}
.reported-number {
font-weight: 500;
}
.non-available {
opacity: 0.4;
}
> * {
position: relative;
min-width: 98px;
padding-top: 14px;
padding-bottom: 14px;
vertical-align: middle;
}
.mi-title,
.mi-name {
.actions {
position: relative;
display: block;
padding-top: 4px;
button {
font-size: 12px;
color: var(--danger-color);
border: 0;
background: none;
&:hover {
text-decoration: underline;
}
}
.popup {
position: absolute;
top: 100%;
left: 0;
font-size: initial;
font-weight: initial;
}
.popup-message-bottom {
position: relative;
float: left;
width: 100%;
button {
position: relative;
float: left;
font-size: 14px;
color: var(--popup-msg-main-text-color);
&.proceed-profile-removal {
float: right;
color: var(--default-theme-color);
}
&.cancel-profile-removal {
float: left;
}
&:hover {
color: inherit;
text-decoration: none;
}
}
}
}
}
&.manage-media-item {
> * {
width: 10%;
text-align: center;
}
.mi-title,
.mi-author {
padding-left: 16px;
padding-right: 16px;
text-align: inherit;
font-weight: 500;
}
.mi-type,
.mi-encoding,
.mi-state {
text-transform: capitalize;
}
.mi-checkbox {
min-width: 48px;
width: 48px;
}
.mi-title {
min-width: 240px;
width: 100%;
}
.mi-author {
min-width: 184px;
}
.mi-added {
min-width: 168px;
min-width: 120px;
}
.mi-type {
}
.mi-encoding {
min-width: 136px;
}
.mi-state,
.mi-reviewed,
.mi-featured,
.mi-reported {
min-width: 88px;
}
}
&.manage-users-item {
> * {
width: math.div(1,8) * 100%;
}
.mi-added,
.mi-role,
.mi-featured,
.mi-verified,
.mi-trusted,
.mi-checkbox {
text-align: center;
}
.mi-name,
.mi-username {
min-width: 240px;
min-width: 200px;
width: 50%;
padding-left: 16px;
padding-right: 16px;
font-weight: 500;
}
.mi-checkbox {
min-width: 48px;
width: 48px;
}
.mi-added {
min-width: 168px;
@media (min-width: 768px) {
min-width: 200px;
}
}
}
&.manage-comments-item {
> * {
width: 16%;
}
.mi-title,
.mi-comment,
.mi-author {
padding-left: 16px;
padding-right: 16px;
}
.mi-comment,
.mi-added {
width: 100%;
}
.mi-author {
min-width: 160px;
font-weight: 500;
}
.mi-comment {
min-width: 240px;
.actions {
margin: 0.5em 0 0;
font-size: 0.92857em;
// font-weight:500;
.seperator {
margin: 0 4px;
opacity: 0.65;
}
button {
color: var(--danger-color);
border: 0;
background: none;
&:hover {
text-decoration: underline;
}
}
.popup {
position: absolute;
top: 100%;
left: 0;
font-size: initial;
font-weight: initial;
}
.popup-message-bottom {
position: relative;
float: left;
width: 100%;
button {
position: relative;
float: left;
font-size: 14px;
color: var(--popup-msg-main-text-color);
&.proceed-profile-removal {
float: right;
color: var(--default-theme-color);
}
&.cancel-profile-removal {
float: left;
}
&:hover {
color: inherit;
text-decoration: none;
}
}
}
}
}
.mi-added {
min-width: 192px;
text-align: center;
}
.mi-checkbox {
min-width: 48px;
width: 48px;
text-align: center;
}
&.manage-item-header {
.mi-comment {
padding-left: 16px;
}
}
}
&.manage-item-header {
font-size: 13px;
font-weight: 500;
letter-spacing: 0.007px;
background-color: #e3e3e3;
.dark_theme & {
background-color: #151515;
}
&:hover {
box-shadow: none;
}
> * {
padding-top: 20px;
padding-bottom: 20px;
border-right: 0;
text-transform: uppercase !important;
}
> .mi-col-sort {
.mi-col-sort-icons {
position: relative;
display: inline;
vertical-align: top;
background-color: yellow;
.material-icons {
width: auto;
height: auto;
padding: 0 0 0 1px;
font-size: 22px;
line-height: 1;
}
> * {
opacity: 0.25;
position: absolute;
left: 0;
&:first-child {
bottom: 0px;
}
&:last-child {
top: 0px;
}
}
}
cursor: pointer;
&:hover {
text-decoration: underline;
.mi-col-sort-icons {
> * {
opacity: 0.35;
}
}
}
&.desc {
.mi-col-sort-icons {
> *:last-child {
opacity: 0.8;
}
}
}
&.asc {
.mi-col-sort-icons {
> *:first-child {
opacity: 0.8;
}
}
}
}
}
}
.manage-items-options {
position: relative;
float: left;
width: 100%;
clear: both;
}
.manage-items-bulk-action {
position: relative;
width: auto;
float: left;
display: inline-block;
margin-bottom: 12px;
> select {
margin-right: 16px;
margin-bottom: 12px;
border-color: var(--input-bg-color);
background-color: var(--user-action-form-inner-bg-color);
box-shadow: 0px 1px 4px 0 rgba(17, 17, 17, 0.06);
}
> button {
padding: 0;
height: 36px;
line-height: 36px;
margin-right: 16px;
margin-bottom: 12px;
color: var(--default-theme-color);
border: 0;
background: none;
&:hover,
&:focus {
text-decoration: underline;
}
}
.popup {
position: absolute;
top: 100%;
left: 0;
background-color: var(--user-action-form-inner-bg-color);
.popup-on-top & {
top: auto;
bottom: 100%;
}
.popup-message-bottom {
float: left;
}
button {
position: relative;
width: auto;
float: left;
padding-top: 4px;
padding-bottom: 4px;
font-size: 1em;
color: var(--popup-msg-main-text-color);
border: 0;
background: none;
&.proceed-profile-removal {
float: right;
color: var(--default-theme-color);
}
&.cancel-profile-removal {
float: left;
}
&:hover {
color: inherit;
}
}
}
}
.manage-items-pagination {
position: relative;
width: auto;
margin-bottom: 12px;
font-size: 13px;
float: right;
display: inline-block;
button,
.pagination-dots {
padding: 0;
margin: 0 12px 12px 0;
}
button {
display: inline-block;
width: 36px;
height: 36px;
border: 0;
color: inherit;
background: var(--user-action-form-inner-bg-color);
border-radius: 1px;
box-shadow: 0px 1px 4px 0 rgba(17, 17, 17, 0.06);
border-width: 1px;
border-style: solid;
border-color: #f0f0f0;
.dark_theme & {
border-color: #2d2d2d;
}
&:hover {
font-weight: 500;
color: var(--default-theme-color);
}
&.active {
color: var(--user-action-form-inner-bg-color);
.dark_theme & {
color: inherit;
}
background-color: var(--default-theme-color);
border-color: var(--default-theme-color);
}
&:last-child {
margin-right: 0;
}
}
.pagination-dots {
letter-spacing: 1px;
}
}

View File

@@ -0,0 +1,132 @@
import { PageStore } from '../../../../utils/stores/';
import { formatInnerLink, getRequest } from '../../../../utils/helpers/';
export function ManageItemsListHandler(itemsPerPage, maxItems, request_url, itemsCountCallback, loadItemsCallback) {
const config = {
maxItems: maxItems || 255,
pageItems: itemsPerPage ? Math.min(maxItems, itemsPerPage) : 1,
};
const state = {
totalItems: 0,
totalPages: 0,
nextRequestUrl: formatInnerLink(request_url, PageStore.get('config-site').url),
};
const waiting = {
pageItems: 0,
requestResponse: false,
};
let firstItemUrl = null;
const items = [];
const responseItems = [];
const callbacks = {
itemsCount: function () {
if ('function' === typeof itemsCountCallback) {
itemsCountCallback(state.totalItems);
}
},
itemsLoad: function () {
if ('function' === typeof loadItemsCallback) {
loadItemsCallback(items);
}
},
};
function loadNextItems(itemsLength) {
let itemsToLoad, needExtraRequest;
itemsLength = !isNaN(itemsLength) ? itemsLength : config.pageItems;
if (waiting.pageItems && waiting.pageItems <= responseItems.length) {
itemsToLoad = waiting.pageItems;
needExtraRequest = false;
waiting.pageItems = 0;
} else {
itemsToLoad = Math.min(itemsLength, responseItems.length);
needExtraRequest = itemsLength > responseItems.length && !!state.nextRequestUrl;
waiting.pageItems = needExtraRequest ? itemsLength - responseItems.length : 0;
}
if (itemsToLoad) {
let i = 0;
while (i < itemsToLoad) {
items.push(responseItems.shift());
i += 1;
}
callbacks.itemsLoad();
}
if (needExtraRequest) {
runRequest();
}
}
function runRequest(initialRequest) {
waiting.requestResponse = true;
function fn(response) {
waiting.requestResponse = false;
if (!!!response || !!!response.data) {
return;
}
let data = response.data;
let results = void 0 !== data.results ? data.results : data; // NOTE: The structure of response data in the case of categories differs from the others.
// console.log( firstItemUrl );
let i = 0;
while (i < results.length && config.maxItems > responseItems.length) {
if (null === firstItemUrl || firstItemUrl !== results[i].url) {
responseItems.push(results[i]);
}
i += 1;
}
state.nextRequestUrl = !!data.next && config.maxItems > responseItems.length ? data.next : null;
if (initialRequest) {
// In some cases, (total) 'count' field is missing, but probably doesn't need (eg. in recommended media).
state.totalItems = !!data.count ? data.count : responseItems.length;
state.totalItems = Math.min(config.maxItems, state.totalItems);
state.totalPages = Math.ceil(state.totalItems / config.pageItems);
callbacks.itemsCount();
}
loadNextItems();
}
getRequest(state.nextRequestUrl, false, fn);
state.nextRequestUrl = null;
}
function loadItems(itemsLength) {
if (!waiting.requestResponse && items.length < state.totalItems) {
loadNextItems(itemsLength);
}
}
function totalPages() {
return state.totalPages;
}
function loadedAllItems() {
return items.length === state.totalItems;
}
runRequest(true);
return {
loadItems,
totalPages,
loadedAllItems,
};
}

View File

@@ -0,0 +1,13 @@
export default class MediaItem {
constructor(item) {
if (!Node.prototype.isPrototypeOf(item)) {
return null;
}
this.element = item;
}
element() {
return this.element;
}
}

View File

@@ -0,0 +1,39 @@
import ManageMediaItem from './ManageMediaItem';
const _ManageMediaItemsListData = {};
export default class MediaItemsList {
constructor(listContainer, initialItems) {
if (!Node.prototype.isPrototypeOf(listContainer)) {
return null;
}
_ManageMediaItemsListData[
Object.defineProperty(this, 'id', {
value: 'ManageMediaItemsList_' + Object.keys(_ManageMediaItemsListData).length,
}).id
] = {};
this.items = [];
this.container = listContainer;
this.appendItems(initialItems);
}
dataObject() {
return _ManageMediaItemsListData;
}
appendItems(items) {
var i;
if (NodeList.prototype.isPrototypeOf(items)) {
i = 0;
while (i < items.length) {
this.items.push(new ManageMediaItem(items[i]));
i += 1;
}
} else if (Node.prototype.isPrototypeOf(items)) {
this.items.push(new ManageMediaItem(items));
}
}
}

View File

@@ -0,0 +1,171 @@
import React from 'react';
import { ManageMediaItem } from '../../ManageItem/ManageMediaItem';
import { ManageUsersItem } from '../../ManageItem/ManageUsersItem';
import { ManageCommentsItem } from '../../ManageItem/ManageCommentsItem';
import { ManageMediaItemHeader } from '../../ManageItem/ManageMediaItemHeader';
import { ManageUsersItemHeader } from '../../ManageItem/ManageUsersItemHeader';
import { ManageCommentsItemHeader } from '../../ManageItem/ManageCommentsItemHeader';
function useManageItem(props) {
const itemData = props.item;
const itemProps = {
order: props.order,
onCheckRow: props.onCheckRow,
selectedRow: props.selectedRow,
onProceedRemoval: props.onProceedRemoval,
hideDeleteAction: props.hideDeleteAction,
};
return [itemData, itemProps];
}
function ListManageMediaItem(props) {
const [itemData, itemProps] = useManageItem(props);
const args = {
...itemProps,
thumbnail_url: itemData.thumbnail_url,
title: itemData.title,
url: itemData.url.replace(' ', '%20'),
author_name: itemData.author_name,
author_url: itemData.author_profile,
add_date: itemData.add_date,
media_type: itemData.media_type,
encoding_status: itemData.encoding_status,
state: itemData.state,
is_reviewed: itemData.is_reviewed,
featured: itemData.featured,
reported_times: itemData.reported_times,
token: itemData.friendly_token,
};
return <ManageMediaItem {...args} />;
}
function ListManageUserItem(props) {
const [itemData, itemProps] = useManageItem(props);
const roles = [];
if (void 0 !== itemData.is_editor && itemData.is_editor) {
roles.push('Editor');
}
if (void 0 !== itemData.is_manager && itemData.is_manager) {
roles.push('Manager');
}
const args = {
...itemProps,
thumbnail_url: itemData.thumbnail_url,
name: itemData.name,
url: itemData.url.replace(' ', '%20'),
username: itemData.username,
add_date: itemData.date_added,
is_featured: itemData.is_featured,
roles: roles,
is_verified: true === itemData.email_is_verified,
is_trusted: true === itemData.advancedUser,
has_roles: void 0 !== itemData.is_editor || void 0 !== itemData.is_manager,
has_verified: void 0 !== itemData.email_is_verified,
has_trusted: void 0 !== itemData.advancedUser,
};
return <ManageUsersItem {...args} />;
}
function ListManageCommentItem(props) {
const [itemData, itemProps] = useManageItem(props);
const args = {
...itemProps,
media_url: void 0 !== itemData.media_url ? itemData.media_url.replace(' ', '%20') : void 0,
author_name: itemData.author_name,
author_url: itemData.author_profile,
author_thumbnail_url: itemData.author_thumbnail_url,
add_date: itemData.add_date,
text: itemData.text,
uid: itemData.uid,
};
return <ManageCommentsItem {...args} />;
}
function ListManageItem(props) {
const args = {
item: props.item,
order: props.order,
hideDeleteAction: false,
onCheckRow: props.onCheckRow,
onProceedRemoval: props.onProceedRemoval,
};
if ('media' === props.type) {
return <ListManageMediaItem {...args} selectedRow={-1 < props.selectedItems.indexOf(props.item.friendly_token)} />;
}
if ('users' === props.type) {
return <ListManageUserItem {...args} selectedRow={-1 < props.selectedItems.indexOf(props.item.username)} />;
}
if ('comments' === props.type) {
return <ListManageCommentItem {...args} selectedRow={-1 < props.selectedItems.indexOf(props.item.uid)} />;
}
return null;
}
function ListManageItemHeader(props) {
const args = {
sort: props.sort,
order: props.order,
selected: props.selected,
onCheckAllRows: props.onCheckAllRows,
onClickColumnSort: props.onClickColumnSort,
};
if ('media' === props.type) {
return <ManageMediaItemHeader {...args} />;
}
if ('users' === props.type) {
args.has_roles =
props.items.length && (void 0 !== props.items[0].is_editor || void 0 !== props.items[0].is_manager);
args.has_verified = props.items.length && void 0 !== props.items[0].email_is_verified;
args.has_trusted = props.items.length && void 0 !== props.items[0].advancedUser;
return <ManageUsersItemHeader {...args} />;
}
if ('comments' === props.type) {
return <ManageCommentsItemHeader {...args} />;
}
return null;
}
export function renderManageItems(items, props) {
return [
<ListManageItemHeader
key={0}
type={props.manageType}
items={items}
sort={props.sortBy}
order={props.ordering}
selected={props.selectedAllItems}
onCheckAllRows={props.onAllRowsCheck}
onClickColumnSort={props.onClickColumnSort}
/>,
...items.map((item, index) => (
<ListManageItem
key={index + 1}
order={index + 1}
item={item}
type={props.manageType}
onCheckRow={props.onRowCheck}
onProceedRemoval={props.onDelete}
selectedItems={props.selectedItems}
/>
)),
];
}

View File

@@ -0,0 +1,28 @@
import ManageMediaItemsList from './ManageMediaItemsList';
var CSS_selectors = {
mediaItems: '.item',
};
var ManageMediaItemsListInstances = [];
export default function (lists) {
if (!lists.length) {
return null;
}
let items,
i = 0;
while (i < lists.length) {
items = lists[i].querySelectorAll(CSS_selectors.mediaItems);
if (items.length) {
ManageMediaItemsListInstances = ManageMediaItemsListInstances || [];
ManageMediaItemsListInstances.push(new ManageMediaItemsList(lists[i], items));
}
i += 1;
}
return ManageMediaItemsListInstances;
}