mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-11-23 14:53:50 -05:00
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:
90
frontend/src/static/js/utils/actions/MediaPageActions.js
Executable file
90
frontend/src/static/js/utils/actions/MediaPageActions.js
Executable file
@@ -0,0 +1,90 @@
|
||||
import Dispatcher from '../dispatcher.js';
|
||||
|
||||
export function loadMediaData() {
|
||||
Dispatcher.dispatch({
|
||||
type: 'LOAD_MEDIA_DATA',
|
||||
});
|
||||
}
|
||||
|
||||
export function likeMedia() {
|
||||
Dispatcher.dispatch({
|
||||
type: 'LIKE_MEDIA',
|
||||
});
|
||||
}
|
||||
|
||||
export function dislikeMedia() {
|
||||
Dispatcher.dispatch({
|
||||
type: 'DISLIKE_MEDIA',
|
||||
});
|
||||
}
|
||||
|
||||
export function reportMedia(reportDescription) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'REPORT_MEDIA',
|
||||
reportDescription: !!reportDescription ? reportDescription.replace(/\s/g, '') : '',
|
||||
});
|
||||
}
|
||||
|
||||
export function copyShareLink(inputElem) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'COPY_SHARE_LINK',
|
||||
inputElement: inputElem,
|
||||
});
|
||||
}
|
||||
|
||||
export function copyEmbedMediaCode(inputElem) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'COPY_EMBED_MEDIA_CODE',
|
||||
inputElement: inputElem,
|
||||
});
|
||||
}
|
||||
|
||||
export function removeMedia() {
|
||||
Dispatcher.dispatch({
|
||||
type: 'REMOVE_MEDIA',
|
||||
});
|
||||
}
|
||||
|
||||
export function submitComment(commentText) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'SUBMIT_COMMENT',
|
||||
commentText,
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteComment(commentId) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'DELETE_COMMENT',
|
||||
commentId,
|
||||
});
|
||||
}
|
||||
|
||||
export function createPlaylist(playlist_data) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'CREATE_PLAYLIST',
|
||||
playlist_data,
|
||||
});
|
||||
}
|
||||
|
||||
export function addMediaToPlaylist(playlist_id, media_id) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'ADD_MEDIA_TO_PLAYLIST',
|
||||
playlist_id,
|
||||
media_id,
|
||||
});
|
||||
}
|
||||
|
||||
export function removeMediaFromPlaylist(playlist_id, media_id) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'REMOVE_MEDIA_FROM_PLAYLIST',
|
||||
playlist_id,
|
||||
media_id,
|
||||
});
|
||||
}
|
||||
|
||||
export function addNewPlaylist(playlist_data) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'APPEND_NEW_PLAYLIST',
|
||||
playlist_data,
|
||||
});
|
||||
}
|
||||
22
frontend/src/static/js/utils/actions/PageActions.js
Executable file
22
frontend/src/static/js/utils/actions/PageActions.js
Executable file
@@ -0,0 +1,22 @@
|
||||
import Dispatcher from '../dispatcher.js';
|
||||
|
||||
export function initPage(page) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'INIT_PAGE',
|
||||
page,
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleMediaAutoPlay() {
|
||||
Dispatcher.dispatch({
|
||||
type: 'TOGGLE_AUTO_PLAY',
|
||||
});
|
||||
}
|
||||
|
||||
export function addNotification(notification, notificationId) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'ADD_NOTIFICATION',
|
||||
notification,
|
||||
notificationId,
|
||||
});
|
||||
}
|
||||
41
frontend/src/static/js/utils/actions/PlaylistPageActions.js
Executable file
41
frontend/src/static/js/utils/actions/PlaylistPageActions.js
Executable file
@@ -0,0 +1,41 @@
|
||||
import Dispatcher from '../dispatcher.js';
|
||||
|
||||
export function loadPlaylistData() {
|
||||
Dispatcher.dispatch({
|
||||
type: 'LOAD_PLAYLIST_DATA',
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleSave() {
|
||||
Dispatcher.dispatch({
|
||||
type: 'TOGGLE_SAVE',
|
||||
});
|
||||
}
|
||||
|
||||
export function updatePlaylist(playlist_data) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'UPDATE_PLAYLIST',
|
||||
playlist_data,
|
||||
});
|
||||
}
|
||||
|
||||
export function removePlaylist() {
|
||||
Dispatcher.dispatch({
|
||||
type: 'REMOVE_PLAYLIST',
|
||||
});
|
||||
}
|
||||
|
||||
export function removedMediaFromPlaylist(media_id, playlist_id) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'MEDIA_REMOVED_FROM_PLAYLIST',
|
||||
media_id,
|
||||
playlist_id,
|
||||
});
|
||||
}
|
||||
|
||||
export function reorderedMediaInPlaylist(newMediaData) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'PLAYLIST_MEDIA_REORDERED',
|
||||
playlist_media: newMediaData,
|
||||
});
|
||||
}
|
||||
19
frontend/src/static/js/utils/actions/PlaylistViewActions.js
Executable file
19
frontend/src/static/js/utils/actions/PlaylistViewActions.js
Executable file
@@ -0,0 +1,19 @@
|
||||
import Dispatcher from '../dispatcher.js';
|
||||
|
||||
export function toggleLoop() {
|
||||
Dispatcher.dispatch({
|
||||
type: 'TOGGLE_LOOP',
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleShuffle() {
|
||||
Dispatcher.dispatch({
|
||||
type: 'TOGGLE_SHUFFLE',
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleSave() {
|
||||
Dispatcher.dispatch({
|
||||
type: 'TOGGLE_SAVE',
|
||||
});
|
||||
}
|
||||
13
frontend/src/static/js/utils/actions/ProfilePageActions.js
Executable file
13
frontend/src/static/js/utils/actions/ProfilePageActions.js
Executable file
@@ -0,0 +1,13 @@
|
||||
import Dispatcher from '../dispatcher.js';
|
||||
|
||||
export function load_author_data() {
|
||||
Dispatcher.dispatch({
|
||||
type: 'LOAD_AUTHOR_DATA',
|
||||
});
|
||||
}
|
||||
|
||||
export function remove_profile() {
|
||||
Dispatcher.dispatch({
|
||||
type: 'REMOVE_PROFILE',
|
||||
});
|
||||
}
|
||||
8
frontend/src/static/js/utils/actions/SearchFieldActions.js
Executable file
8
frontend/src/static/js/utils/actions/SearchFieldActions.js
Executable file
@@ -0,0 +1,8 @@
|
||||
import Dispatcher from '../dispatcher.js';
|
||||
|
||||
export function requestPredictions(query) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'REQUEST_PREDICTIONS',
|
||||
query,
|
||||
});
|
||||
}
|
||||
36
frontend/src/static/js/utils/actions/VideoViewerActions.js
Executable file
36
frontend/src/static/js/utils/actions/VideoViewerActions.js
Executable file
@@ -0,0 +1,36 @@
|
||||
import Dispatcher from '../dispatcher.js';
|
||||
|
||||
export function set_viewer_mode(inTheaterMode) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'SET_VIEWER_MODE',
|
||||
inTheaterMode,
|
||||
});
|
||||
}
|
||||
|
||||
export function set_player_volume(playerVolume) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'SET_PLAYER_VOLUME',
|
||||
playerVolume,
|
||||
});
|
||||
}
|
||||
|
||||
export function set_player_sound_muted(playerSoundMuted) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'SET_PLAYER_SOUND_MUTED',
|
||||
playerSoundMuted,
|
||||
});
|
||||
}
|
||||
|
||||
export function set_video_quality(quality) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'SET_VIDEO_QUALITY',
|
||||
quality,
|
||||
});
|
||||
}
|
||||
|
||||
export function set_video_playback_speed(playbackSpeed) {
|
||||
Dispatcher.dispatch({
|
||||
type: 'SET_VIDEO_PLAYBACK_SPEED',
|
||||
playbackSpeed,
|
||||
});
|
||||
}
|
||||
7
frontend/src/static/js/utils/actions/index.ts
Normal file
7
frontend/src/static/js/utils/actions/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export * as MediaPageActions from './MediaPageActions';
|
||||
export * as PageActions from './PageActions';
|
||||
export * as PlaylistPageActions from './PlaylistPageActions';
|
||||
export * as PlaylistViewActions from './PlaylistViewActions';
|
||||
export * as ProfilePageActions from './ProfilePageActions';
|
||||
export * as SearchFieldActions from './SearchFieldActions';
|
||||
export * as VideoViewerActions from './VideoViewerActions';
|
||||
67
frontend/src/static/js/utils/classes/BrowserCache.js
Executable file
67
frontend/src/static/js/utils/classes/BrowserCache.js
Executable file
@@ -0,0 +1,67 @@
|
||||
import { logErrorAndReturnError, logWarningAndReturnError } from '../helpers/';
|
||||
|
||||
function supportLocalstorage() {
|
||||
var mod = 'test-slug';
|
||||
try {
|
||||
localStorage.setItem(mod, mod);
|
||||
localStorage.removeItem(mod);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function BrowserCache(prefix, default_expire_seconds) {
|
||||
var default_expire_seconds = parseInt(default_expire_seconds, 10) || 3600;
|
||||
|
||||
if (!supportLocalstorage) {
|
||||
console.warn(['Current browser does not support localStorage.']);
|
||||
}
|
||||
|
||||
return !prefix
|
||||
? logErrorAndReturnError(['Cache object prefix is required'])
|
||||
: {
|
||||
prefix: prefix,
|
||||
seconds: default_expire_seconds,
|
||||
set: function (key, value, expire_seconds, ret) {
|
||||
if (supportLocalstorage) {
|
||||
expire_seconds = expire_seconds ? expire_seconds : default_expire_seconds;
|
||||
if (!expire_seconds) {
|
||||
ret = logWarningAndReturnError(['Invalid cache expiration value', expire_seconds]);
|
||||
}
|
||||
try {
|
||||
localStorage.setItem(
|
||||
prefix + '[' + key + ']',
|
||||
JSON.stringify({
|
||||
value: value,
|
||||
expire: new Date().getTime() + expire_seconds * 1000,
|
||||
})
|
||||
);
|
||||
ret = !0;
|
||||
} catch (error) {
|
||||
ret = logWarningAndReturnError([error]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
get: function (key, ret) {
|
||||
ret = supportLocalstorage ? localStorage.getItem(prefix + '[' + key + ']') : null;
|
||||
ret = ret ? JSON.parse(ret) : null;
|
||||
ret = null !== ret ? (void 0 !== ret.expire && ret.expire > new Date().getTime() ? ret.value : null) : ret;
|
||||
return ret;
|
||||
},
|
||||
clear: function () {
|
||||
var k;
|
||||
if (supportLocalstorage && Object.keys(localStorage).length) {
|
||||
for (k in localStorage) {
|
||||
if (localStorage.hasOwnProperty(k)) {
|
||||
if (0 === k.indexOf(prefix)) {
|
||||
localStorage.removeItem(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return !0;
|
||||
},
|
||||
};
|
||||
}
|
||||
85
frontend/src/static/js/utils/classes/MediaDurationInfo.js
Executable file
85
frontend/src/static/js/utils/classes/MediaDurationInfo.js
Executable file
@@ -0,0 +1,85 @@
|
||||
import { isPositiveIntegerOrZero } from '../helpers/';
|
||||
|
||||
const _MediaDurationInfoData = {};
|
||||
|
||||
export class MediaDurationInfo {
|
||||
constructor(seconds) {
|
||||
_MediaDurationInfoData[
|
||||
Object.defineProperty(this, 'id', { value: 'MediaDurationInfo_' + Object.keys(_MediaDurationInfoData).length }).id
|
||||
] = {
|
||||
fn: {
|
||||
infoToString: function (v) {
|
||||
return v < 10 ? '0' + v : v;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (isPositiveIntegerOrZero(seconds)) {
|
||||
this.update(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
update(seconds) {
|
||||
_MediaDurationInfoData[this.id].toString = void 0;
|
||||
_MediaDurationInfoData[this.id].ariaLabel = void 0;
|
||||
|
||||
if (isPositiveIntegerOrZero(seconds)) {
|
||||
_MediaDurationInfoData[this.id].days = Math.floor(seconds / 86400);
|
||||
_MediaDurationInfoData[this.id].seconds = seconds % 86400;
|
||||
_MediaDurationInfoData[this.id].date = _MediaDurationInfoData[this.id].seconds
|
||||
? new Date(_MediaDurationInfoData[this.id].seconds * 1000)
|
||||
: null;
|
||||
_MediaDurationInfoData[this.id].hours = _MediaDurationInfoData[this.id].date
|
||||
? 24 * _MediaDurationInfoData[this.id].days + _MediaDurationInfoData[this.id].date.getUTCHours()
|
||||
: 0;
|
||||
_MediaDurationInfoData[this.id].minutes = _MediaDurationInfoData[this.id].date
|
||||
? _MediaDurationInfoData[this.id].date.getUTCMinutes()
|
||||
: 0;
|
||||
_MediaDurationInfoData[this.id].seconds = _MediaDurationInfoData[this.id].date
|
||||
? _MediaDurationInfoData[this.id].date.getSeconds()
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
if (void 0 === _MediaDurationInfoData[this.id].toString) {
|
||||
_MediaDurationInfoData[this.id].toString =
|
||||
(0 < _MediaDurationInfoData[this.id].hours ? _MediaDurationInfoData[this.id].hours + ':' : '') +
|
||||
(0 < _MediaDurationInfoData[this.id].hours && 10 > _MediaDurationInfoData[this.id].minutes ? '0' : '') +
|
||||
_MediaDurationInfoData[this.id].minutes +
|
||||
':' +
|
||||
_MediaDurationInfoData[this.id].fn.infoToString(_MediaDurationInfoData[this.id].seconds);
|
||||
}
|
||||
return _MediaDurationInfoData[this.id].toString;
|
||||
}
|
||||
|
||||
ariaLabel() {
|
||||
if (void 0 === _MediaDurationInfoData[this.id].ariaLabel) {
|
||||
let r = [];
|
||||
if (0 < _MediaDurationInfoData[this.id].hours) {
|
||||
r.push(_MediaDurationInfoData[this.id].hours + ' hours');
|
||||
}
|
||||
if (0 < _MediaDurationInfoData[this.id].minutes) {
|
||||
r.push(_MediaDurationInfoData[this.id].minutes + ' minutes');
|
||||
}
|
||||
if (0 < _MediaDurationInfoData[this.id].seconds) {
|
||||
r.push(_MediaDurationInfoData[this.id].seconds + ' seconds');
|
||||
}
|
||||
_MediaDurationInfoData[this.id].ariaLabel = r.join(', ');
|
||||
}
|
||||
return _MediaDurationInfoData[this.id].ariaLabel;
|
||||
}
|
||||
|
||||
// LINK: https://en.wikipedia.org/wiki/ISO_8601#Durations
|
||||
ISO8601() {
|
||||
return (
|
||||
'P0Y0M0DT' +
|
||||
_MediaDurationInfoData[this.id].hours +
|
||||
'H' +
|
||||
_MediaDurationInfoData[this.id].minutes +
|
||||
'M' +
|
||||
_MediaDurationInfoData[this.id].seconds +
|
||||
'S'
|
||||
);
|
||||
}
|
||||
}
|
||||
467
frontend/src/static/js/utils/classes/PlayerRecommendedMedia.js
Executable file
467
frontend/src/static/js/utils/classes/PlayerRecommendedMedia.js
Executable file
@@ -0,0 +1,467 @@
|
||||
import { formatViewsNumber, quickSort, greaterCommonDivision, addClassname, removeClassname } from '../helpers/';
|
||||
import ItemsInlineSlider from '../../components/item-list/includes/itemLists/ItemsInlineSlider';
|
||||
import { MediaDurationInfo } from './MediaDurationInfo';
|
||||
|
||||
const _MediaDurationInfo = new MediaDurationInfo();
|
||||
|
||||
function itemDuration(duration) {
|
||||
return '<span class="more-media-duration"><span>' + duration + '</span></span>';
|
||||
}
|
||||
|
||||
function itemThumb(img, duration) {
|
||||
return img
|
||||
? '<span class="more-media-item-thumb" style="background-image:url(\'' +
|
||||
img +
|
||||
'\');">' +
|
||||
itemDuration(duration) +
|
||||
'</span>'
|
||||
: '';
|
||||
}
|
||||
|
||||
function itemTitle(title) {
|
||||
return '<span class="more-media-title">' + title + '</span>';
|
||||
}
|
||||
|
||||
function itemAuthor(author) {
|
||||
return '<span class="more-media-author">' + author + '</span>';
|
||||
}
|
||||
|
||||
function itemViews(views, hideViews) {
|
||||
return hideViews ? '' : '<span class="more-media-views">' + views + '</span>';
|
||||
}
|
||||
|
||||
function itemMeta(author, views, hideViews) {
|
||||
return '<span class="more-media-meta">' + itemAuthor(author) + itemViews(views, hideViews) + '</span>';
|
||||
}
|
||||
|
||||
function itemContent(title, author, views, hideViews) {
|
||||
return '<span class="more-media-item-content">' + itemTitle(title) + itemMeta(author, views, hideViews) + '</span>';
|
||||
}
|
||||
|
||||
function generateRatiosGrids(columnsArr, rowsArr, itemsLength, viewportWidth, viewportHeight) {
|
||||
let i = 0,
|
||||
j,
|
||||
rw,
|
||||
rh,
|
||||
gcd,
|
||||
ret = {};
|
||||
|
||||
let defaultRatioWidth = 16;
|
||||
let defaultRatioheight = 9;
|
||||
|
||||
while (i < columnsArr.length) {
|
||||
j = 0;
|
||||
|
||||
while (j < rowsArr.length) {
|
||||
rw = defaultRatioWidth * columnsArr[i];
|
||||
rh = defaultRatioheight * rowsArr[j];
|
||||
|
||||
gcd = greaterCommonDivision(rw, rh);
|
||||
|
||||
if (1 < gcd) {
|
||||
rw = rw / gcd;
|
||||
rh = rh / gcd;
|
||||
}
|
||||
|
||||
if (columnsArr[i] * (rowsArr[j] - 1) < itemsLength) {
|
||||
ret[rw + '/' + rh] = ret[rw + '/' + rh] || { val: rw / rh, grid: [] };
|
||||
ret[rw + '/' + rh].grid.push([columnsArr[i], rowsArr[j]]);
|
||||
}
|
||||
|
||||
j += 1;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function matchedRatioGrids(ratios, ww, wh, pw, ph, pr, itemWidthBreakpoint) {
|
||||
let dist = [],
|
||||
pntr = {},
|
||||
ret = [];
|
||||
let x, k, availableGrids;
|
||||
|
||||
if (3 * itemWidthBreakpoint <= pw) {
|
||||
ret = 1.6 < pr ? [4, 3] : [3, 4];
|
||||
} else if (1.5 * itemWidthBreakpoint >= pw) {
|
||||
if (160 >= ph) {
|
||||
ret = [1, 1];
|
||||
} else if (320 >= ph) {
|
||||
ret = [1, 2];
|
||||
} else if (480 >= ph) {
|
||||
ret = [1, 3];
|
||||
} else if (640 >= ph) {
|
||||
ret = [1, 4];
|
||||
} else if (800 >= ph) {
|
||||
ret = [1, 6];
|
||||
} else {
|
||||
ret = [1, 6];
|
||||
}
|
||||
} else if (2.5 * itemWidthBreakpoint >= pw) {
|
||||
ret = [2, 1];
|
||||
|
||||
if (160 >= ph) {
|
||||
ret = [2, 1];
|
||||
} else if (320 >= ph) {
|
||||
ret = [2, 2];
|
||||
} else if (480 >= ph) {
|
||||
ret = [2, 3];
|
||||
} else if (640 >= ph) {
|
||||
ret = [2, 4];
|
||||
} else if (800 >= ph) {
|
||||
ret = [2, 5];
|
||||
} else {
|
||||
ret = [2, 6];
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret.length) {
|
||||
for (k in ratios) {
|
||||
if (ratios.hasOwnProperty(k)) {
|
||||
x = Math.abs(pr - ratios[k].val);
|
||||
dist.push(x);
|
||||
pntr[x] = k;
|
||||
}
|
||||
}
|
||||
|
||||
availableGrids = ratios[pntr[quickSort(dist, 0, dist.length - 1)[0]]].grid;
|
||||
|
||||
if (1 < availableGrids.length) {
|
||||
dist = [];
|
||||
pntr = {};
|
||||
k = 0;
|
||||
while (k < availableGrids.length) {
|
||||
x = Math.abs(pw - availableGrids[k][0] * itemWidthBreakpoint);
|
||||
dist.push(x);
|
||||
pntr[x] = k;
|
||||
k += 1;
|
||||
}
|
||||
ret = availableGrids[pntr[quickSort(dist, 0, dist.length - 1)[0]]];
|
||||
} else {
|
||||
ret = availableGrids[0];
|
||||
}
|
||||
|
||||
if (2 * itemWidthBreakpoint >= pw) {
|
||||
ret[0] = Math.min(2, ret[0]);
|
||||
} else if (3 * itemWidthBreakpoint >= pw) {
|
||||
ret[0] = Math.min(3, ret[0]);
|
||||
}
|
||||
|
||||
if (390 >= ph) {
|
||||
ret[1] = Math.min(2, ret[1]);
|
||||
} else if (590 >= ph) {
|
||||
ret[1] = Math.min(3, ret[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function gridClassname(itemsLength, container) {
|
||||
if (!itemsLength || !container || !container.firstChild) {
|
||||
return '';
|
||||
}
|
||||
const ww = window.outerWidth;
|
||||
const wh = window.outerHeight;
|
||||
const child = container.firstChild;
|
||||
const pw = child.offsetWidth;
|
||||
const ph = child.offsetHeight;
|
||||
const pr = pw / ph;
|
||||
let ret = matchedRatioGrids(
|
||||
generateRatiosGrids([1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6], itemsLength, parseInt(ww, 10), parseInt(wh, 10)),
|
||||
ww,
|
||||
wh,
|
||||
pw,
|
||||
ph,
|
||||
pr,
|
||||
250
|
||||
);
|
||||
return ret.length ? ' grid-col-' + ret[0] + ' grid-row-' + ret[1] : '';
|
||||
}
|
||||
|
||||
function buildItemsElements(itemsData, items, wrapper, inEmbed, hideViews) {
|
||||
let i = 0;
|
||||
|
||||
while (i < itemsData.length) {
|
||||
_MediaDurationInfo.update(itemsData[i].duration);
|
||||
|
||||
items[i] = document.createElement('div');
|
||||
|
||||
items[i].setAttribute('class', 'more-media-item before-more-media-item-load');
|
||||
items[i].setAttribute('style', '--n: ' + i);
|
||||
|
||||
items[i].innerHTML =
|
||||
'<a href="' +
|
||||
itemsData[i].url +
|
||||
'" title="' +
|
||||
itemsData[i].title +
|
||||
'"' +
|
||||
(inEmbed ? 'target="_blank"' : '') +
|
||||
'>' +
|
||||
itemThumb(itemsData[i].thumbnail_url, _MediaDurationInfo.toString()) +
|
||||
itemContent(
|
||||
itemsData[i].title,
|
||||
itemsData[i].author_name,
|
||||
formatViewsNumber(itemsData[i].views) + ' ' + (1 >= itemsData[i].views ? 'view' : 'views'),
|
||||
hideViews
|
||||
) +
|
||||
'</a>';
|
||||
|
||||
wrapper.appendChild(items[i]);
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
export function PlayerRecommendedMedia(itemsData, inEmbed, hideViews) {
|
||||
inEmbed = inEmbed || false;
|
||||
|
||||
let container = null;
|
||||
|
||||
function updateSlider(afterItemsUpdate) {
|
||||
if (!inlineSlider) {
|
||||
if (!domElems.contentInner.offsetWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
inlineSlider = new ItemsInlineSlider(domElems.contentInner, '.more-media-item');
|
||||
disableItemsRevealAnim();
|
||||
}
|
||||
|
||||
inlineSlider.updateDataState(itemsData.length, true, true);
|
||||
|
||||
updateSliderButtonsView();
|
||||
}
|
||||
|
||||
function disableItemsRevealAnim() {
|
||||
setTimeout(function () {
|
||||
let i = 0;
|
||||
|
||||
while (i < domElems.items.length) {
|
||||
domElems.items[i].setAttribute('class', 'more-media-item');
|
||||
domElems.items[i].setAttribute('style', null);
|
||||
i += 1;
|
||||
}
|
||||
}, domElems.items.length * 75 + 200); // NOTE: 200ms is reveal animation duration and 75ms is reveal animation delay (in CSS, with class selector '.before-more-media-item-load' ).
|
||||
}
|
||||
|
||||
function updateSliderButtonsView() {
|
||||
domElems.prevSlide.style.display = inlineSlider.hasPreviousSlide() ? '' : 'none';
|
||||
domElems.nextSlide.style.display = inlineSlider.hasNextSlide() ? '' : 'none';
|
||||
}
|
||||
|
||||
function toggleInlineVisibility(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
state.openInlineMoreMedia = !state.openInlineMoreMedia;
|
||||
(state.openInlineMoreMedia ? removeClassname : addClassname)(domElems.wrapper, 'hidden-inline-more-media');
|
||||
updateSlider(false);
|
||||
}
|
||||
|
||||
function clickPreviousBtn(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
inlineSlider.previousSlide();
|
||||
updateSliderButtonsView();
|
||||
inlineSlider.scrollToCurrentSlide();
|
||||
}
|
||||
|
||||
function clickNextBtn(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
inlineSlider.nextSlide();
|
||||
updateSliderButtonsView();
|
||||
inlineSlider.scrollToCurrentSlide();
|
||||
}
|
||||
|
||||
function wrapperClassname() {
|
||||
if (null === container) {
|
||||
return;
|
||||
}
|
||||
|
||||
let ret = 'more-media';
|
||||
|
||||
switch (state.displayType) {
|
||||
case 'full':
|
||||
ret += ' full-wrapper';
|
||||
break;
|
||||
case 'inline-small':
|
||||
ret += ' inline-slider-small';
|
||||
break;
|
||||
case 'inline':
|
||||
ret += ' inline-slider';
|
||||
break;
|
||||
}
|
||||
|
||||
ret += state.openInlineMoreMedia ? '' : ' hidden-inline-more-media';
|
||||
|
||||
return ret + gridClassname(itemsData.length, container);
|
||||
}
|
||||
|
||||
function updateWrapperParentStyle() {
|
||||
switch (state.displayType) {
|
||||
case 'full':
|
||||
domElems.wrapper.parentNode.style.top = '';
|
||||
break;
|
||||
case 'inline-small':
|
||||
case 'inline':
|
||||
domElems.wrapper.parentNode.style.top = 'auto';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function updateWrapperClassname() {
|
||||
domElems.wrapper.setAttribute('class', wrapperClassname());
|
||||
}
|
||||
|
||||
let inlineSlider;
|
||||
|
||||
const domElems = {
|
||||
wrapper: document.createElement('div'),
|
||||
title: document.createElement('h2'),
|
||||
openBtn: document.createElement('button'),
|
||||
closeBtn: document.createElement('button'),
|
||||
prevSlide: document.createElement('div'),
|
||||
nextSlide: document.createElement('div'),
|
||||
prevSlideBtn: null,
|
||||
nextSlideBtn: null,
|
||||
content: document.createElement('div'),
|
||||
contentInner: document.createElement('div'),
|
||||
items: [],
|
||||
};
|
||||
|
||||
const state = {
|
||||
isInited: false,
|
||||
displayType: 'inline',
|
||||
openInlineMoreMedia: true,
|
||||
};
|
||||
|
||||
domElems.title.innerHTML = 'More videos';
|
||||
domElems.openBtn.innerHTML = 'More videos';
|
||||
domElems.prevSlide.innerHTML =
|
||||
'<button class="circle-icon-button"><span><span><i class="vjs-icon-navigate-before"></i></span></span></button>';
|
||||
domElems.nextSlide.innerHTML =
|
||||
'<button class="circle-icon-button"><span><span><i class="vjs-icon-navigate-next"></i></span></span></button>';
|
||||
|
||||
domElems.title.setAttribute('class', 'more-media-wrap-title');
|
||||
domElems.openBtn.setAttribute('class', 'open-more-videos');
|
||||
domElems.closeBtn.setAttribute('class', 'close-more-videos vjs-icon-close');
|
||||
domElems.prevSlide.setAttribute('class', 'prev-slide');
|
||||
domElems.nextSlide.setAttribute('class', 'next-slide');
|
||||
|
||||
domElems.content.appendChild(domElems.contentInner);
|
||||
domElems.wrapper.appendChild(domElems.title);
|
||||
domElems.wrapper.appendChild(domElems.openBtn);
|
||||
domElems.wrapper.appendChild(domElems.closeBtn);
|
||||
domElems.wrapper.appendChild(domElems.content);
|
||||
domElems.content.appendChild(domElems.prevSlide);
|
||||
domElems.content.appendChild(domElems.nextSlide);
|
||||
|
||||
domElems.prevSlideBtn = domElems.prevSlide.querySelector('button');
|
||||
domElems.nextSlideBtn = domElems.nextSlide.querySelector('button');
|
||||
|
||||
function bindEvents() {
|
||||
if (domElems.prevSlideBtn) {
|
||||
domElems.prevSlideBtn.addEventListener('click', clickPreviousBtn);
|
||||
}
|
||||
|
||||
if (domElems.nextSlideBtn) {
|
||||
domElems.nextSlideBtn.addEventListener('click', clickNextBtn);
|
||||
}
|
||||
|
||||
domElems.openBtn.addEventListener('click', toggleInlineVisibility);
|
||||
domElems.closeBtn.addEventListener('click', toggleInlineVisibility);
|
||||
}
|
||||
|
||||
function unbindEvents() {
|
||||
if (domElems.prevSlideBtn) {
|
||||
domElems.prevSlideBtn.removeEventListener('click', clickPreviousBtn);
|
||||
}
|
||||
|
||||
if (domElems.nextSlideBtn) {
|
||||
domElems.nextSlideBtn.removeEventListener('click', clickNextBtn);
|
||||
}
|
||||
|
||||
domElems.openBtn.removeEventListener('click', toggleInlineVisibility);
|
||||
domElems.closeBtn.removeEventListener('click', toggleInlineVisibility);
|
||||
}
|
||||
|
||||
this.html = function () {
|
||||
return domElems.wrapper;
|
||||
};
|
||||
|
||||
this.onResize = function () {
|
||||
updateWrapperClassname();
|
||||
|
||||
switch (state.displayType) {
|
||||
case 'inline':
|
||||
updateSlider(false);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
this.initWrappers = function (wrapper) {
|
||||
container = wrapper;
|
||||
|
||||
updateWrapperParentStyle();
|
||||
updateWrapperClassname();
|
||||
};
|
||||
|
||||
this.init = function () {
|
||||
if (!state.itemsAreBuilt) {
|
||||
state.itemsAreBuilt = true;
|
||||
buildItemsElements(itemsData, domElems.items, domElems.contentInner, inEmbed, hideViews);
|
||||
}
|
||||
|
||||
switch (state.displayType) {
|
||||
case 'inline':
|
||||
if (inlineSlider) {
|
||||
updateSlider(false);
|
||||
disableItemsRevealAnim();
|
||||
} else {
|
||||
updateSlider(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
this.destroy = function () {
|
||||
unbindEvents();
|
||||
};
|
||||
|
||||
this.updateDisplayType = function (type) {
|
||||
let newType, i;
|
||||
|
||||
switch (type) {
|
||||
case 'full':
|
||||
case 'inline':
|
||||
case 'inline-small':
|
||||
newType = type;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newType && newType !== state.displayType) {
|
||||
state.displayType = newType;
|
||||
|
||||
updateWrapperParentStyle();
|
||||
updateWrapperClassname();
|
||||
|
||||
i = 0;
|
||||
while (i < domElems.items.length) {
|
||||
domElems.items[i].setAttribute('class', 'more-media-item before-more-media-item-load');
|
||||
domElems.items[i].setAttribute('style', '--n: ' + i);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
switch (newType) {
|
||||
case 'full':
|
||||
disableItemsRevealAnim();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bindEvents();
|
||||
}
|
||||
140
frontend/src/static/js/utils/classes/UpNextLoaderView.js
Executable file
140
frontend/src/static/js/utils/classes/UpNextLoaderView.js
Executable file
@@ -0,0 +1,140 @@
|
||||
import { addClassname, removeClassname } from '../helpers/';
|
||||
|
||||
export function UpNextLoaderView(nextItemData) {
|
||||
var timerTimeout;
|
||||
|
||||
var onTimerComplete = function () {
|
||||
window.location.href = nextItemData.url;
|
||||
};
|
||||
|
||||
var showView = function () {
|
||||
removeClassname(this.vjsPlayerElem, 'vjs-mediacms-up-next-hidden');
|
||||
}.bind(this);
|
||||
|
||||
var hideView = function () {
|
||||
this.cancelTimer();
|
||||
addClassname(this.vjsPlayerElem, 'vjs-mediacms-up-next-hidden');
|
||||
}.bind(this);
|
||||
|
||||
var domElems = {
|
||||
nextMediaPoster: document.createElement('div'),
|
||||
wrapper: document.createElement('div'),
|
||||
inner: document.createElement('div'),
|
||||
innerContent: document.createElement('div'),
|
||||
upNextLabel: document.createElement('div'),
|
||||
nextMediaTitle: document.createElement('div'),
|
||||
nextMediaAuthor: document.createElement('div'),
|
||||
cancelNext: document.createElement('div'),
|
||||
cancelNextButton: document.createElement('button'),
|
||||
goNext: document.createElement('div'),
|
||||
};
|
||||
|
||||
domElems.nextMediaPoster.setAttribute('class', 'next-media-poster');
|
||||
domElems.wrapper.setAttribute('class', 'up-next-loader');
|
||||
domElems.inner.setAttribute('class', 'up-next-loader-inner');
|
||||
domElems.goNext.setAttribute('class', 'go-next');
|
||||
domElems.cancelNext.setAttribute('class', 'up-next-cancel');
|
||||
|
||||
domElems.upNextLabel.setAttribute('class', 'up-next-label');
|
||||
domElems.nextMediaTitle.setAttribute('class', 'next-media-title');
|
||||
domElems.nextMediaAuthor.setAttribute('class', 'next-media-author');
|
||||
|
||||
domElems.upNextLabel.innerHTML = 'Up Next';
|
||||
domElems.nextMediaTitle.innerHTML = nextItemData.title;
|
||||
domElems.nextMediaAuthor.innerHTML = nextItemData.author_name;
|
||||
|
||||
domElems.goNext.innerHTML =
|
||||
'<a href="' +
|
||||
nextItemData.url +
|
||||
'"><svg class="radial-timer"><circle r="30" cx="32" cy="32"></circle><circle r="25" cx="28" cy="28"></circle></svg><span></span><i class="material-icons">skip_next</i></a>';
|
||||
|
||||
domElems.cancelNextButton.innerHTML = 'CANCEL';
|
||||
|
||||
domElems.cancelNextButton.addEventListener('click', hideView);
|
||||
|
||||
domElems.nextMediaPoster.style.backgroundImage = "url('" + nextItemData.thumbnail_url + "')";
|
||||
|
||||
domElems.cancelNext.appendChild(domElems.cancelNextButton);
|
||||
|
||||
domElems.innerContent.appendChild(domElems.upNextLabel);
|
||||
domElems.innerContent.appendChild(domElems.nextMediaTitle);
|
||||
domElems.innerContent.appendChild(domElems.nextMediaAuthor);
|
||||
domElems.innerContent.appendChild(domElems.goNext);
|
||||
domElems.innerContent.appendChild(domElems.cancelNext);
|
||||
|
||||
domElems.inner.appendChild(domElems.innerContent);
|
||||
|
||||
domElems.wrapper.appendChild(domElems.nextMediaPoster);
|
||||
domElems.wrapper.appendChild(domElems.inner);
|
||||
|
||||
var pauseTimerOnScroll = false;
|
||||
|
||||
function onScrollHandler() {
|
||||
var rect = this.vjsPlayerElem.getBoundingClientRect();
|
||||
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||||
if (0 >= this.vjsPlayerElem.offsetHeight - 56 + rect.top) {
|
||||
// NOTE: 56 pixels is the value of pages header bar height.
|
||||
if (!pauseTimerOnScroll) {
|
||||
this.cancelTimer(true);
|
||||
}
|
||||
pauseTimerOnScroll = true;
|
||||
} else {
|
||||
if (pauseTimerOnScroll) {
|
||||
this.startTimer();
|
||||
}
|
||||
pauseTimerOnScroll = false;
|
||||
}
|
||||
}
|
||||
|
||||
onScrollHandler = onScrollHandler.bind(this);
|
||||
|
||||
function startOnScrollHandler() {
|
||||
window.addEventListener('scroll', onScrollHandler);
|
||||
}
|
||||
|
||||
function stopOnScrollHandler() {
|
||||
window.removeEventListener('scroll', onScrollHandler);
|
||||
}
|
||||
|
||||
this.vjsPlayerElem = null;
|
||||
|
||||
this.html = function () {
|
||||
return domElems.wrapper;
|
||||
};
|
||||
|
||||
this.startTimer = function () {
|
||||
showView();
|
||||
timerTimeout = setTimeout(onTimerComplete, 10 * 1000);
|
||||
if (this.vjsPlayerElem) removeClassname(this.vjsPlayerElem, 'vjs-mediacms-canceled-next');
|
||||
startOnScrollHandler();
|
||||
};
|
||||
|
||||
this.cancelTimer = function (onScrollPause) {
|
||||
onScrollPause = !!onScrollPause;
|
||||
if (!onScrollPause) {
|
||||
stopOnScrollHandler();
|
||||
}
|
||||
clearTimeout(timerTimeout);
|
||||
timerTimeout = null;
|
||||
if (this.vjsPlayerElem) addClassname(this.vjsPlayerElem, 'vjs-mediacms-canceled-next');
|
||||
};
|
||||
|
||||
this.setVideoJsPlayerElem = function (el) {
|
||||
if (!el) return;
|
||||
this.vjsPlayerElem = el;
|
||||
addClassname(this.vjsPlayerElem, 'vjs-mediacms-has-up-next-view');
|
||||
};
|
||||
|
||||
this.showTimerView = function (beginTimer) {
|
||||
beginTimer = !!beginTimer;
|
||||
if (beginTimer) {
|
||||
this.startTimer();
|
||||
} else {
|
||||
showView();
|
||||
}
|
||||
};
|
||||
|
||||
this.hideTimerView = function () {
|
||||
hideView();
|
||||
};
|
||||
}
|
||||
4
frontend/src/static/js/utils/classes/index.ts
Normal file
4
frontend/src/static/js/utils/classes/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './BrowserCache';
|
||||
export * from './MediaDurationInfo';
|
||||
export * from './PlayerRecommendedMedia';
|
||||
export * from './UpNextLoaderView';
|
||||
2
frontend/src/static/js/utils/constants/index.js
Normal file
2
frontend/src/static/js/utils/constants/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as months } from './months';
|
||||
export { default as weekdays } from './weekdays';
|
||||
14
frontend/src/static/js/utils/constants/months.js
Executable file
14
frontend/src/static/js/utils/constants/months.js
Executable file
@@ -0,0 +1,14 @@
|
||||
export default [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
];
|
||||
1
frontend/src/static/js/utils/constants/weekdays.js
Executable file
1
frontend/src/static/js/utils/constants/weekdays.js
Executable file
@@ -0,0 +1 @@
|
||||
export default ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
||||
5
frontend/src/static/js/utils/contexts/ApiUrlContext.js
Normal file
5
frontend/src/static/js/utils/contexts/ApiUrlContext.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import React, { createContext } from 'react';
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
export const ApiUrlContext = createContext(mediacmsConfig(window.MediaCMS).api);
|
||||
export const ApiUrlConsumer = ApiUrlContext.Consumer;
|
||||
129
frontend/src/static/js/utils/contexts/HeaderContext.js
Normal file
129
frontend/src/static/js/utils/contexts/HeaderContext.js
Normal file
@@ -0,0 +1,129 @@
|
||||
import React, { createContext } from 'react';
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
const config = mediacmsConfig(window.MediaCMS);
|
||||
|
||||
const links = config.url;
|
||||
const theme = config.theme;
|
||||
const user = config.member;
|
||||
|
||||
const hasThemeSwitcher = theme.switch.enabled && 'header' === theme.switch.position;
|
||||
|
||||
function popupTopNavItems() {
|
||||
const items = [];
|
||||
|
||||
if (!user.is.anonymous) {
|
||||
if (user.can.addMedia) {
|
||||
items.push({
|
||||
link: links.user.addMedia,
|
||||
icon: 'video_call',
|
||||
text: 'Upload media',
|
||||
itemAttr: {
|
||||
className: 'visible-only-in-small',
|
||||
},
|
||||
});
|
||||
|
||||
if (user.pages.media) {
|
||||
items.push({
|
||||
link: user.pages.media,
|
||||
icon: 'video_library',
|
||||
text: 'My media',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
items.push({
|
||||
link: links.signout,
|
||||
icon: 'exit_to_app',
|
||||
text: 'Sign out',
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
function popupMiddleNavItems() {
|
||||
const items = [];
|
||||
|
||||
if (hasThemeSwitcher) {
|
||||
items.push({
|
||||
itemType: 'open-subpage',
|
||||
icon: 'brightness_4',
|
||||
iconPos: 'left',
|
||||
text: 'Switch theme',
|
||||
buttonAttr: {
|
||||
className: 'change-page',
|
||||
'data-page-id': 'switch-theme',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (user.is.anonymous) {
|
||||
if (user.can.login) {
|
||||
items.push({
|
||||
itemType: 'link',
|
||||
icon: 'login',
|
||||
iconPos: 'left',
|
||||
text: 'Sign in',
|
||||
link: links.signin,
|
||||
linkAttr: {
|
||||
className: hasThemeSwitcher ? 'visible-only-in-small' : 'visible-only-in-extra-small',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (user.can.register) {
|
||||
items.push({
|
||||
itemType: 'link',
|
||||
icon: 'person_add',
|
||||
iconPos: 'left',
|
||||
text: 'Register',
|
||||
link: links.register,
|
||||
linkAttr: {
|
||||
className: hasThemeSwitcher ? 'visible-only-in-small' : 'visible-only-in-extra-small',
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
items.push({
|
||||
link: links.user.editProfile,
|
||||
icon: 'brush',
|
||||
text: 'Edit profile',
|
||||
});
|
||||
|
||||
if (user.can.changePassword) {
|
||||
items.push({
|
||||
link: links.changePassword,
|
||||
icon: 'lock',
|
||||
text: 'Change password',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
function popupBottomNavItems() {
|
||||
const items = [];
|
||||
|
||||
if (user.is.admin) {
|
||||
items.push({
|
||||
link: links.admin,
|
||||
icon: 'admin_panel_settings',
|
||||
text: 'MediaCMS administration',
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
export const HeaderContext = createContext({
|
||||
hasThemeSwitcher,
|
||||
popupNavItems: {
|
||||
top: popupTopNavItems(),
|
||||
middle: popupMiddleNavItems(),
|
||||
bottom: popupBottomNavItems(),
|
||||
},
|
||||
});
|
||||
|
||||
export const HeaderConsumer = HeaderContext.Consumer;
|
||||
101
frontend/src/static/js/utils/contexts/LayoutContext.js
Normal file
101
frontend/src/static/js/utils/contexts/LayoutContext.js
Normal file
@@ -0,0 +1,101 @@
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { BrowserCache } from '../classes/';
|
||||
import { PageStore } from '../stores/';
|
||||
import { addClassname, removeClassname } from '../helpers/';
|
||||
import SiteContext from './SiteContext';
|
||||
|
||||
let slidingSidebarTimeout;
|
||||
|
||||
function onSidebarVisibilityChange(visibleSidebar) {
|
||||
clearTimeout(slidingSidebarTimeout);
|
||||
|
||||
addClassname(document.body, 'sliding-sidebar');
|
||||
|
||||
slidingSidebarTimeout = setTimeout(function () {
|
||||
if ('media' === PageStore.get('current-page')) {
|
||||
if (visibleSidebar) {
|
||||
addClassname(document.body, 'overflow-hidden');
|
||||
} else {
|
||||
removeClassname(document.body, 'overflow-hidden');
|
||||
}
|
||||
} else {
|
||||
if (!visibleSidebar || 767 < window.innerWidth) {
|
||||
removeClassname(document.body, 'overflow-hidden');
|
||||
} else {
|
||||
addClassname(document.body, 'overflow-hidden');
|
||||
}
|
||||
}
|
||||
|
||||
if (visibleSidebar) {
|
||||
addClassname(document.body, 'visible-sidebar');
|
||||
} else {
|
||||
removeClassname(document.body, 'visible-sidebar');
|
||||
}
|
||||
|
||||
slidingSidebarTimeout = setTimeout(function () {
|
||||
slidingSidebarTimeout = null;
|
||||
removeClassname(document.body, 'sliding-sidebar');
|
||||
}, 220);
|
||||
}, 20);
|
||||
}
|
||||
|
||||
export const LayoutContext = createContext();
|
||||
|
||||
export const LayoutProvider = ({ children }) => {
|
||||
const site = useContext(SiteContext);
|
||||
const cache = new BrowserCache('MediaCMS[' + site.id + '][layout]', 86400);
|
||||
|
||||
const enabledSidebar = !!(document.getElementById('app-sidebar') || document.querySelector('.page-sidebar'));
|
||||
|
||||
const [visibleSidebar, setVisibleSidebar] = useState(cache.get('visible-sidebar'));
|
||||
const [visibleMobileSearch, setVisibleMobileSearch] = useState(false);
|
||||
|
||||
const toggleMobileSearch = () => {
|
||||
setVisibleMobileSearch(!visibleMobileSearch);
|
||||
};
|
||||
|
||||
const toggleSidebar = () => {
|
||||
const newval = !visibleSidebar;
|
||||
onSidebarVisibilityChange(newval);
|
||||
setVisibleSidebar(newval);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (visibleSidebar) {
|
||||
addClassname(document.body, 'visible-sidebar');
|
||||
} else {
|
||||
removeClassname(document.body, 'visible-sidebar');
|
||||
}
|
||||
if ('media' !== PageStore.get('current-page') && 1023 < window.innerWidth) {
|
||||
cache.set('visible-sidebar', visibleSidebar);
|
||||
}
|
||||
}, [visibleSidebar]);
|
||||
|
||||
useEffect(() => {
|
||||
PageStore.once('page_init', () => {
|
||||
if ('media' === PageStore.get('current-page')) {
|
||||
setVisibleSidebar(false);
|
||||
removeClassname(document.body, 'visible-sidebar');
|
||||
}
|
||||
});
|
||||
|
||||
setVisibleSidebar(
|
||||
'media' !== PageStore.get('current-page') &&
|
||||
1023 < window.innerWidth &&
|
||||
(null === visibleSidebar || visibleSidebar)
|
||||
);
|
||||
}, []);
|
||||
|
||||
const value = {
|
||||
enabledSidebar,
|
||||
visibleSidebar,
|
||||
setVisibleSidebar,
|
||||
visibleMobileSearch,
|
||||
toggleMobileSearch,
|
||||
toggleSidebar,
|
||||
};
|
||||
|
||||
return <LayoutContext.Provider value={value}>{children}</LayoutContext.Provider>;
|
||||
};
|
||||
|
||||
export const LayoutConsumer = LayoutContext.Consumer;
|
||||
5
frontend/src/static/js/utils/contexts/LinksContext.js
Normal file
5
frontend/src/static/js/utils/contexts/LinksContext.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import React, { createContext } from 'react';
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
export const LinksContext = createContext(mediacmsConfig(window.MediaCMS).url);
|
||||
export const LinksConsumer = LinksContext.Consumer;
|
||||
5
frontend/src/static/js/utils/contexts/MemberContext.js
Normal file
5
frontend/src/static/js/utils/contexts/MemberContext.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import React, { createContext } from 'react';
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
export const MemberContext = createContext(mediacmsConfig(window.MediaCMS).member);
|
||||
export const MemberConsumer = MemberContext.Consumer;
|
||||
@@ -0,0 +1,4 @@
|
||||
import React, { createContext } from 'react';
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
export const PlaylistsContext = createContext(mediacmsConfig(window.MediaCMS).playlists);
|
||||
@@ -0,0 +1,5 @@
|
||||
import React, { createContext } from 'react';
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
export const ShareOptionsContext = createContext(mediacmsConfig(window.MediaCMS).media.share.options);
|
||||
|
||||
5
frontend/src/static/js/utils/contexts/SidebarContext.js
Normal file
5
frontend/src/static/js/utils/contexts/SidebarContext.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import React, { createContext } from 'react';
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
export const SidebarContext = createContext(mediacmsConfig(window.MediaCMS).sidebar);
|
||||
export const SidebarConsumer = SidebarContext.Consumer;
|
||||
7
frontend/src/static/js/utils/contexts/SiteContext.js
Normal file
7
frontend/src/static/js/utils/contexts/SiteContext.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import React, { createContext } from 'react';
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
export const SiteContext = createContext(mediacmsConfig(window.MediaCMS).site);
|
||||
export const SiteConsumer = SiteContext.Consumer;
|
||||
|
||||
export default SiteContext;
|
||||
12
frontend/src/static/js/utils/contexts/TextsContext.js
Normal file
12
frontend/src/static/js/utils/contexts/TextsContext.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import React, { createContext } from 'react';
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
const notifications = mediacmsConfig(window.MediaCMS).notifications.messages;
|
||||
|
||||
const texts = {
|
||||
notifications,
|
||||
};
|
||||
|
||||
export const TextsContext = createContext(texts);
|
||||
|
||||
export const TextsConsumer = TextsContext.Consumer;
|
||||
80
frontend/src/static/js/utils/contexts/ThemeContext.js
Normal file
80
frontend/src/static/js/utils/contexts/ThemeContext.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { BrowserCache } from '../classes/';
|
||||
import { addClassname, removeClassname, supportsSvgAsImg } from '../helpers/';
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
import SiteContext from './SiteContext';
|
||||
|
||||
const config = mediacmsConfig(window.MediaCMS);
|
||||
|
||||
function initLogo(logo) {
|
||||
let light = null;
|
||||
let dark = null;
|
||||
|
||||
if (void 0 !== logo.darkMode) {
|
||||
if (supportsSvgAsImg() && void 0 !== logo.darkMode.svg && '' !== logo.darkMode.svg) {
|
||||
dark = logo.darkMode.svg;
|
||||
} else if (void 0 !== logo.darkMode.img && '' !== logo.darkMode.img) {
|
||||
dark = logo.darkMode.img;
|
||||
}
|
||||
}
|
||||
|
||||
if (void 0 !== logo.lightMode) {
|
||||
if (supportsSvgAsImg() && void 0 !== logo.lightMode.svg && '' !== logo.lightMode.svg) {
|
||||
light = logo.lightMode.svg;
|
||||
} else if (void 0 !== logo.lightMode.img && '' !== logo.lightMode.img) {
|
||||
light = logo.lightMode.img;
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== light || null !== dark) {
|
||||
if (null === light) {
|
||||
light = dark;
|
||||
} else if (null === dark) {
|
||||
dark = light;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
light,
|
||||
dark,
|
||||
};
|
||||
}
|
||||
|
||||
function initMode(cachedValue, defaultValue) {
|
||||
return 'light' === cachedValue || 'dark' === cachedValue ? cachedValue : defaultValue;
|
||||
}
|
||||
|
||||
export const ThemeContext = createContext();
|
||||
|
||||
export const ThemeProvider = ({ children }) => {
|
||||
const site = useContext(SiteContext);
|
||||
const cache = new BrowserCache('MediaCMS[' + site.id + '][theme]', 86400);
|
||||
const [themeMode, setThemeMode] = useState(initMode(cache.get('mode'), config.theme.mode));
|
||||
const logos = initLogo(config.theme.logo);
|
||||
const [logo, setLogo] = useState(logos[themeMode]);
|
||||
|
||||
const changeMode = () => {
|
||||
setThemeMode('light' === themeMode ? 'dark' : 'light');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if ('dark' === themeMode) {
|
||||
addClassname(document.body, 'dark_theme');
|
||||
} else {
|
||||
removeClassname(document.body, 'dark_theme');
|
||||
}
|
||||
cache.set('mode', themeMode);
|
||||
setLogo(logos[themeMode]);
|
||||
}, [themeMode]);
|
||||
|
||||
const value = {
|
||||
logo,
|
||||
currentThemeMode: themeMode,
|
||||
changeThemeMode: changeMode,
|
||||
themeModeSwitcher: config.theme.switch,
|
||||
};
|
||||
|
||||
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
|
||||
};
|
||||
|
||||
export const ThemeConsumer = ThemeContext.Consumer;
|
||||
22
frontend/src/static/js/utils/contexts/UserContext.js
Normal file
22
frontend/src/static/js/utils/contexts/UserContext.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import React, { createContext } from 'react';
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
export const UserContext = createContext();
|
||||
|
||||
const member = mediacmsConfig(window.MediaCMS).member;
|
||||
|
||||
export const UserProvider = ({ children }) => {
|
||||
const value = {
|
||||
isAnonymous: member.is.anonymous,
|
||||
username: member.username,
|
||||
thumbnail: member.thumbnail,
|
||||
userCan: member.can,
|
||||
pages: member.pages,
|
||||
};
|
||||
|
||||
return <UserContext.Provider value={value}>{children}</UserContext.Provider>;
|
||||
};
|
||||
|
||||
export const UserConsumer = UserContext.Consumer;
|
||||
|
||||
export default UserContext;
|
||||
12
frontend/src/static/js/utils/contexts/index.ts
Normal file
12
frontend/src/static/js/utils/contexts/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export * from './ApiUrlContext';
|
||||
export * from './HeaderContext';
|
||||
export * from './LayoutContext';
|
||||
export * from './LinksContext';
|
||||
export * from './MemberContext';
|
||||
export * from './PlaylistsContext';
|
||||
export * from './ShareOptionsContext';
|
||||
export * from './SidebarContext';
|
||||
export * from './SiteContext';
|
||||
export * from './TextsContext';
|
||||
export * from './ThemeContext';
|
||||
export * from './UserContext';
|
||||
2
frontend/src/static/js/utils/dispatcher.js
Executable file
2
frontend/src/static/js/utils/dispatcher.js
Executable file
@@ -0,0 +1,2 @@
|
||||
const Dispatcher = require('flux').Dispatcher;
|
||||
module.exports = new Dispatcher();
|
||||
19
frontend/src/static/js/utils/helpers/csrfToken.js
Executable file
19
frontend/src/static/js/utils/helpers/csrfToken.js
Executable file
@@ -0,0 +1,19 @@
|
||||
export function csrfToken() {
|
||||
var i,
|
||||
cookies,
|
||||
cookie,
|
||||
cookieVal = null;
|
||||
if (document.cookie && '' !== document.cookie) {
|
||||
cookies = document.cookie.split(';');
|
||||
i = 0;
|
||||
while (i < cookies.length) {
|
||||
cookie = cookies[i].trim();
|
||||
if ('csrftoken=' === cookie.substring(0, 10)) {
|
||||
cookieVal = decodeURIComponent(cookie.substring(10));
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
return cookieVal;
|
||||
}
|
||||
79
frontend/src/static/js/utils/helpers/dom.js
Executable file
79
frontend/src/static/js/utils/helpers/dom.js
Executable file
@@ -0,0 +1,79 @@
|
||||
export function supportsSvgAsImg() {
|
||||
// @link: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/svg/asimg.js
|
||||
return document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#Image', '1.1');
|
||||
}
|
||||
|
||||
export function removeClassname(el, cls) {
|
||||
if (el.classList) {
|
||||
el.classList.remove(cls);
|
||||
} else {
|
||||
el.className = el.className.replace(new RegExp('(^|\\b)' + cls.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
|
||||
}
|
||||
}
|
||||
|
||||
export function addClassname(el, cls) {
|
||||
if (el.classList) {
|
||||
el.classList.add(cls);
|
||||
} else {
|
||||
el.className += ' ' + cls;
|
||||
}
|
||||
}
|
||||
|
||||
export function hasClassname(el, cls) {
|
||||
return el.className && new RegExp('(\\s|^)' + cls + '(\\s|$)').test(el.className);
|
||||
}
|
||||
|
||||
export const cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;
|
||||
|
||||
export const requestAnimationFrame =
|
||||
window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
|
||||
|
||||
export function BrowserEvents() {
|
||||
const callbacks = {
|
||||
document: {
|
||||
visibility: [],
|
||||
},
|
||||
window: {
|
||||
resize: [],
|
||||
scroll: [],
|
||||
},
|
||||
};
|
||||
|
||||
function onDocumentVisibilityChange() {
|
||||
callbacks.document.visibility.map((fn) => fn());
|
||||
}
|
||||
|
||||
function onWindowResize() {
|
||||
callbacks.window.resize.map((fn) => fn());
|
||||
}
|
||||
|
||||
function onWindowScroll() {
|
||||
callbacks.window.scroll.map((fn) => fn());
|
||||
}
|
||||
|
||||
function windowEvents(resizeCallback, scrollCallback) {
|
||||
if ('function' === typeof resizeCallback) {
|
||||
callbacks.window.resize.push(resizeCallback);
|
||||
}
|
||||
|
||||
if ('function' === typeof scrollCallback) {
|
||||
callbacks.window.scroll.push(scrollCallback);
|
||||
}
|
||||
}
|
||||
|
||||
function documentEvents(visibilityChangeCallback) {
|
||||
if ('function' === typeof visibilityChangeCallback) {
|
||||
callbacks.document.visibility.push(visibilityChangeCallback);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('visibilitychange', onDocumentVisibilityChange);
|
||||
|
||||
window.addEventListener('resize', onWindowResize);
|
||||
window.addEventListener('scroll', onWindowScroll);
|
||||
|
||||
return {
|
||||
doc: documentEvents,
|
||||
win: windowEvents,
|
||||
};
|
||||
}
|
||||
27
frontend/src/static/js/utils/helpers/errors.js
Executable file
27
frontend/src/static/js/utils/helpers/errors.js
Executable file
@@ -0,0 +1,27 @@
|
||||
// TODO: Improve or (even better) remove this file code.
|
||||
|
||||
import { error as logErrFn, warn as logWarnFn } from './log';
|
||||
|
||||
function logAndReturnError(logFn, msgArr, ErrorConstructor) {
|
||||
let err;
|
||||
switch (ErrorConstructor) {
|
||||
case TypeError:
|
||||
case RangeError:
|
||||
case SyntaxError:
|
||||
case ReferenceError:
|
||||
err = new ErrorConstructor(msgArr[0]);
|
||||
break;
|
||||
default:
|
||||
err = new Error(msgArr[0]);
|
||||
}
|
||||
logFn(err.message, ...msgArr.slice(1));
|
||||
return err;
|
||||
}
|
||||
|
||||
export function logErrorAndReturnError(msgArr, ErrorConstructor) {
|
||||
return logAndReturnError(logErrFn, msgArr, ErrorConstructor);
|
||||
}
|
||||
|
||||
export function logWarningAndReturnError(msgArr, ErrorConstructor) {
|
||||
return logAndReturnError(logWarnFn, msgArr, ErrorConstructor);
|
||||
}
|
||||
5
frontend/src/static/js/utils/helpers/exportStore.js
Executable file
5
frontend/src/static/js/utils/helpers/exportStore.js
Executable file
@@ -0,0 +1,5 @@
|
||||
import * as dispatcher from '../dispatcher.js';
|
||||
export default function (store, handler) {
|
||||
dispatcher.register(store[handler].bind(store));
|
||||
return store;
|
||||
}
|
||||
11
frontend/src/static/js/utils/helpers/formatInnerLink.js
Executable file
11
frontend/src/static/js/utils/helpers/formatInnerLink.js
Executable file
@@ -0,0 +1,11 @@
|
||||
import urlParse from 'url-parse';
|
||||
|
||||
export function formatInnerLink(url, baseUrl) {
|
||||
let link = urlParse(url, {});
|
||||
|
||||
if ('' === link.origin || 'null' === link.origin || !link.origin) {
|
||||
link = urlParse(baseUrl + '/' + url.replace(/^\//g, ''), {});
|
||||
}
|
||||
|
||||
return link.toString();
|
||||
}
|
||||
15
frontend/src/static/js/utils/helpers/formatManagementTableDate.js
Executable file
15
frontend/src/static/js/utils/helpers/formatManagementTableDate.js
Executable file
@@ -0,0 +1,15 @@
|
||||
import { months as monthList } from '../constants/';
|
||||
|
||||
export function formatManagementTableDate(date) {
|
||||
const day = date.getDate();
|
||||
const month = monthList[date.getMonth()].substring(0, 3);
|
||||
const year = date.getFullYear();
|
||||
const hours = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
const seconds = date.getSeconds();
|
||||
let ret = month + ' ' + day + ', ' + year;
|
||||
ret += ' ' + (hours < 10 ? '0' : '') + hours;
|
||||
ret += ':' + (minutes < 10 ? '0' : '') + minutes;
|
||||
ret += ':' + (seconds < 10 ? '0' : '') + seconds;
|
||||
return ret;
|
||||
}
|
||||
18
frontend/src/static/js/utils/helpers/formatViewsNumber.js
Executable file
18
frontend/src/static/js/utils/helpers/formatViewsNumber.js
Executable file
@@ -0,0 +1,18 @@
|
||||
export default function (views_number, fullNumber) {
|
||||
function formattedValue(val, lim, unit) {
|
||||
return Number(parseFloat(val / lim).toFixed(val < 10 * lim ? 1 : 0)) + unit;
|
||||
}
|
||||
|
||||
function format(i, views, mult, compare, limit, units) {
|
||||
while (views >= compare) {
|
||||
limit *= mult;
|
||||
compare *= mult;
|
||||
i += 1;
|
||||
}
|
||||
return i < units.length
|
||||
? formattedValue(views, limit, units[i])
|
||||
: formattedValue(views * (mult * (i - (units.length - 1))), limit, units[units.length - 1]);
|
||||
}
|
||||
|
||||
return fullNumber ? views_number.toLocaleString() : format(0, views_number, 1000, 1000, 1, ['', 'K', 'M', 'B', 'T']);
|
||||
}
|
||||
7
frontend/src/static/js/utils/helpers/imageExtension.js
Executable file
7
frontend/src/static/js/utils/helpers/imageExtension.js
Executable file
@@ -0,0 +1,7 @@
|
||||
export const imageExtension = (img) => {
|
||||
if (!img) {
|
||||
return;
|
||||
}
|
||||
const ext = img.split('.');
|
||||
return ext[ext.length - 1];
|
||||
};
|
||||
14
frontend/src/static/js/utils/helpers/index.js
Normal file
14
frontend/src/static/js/utils/helpers/index.js
Normal file
@@ -0,0 +1,14 @@
|
||||
export * from './dom';
|
||||
export * from './errors';
|
||||
export { default as exportStore } from './exportStore';
|
||||
export { formatInnerLink } from './formatInnerLink';
|
||||
export * from './formatManagementTableDate';
|
||||
export { default as formatViewsNumber } from './formatViewsNumber';
|
||||
export * from './csrfToken';
|
||||
export { imageExtension } from './imageExtension';
|
||||
export * from './log';
|
||||
export * from './math';
|
||||
export * from './propTypeFilters';
|
||||
export { default as publishedOnDate } from './publishedOnDate';
|
||||
export * from './quickSort';
|
||||
export * from './requests';
|
||||
4
frontend/src/static/js/utils/helpers/log.js
Executable file
4
frontend/src/static/js/utils/helpers/log.js
Executable file
@@ -0,0 +1,4 @@
|
||||
const log = (...x) => console[x[0]](...x.slice(1));
|
||||
|
||||
export const warn = (...x) => log('warn', ...x);
|
||||
export const error = (...x) => log('error', ...x);
|
||||
10
frontend/src/static/js/utils/helpers/math.js
Executable file
10
frontend/src/static/js/utils/helpers/math.js
Executable file
@@ -0,0 +1,10 @@
|
||||
export const isGt = (x, y) => x > y;
|
||||
export const isZero = (x) => 0 === x;
|
||||
export const isNumber = (x) => !isNaN(x) && x === 0 + x;
|
||||
export const isInteger = (x) => x === Math.trunc(x);
|
||||
export const isPositive = (x) => isGt(x, 0);
|
||||
export const isPositiveNumber = (x) => isNumber(x) && isPositive(x);
|
||||
export const isPositiveInteger = (x) => isInteger(x) && isPositive(x);
|
||||
export const isPositiveIntegerOrZero = (x) => isInteger(x) && (isPositive(x) || isZero(x));
|
||||
|
||||
export const greaterCommonDivision = (a, b) => (!b ? a : greaterCommonDivision(b, a % b));
|
||||
38
frontend/src/static/js/utils/helpers/propTypeFilters.js
Executable file
38
frontend/src/static/js/utils/helpers/propTypeFilters.js
Executable file
@@ -0,0 +1,38 @@
|
||||
import { logErrorAndReturnError } from './errors';
|
||||
import { isPositiveInteger, isPositiveIntegerOrZero } from './math';
|
||||
|
||||
export const PositiveIntegerOrZero = (function () {
|
||||
return function (obj, key, comp) {
|
||||
return void 0 === obj[key] || isPositiveIntegerOrZero(obj[key])
|
||||
? null
|
||||
: logErrorAndReturnError([
|
||||
'Invalid prop `' +
|
||||
key +
|
||||
'` of type `' +
|
||||
typeof obj[key] +
|
||||
'` supplied to `' +
|
||||
(comp || 'N/A') +
|
||||
'`, expected `positive integer or zero` (' +
|
||||
obj[key] +
|
||||
').',
|
||||
]);
|
||||
};
|
||||
})();
|
||||
|
||||
export const PositiveInteger = (function () {
|
||||
return function (obj, key, comp) {
|
||||
return void 0 === obj[key] || isPositiveInteger(obj[key])
|
||||
? null
|
||||
: logErrorAndReturnError([
|
||||
'Invalid prop `' +
|
||||
key +
|
||||
'` of type `' +
|
||||
typeof obj[key] +
|
||||
'` supplied to `' +
|
||||
(comp || 'N/A') +
|
||||
'`, expected `positive integer` (' +
|
||||
obj[key] +
|
||||
').',
|
||||
]);
|
||||
};
|
||||
})();
|
||||
17
frontend/src/static/js/utils/helpers/publishedOnDate.js
Executable file
17
frontend/src/static/js/utils/helpers/publishedOnDate.js
Executable file
@@ -0,0 +1,17 @@
|
||||
import { months } from '../constants';
|
||||
|
||||
export default function publishedOnDate(date, type) {
|
||||
if (date instanceof Date) {
|
||||
type = 0 + type;
|
||||
type = 0 < type ? type : 1;
|
||||
switch (type) {
|
||||
case 1:
|
||||
return months[date.getMonth()].substring(0, 3) + ' ' + date.getDate() + ', ' + date.getFullYear();
|
||||
case 2:
|
||||
return date.getDate() + ' ' + months[date.getMonth()].substring(0, 3) + ' ' + date.getFullYear();
|
||||
case 3:
|
||||
return date.getDate() + ' ' + months[date.getMonth()] + ' ' + date.getFullYear();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
35
frontend/src/static/js/utils/helpers/quickSort.js
Executable file
35
frontend/src/static/js/utils/helpers/quickSort.js
Executable file
@@ -0,0 +1,35 @@
|
||||
function swap(arr, i, j) {
|
||||
var temp = arr[i];
|
||||
arr[i] = arr[j];
|
||||
arr[j] = temp;
|
||||
}
|
||||
|
||||
function partition(arr, pivot, left, right) {
|
||||
var pivotValue = arr[pivot],
|
||||
partitionIndex = left;
|
||||
|
||||
for (var i = left; i < right; i++) {
|
||||
if (arr[i] < pivotValue) {
|
||||
swap(arr, i, partitionIndex);
|
||||
partitionIndex++;
|
||||
}
|
||||
}
|
||||
swap(arr, right, partitionIndex);
|
||||
return partitionIndex;
|
||||
}
|
||||
|
||||
export function quickSort(arr, left, right) {
|
||||
var len = arr.length,
|
||||
pivot,
|
||||
partitionIndex;
|
||||
|
||||
if (left < right) {
|
||||
pivot = right;
|
||||
partitionIndex = partition(arr, pivot, left, right);
|
||||
|
||||
//sort left and right
|
||||
quickSort(arr, left, partitionIndex - 1);
|
||||
quickSort(arr, partitionIndex + 1, right);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
135
frontend/src/static/js/utils/helpers/requests.js
Normal file
135
frontend/src/static/js/utils/helpers/requests.js
Normal file
@@ -0,0 +1,135 @@
|
||||
import axios, { get as axiosGet, post as axiosPost, put as axiosPut } from 'axios';
|
||||
|
||||
export async function getRequest(url, sync, callback, errorCallback) {
|
||||
const requestConfig = {
|
||||
timeout: null,
|
||||
maxContentLength: null,
|
||||
};
|
||||
|
||||
function responseHandler(result) {
|
||||
if (callback instanceof Function) {
|
||||
callback(result);
|
||||
}
|
||||
}
|
||||
|
||||
function errorHandler(error) {
|
||||
if (errorCallback instanceof Function) {
|
||||
let err = error;
|
||||
if (void 0 === error.response) {
|
||||
err = {
|
||||
type: 'network',
|
||||
error: error,
|
||||
};
|
||||
} else if (void 0 !== error.response.status) {
|
||||
// TODO: Improve this, it's valid only in case of media requests.
|
||||
switch (error.response.status) {
|
||||
case 401:
|
||||
err = {
|
||||
type: 'private',
|
||||
error: error,
|
||||
message: 'Media is private',
|
||||
};
|
||||
break;
|
||||
case 400:
|
||||
err = {
|
||||
type: 'unavailable',
|
||||
error: error,
|
||||
message: 'Media is unavailable',
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
errorCallback(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
await axiosGet(url, requestConfig)
|
||||
.then(responseHandler)
|
||||
.catch(errorHandler || null);
|
||||
} else {
|
||||
axiosGet(url, requestConfig)
|
||||
.then(responseHandler)
|
||||
.catch(errorHandler || null);
|
||||
}
|
||||
}
|
||||
|
||||
export async function postRequest(url, postData, configData, sync, callback, errorCallback) {
|
||||
postData = postData || {};
|
||||
|
||||
function responseHandler(result) {
|
||||
if (callback instanceof Function) {
|
||||
callback(result);
|
||||
}
|
||||
}
|
||||
|
||||
function errorHandler(error) {
|
||||
if (errorCallback instanceof Function) {
|
||||
errorCallback(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
await axiosPost(url, postData, configData || null)
|
||||
.then(responseHandler)
|
||||
.catch(errorHandler || null);
|
||||
} else {
|
||||
axiosPost(url, postData, configData || null)
|
||||
.then(responseHandler)
|
||||
.catch(errorHandler || null);
|
||||
}
|
||||
}
|
||||
|
||||
export async function putRequest(url, putData, configData, sync, callback, errorCallback) {
|
||||
putData = putData || {};
|
||||
|
||||
function responseHandler(result) {
|
||||
if (callback instanceof Function) {
|
||||
callback(result);
|
||||
}
|
||||
}
|
||||
|
||||
function errorHandler(error) {
|
||||
if (errorCallback instanceof Function) {
|
||||
errorCallback(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
await axiosPut(url, putData, configData || null)
|
||||
.then(responseHandler)
|
||||
.catch(errorHandler || null);
|
||||
} else {
|
||||
axiosPut(url, putData, configData || null)
|
||||
.then(responseHandler)
|
||||
.catch(errorHandler || null);
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteRequest(url, configData, sync, callback, errorCallback) {
|
||||
configData = configData || {};
|
||||
|
||||
function responseHandler(result) {
|
||||
if (callback instanceof Function) {
|
||||
callback(result);
|
||||
}
|
||||
}
|
||||
|
||||
function errorHandler(error) {
|
||||
if (errorCallback instanceof Function) {
|
||||
errorCallback(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
await axios
|
||||
.delete(url, configData || null)
|
||||
.then(responseHandler)
|
||||
.catch(errorHandler || null);
|
||||
} else {
|
||||
axios
|
||||
.delete(url, configData || null)
|
||||
.then(responseHandler)
|
||||
.catch(errorHandler || null);
|
||||
}
|
||||
}
|
||||
12
frontend/src/static/js/utils/hooks/index.ts
Normal file
12
frontend/src/static/js/utils/hooks/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export * from './useItem';
|
||||
export * from './useItemList';
|
||||
export * from './useItemListInlineSlider';
|
||||
export * from './useItemListLazyLoad';
|
||||
export * from './useItemListSync';
|
||||
export * from './useLayout';
|
||||
export * from './useManagementTableHeader';
|
||||
export * from './useMediaFilter';
|
||||
export * from './useMediaItem';
|
||||
export * from './usePopup';
|
||||
export * from './useTheme';
|
||||
export * from './useUser';
|
||||
60
frontend/src/static/js/utils/hooks/useItem.js
Normal file
60
frontend/src/static/js/utils/hooks/useItem.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { formatInnerLink } from '../helpers/';
|
||||
import {
|
||||
ItemDescription,
|
||||
ItemMain,
|
||||
ItemMainInLink,
|
||||
ItemTitle,
|
||||
ItemTitleLink,
|
||||
} from '../../components/list-item/includes/items';
|
||||
|
||||
import PageStore from '../stores/PageStore.js';
|
||||
|
||||
export function useItem(props) {
|
||||
const [duration, setDuration] = useState('');
|
||||
const [publishDate, setPublishDate] = useState('');
|
||||
const [publishDateTime, setPublishDateTime] = useState('');
|
||||
|
||||
const itemType = props.type;
|
||||
|
||||
const UnderThumbWrapper = props.singleLinkContent ? ItemMainInLink : ItemMain;
|
||||
|
||||
const thumbnailUrl =
|
||||
'' === props.thumbnail ? null : formatInnerLink(props.thumbnail, PageStore.get('config-site').url);
|
||||
|
||||
function titleComponent() {
|
||||
let ariaLabel = props.title;
|
||||
|
||||
if ('' !== publishDate) {
|
||||
ariaLabel += ' ' + publishDate;
|
||||
}
|
||||
|
||||
if ('' !== duration) {
|
||||
ariaLabel += ' ' + duration;
|
||||
}
|
||||
|
||||
if (props.singleLinkContent) {
|
||||
return <ItemTitle title={props.title} ariaLabel={ariaLabel} />;
|
||||
}
|
||||
|
||||
return <ItemTitleLink title={props.title} ariaLabel={ariaLabel} link={props.link} />;
|
||||
}
|
||||
|
||||
function descriptionComponent() {
|
||||
if (props.hasMediaViewer && props.hasMediaViewerDescr) {
|
||||
return [
|
||||
<ItemDescription key="1" description={props.meta_description ? props.meta_description.trim() : ' '} />,
|
||||
<ItemDescription key="2" description={props.description ? props.description.trim() : ' '} />,
|
||||
];
|
||||
}
|
||||
return <ItemDescription description={props.description.trim()} />;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (void 0 !== props.onMount) {
|
||||
props.onMount();
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { titleComponent, descriptionComponent, thumbnailUrl, UnderThumbWrapper };
|
||||
}
|
||||
57
frontend/src/static/js/utils/hooks/useItemList.js
Normal file
57
frontend/src/static/js/utils/hooks/useItemList.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import initItemsList from '../../components/item-list/includes/itemLists/initItemsList';
|
||||
import '../../components/item-list/ItemList.scss'; // TODO: Remove it from here
|
||||
|
||||
export function useItemList(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 = initItemsList([itemsListRef.current])[0];
|
||||
}
|
||||
|
||||
// TODO: 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];
|
||||
}
|
||||
190
frontend/src/static/js/utils/hooks/useItemListInlineSlider.js
Normal file
190
frontend/src/static/js/utils/hooks/useItemListInlineSlider.js
Normal file
@@ -0,0 +1,190 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { addClassname, removeClassname } from '../helpers/';
|
||||
import { CircleIconButton } from '../../components/_shared';
|
||||
import ItemsInlineSlider from '../../components/item-list/includes/itemLists/ItemsInlineSlider';
|
||||
import { useItemList } from './useItemList';
|
||||
|
||||
export function useItemListInlineSlider(props) {
|
||||
const itemsListRef = useRef(null);
|
||||
const itemsListWrapperRef = useRef(null);
|
||||
|
||||
const [items, countedItems, listHandler, setListHandler, onItemsLoad, onItemsCount, addListItems] = useItemList(
|
||||
props,
|
||||
itemsListRef
|
||||
);
|
||||
|
||||
const [inlineSlider, setInlineSlider] = useState(null);
|
||||
|
||||
const [displayNext, setDisplayNext] = useState(false);
|
||||
const [displayPrev, setDisplayPrev] = useState(false);
|
||||
|
||||
const [resizeDate, setResizeDate] = useState(null);
|
||||
const [sidebarVisibilityChangeDate, setSidebarVisibilityChangeDate] = useState(null);
|
||||
|
||||
let resizeTimeout = null;
|
||||
|
||||
let sliderRecalTimeout = null;
|
||||
|
||||
let pendingChangeSlide = true; // NOTE: Allow to run method `this.inlineSlider.scrollToCurrentSlide()` on object `this.inlineSlider` initialization.
|
||||
|
||||
let classname = {
|
||||
list: 'items-list',
|
||||
listOuter: 'items-list-outer list-inline list-slider' + (!!props.className ? ' ' + props.className : ''),
|
||||
};
|
||||
|
||||
function afterItemsLoad() {
|
||||
updateSlider(true);
|
||||
}
|
||||
|
||||
function renderBeforeListWrap() {
|
||||
return !displayPrev ? null : (
|
||||
<span className="previous-slide">
|
||||
<CircleIconButton buttonShadow={true} onClick={prevSlide}>
|
||||
<i className="material-icons">keyboard_arrow_left</i>
|
||||
</CircleIconButton>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function renderAfterListWrap() {
|
||||
return !displayNext ? null : (
|
||||
<span className="next-slide">
|
||||
<CircleIconButton buttonShadow={true} onClick={nextSlide}>
|
||||
<i className="material-icons">keyboard_arrow_right</i>
|
||||
</CircleIconButton>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function winResizeListener() {
|
||||
setResizeDate(new Date());
|
||||
}
|
||||
|
||||
function sidebarVisibilityChangeListener() {
|
||||
setSidebarVisibilityChangeDate(new Date());
|
||||
}
|
||||
|
||||
function onWinResize() {
|
||||
// 767 < window.innerWidth ||
|
||||
if (null === inlineSlider) {
|
||||
updateSlider(false);
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout(resizeTimeout);
|
||||
|
||||
addClassname(itemsListWrapperRef.current, 'resizing');
|
||||
|
||||
inlineSlider.updateDataStateOnResize(items.length, listHandler.loadedAllItems());
|
||||
inlineSlider.scrollToCurrentSlide();
|
||||
|
||||
resizeTimeout = setTimeout(onWinResizeUpdate, 200);
|
||||
}
|
||||
|
||||
function onWinResizeUpdate() {
|
||||
inlineSlider.updateDataStateOnResize(items.length, listHandler.loadedAllItems());
|
||||
inlineSlider.scrollToCurrentSlide();
|
||||
removeClassname(itemsListWrapperRef.current, 'resizing');
|
||||
resizeTimeout = null;
|
||||
}
|
||||
|
||||
function onSidebarVisibilityChange() {
|
||||
clearTimeout(sliderRecalTimeout);
|
||||
|
||||
sliderRecalTimeout = setTimeout(function () {
|
||||
updateSliderButtonsView();
|
||||
sliderRecalTimeout = setTimeout(function () {
|
||||
sliderRecalTimeout = null;
|
||||
updateSlider();
|
||||
}, 50);
|
||||
}, 150); // NOTE: 200ms is transition duration, set in CSS.
|
||||
}
|
||||
|
||||
function nextSlide() {
|
||||
inlineSlider.nextSlide();
|
||||
|
||||
updateSliderButtonsView();
|
||||
|
||||
if (!listHandler.loadedAllItems() && inlineSlider.loadMoreItems()) {
|
||||
pendingChangeSlide = true;
|
||||
listHandler.loadItems(inlineSlider.itemsFit());
|
||||
} else {
|
||||
inlineSlider.scrollToCurrentSlide();
|
||||
}
|
||||
}
|
||||
|
||||
function prevSlide() {
|
||||
inlineSlider.previousSlide();
|
||||
updateSliderButtonsView();
|
||||
inlineSlider.scrollToCurrentSlide();
|
||||
}
|
||||
|
||||
function initSlider() {
|
||||
if (!itemsListWrapperRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
setInlineSlider(new ItemsInlineSlider(itemsListWrapperRef.current, '.item'));
|
||||
}
|
||||
|
||||
function updateSlider(afterItemsUpdate) {
|
||||
if (null === inlineSlider) {
|
||||
initSlider();
|
||||
return;
|
||||
}
|
||||
|
||||
inlineSlider.updateDataState(items.length, listHandler.loadedAllItems(), !afterItemsUpdate);
|
||||
|
||||
if (!listHandler.loadedAllItems() && inlineSlider.loadItemsToFit()) {
|
||||
listHandler.loadItems(inlineSlider.itemsFit());
|
||||
} else {
|
||||
updateSliderButtonsView();
|
||||
|
||||
if (pendingChangeSlide) {
|
||||
pendingChangeSlide = false;
|
||||
inlineSlider.scrollToCurrentSlide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateSliderButtonsView() {
|
||||
if (!inlineSlider) {
|
||||
return;
|
||||
}
|
||||
setDisplayNext(inlineSlider.hasNextSlide());
|
||||
setDisplayPrev(inlineSlider.hasPreviousSlide());
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
addListItems();
|
||||
afterItemsLoad();
|
||||
}, [items]);
|
||||
|
||||
useEffect(() => {
|
||||
updateSlider(true);
|
||||
}, [inlineSlider]);
|
||||
|
||||
useEffect(() => {
|
||||
onWinResize();
|
||||
}, [resizeDate]);
|
||||
|
||||
useEffect(() => {
|
||||
onSidebarVisibilityChange();
|
||||
}, [sidebarVisibilityChangeDate]);
|
||||
|
||||
return [
|
||||
items,
|
||||
countedItems,
|
||||
listHandler,
|
||||
classname,
|
||||
setListHandler,
|
||||
onItemsCount,
|
||||
onItemsLoad,
|
||||
winResizeListener,
|
||||
sidebarVisibilityChangeListener,
|
||||
itemsListWrapperRef,
|
||||
itemsListRef,
|
||||
renderBeforeListWrap,
|
||||
renderAfterListWrap,
|
||||
];
|
||||
}
|
||||
82
frontend/src/static/js/utils/hooks/useItemListLazyLoad.js
Normal file
82
frontend/src/static/js/utils/hooks/useItemListLazyLoad.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { PageStore } from '../stores/';
|
||||
import { useItemList } from './useItemList';
|
||||
|
||||
export function useItemListLazyLoad(props) {
|
||||
const itemsListRef = useRef(null);
|
||||
const itemsListWrapperRef = useRef(null);
|
||||
|
||||
const [items, countedItems, listHandler, setListHandler, onItemsLoad, onItemsCount, addListItems] = useItemList(
|
||||
props,
|
||||
itemsListRef
|
||||
);
|
||||
|
||||
const [topScroll, setTopScroll] = useState(window.scrollY + 2 * window.outerHeight);
|
||||
|
||||
let classname = {
|
||||
list: 'items-list',
|
||||
listOuter: 'items-list-outer' + ('string' === typeof props.className ? ' ' + props.className.trim() : ''),
|
||||
};
|
||||
|
||||
function afterItemsLoad() {
|
||||
if (null === itemsListRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
onWindowScroll();
|
||||
|
||||
if (listHandler.loadedAllItems()) {
|
||||
PageStore.removeListener('window_scroll', onWindowScroll);
|
||||
}
|
||||
}
|
||||
|
||||
function renderBeforeListWrap() {
|
||||
return null;
|
||||
}
|
||||
|
||||
function renderAfterListWrap() {
|
||||
return null;
|
||||
}
|
||||
|
||||
function onWindowScroll() {
|
||||
setTopScroll(window.scrollY + 2 * window.outerHeight);
|
||||
}
|
||||
|
||||
function onDocumentVisibilityChange() {
|
||||
if (!document.hidden) {
|
||||
// NOTE: The delay fixes the problem that occurs when the list loads within a non-focused browser's tab.
|
||||
setTimeout(onWindowScroll, 10);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
addListItems();
|
||||
afterItemsLoad();
|
||||
}, [items]);
|
||||
|
||||
useEffect(() => {
|
||||
if (null === itemsListRef.current || null === listHandler) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (topScroll >= itemsListRef.current.offsetTop + itemsListRef.current.offsetHeight) {
|
||||
listHandler.loadItems();
|
||||
}
|
||||
}, [items, topScroll]);
|
||||
|
||||
return [
|
||||
items,
|
||||
countedItems,
|
||||
listHandler,
|
||||
setListHandler,
|
||||
classname,
|
||||
onItemsCount,
|
||||
onItemsLoad,
|
||||
onWindowScroll,
|
||||
onDocumentVisibilityChange,
|
||||
itemsListWrapperRef,
|
||||
itemsListRef,
|
||||
renderBeforeListWrap,
|
||||
renderAfterListWrap,
|
||||
];
|
||||
}
|
||||
58
frontend/src/static/js/utils/hooks/useItemListSync.js
Normal file
58
frontend/src/static/js/utils/hooks/useItemListSync.js
Normal file
@@ -0,0 +1,58 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { useItemList } from './useItemList';
|
||||
|
||||
export function useItemListSync(props) {
|
||||
const itemsListRef = useRef(null);
|
||||
const itemsListWrapperRef = useRef(null);
|
||||
|
||||
const [items, countedItems, listHandler, setListHandler, onItemsLoad, onItemsCount, addListItems] = useItemList(
|
||||
props,
|
||||
itemsListRef
|
||||
);
|
||||
|
||||
let classname = {
|
||||
list: 'items-list',
|
||||
listOuter: 'items-list-outer' + ('string' === typeof props.className ? ' ' + props.className.trim() : ''),
|
||||
};
|
||||
|
||||
function onClickLoadMore() {
|
||||
listHandler.loadItems();
|
||||
}
|
||||
|
||||
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,
|
||||
items,
|
||||
listHandler,
|
||||
setListHandler,
|
||||
classname,
|
||||
itemsListWrapperRef,
|
||||
itemsListRef,
|
||||
onItemsCount,
|
||||
onItemsLoad,
|
||||
renderBeforeListWrap,
|
||||
renderAfterListWrap,
|
||||
];
|
||||
}
|
||||
4
frontend/src/static/js/utils/hooks/useLayout.js
Normal file
4
frontend/src/static/js/utils/hooks/useLayout.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { LayoutContext } from '../contexts/';
|
||||
|
||||
export const useLayout = () => useContext(LayoutContext);
|
||||
@@ -0,0 +1,45 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
export function useManagementTableHeader(props) {
|
||||
const [sort, setSort] = useState(props.sort);
|
||||
const [order, setOrder] = useState(props.order);
|
||||
const [isSelected, setIsSelected] = useState(props.selected);
|
||||
|
||||
function sortByColumn(ev) {
|
||||
const colId = ev.currentTarget.getAttribute('id');
|
||||
|
||||
const newSort = colId;
|
||||
const newOrder = sort === colId ? ('desc' === order ? 'asc' : 'desc') : 'desc';
|
||||
|
||||
setSort(newSort);
|
||||
setOrder(newOrder);
|
||||
|
||||
if (void 0 !== props.onClickColumnSort) {
|
||||
props.onClickColumnSort(newSort, newOrder);
|
||||
}
|
||||
}
|
||||
|
||||
function checkAll() {
|
||||
const newSelected = !isSelected;
|
||||
|
||||
setIsSelected(!newSelected);
|
||||
|
||||
if (void 0 !== props.onCheckAllRows) {
|
||||
props.onCheckAllRows(newSelected, props.type);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setSort(props.sort);
|
||||
}, [props.sort]);
|
||||
|
||||
useEffect(() => {
|
||||
setOrder(props.order);
|
||||
}, [props.order]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsSelected(props.selected);
|
||||
}, [props.selected]);
|
||||
|
||||
return [sort, order, isSelected, sortByColumn, checkAll];
|
||||
}
|
||||
9
frontend/src/static/js/utils/hooks/useMediaFilter.js
Normal file
9
frontend/src/static/js/utils/hooks/useMediaFilter.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { usePopup } from './usePopup';
|
||||
|
||||
export function useMediaFilter(initialValue) {
|
||||
const containerRef = useRef(null);
|
||||
const [value, setValue] = useState(initialValue);
|
||||
const [popupContentRef, PopupContent, PopupTrigger] = usePopup();
|
||||
return [containerRef, value, setValue, popupContentRef, PopupContent, PopupTrigger];
|
||||
}
|
||||
79
frontend/src/static/js/utils/hooks/useMediaItem.js
Normal file
79
frontend/src/static/js/utils/hooks/useMediaItem.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import React from 'react';
|
||||
import { format } from 'timeago.js';
|
||||
import { formatInnerLink } from '../helpers/';
|
||||
import { PageStore } from '../stores/';
|
||||
import {
|
||||
MediaItemAuthor,
|
||||
MediaItemAuthorLink,
|
||||
MediaItemMetaViews,
|
||||
MediaItemMetaDate,
|
||||
MediaItemEditLink,
|
||||
} from '../../components/list-item/includes/items';
|
||||
import { useItem } from './useItem';
|
||||
|
||||
export function itemClassname(defaultClassname, inheritedClassname, isActiveInPlaylistPlayback) {
|
||||
let classname = defaultClassname;
|
||||
|
||||
if ('' !== inheritedClassname) {
|
||||
classname += ' ' + inheritedClassname;
|
||||
}
|
||||
|
||||
if (isActiveInPlaylistPlayback) {
|
||||
classname += ' pl-active-item';
|
||||
}
|
||||
|
||||
return classname;
|
||||
}
|
||||
|
||||
export function useMediaItem(props) {
|
||||
const { titleComponent, descriptionComponent, thumbnailUrl, UnderThumbWrapper } = useItem({ ...props });
|
||||
|
||||
function editMediaComponent() {
|
||||
return <MediaItemEditLink link={props.editLink} />;
|
||||
}
|
||||
|
||||
function authorComponent() {
|
||||
if (props.hideAuthor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (props.singleLinkContent) {
|
||||
return <MediaItemAuthor name={props.author_name} />;
|
||||
}
|
||||
|
||||
const authorUrl =
|
||||
'' === props.author_link ? null : formatInnerLink(props.author_link, PageStore.get('config-site').url);
|
||||
|
||||
return <MediaItemAuthorLink name={props.author_name} link={authorUrl} />;
|
||||
}
|
||||
|
||||
function viewsComponent() {
|
||||
return props.hideViews ? null : <MediaItemMetaViews views={props.views} />;
|
||||
}
|
||||
|
||||
function dateComponent() {
|
||||
if (props.hideDate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const publishDate = format(new Date(props.publish_date));
|
||||
const publishDateTime =
|
||||
'string' === typeof props.publish_date
|
||||
? Date.parse(props.publish_date)
|
||||
: Date.parse(new Date(props.publish_date));
|
||||
|
||||
return <MediaItemMetaDate time={props.publish_date} dateTime={publishDateTime} text={publishDate} />;
|
||||
}
|
||||
|
||||
function metaComponents() {
|
||||
return props.hideAllMeta ? null : (
|
||||
<span className="item-meta">
|
||||
{authorComponent()}
|
||||
{viewsComponent()}
|
||||
{dateComponent()}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return [titleComponent, descriptionComponent, thumbnailUrl, UnderThumbWrapper, editMediaComponent, metaComponents];
|
||||
}
|
||||
9
frontend/src/static/js/utils/hooks/usePopup.js
Normal file
9
frontend/src/static/js/utils/hooks/usePopup.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { PopupContent } from '../../components/_shared/popup/PopupContent.jsx';
|
||||
import { PopupTrigger } from '../../components/_shared/popup/PopupTrigger.jsx';
|
||||
|
||||
export function usePopup() {
|
||||
const popupContentRef = useRef(null);
|
||||
|
||||
return [popupContentRef, PopupContent, PopupTrigger];
|
||||
}
|
||||
4
frontend/src/static/js/utils/hooks/useTheme.js
Normal file
4
frontend/src/static/js/utils/hooks/useTheme.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { ThemeContext } from '../contexts/';
|
||||
|
||||
export const useTheme = () => useContext(ThemeContext);
|
||||
4
frontend/src/static/js/utils/hooks/useUser.js
Normal file
4
frontend/src/static/js/utils/hooks/useUser.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import React, { useContext } from 'react';
|
||||
import UserContext from '../contexts/UserContext';
|
||||
|
||||
export const useUser = () => useContext(UserContext);
|
||||
66
frontend/src/static/js/utils/renderer.js
Executable file
66
frontend/src/static/js/utils/renderer.js
Executable file
@@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { ThemeProvider } from './contexts/ThemeContext';
|
||||
import { LayoutProvider } from './contexts/LayoutContext';
|
||||
import { UserProvider } from './contexts/UserContext';
|
||||
|
||||
const AppProviders = ({ children }) => (
|
||||
<LayoutProvider>
|
||||
<ThemeProvider>
|
||||
<UserProvider>{children}</UserProvider>
|
||||
</ThemeProvider>
|
||||
</LayoutProvider>
|
||||
);
|
||||
|
||||
import { PageHeader, PageSidebar } from '../components/page-layout';
|
||||
|
||||
export function renderPage(idSelector, PageComponent) {
|
||||
const appHeader = document.getElementById('app-header');
|
||||
const appSidebar = document.getElementById('app-sidebar');
|
||||
const appContent = idSelector ? document.getElementById(idSelector) : undefined;
|
||||
|
||||
if (appContent && PageComponent) {
|
||||
ReactDOM.render(
|
||||
<AppProviders>
|
||||
{appHeader ? ReactDOM.createPortal(<PageHeader />, appHeader) : null}
|
||||
{appSidebar ? ReactDOM.createPortal(<PageSidebar />, appSidebar) : null}
|
||||
<PageComponent />
|
||||
</AppProviders>,
|
||||
appContent
|
||||
);
|
||||
} else if (appHeader && appSidebar) {
|
||||
ReactDOM.render(
|
||||
<AppProviders>
|
||||
{ReactDOM.createPortal(<PageHeader />, appHeader)}
|
||||
<PageSidebar />
|
||||
</AppProviders>,
|
||||
appSidebar
|
||||
);
|
||||
} else if (appHeader) {
|
||||
ReactDOM.render(
|
||||
<LayoutProvider>
|
||||
<ThemeProvider>
|
||||
<UserProvider>
|
||||
<PageHeader />
|
||||
</UserProvider>
|
||||
</ThemeProvider>
|
||||
</LayoutProvider>,
|
||||
appSidebar
|
||||
);
|
||||
} else if (appSidebar) {
|
||||
ReactDOM.render(
|
||||
<AppProviders>
|
||||
<PageSidebar />
|
||||
</AppProviders>,
|
||||
appSidebar
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function renderEmbedPage(idSelector, PageComponent) {
|
||||
const appContent = idSelector ? document.getElementById(idSelector) : undefined;
|
||||
|
||||
if (appContent && PageComponent) {
|
||||
ReactDOM.render(<PageComponent />, appContent);
|
||||
}
|
||||
}
|
||||
59
frontend/src/static/js/utils/settings/api.js
Executable file
59
frontend/src/static/js/utils/settings/api.js
Executable file
@@ -0,0 +1,59 @@
|
||||
const urlParse = require('url-parse');
|
||||
|
||||
let BASE_URL = null;
|
||||
let ENDPOINTS = null;
|
||||
|
||||
function endpointsIter(ret, endpoints) {
|
||||
const baseUrl = BASE_URL.toString().replace(/\/+$/, '');
|
||||
|
||||
for (let k in endpoints) {
|
||||
if ('string' === typeof endpoints[k]) {
|
||||
ret[k] = baseUrl + '/' + endpoints[k].replace(/^\//g, '');
|
||||
} else {
|
||||
endpointsIter(ret[k], endpoints[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function formatEndpoints(endpoints) {
|
||||
const baseUrl = BASE_URL.toString();
|
||||
const ret = endpoints;
|
||||
endpointsIter(ret, endpoints);
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function init(base_url, endpoints) {
|
||||
BASE_URL = urlParse(base_url);
|
||||
|
||||
ENDPOINTS = formatEndpoints({
|
||||
media: endpoints.media,
|
||||
featured: endpoints.media + '?show=featured',
|
||||
recommended: endpoints.media + '?show=recommended',
|
||||
playlists: endpoints.playlists,
|
||||
users: endpoints.members,
|
||||
user: {
|
||||
liked: endpoints.liked,
|
||||
history: endpoints.history,
|
||||
playlists: endpoints.playlists + '?author=',
|
||||
},
|
||||
archive: {
|
||||
tags: endpoints.tags,
|
||||
categories: endpoints.categories,
|
||||
},
|
||||
manage: {
|
||||
media: endpoints.manage_media,
|
||||
users: endpoints.manage_users,
|
||||
comments: endpoints.manage_comments,
|
||||
},
|
||||
search: {
|
||||
query: endpoints.search + '?q=',
|
||||
titles: endpoints.search + '?show=titles&q=',
|
||||
tag: endpoints.search + '?t=',
|
||||
category: endpoints.search + '?c=',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function endpoints() {
|
||||
return ENDPOINTS;
|
||||
}
|
||||
109
frontend/src/static/js/utils/settings/config.js
Normal file
109
frontend/src/static/js/utils/settings/config.js
Normal file
@@ -0,0 +1,109 @@
|
||||
import * as api from './api.js';
|
||||
import * as media from './media.js';
|
||||
import * as site from './site.js';
|
||||
import * as theme from './theme.js';
|
||||
import * as url from './url.js';
|
||||
import * as member from './member.js';
|
||||
import * as contents from './contents.js';
|
||||
import * as pages from './pages.js';
|
||||
import * as sidebar from './sidebar.js';
|
||||
import * as taxonomies from './taxonomies.js';
|
||||
import * as optionsPages from './optionsPages.js';
|
||||
import * as optionsEmbedded from './optionsEmbedded.js';
|
||||
import * as playlists from './playlists.js';
|
||||
import * as notifications from './notifications.js';
|
||||
|
||||
let DATA = null;
|
||||
|
||||
export function config(glbl) {
|
||||
if (DATA) {
|
||||
return DATA;
|
||||
}
|
||||
|
||||
pages.init({ ...glbl.site.pages, ...glbl.site.userPages });
|
||||
optionsPages.init(glbl.pages.home, glbl.pages.search, glbl.pages.media, glbl.pages.profile, pages.settings());
|
||||
|
||||
url.init({
|
||||
home: glbl.url.home,
|
||||
admin: !glbl.user.is.anonymous && glbl.user.is.admin ? glbl.url.admin : '',
|
||||
error404: glbl.url.error404,
|
||||
embed: glbl.site.url.replace(/\/+$/, '') + '/embed?m=',
|
||||
latest: glbl.url.latestMedia,
|
||||
featured: glbl.url.featuredMedia,
|
||||
recommended: glbl.url.recommendedMedia,
|
||||
signin: glbl.url.signin,
|
||||
signout: !glbl.user.is.anonymous ? glbl.url.signout : '',
|
||||
register: glbl.url.register,
|
||||
changePassword: !glbl.user.is.anonymous ? glbl.url.changePassword : '',
|
||||
members: glbl.url.members,
|
||||
search: {
|
||||
base: glbl.url.search,
|
||||
query: glbl.url.search + '?q=',
|
||||
tag: glbl.url.search + '?t=',
|
||||
category: glbl.url.search + '?c=',
|
||||
},
|
||||
profile: !!glbl.site.devEnv
|
||||
? {
|
||||
media: glbl.user.pages.media,
|
||||
about: glbl.user.pages.about,
|
||||
playlists: glbl.user.pages.playlists,
|
||||
}
|
||||
: {
|
||||
media: glbl.site.url.replace(/\/$/, '') + '/user/' + glbl.profileId,
|
||||
about: glbl.site.url.replace(/\/$/, '') + '/user/' + glbl.profileId + '/about',
|
||||
playlists: glbl.site.url.replace(/\/$/, '') + '/user/' + glbl.profileId + '/playlists',
|
||||
},
|
||||
user: {
|
||||
liked: glbl.url.likedMedia,
|
||||
history: glbl.url.history,
|
||||
addMedia: glbl.url.addMedia,
|
||||
editChannel: glbl.url.editChannel,
|
||||
editProfile: glbl.url.editProfile,
|
||||
},
|
||||
archive: {
|
||||
tags: glbl.url.tags,
|
||||
categories: glbl.url.categories,
|
||||
},
|
||||
manage: {
|
||||
media: !glbl.user.is.anonymous ? glbl.url.manageMedia : '',
|
||||
users: !glbl.user.is.anonymous ? glbl.url.manageUsers : '',
|
||||
comments: !glbl.user.is.anonymous ? glbl.url.manageComments : '',
|
||||
},
|
||||
});
|
||||
|
||||
site.init(glbl.site);
|
||||
contents.init(glbl.contents);
|
||||
api.init(glbl.site.api, glbl.api);
|
||||
sidebar.init(glbl.features.sideBar);
|
||||
taxonomies.init(glbl.site.taxonomies);
|
||||
member.init(glbl.user, glbl.features);
|
||||
theme.init(glbl.site.theme, glbl.site.logo);
|
||||
optionsEmbedded.init(glbl.features.embeddedVideo);
|
||||
media.init(glbl.features.mediaItem, glbl.features.media.shareOptions);
|
||||
playlists.init(glbl.features.playlists);
|
||||
|
||||
notifications.init(glbl.contents.notifications);
|
||||
|
||||
DATA = {
|
||||
site: site.settings(),
|
||||
theme: theme.settings(),
|
||||
member: member.settings(),
|
||||
media: media.settings(),
|
||||
playlists: playlists.settings(),
|
||||
url: url.pages(),
|
||||
api: api.endpoints(),
|
||||
sidebar: sidebar.settings(),
|
||||
contents: contents.settings(),
|
||||
options: {
|
||||
pages: optionsPages.settings(),
|
||||
embedded: optionsEmbedded.settings(),
|
||||
},
|
||||
enabled: {
|
||||
pages: pages.settings(),
|
||||
taxonomies: taxonomies.settings(),
|
||||
},
|
||||
notifications: notifications.settings(),
|
||||
};
|
||||
|
||||
return DATA;
|
||||
}
|
||||
121
frontend/src/static/js/utils/settings/contents.js
Executable file
121
frontend/src/static/js/utils/settings/contents.js
Executable file
@@ -0,0 +1,121 @@
|
||||
let CONTENTS = null;
|
||||
|
||||
function headerContents(contents) {
|
||||
const ret = {
|
||||
right: '',
|
||||
onLogoRight: '',
|
||||
};
|
||||
|
||||
if (void 0 !== contents) {
|
||||
if ('string' === typeof contents.right) {
|
||||
ret.right = contents.right.trim();
|
||||
}
|
||||
|
||||
if ('string' === typeof contents.onLogoRight) {
|
||||
ret.onLogoRight = contents.onLogoRight.trim();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function sidebarContents(contents) {
|
||||
const ret = {
|
||||
navMenu: {
|
||||
items: [],
|
||||
},
|
||||
mainMenuExtra: {
|
||||
items: [],
|
||||
},
|
||||
belowNavMenu: '',
|
||||
belowThemeSwitcher: '',
|
||||
footer: '',
|
||||
};
|
||||
|
||||
if (undefined !== contents) {
|
||||
if (undefined !== contents.mainMenuExtraItems) {
|
||||
let i = 0;
|
||||
while (i < contents.mainMenuExtraItems.length) {
|
||||
if (
|
||||
'string' === typeof contents.mainMenuExtraItems[i].text &&
|
||||
'string' === typeof contents.mainMenuExtraItems[i].link &&
|
||||
'string' === typeof contents.mainMenuExtraItems[i].icon
|
||||
) {
|
||||
ret.mainMenuExtra.items.push({
|
||||
text: contents.mainMenuExtraItems[i].text,
|
||||
link: contents.mainMenuExtraItems[i].link,
|
||||
icon: contents.mainMenuExtraItems[i].icon,
|
||||
className: contents.mainMenuExtraItems[i].className,
|
||||
});
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (undefined !== contents.navMenuItems) {
|
||||
let i = 0;
|
||||
while (i < contents.navMenuItems.length) {
|
||||
if (
|
||||
'string' === typeof contents.navMenuItems[i].text &&
|
||||
'string' === typeof contents.navMenuItems[i].link &&
|
||||
'string' === typeof contents.navMenuItems[i].icon
|
||||
) {
|
||||
ret.navMenu.items.push({
|
||||
text: contents.navMenuItems[i].text,
|
||||
link: contents.navMenuItems[i].link,
|
||||
icon: contents.navMenuItems[i].icon,
|
||||
className: contents.navMenuItems[i].className,
|
||||
});
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ('string' === typeof contents.belowNavMenu) {
|
||||
ret.belowNavMenu = contents.belowNavMenu.trim();
|
||||
}
|
||||
|
||||
if ('string' === typeof contents.belowThemeSwitcher) {
|
||||
ret.belowThemeSwitcher = contents.belowThemeSwitcher.trim();
|
||||
}
|
||||
|
||||
if ('string' === typeof contents.footer) {
|
||||
ret.footer = contents.footer.trim();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function uploaderContents(contents) {
|
||||
const ret = {
|
||||
belowUploadArea: '',
|
||||
postUploadMessage: '',
|
||||
};
|
||||
|
||||
if (void 0 !== contents) {
|
||||
if ('string' === typeof contents.belowUploadArea) {
|
||||
ret.belowUploadArea = contents.belowUploadArea.trim();
|
||||
}
|
||||
|
||||
if ('string' === typeof contents.postUploadMessage) {
|
||||
ret.postUploadMessage = contents.postUploadMessage.trim();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function init(contents) {
|
||||
CONTENTS = {
|
||||
header: headerContents(contents.header),
|
||||
sidebar: sidebarContents(contents.sidebar),
|
||||
uploader: uploaderContents(contents.uploader),
|
||||
};
|
||||
}
|
||||
|
||||
export function settings() {
|
||||
return CONTENTS;
|
||||
}
|
||||
59
frontend/src/static/js/utils/settings/media.js
Executable file
59
frontend/src/static/js/utils/settings/media.js
Executable file
@@ -0,0 +1,59 @@
|
||||
let MEDIA = null;
|
||||
|
||||
export function init(item, shareOptions) {
|
||||
MEDIA = {
|
||||
item: {
|
||||
displayAuthor: true,
|
||||
displayViews: true,
|
||||
displayPublishDate: true,
|
||||
},
|
||||
share: {
|
||||
options: [],
|
||||
},
|
||||
};
|
||||
|
||||
if (void 0 !== item) {
|
||||
|
||||
if (true === item.hideAuthor) {
|
||||
MEDIA.item.displayAuthor = false;
|
||||
}
|
||||
|
||||
if (true === item.hideViews) {
|
||||
MEDIA.item.displayViews = false;
|
||||
}
|
||||
|
||||
if (true === item.hideDate) {
|
||||
MEDIA.item.displayPublishDate = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (void 0 !== shareOptions) {
|
||||
const validShareOptions = [
|
||||
'embed',
|
||||
'fb',
|
||||
'tw',
|
||||
'whatsapp',
|
||||
'telegram',
|
||||
'reddit',
|
||||
'tumblr',
|
||||
'vk',
|
||||
'pinterest',
|
||||
'mix',
|
||||
'linkedin',
|
||||
'email',
|
||||
];
|
||||
|
||||
let i = 0;
|
||||
while (i < shareOptions.length) {
|
||||
if (-1 < validShareOptions.indexOf(shareOptions[i])) {
|
||||
MEDIA.share.options.push(shareOptions[i]);
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function settings() {
|
||||
return MEDIA;
|
||||
}
|
||||
128
frontend/src/static/js/utils/settings/member.js
Executable file
128
frontend/src/static/js/utils/settings/member.js
Executable file
@@ -0,0 +1,128 @@
|
||||
let MEMBER = null;
|
||||
|
||||
export function init(user, features) {
|
||||
MEMBER = {
|
||||
name: null,
|
||||
username: null,
|
||||
thumbnail: null,
|
||||
is: {
|
||||
admin: false,
|
||||
anonymous: true,
|
||||
},
|
||||
can: {
|
||||
login: true,
|
||||
register: true,
|
||||
addMedia: false,
|
||||
editProfile: false,
|
||||
changePassword: true,
|
||||
deleteProfile: false,
|
||||
readComment: true,
|
||||
addComment: false,
|
||||
deleteComment: false,
|
||||
editMedia: false,
|
||||
deleteMedia: false,
|
||||
editSubtitle: false,
|
||||
manageMedia: false,
|
||||
manageUsers: false,
|
||||
manageComments: false,
|
||||
reportMedia: false,
|
||||
downloadMedia: false,
|
||||
saveMedia: false,
|
||||
likeMedia: true,
|
||||
dislikeMedia: true,
|
||||
shareMedia: true,
|
||||
contactUser: false,
|
||||
},
|
||||
pages: {
|
||||
home: null,
|
||||
about: null,
|
||||
media: null,
|
||||
playlists: null,
|
||||
},
|
||||
};
|
||||
|
||||
if (void 0 !== user) {
|
||||
MEMBER.is.anonymous = true === user.is.anonymous ? true : false;
|
||||
|
||||
if (!MEMBER.is.anonymous) {
|
||||
MEMBER.is.admin = true === user.is.admin;
|
||||
|
||||
MEMBER.name = 'string' === typeof user.name ? user.name.trim() : '';
|
||||
MEMBER.name = '' === MEMBER.name ? null : MEMBER.name;
|
||||
|
||||
MEMBER.username = 'string' === typeof user.username ? user.username.trim() : '';
|
||||
MEMBER.username = '' === MEMBER.username ? null : MEMBER.username;
|
||||
|
||||
MEMBER.thumbnail = 'string' === typeof user.thumbnail ? user.thumbnail.trim() : '';
|
||||
MEMBER.thumbnail = '' === MEMBER.thumbnail ? null : MEMBER.thumbnail;
|
||||
|
||||
MEMBER.can.changePassword = false === user.can.changePassword ? false : MEMBER.can.changePassword;
|
||||
|
||||
MEMBER.can.deleteProfile = true === user.can.deleteProfile;
|
||||
MEMBER.can.addComment = true === user.can.addComment;
|
||||
MEMBER.can.deleteComment = true === user.can.deleteComment;
|
||||
MEMBER.can.editMedia = true === user.can.editMedia;
|
||||
MEMBER.can.deleteMedia = true === user.can.deleteMedia;
|
||||
MEMBER.can.editSubtitle = true === user.can.editSubtitle;
|
||||
MEMBER.can.manageMedia = true === user.can.manageMedia;
|
||||
MEMBER.can.manageUsers = true === user.can.manageUsers;
|
||||
MEMBER.can.manageComments = true === user.can.manageComments;
|
||||
|
||||
MEMBER.can.contactUser = true === user.can.contactUser;
|
||||
|
||||
if (void 0 !== user.pages) {
|
||||
|
||||
if ('string' === typeof user.pages.about) {
|
||||
MEMBER.pages.about = user.pages.about.trim();
|
||||
MEMBER.pages.about = '' === MEMBER.pages.about ? null : MEMBER.pages.about;
|
||||
}
|
||||
|
||||
if ('string' === typeof user.pages.media) {
|
||||
MEMBER.pages.media = user.pages.media.trim();
|
||||
MEMBER.pages.media = '' === MEMBER.pages.media ? null : MEMBER.pages.media;
|
||||
}
|
||||
|
||||
if ('string' === typeof user.pages.playlists) {
|
||||
MEMBER.pages.playlists = user.pages.playlists.trim();
|
||||
MEMBER.pages.playlists = '' === MEMBER.pages.playlists ? null : MEMBER.pages.playlists;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MEMBER.can.addMedia = true === user.can.addMedia;
|
||||
MEMBER.can.editProfile = true === user.can.editProfile;
|
||||
MEMBER.can.readComment = false === user.can.readComment ? false : true;
|
||||
}
|
||||
|
||||
if (void 0 !== features) {
|
||||
if (void 0 !== features.media) {
|
||||
if (void 0 !== features.media.actions) {
|
||||
const mediaActions = features.media.actions;
|
||||
|
||||
MEMBER.can.addComment = MEMBER.can.addComment && true === mediaActions.comment;
|
||||
|
||||
MEMBER.can.likeMedia = false === mediaActions.like ? false : true;
|
||||
MEMBER.can.dislikeMedia = false === mediaActions.dislike ? false : true;
|
||||
MEMBER.can.reportMedia = false === mediaActions.report ? false : true;
|
||||
|
||||
MEMBER.can.downloadMedia = true === mediaActions.download;
|
||||
MEMBER.can.saveMedia = true === mediaActions.save;
|
||||
MEMBER.can.shareMedia = true === mediaActions.share;
|
||||
}
|
||||
}
|
||||
|
||||
if (void 0 !== features.headerBar) {
|
||||
if (true === features.headerBar.hideLogin) {
|
||||
MEMBER.can.login = false;
|
||||
}
|
||||
|
||||
if (true === features.headerBar.hideRegister) {
|
||||
MEMBER.can.register = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function settings() {
|
||||
return MEMBER;
|
||||
}
|
||||
32
frontend/src/static/js/utils/settings/notifications.js
Normal file
32
frontend/src/static/js/utils/settings/notifications.js
Normal file
@@ -0,0 +1,32 @@
|
||||
let NOTIFICATIONS = null;
|
||||
|
||||
export function init(settings) {
|
||||
NOTIFICATIONS = {
|
||||
messages: {
|
||||
addToLiked: 'Added to liked media',
|
||||
removeFromLiked: 'Removed from liked media',
|
||||
addToDisliked: 'Added to disliked media',
|
||||
removeFromDisliked: 'Removed from disliked media',
|
||||
},
|
||||
};
|
||||
|
||||
let k, g;
|
||||
|
||||
if (void 0 !== settings) {
|
||||
for (k in NOTIFICATIONS) {
|
||||
if (void 0 !== settings[k]) {
|
||||
if ('messages' === k) {
|
||||
for (g in NOTIFICATIONS[k]) {
|
||||
if ('string' === typeof settings[k][g]) {
|
||||
NOTIFICATIONS[k][g] = settings[k][g];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function settings() {
|
||||
return NOTIFICATIONS;
|
||||
}
|
||||
42
frontend/src/static/js/utils/settings/optionsEmbedded.js
Executable file
42
frontend/src/static/js/utils/settings/optionsEmbedded.js
Executable file
@@ -0,0 +1,42 @@
|
||||
let EMBEDDED = null;
|
||||
|
||||
export function init(embeddedVideo) {
|
||||
EMBEDDED = {
|
||||
video: {
|
||||
dimensions: {
|
||||
width: 560,
|
||||
widthUnit: 'px', // Valid values: 'px', 'percent'
|
||||
height: 315,
|
||||
heightUnit: 'px', // Valid values: 'px', 'percent'
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (void 0 !== embeddedVideo) {
|
||||
if (void 0 !== embeddedVideo.initialDimensions) {
|
||||
if (!isNaN(embeddedVideo.initialDimensions.width)) {
|
||||
EMBEDDED.video.dimensions.width = embeddedVideo.initialDimensions.width;
|
||||
}
|
||||
|
||||
if ('string' === typeof embeddedVideo.initialDimensions.widthUnit) {
|
||||
if ('percent' === embeddedVideo.initialDimensions.widthUnit) {
|
||||
embeddedVideo.initialDimensions.widthUnit = 'percent';
|
||||
}
|
||||
}
|
||||
|
||||
if (!isNaN(embeddedVideo.initialDimensions.height)) {
|
||||
EMBEDDED.video.dimensions.height = embeddedVideo.initialDimensions.height;
|
||||
}
|
||||
|
||||
if ('string' === typeof embeddedVideo.initialDimensions.heightUnit) {
|
||||
if ('percent' === embeddedVideo.initialDimensions.heightUnit) {
|
||||
embeddedVideo.initialDimensions.heightUnit = 'percent';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function settings() {
|
||||
return EMBEDDED;
|
||||
}
|
||||
108
frontend/src/static/js/utils/settings/optionsPages.js
Executable file
108
frontend/src/static/js/utils/settings/optionsPages.js
Executable file
@@ -0,0 +1,108 @@
|
||||
let PAGES = null;
|
||||
|
||||
export function init(home, search, media, profile, VALID_PAGES) {
|
||||
PAGES = {
|
||||
home: {
|
||||
sections: {
|
||||
latest: {
|
||||
title: '',
|
||||
},
|
||||
featured: {
|
||||
title: '',
|
||||
},
|
||||
recommended: {
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
search: {
|
||||
advancedFilters: false,
|
||||
},
|
||||
media: {
|
||||
categoriesWithTitle: false,
|
||||
htmlInDescription: false,
|
||||
displayViews: true,
|
||||
related: {
|
||||
initialSize: 10,
|
||||
},
|
||||
},
|
||||
profile: {
|
||||
htmlInDescription: false,
|
||||
includeHistory: false,
|
||||
includeLikedMedia: false,
|
||||
},
|
||||
};
|
||||
|
||||
if (void 0 !== home) {
|
||||
if (void 0 !== home.sections) {
|
||||
if (void 0 !== home.sections.latest) {
|
||||
if ('string' === typeof home.sections.latest.title) {
|
||||
PAGES.home.sections.latest.title = home.sections.latest.title.trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (void 0 !== home.sections.featured) {
|
||||
if ('string' === typeof home.sections.featured.title) {
|
||||
PAGES.home.sections.featured.title = home.sections.featured.title.trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (void 0 !== home.sections.recommended) {
|
||||
if ('string' === typeof home.sections.recommended.title) {
|
||||
PAGES.home.sections.recommended.title = home.sections.recommended.title.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (void 0 !== search) {
|
||||
if (true === search.advancedFilters) {
|
||||
PAGES.search.advancedFilters = search.advancedFilters;
|
||||
}
|
||||
}
|
||||
|
||||
if ('' === PAGES.home.sections.latest.title) {
|
||||
PAGES.home.sections.latest.title = void 0 !== VALID_PAGES.latest ? VALID_PAGES.latest.title : 'Latest';
|
||||
}
|
||||
|
||||
if ('' === PAGES.home.sections.featured.title) {
|
||||
PAGES.home.sections.featured.title = void 0 !== VALID_PAGES.featured ? VALID_PAGES.featured.title : 'Featured';
|
||||
}
|
||||
|
||||
if ('' === PAGES.home.sections.recommended.title) {
|
||||
PAGES.home.sections.recommended.title =
|
||||
void 0 !== VALID_PAGES.recommended ? VALID_PAGES.recommended.title : 'Recommended';
|
||||
}
|
||||
|
||||
if (void 0 !== media) {
|
||||
if (true === media.categoriesWithTitle) {
|
||||
PAGES.media.categoriesWithTitle = media.categoriesWithTitle;
|
||||
}
|
||||
|
||||
if (true === media.hideViews) {
|
||||
PAGES.media.displayViews = false;
|
||||
}
|
||||
|
||||
if (true === media.htmlInDescription) {
|
||||
PAGES.media.htmlInDescription = media.htmlInDescription;
|
||||
}
|
||||
}
|
||||
|
||||
if (void 0 !== profile) {
|
||||
if (true === profile.htmlInDescription) {
|
||||
PAGES.profile.htmlInDescription = profile.htmlInDescription;
|
||||
}
|
||||
|
||||
if (true === profile.includeHistory) {
|
||||
PAGES.profile.includeHistory = profile.includeHistory;
|
||||
}
|
||||
|
||||
if (true === profile.includeLikedMedia) {
|
||||
PAGES.profile.includeLikedMedia = profile.includeLikedMedia;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function settings() {
|
||||
return PAGES;
|
||||
}
|
||||
50
frontend/src/static/js/utils/settings/pages.js
Executable file
50
frontend/src/static/js/utils/settings/pages.js
Executable file
@@ -0,0 +1,50 @@
|
||||
let PAGES = null;
|
||||
|
||||
export function init(settings) {
|
||||
PAGES = {
|
||||
latest: {
|
||||
enabled: false,
|
||||
title: 'Recent uploads',
|
||||
},
|
||||
featured: {
|
||||
enabled: false,
|
||||
title: 'Featured',
|
||||
},
|
||||
recommended: {
|
||||
enabled: false,
|
||||
title: 'Recommended',
|
||||
},
|
||||
members: {
|
||||
enabled: false,
|
||||
title: 'Members',
|
||||
},
|
||||
liked: {
|
||||
enabled: false,
|
||||
title: 'Liked media',
|
||||
},
|
||||
history: {
|
||||
enabled: false,
|
||||
title: 'History',
|
||||
},
|
||||
};
|
||||
|
||||
if (void 0 !== settings) {
|
||||
for (let k in PAGES) {
|
||||
if (void 0 !== settings[k]) {
|
||||
PAGES[k].enabled = true;
|
||||
|
||||
if (void 0 !== settings[k].enabled && false === settings[k].enabled) {
|
||||
PAGES[k].enabled = false;
|
||||
}
|
||||
|
||||
if ('string' === typeof settings[k].title) {
|
||||
PAGES[k].title = settings[k].title.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function settings() {
|
||||
return PAGES;
|
||||
}
|
||||
35
frontend/src/static/js/utils/settings/playlists.js
Executable file
35
frontend/src/static/js/utils/settings/playlists.js
Executable file
@@ -0,0 +1,35 @@
|
||||
let PLAYLISTS = null;
|
||||
|
||||
export function init(plists) {
|
||||
PLAYLISTS = {
|
||||
mediaTypes: [],
|
||||
};
|
||||
|
||||
if (void 0 !== plists) {
|
||||
if (void 0 !== plists.mediaTypes) {
|
||||
if (plists.mediaTypes.length) {
|
||||
PLAYLISTS.mediaTypes = [];
|
||||
|
||||
let i = 0;
|
||||
while (i < plists.mediaTypes.length) {
|
||||
switch (plists.mediaTypes[i]) {
|
||||
case 'audio':
|
||||
case 'video':
|
||||
PLAYLISTS.mediaTypes.push(plists.mediaTypes[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!PLAYLISTS.mediaTypes.length) {
|
||||
PLAYLISTS.mediaTypes = ['audio', 'video'];
|
||||
}
|
||||
}
|
||||
|
||||
export function settings() {
|
||||
return PLAYLISTS;
|
||||
}
|
||||
27
frontend/src/static/js/utils/settings/sidebar.js
Normal file
27
frontend/src/static/js/utils/settings/sidebar.js
Normal file
@@ -0,0 +1,27 @@
|
||||
let SIDEBAR = null;
|
||||
|
||||
export function init(settings) {
|
||||
SIDEBAR = {
|
||||
hideHomeLink: false,
|
||||
hideTagsLink: false,
|
||||
hideCategoriesLink: false,
|
||||
};
|
||||
|
||||
if (void 0 !== settings) {
|
||||
if ('boolean' === typeof settings.hideHomeLink) {
|
||||
SIDEBAR.hideHomeLink = settings.hideHomeLink;
|
||||
}
|
||||
|
||||
if ('boolean' === typeof settings.hideTagsLink) {
|
||||
SIDEBAR.hideTagsLink = settings.hideTagsLink;
|
||||
}
|
||||
|
||||
if ('boolean' === typeof settings.hideCategoriesLink) {
|
||||
SIDEBAR.hideCategoriesLink = settings.hideCategoriesLink;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function settings() {
|
||||
return SIDEBAR;
|
||||
}
|
||||
32
frontend/src/static/js/utils/settings/site.js
Executable file
32
frontend/src/static/js/utils/settings/site.js
Executable file
@@ -0,0 +1,32 @@
|
||||
let SITE = null;
|
||||
|
||||
export function init(settings) {
|
||||
SITE = {
|
||||
id: 'media-cms',
|
||||
url: '',
|
||||
api: '',
|
||||
title: '',
|
||||
};
|
||||
|
||||
if (void 0 !== settings) {
|
||||
if ('string' === typeof settings.id) {
|
||||
SITE.id = settings.id.trim();
|
||||
}
|
||||
|
||||
if ('string' === typeof settings.url) {
|
||||
SITE.url = settings.url.trim();
|
||||
}
|
||||
|
||||
if ('string' === typeof settings.api) {
|
||||
SITE.api = settings.api.trim();
|
||||
}
|
||||
|
||||
if ('string' === typeof settings.title) {
|
||||
SITE.title = settings.title.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function settings() {
|
||||
return SITE;
|
||||
}
|
||||
34
frontend/src/static/js/utils/settings/taxonomies.js
Executable file
34
frontend/src/static/js/utils/settings/taxonomies.js
Executable file
@@ -0,0 +1,34 @@
|
||||
let TAXONOMIES = null;
|
||||
|
||||
export function init(settings) {
|
||||
TAXONOMIES = {
|
||||
tags: {
|
||||
enabled: false,
|
||||
title: 'Tags',
|
||||
},
|
||||
categories: {
|
||||
enabled: false,
|
||||
title: 'Categories',
|
||||
},
|
||||
};
|
||||
|
||||
if (void 0 !== settings) {
|
||||
for (let k in TAXONOMIES) {
|
||||
if (void 0 !== settings[k]) {
|
||||
TAXONOMIES[k].enabled = true;
|
||||
|
||||
if (void 0 !== settings[k].enabled && false === settings[k].enabled) {
|
||||
TAXONOMIES[k].enabled = false;
|
||||
}
|
||||
|
||||
if ('string' === typeof settings[k].title) {
|
||||
TAXONOMIES[k].title = settings[k].title.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function settings() {
|
||||
return TAXONOMIES;
|
||||
}
|
||||
65
frontend/src/static/js/utils/settings/theme.js
Executable file
65
frontend/src/static/js/utils/settings/theme.js
Executable file
@@ -0,0 +1,65 @@
|
||||
let THEME = null;
|
||||
|
||||
export function init(theme, logo) {
|
||||
THEME = {
|
||||
mode: 'light', // Valid options: 'light', 'dark'.
|
||||
switch: {
|
||||
enabled: true,
|
||||
position: 'header', // Valid options: 'header', 'sidebar'.
|
||||
},
|
||||
logo: {
|
||||
lightMode: {
|
||||
img: '',
|
||||
svg: '',
|
||||
},
|
||||
darkMode: {
|
||||
img: '',
|
||||
svg: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (void 0 !== theme) {
|
||||
if ('string' === typeof theme.mode) {
|
||||
THEME.mode = theme.mode.trim();
|
||||
THEME.mode = 'dark' === THEME.mode ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
if (void 0 !== theme.switch) {
|
||||
if (false === theme.switch.enabled) {
|
||||
THEME.switch.enabled = theme.switch.enabled;
|
||||
}
|
||||
|
||||
if ('string' === typeof theme.switch.position) {
|
||||
THEME.switch.position = theme.switch.position.trim();
|
||||
THEME.switch.position = 'sidebar' === theme.switch.position ? 'sidebar' : 'header';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (void 0 !== logo) {
|
||||
if (void 0 !== logo.lightMode) {
|
||||
if ('string' === typeof logo.lightMode.img) {
|
||||
THEME.logo.lightMode.img = logo.lightMode.img.trim();
|
||||
}
|
||||
|
||||
if ('string' === typeof logo.lightMode.svg) {
|
||||
THEME.logo.lightMode.svg = logo.lightMode.svg.trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (void 0 !== logo.darkMode) {
|
||||
if ('string' === typeof logo.darkMode.img) {
|
||||
THEME.logo.darkMode.img = logo.darkMode.img.trim();
|
||||
}
|
||||
|
||||
if ('string' === typeof logo.darkMode.svg) {
|
||||
THEME.logo.darkMode.svg = logo.darkMode.svg.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function settings() {
|
||||
return THEME;
|
||||
}
|
||||
13
frontend/src/static/js/utils/settings/url.js
Executable file
13
frontend/src/static/js/utils/settings/url.js
Executable file
@@ -0,0 +1,13 @@
|
||||
let PAGES = null;
|
||||
|
||||
export function init(pages_url) {
|
||||
PAGES = {};
|
||||
|
||||
for (let k in pages_url) {
|
||||
PAGES[k] = pages_url[k];
|
||||
}
|
||||
}
|
||||
|
||||
export function pages() {
|
||||
return PAGES;
|
||||
}
|
||||
876
frontend/src/static/js/utils/stores/MediaPageStore.js
Normal file
876
frontend/src/static/js/utils/stores/MediaPageStore.js
Normal file
@@ -0,0 +1,876 @@
|
||||
import EventEmitter from 'events';
|
||||
import { exportStore, getRequest, postRequest, putRequest, deleteRequest, csrfToken } from '../helpers';
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
import UrlParse from 'url-parse';
|
||||
|
||||
import PageStore from './PageStore.js';
|
||||
|
||||
function extractPlaylistId() {
|
||||
let playlistId = null;
|
||||
|
||||
const getParamsString = window.location.search;
|
||||
|
||||
if ('' !== getParamsString) {
|
||||
let tmp = getParamsString.split('?');
|
||||
|
||||
if (2 === tmp.length) {
|
||||
tmp = tmp[1].split('&');
|
||||
|
||||
let x;
|
||||
|
||||
let i = 0;
|
||||
while (i < tmp.length) {
|
||||
x = tmp[i].split('=');
|
||||
|
||||
if ('pl' === x[0]) {
|
||||
if (2 === x.length) {
|
||||
playlistId = x[1];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return playlistId;
|
||||
}
|
||||
|
||||
const MediaPageStoreData = {};
|
||||
|
||||
class MediaPageStore extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.mediacms_config = mediacmsConfig(window.MediaCMS);
|
||||
|
||||
this._MEDIA = null;
|
||||
|
||||
this.pagePlaylistId = null;
|
||||
this.pagePlaylistData = null;
|
||||
|
||||
MediaPageStoreData[
|
||||
Object.defineProperty(this, 'id', { value: 'MediaPageStoreData_' + Object.keys(MediaPageStoreData).length }).id
|
||||
] = {
|
||||
likedMedia: false,
|
||||
dislikedMedia: false,
|
||||
reported_times: 0,
|
||||
while: {
|
||||
deleteMedia: false,
|
||||
submitComment: false,
|
||||
deleteCommentId: null,
|
||||
},
|
||||
};
|
||||
|
||||
this.removeMediaResponse = this.removeMediaResponse.bind(this);
|
||||
this.removeMediaFail = this.removeMediaFail.bind(this);
|
||||
|
||||
this.submitCommentFail = this.submitCommentFail.bind(this);
|
||||
this.submitCommentResponse = this.submitCommentResponse.bind(this);
|
||||
|
||||
this.removeCommentFail = this.removeCommentFail.bind(this);
|
||||
this.removeCommentResponse = this.removeCommentResponse.bind(this);
|
||||
}
|
||||
|
||||
loadData() {
|
||||
if (!MediaPageStoreData[this.id].mediaId) {
|
||||
let urlParams = (function () {
|
||||
let ret = new UrlParse(window.location.href).query;
|
||||
if (!ret) {
|
||||
ret = [];
|
||||
} else {
|
||||
ret = ret.substring(1);
|
||||
ret.split('&');
|
||||
ret = ret.length ? ret.split('=') : [];
|
||||
}
|
||||
return ret;
|
||||
})();
|
||||
|
||||
if (urlParams.length) {
|
||||
let i = 0;
|
||||
while (i < urlParams.length) {
|
||||
if ('m' === urlParams[i]) {
|
||||
// NOTE: "m" => media id/token.
|
||||
MediaPageStoreData[this.id].mediaId = urlParams[i + 1];
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!MediaPageStoreData[this.id].mediaId) {
|
||||
console.warn('Invalid media id:', MediaPageStoreData[this.id].mediaId);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.mediaAPIUrl = this.mediacms_config.api.media + '/' + MediaPageStoreData[this.id].mediaId;
|
||||
this.dataResponse = this.dataResponse.bind(this);
|
||||
this.dataErrorResponse = this.dataErrorResponse.bind(this);
|
||||
getRequest(this.mediaAPIUrl, !1, this.dataResponse, this.dataErrorResponse);
|
||||
}
|
||||
|
||||
loadPlaylistData() {
|
||||
const playlistApiUrl = this.mediacms_config.api.playlists + '/' + this.pagePlaylistId;
|
||||
this.playlistDataResponse = this.playlistDataResponse.bind(this);
|
||||
this.playlistDataErrorResponse = this.playlistDataErrorResponse.bind(this);
|
||||
getRequest(playlistApiUrl, !1, this.playlistDataResponse, this.playlistDataErrorResponse);
|
||||
}
|
||||
|
||||
playlistDataResponse(response) {
|
||||
if (response && response.data) {
|
||||
let validPlaylistMedia = false;
|
||||
|
||||
let i = 0;
|
||||
while (i < response.data.playlist_media.length) {
|
||||
if (MediaPageStoreData[this.id].mediaId === response.data.playlist_media[i].friendly_token) {
|
||||
validPlaylistMedia = true;
|
||||
break;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (validPlaylistMedia) {
|
||||
this.pagePlaylistData = response.data;
|
||||
} else {
|
||||
this.pagePlaylistId = null;
|
||||
}
|
||||
|
||||
this.emit('loaded_viewer_playlist_data');
|
||||
} else {
|
||||
this.pagePlaylistId = null;
|
||||
}
|
||||
|
||||
this.emit('loaded_page_playlist_data');
|
||||
}
|
||||
|
||||
playlistDataErrorResponse(response) {
|
||||
this.emit('loaded_viewer_playlist_error');
|
||||
this.emit('loaded_page_playlist_data');
|
||||
}
|
||||
|
||||
loadComments() {
|
||||
this.commentsAPIUrl = this.mediacms_config.api.media + '/' + MediaPageStoreData[this.id].mediaId + '/comments';
|
||||
this.commentsResponse = this.commentsResponse.bind(this);
|
||||
getRequest(this.commentsAPIUrl, !1, this.commentsResponse);
|
||||
}
|
||||
|
||||
loadPlaylists() {
|
||||
if (!this.mediacms_config.member.can.saveMedia) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playlistsAPIUrl = this.mediacms_config.api.user.playlists + this.mediacms_config.member.username;
|
||||
|
||||
this.playlistsResponse = this.playlistsResponse.bind(this);
|
||||
|
||||
getRequest(this.playlistsAPIUrl, !1, this.playlistsResponse);
|
||||
}
|
||||
|
||||
dataResponse(response) {
|
||||
if (response && response.data) {
|
||||
MediaPageStoreData[this.id].data = response.data;
|
||||
MediaPageStoreData[this.id].reported_times = !!MediaPageStoreData[this.id].data.reported_times;
|
||||
|
||||
switch (this.get('media-type')) {
|
||||
case 'video':
|
||||
case 'audio':
|
||||
case 'image':
|
||||
this.emit('loaded_' + this.get('media-type') + '_data');
|
||||
break;
|
||||
}
|
||||
|
||||
this.emit('loaded_media_data');
|
||||
}
|
||||
|
||||
this.loadPlaylists();
|
||||
|
||||
if (this.mediacms_config.member.can.readComment) {
|
||||
this.loadComments();
|
||||
}
|
||||
}
|
||||
|
||||
dataErrorResponse(response) {
|
||||
if (void 0 !== response.type) {
|
||||
switch (response.type) {
|
||||
case 'network':
|
||||
case 'private':
|
||||
case 'unavailable':
|
||||
MediaPageStoreData[this.id].loadErrorType = response.type;
|
||||
MediaPageStoreData[this.id].loadErrorMessage =
|
||||
void 0 !== response.message ? response.message : "Αn error occurred while loading the media's data";
|
||||
this.emit('loaded_media_error');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commentsResponse(response) {
|
||||
if (response && response.data) {
|
||||
MediaPageStoreData[this.id].comments = response.data.count ? response.data.results : [];
|
||||
this.emit('comments_load');
|
||||
}
|
||||
}
|
||||
|
||||
playlistsResponse(response) {
|
||||
if (response && response.data) {
|
||||
let tmp_playlists = response.data.count ? response.data.results : [];
|
||||
|
||||
MediaPageStoreData[this.id].playlists = [];
|
||||
|
||||
let i = 0;
|
||||
let cntr = 0;
|
||||
|
||||
while (i < tmp_playlists.length) {
|
||||
(function (pos) {
|
||||
let _this = this;
|
||||
|
||||
if (tmp_playlists[pos].user === this.mediacms_config.member.username) {
|
||||
let playlistsIndex = MediaPageStoreData[_this.id].playlists.length;
|
||||
|
||||
MediaPageStoreData[_this.id].playlists[playlistsIndex] = {
|
||||
playlist_id: (function (_url_) {
|
||||
let ret = _url_.split('/');
|
||||
return 1 < ret.length ? ret[ret.length - 1] : null;
|
||||
})(tmp_playlists[pos].url),
|
||||
title: tmp_playlists[pos].title,
|
||||
description: tmp_playlists[pos].description,
|
||||
add_date: tmp_playlists[pos].add_date,
|
||||
};
|
||||
|
||||
getRequest(
|
||||
this.mediacms_config.site.url + '/' + tmp_playlists[pos].api_url.replace(/^\//g, ''),
|
||||
!1,
|
||||
function (resp) {
|
||||
if (!!resp && !!resp.data) {
|
||||
MediaPageStoreData[_this.id].playlists[playlistsIndex].media_list = [];
|
||||
|
||||
let f = 0;
|
||||
let arr;
|
||||
|
||||
while (f < resp.data.playlist_media.length) {
|
||||
arr = resp.data.playlist_media[f].url.split('m=');
|
||||
if (2 === arr.length) {
|
||||
MediaPageStoreData[_this.id].playlists[playlistsIndex].media_list.push(arr[1]);
|
||||
}
|
||||
f += 1;
|
||||
}
|
||||
}
|
||||
|
||||
cntr += 1;
|
||||
|
||||
if (cntr === tmp_playlists.length) {
|
||||
this.emit('playlists_load');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}.bind(this)(i));
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requestMediaLike() {
|
||||
if (!MediaPageStoreData[this.id].mediaId) {
|
||||
console.warn('Invalid media id:', MediaPageStoreData[this.id].mediaId);
|
||||
return false;
|
||||
}
|
||||
|
||||
const url = this.mediacms_config.api.media + '/' + MediaPageStoreData[this.id].mediaId + '/actions';
|
||||
|
||||
this.likeActionResponse = this.likeActionResponse.bind(this);
|
||||
|
||||
postRequest(
|
||||
url,
|
||||
{
|
||||
type: 'like',
|
||||
// `headers` are custom headers to be sent
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken(),
|
||||
},
|
||||
},
|
||||
false,
|
||||
this.likeActionResponse,
|
||||
function () {
|
||||
this.emit('liked_media_failed_request');
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
likeActionResponse(response) {
|
||||
if (response) {
|
||||
if (response instanceof Error) {
|
||||
} else if (response.data) {
|
||||
MediaPageStoreData[this.id].likedMedia = true;
|
||||
this.emit('liked_media');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requestMediaDislike() {
|
||||
if (!MediaPageStoreData[this.id].mediaId) {
|
||||
console.warn('Invalid media id:', MediaPageStoreData[this.id].mediaId);
|
||||
return false;
|
||||
}
|
||||
|
||||
const url = this.mediacms_config.api.media + '/' + MediaPageStoreData[this.id].mediaId + '/actions';
|
||||
this.dislikeActionResponse = this.dislikeActionResponse.bind(this);
|
||||
postRequest(
|
||||
url,
|
||||
{
|
||||
type: 'dislike',
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken(),
|
||||
},
|
||||
},
|
||||
false,
|
||||
this.dislikeActionResponse,
|
||||
function () {
|
||||
this.emit('disliked_media_failed_request');
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
dislikeActionResponse(response) {
|
||||
if (response) {
|
||||
if (response instanceof Error) {
|
||||
} else if (response.data) {
|
||||
MediaPageStoreData[this.id].dislikedMedia = true;
|
||||
this.emit('disliked_media');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requestMediaReport(descr) {
|
||||
if (!MediaPageStoreData[this.id].mediaId) {
|
||||
console.warn('Invalid media id:', MediaPageStoreData[this.id].mediaId);
|
||||
return false;
|
||||
}
|
||||
|
||||
const url = this.mediacms_config.api.media + '/' + MediaPageStoreData[this.id].mediaId + '/actions';
|
||||
this.reportActionResponse = this.reportActionResponse.bind(this);
|
||||
postRequest(
|
||||
url,
|
||||
{
|
||||
type: 'report',
|
||||
extra_info: descr,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken(),
|
||||
},
|
||||
},
|
||||
false,
|
||||
this.reportActionResponse,
|
||||
this.reportActionResponse
|
||||
);
|
||||
}
|
||||
|
||||
reportActionResponse(response) {
|
||||
if (response) {
|
||||
if (response instanceof Error) {
|
||||
} else if (response.data) {
|
||||
MediaPageStoreData[this.id].reported_times += 1;
|
||||
this.emit('reported_media');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set(type, value) {
|
||||
switch (type) {
|
||||
case 'media-load-error-type':
|
||||
MediaPageStoreData[this.id].loadErrorType = value;
|
||||
break;
|
||||
case 'media-load-error-message':
|
||||
MediaPageStoreData[this.id].loadErrorMessage = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
get(type) {
|
||||
let tmp,
|
||||
activeItem,
|
||||
browserCache,
|
||||
i,
|
||||
r = null;
|
||||
switch (type) {
|
||||
case 'playlists':
|
||||
r = MediaPageStoreData[this.id].playlists || [];
|
||||
break;
|
||||
case 'media-load-error-type':
|
||||
r = void 0 !== MediaPageStoreData[this.id].loadErrorType ? MediaPageStoreData[this.id].loadErrorType : null;
|
||||
break;
|
||||
case 'media-load-error-message':
|
||||
r =
|
||||
void 0 !== MediaPageStoreData[this.id].loadErrorMessage ? MediaPageStoreData[this.id].loadErrorMessage : null;
|
||||
break;
|
||||
case 'media-comments':
|
||||
r = MediaPageStoreData[this.id].comments || [];
|
||||
break;
|
||||
case 'media-data':
|
||||
r = MediaPageStoreData[this.id].data || null;
|
||||
break;
|
||||
case 'media-id':
|
||||
r = MediaPageStoreData[this.id].mediaId;
|
||||
break;
|
||||
case 'media-url':
|
||||
r =
|
||||
void 0 !== MediaPageStoreData[this.id].data && void 0 !== MediaPageStoreData[this.id].data.url
|
||||
? MediaPageStoreData[this.id].data.url
|
||||
: 'N/A';
|
||||
break;
|
||||
case 'media-edit-subtitle-url':
|
||||
r =
|
||||
void 0 !== MediaPageStoreData[this.id].data &&
|
||||
'string' === typeof MediaPageStoreData[this.id].data.add_subtitle_url
|
||||
? MediaPageStoreData[this.id].data.add_subtitle_url
|
||||
: null;
|
||||
break;
|
||||
case 'media-likes':
|
||||
tmp = MediaPageStoreData[this.id].likedMedia ? 1 : 0;
|
||||
if (tmp) {
|
||||
r =
|
||||
void 0 !== MediaPageStoreData[this.id].data && void 0 !== MediaPageStoreData[this.id].data.likes
|
||||
? MediaPageStoreData[this.id].data.likes + tmp
|
||||
: tmp;
|
||||
} else {
|
||||
r =
|
||||
void 0 !== MediaPageStoreData[this.id].data && void 0 !== MediaPageStoreData[this.id].data.likes
|
||||
? MediaPageStoreData[this.id].data.likes
|
||||
: 'N/A';
|
||||
}
|
||||
break;
|
||||
case 'media-dislikes':
|
||||
tmp = MediaPageStoreData[this.id].dislikedMedia ? 1 : 0;
|
||||
if (tmp) {
|
||||
r =
|
||||
void 0 !== MediaPageStoreData[this.id].data && void 0 !== MediaPageStoreData[this.id].data.dislikes
|
||||
? MediaPageStoreData[this.id].data.dislikes + tmp
|
||||
: tmp;
|
||||
} else {
|
||||
r =
|
||||
void 0 !== MediaPageStoreData[this.id].data && void 0 !== MediaPageStoreData[this.id].data.dislikes
|
||||
? MediaPageStoreData[this.id].data.dislikes
|
||||
: 'N/A';
|
||||
}
|
||||
break;
|
||||
case 'media-summary':
|
||||
r =
|
||||
void 0 !== MediaPageStoreData[this.id].data && void 0 !== MediaPageStoreData[this.id].data.summary
|
||||
? MediaPageStoreData[this.id].data.summary
|
||||
: null;
|
||||
break;
|
||||
case 'media-categories':
|
||||
r =
|
||||
void 0 !== MediaPageStoreData[this.id].data && void 0 !== MediaPageStoreData[this.id].data.categories_info
|
||||
? MediaPageStoreData[this.id].data.categories_info
|
||||
: [];
|
||||
break;
|
||||
case 'media-tags':
|
||||
r =
|
||||
void 0 !== MediaPageStoreData[this.id].data && void 0 !== MediaPageStoreData[this.id].data.tags_info
|
||||
? MediaPageStoreData[this.id].data.tags_info
|
||||
: [];
|
||||
break;
|
||||
case 'media-type':
|
||||
r =
|
||||
void 0 !== MediaPageStoreData[this.id].data && void 0 !== MediaPageStoreData[this.id].data.media_type
|
||||
? MediaPageStoreData[this.id].data.media_type
|
||||
: null;
|
||||
break;
|
||||
case 'media-original-url':
|
||||
r =
|
||||
void 0 !== MediaPageStoreData[this.id].data && void 0 !== MediaPageStoreData[this.id].data.original_media_url
|
||||
? MediaPageStoreData[this.id].data.original_media_url
|
||||
: null;
|
||||
break;
|
||||
case 'media-thumbnail-url':
|
||||
r =
|
||||
void 0 !== MediaPageStoreData[this.id].data && void 0 !== MediaPageStoreData[this.id].data.thumbnail_url
|
||||
? MediaPageStoreData[this.id].data.thumbnail_url
|
||||
: null;
|
||||
break;
|
||||
case 'user-liked-media':
|
||||
r = MediaPageStoreData[this.id].likedMedia;
|
||||
break;
|
||||
case 'user-disliked-media':
|
||||
r = MediaPageStoreData[this.id].dislikedMedia;
|
||||
break;
|
||||
case 'media-author-thumbnail-url':
|
||||
r =
|
||||
void 0 !== MediaPageStoreData[this.id].data && void 0 !== MediaPageStoreData[this.id].data.author_thumbnail
|
||||
? this.mediacms_config.site.url +
|
||||
'/' +
|
||||
MediaPageStoreData[this.id].data.author_thumbnail.replace(/^\//g, '')
|
||||
: null;
|
||||
break;
|
||||
case 'playlist-data':
|
||||
r = this.pagePlaylistData;
|
||||
break;
|
||||
case 'playlist-id':
|
||||
r = this.pagePlaylistId;
|
||||
break;
|
||||
case 'playlist-next-media-url':
|
||||
if (!this.pagePlaylistData) {
|
||||
break;
|
||||
}
|
||||
|
||||
activeItem = 0;
|
||||
i = 0;
|
||||
while (i < this.pagePlaylistData.playlist_media.length) {
|
||||
if (MediaPageStoreData[this.id].mediaId === this.pagePlaylistData.playlist_media[i].friendly_token) {
|
||||
activeItem = i;
|
||||
break;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let nextItem = activeItem + 1;
|
||||
|
||||
if (nextItem === this.pagePlaylistData.playlist_media.length) {
|
||||
browserCache = PageStore.get('browser-cache');
|
||||
if (true === browserCache.get('loopPlaylist[' + this.pagePlaylistId + ']')) {
|
||||
nextItem = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (void 0 !== this.pagePlaylistData.playlist_media[nextItem]) {
|
||||
r = this.pagePlaylistData.playlist_media[nextItem].url + '&pl=' + this.pagePlaylistId;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'playlist-previous-media-url':
|
||||
if (!this.pagePlaylistData) {
|
||||
break;
|
||||
}
|
||||
|
||||
activeItem = 0;
|
||||
i = 0;
|
||||
while (i < this.pagePlaylistData.playlist_media.length) {
|
||||
if (MediaPageStoreData[this.id].mediaId === this.pagePlaylistData.playlist_media[i].friendly_token) {
|
||||
activeItem = i;
|
||||
break;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let previousItem = activeItem - 1;
|
||||
|
||||
if (0 === activeItem) {
|
||||
previousItem = null;
|
||||
|
||||
browserCache = PageStore.get('browser-cache');
|
||||
|
||||
if (true === browserCache.get('loopPlaylist[' + this.pagePlaylistId + ']')) {
|
||||
previousItem = this.pagePlaylistData.playlist_media.length - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (void 0 !== this.pagePlaylistData.playlist_media[previousItem]) {
|
||||
r = this.pagePlaylistData.playlist_media[previousItem].url + '&pl=' + this.pagePlaylistId;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
isVideo() {
|
||||
return 'video' === this.get('media-type');
|
||||
}
|
||||
|
||||
onPlaylistCreationCompleted(response) {
|
||||
if (response && response.data) {
|
||||
this.emit('playlist_creation_completed', response.data);
|
||||
}
|
||||
}
|
||||
|
||||
onPlaylistCreationFailed() {
|
||||
this.emit('playlist_creation_failed');
|
||||
}
|
||||
|
||||
onPlaylistMediaAdditionCompleted(playlist_id, response) {
|
||||
if (response) {
|
||||
let i = 0;
|
||||
while (i < MediaPageStoreData[this.id].playlists.length) {
|
||||
if (playlist_id === MediaPageStoreData[this.id].playlists[i].playlist_id) {
|
||||
MediaPageStoreData[this.id].playlists[i].media_list.push(MediaPageStoreData[this.id].mediaId);
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
this.emit('media_playlist_addition_completed', playlist_id);
|
||||
}
|
||||
}
|
||||
|
||||
onPlaylistMediaAdditionFailed(playlist_id, response) {
|
||||
this.emit('media_playlist_addition_failed');
|
||||
}
|
||||
|
||||
onPlaylistMediaRemovalCompleted(playlist_id, response) {
|
||||
if (response) {
|
||||
let j, new_playlist_media;
|
||||
let i = 0;
|
||||
while (i < MediaPageStoreData[this.id].playlists.length) {
|
||||
if (playlist_id === MediaPageStoreData[this.id].playlists[i].playlist_id) {
|
||||
new_playlist_media = [];
|
||||
j = 0;
|
||||
while (j < MediaPageStoreData[this.id].playlists[i].media_list.length) {
|
||||
if (MediaPageStoreData[this.id].mediaId !== MediaPageStoreData[this.id].playlists[i].media_list[j]) {
|
||||
new_playlist_media.push(MediaPageStoreData[this.id].playlists[i].media_list[j]);
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
MediaPageStoreData[this.id].playlists[i].media_list = new_playlist_media;
|
||||
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
this.emit('media_playlist_removal_completed', playlist_id);
|
||||
}
|
||||
}
|
||||
|
||||
onPlaylistMediaRemovalFailed(playlist_id, response) {
|
||||
this.emit('media_playlist_removal_failed');
|
||||
}
|
||||
|
||||
actions_handler(action) {
|
||||
switch (action.type) {
|
||||
case 'LOAD_MEDIA_DATA':
|
||||
MediaPageStoreData[this.id].mediaId = window.MediaCMS.mediaId || MediaPageStoreData[this.id].mediaId;
|
||||
|
||||
this.pagePlaylistId = extractPlaylistId();
|
||||
|
||||
if (this.pagePlaylistId) {
|
||||
this.loadPlaylistData();
|
||||
this.loadData();
|
||||
} else {
|
||||
this.emit('loaded_page_playlist_data');
|
||||
this.loadData();
|
||||
}
|
||||
|
||||
break;
|
||||
case 'LIKE_MEDIA':
|
||||
if (!MediaPageStoreData[this.id].likedMedia && !MediaPageStoreData[this.id].dislikedMedia) {
|
||||
this.requestMediaLike();
|
||||
}
|
||||
break;
|
||||
case 'DISLIKE_MEDIA':
|
||||
if (!MediaPageStoreData[this.id].likedMedia && !MediaPageStoreData[this.id].dislikedMedia) {
|
||||
this.requestMediaDislike();
|
||||
}
|
||||
break;
|
||||
case 'REPORT_MEDIA':
|
||||
if (!MediaPageStoreData[this.id].reported_times) {
|
||||
if ('' !== action.reportDescription) {
|
||||
this.requestMediaReport(action.reportDescription);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'COPY_SHARE_LINK':
|
||||
if (action.inputElement instanceof HTMLElement) {
|
||||
action.inputElement.select();
|
||||
document.execCommand('copy');
|
||||
this.emit('copied_media_link');
|
||||
}
|
||||
break;
|
||||
case 'COPY_EMBED_MEDIA_CODE':
|
||||
if (action.inputElement instanceof HTMLElement) {
|
||||
action.inputElement.select();
|
||||
document.execCommand('copy');
|
||||
this.emit('copied_embed_media_code');
|
||||
}
|
||||
break;
|
||||
case 'REMOVE_MEDIA':
|
||||
if (MediaPageStoreData[this.id].while.deleteMedia) {
|
||||
return;
|
||||
}
|
||||
MediaPageStoreData[this.id].while.deleteMedia = true;
|
||||
deleteRequest(
|
||||
this.mediaAPIUrl,
|
||||
{ headers: { 'X-CSRFToken': csrfToken() } },
|
||||
false,
|
||||
this.removeMediaResponse,
|
||||
this.removeMediaFail
|
||||
);
|
||||
break;
|
||||
case 'SUBMIT_COMMENT':
|
||||
if (MediaPageStoreData[this.id].while.submitComment) {
|
||||
return;
|
||||
}
|
||||
|
||||
MediaPageStoreData[this.id].while.submitComment = true;
|
||||
|
||||
postRequest(
|
||||
this.commentsAPIUrl,
|
||||
{ text: action.commentText },
|
||||
{ headers: { 'X-CSRFToken': csrfToken() } },
|
||||
false,
|
||||
this.submitCommentResponse,
|
||||
this.submitCommentFail
|
||||
);
|
||||
break;
|
||||
case 'DELETE_COMMENT':
|
||||
if (null !== MediaPageStoreData[this.id].while.deleteCommentId) {
|
||||
return;
|
||||
}
|
||||
|
||||
MediaPageStoreData[this.id].while.deleteCommentId = action.commentId;
|
||||
deleteRequest(
|
||||
this.commentsAPIUrl + '/' + action.commentId,
|
||||
{ headers: { 'X-CSRFToken': csrfToken() } },
|
||||
false,
|
||||
this.removeCommentResponse,
|
||||
this.removeCommentFail
|
||||
);
|
||||
break;
|
||||
case 'CREATE_PLAYLIST':
|
||||
postRequest(
|
||||
this.mediacms_config.api.playlists,
|
||||
{
|
||||
title: action.playlist_data.title,
|
||||
description: action.playlist_data.description,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken(),
|
||||
},
|
||||
},
|
||||
false,
|
||||
this.onPlaylistCreationCompleted.bind(this),
|
||||
this.onPlaylistCreationFailed.bind(this)
|
||||
);
|
||||
break;
|
||||
case 'ADD_MEDIA_TO_PLAYLIST':
|
||||
putRequest(
|
||||
this.mediacms_config.api.playlists + '/' + action.playlist_id,
|
||||
{
|
||||
type: 'add',
|
||||
media_friendly_token: action.media_id,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken(),
|
||||
},
|
||||
},
|
||||
false,
|
||||
this.onPlaylistMediaAdditionCompleted.bind(this, action.playlist_id),
|
||||
this.onPlaylistMediaAdditionFailed.bind(this, action.playlist_id)
|
||||
);
|
||||
break;
|
||||
case 'REMOVE_MEDIA_FROM_PLAYLIST':
|
||||
putRequest(
|
||||
this.mediacms_config.api.playlists + '/' + action.playlist_id,
|
||||
{
|
||||
type: 'remove',
|
||||
media_friendly_token: action.media_id,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken(),
|
||||
},
|
||||
},
|
||||
false,
|
||||
this.onPlaylistMediaRemovalCompleted.bind(this, action.playlist_id),
|
||||
this.onPlaylistMediaRemovalFailed.bind(this, action.playlist_id)
|
||||
);
|
||||
break;
|
||||
case 'APPEND_NEW_PLAYLIST':
|
||||
MediaPageStoreData[this.id].playlists.push(action.playlist_data);
|
||||
this.emit('playlists_load');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
removeMediaResponse(response) {
|
||||
if (response && 204 === response.status) {
|
||||
this.emit('media_delete', MediaPageStoreData[this.id].mediaId);
|
||||
}
|
||||
}
|
||||
|
||||
removeMediaFail() {
|
||||
this.emit('media_delete_fail', MediaPageStoreData[this.id].mediaId);
|
||||
setTimeout(
|
||||
function (ins) {
|
||||
MediaPageStoreData[ins.id].while.deleteMedia = null;
|
||||
},
|
||||
100,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
removeCommentFail(err) {
|
||||
this.emit('comment_delete_fail', MediaPageStoreData[this.id].while.deleteCommentId);
|
||||
setTimeout(
|
||||
function (ins) {
|
||||
MediaPageStoreData[ins.id].while.deleteCommentId = null;
|
||||
},
|
||||
100,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
removeCommentResponse(response) {
|
||||
if (response && 204 === response.status) {
|
||||
let k;
|
||||
let newComments = [];
|
||||
for (k in MediaPageStoreData[this.id].comments) {
|
||||
if (MediaPageStoreData[this.id].comments.hasOwnProperty(k)) {
|
||||
if (MediaPageStoreData[this.id].while.deleteCommentId !== MediaPageStoreData[this.id].comments[k].uid) {
|
||||
newComments.push(MediaPageStoreData[this.id].comments[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
MediaPageStoreData[this.id].comments = newComments;
|
||||
newComments = null;
|
||||
|
||||
this.emit('comment_delete', MediaPageStoreData[this.id].while.deleteCommentId);
|
||||
}
|
||||
setTimeout(
|
||||
function (ins) {
|
||||
MediaPageStoreData[ins.id].while.deleteCommentId = null;
|
||||
},
|
||||
100,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
submitCommentFail(err) {
|
||||
this.emit('comment_submit_fail');
|
||||
setTimeout(
|
||||
function (ins) {
|
||||
MediaPageStoreData[ins.id].while.submitComment = false;
|
||||
},
|
||||
100,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
submitCommentResponse(response) {
|
||||
if (response && 201 === response.status && response.data && Object.keys(response.data)) {
|
||||
MediaPageStoreData[this.id].comments.push(response.data);
|
||||
this.emit('comment_submit', response.data.uid);
|
||||
}
|
||||
setTimeout(
|
||||
function (ins) {
|
||||
MediaPageStoreData[ins.id].while.submitComment = false;
|
||||
},
|
||||
100,
|
||||
this
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default exportStore(new MediaPageStore(), 'actions_handler');
|
||||
155
frontend/src/static/js/utils/stores/PageStore.js
Normal file
155
frontend/src/static/js/utils/stores/PageStore.js
Normal file
@@ -0,0 +1,155 @@
|
||||
import EventEmitter from 'events';
|
||||
import { BrowserCache } from '../classes/';
|
||||
import { BrowserEvents, exportStore } from '../helpers';
|
||||
import { config as mediaCmsConfig } from '../settings/config.js';
|
||||
|
||||
function uniqid() {
|
||||
let a = new Uint32Array(3);
|
||||
window.crypto.getRandomValues(a);
|
||||
return (
|
||||
performance.now().toString(36) +
|
||||
Array.from(a)
|
||||
.map((A) => A.toString(36))
|
||||
.join('')
|
||||
).replace(/./g, '' + Math.random() + Intl.DateTimeFormat().resolvedOptions().timeZone + Date.now());
|
||||
}
|
||||
|
||||
function Notifications(initialNotifications) {
|
||||
let stack = [];
|
||||
|
||||
function push(msg) {
|
||||
if ('string' === typeof msg) {
|
||||
stack.push([uniqid(), msg]);
|
||||
}
|
||||
}
|
||||
|
||||
function messages() {
|
||||
return [...stack];
|
||||
}
|
||||
|
||||
function clear() {
|
||||
stack = [];
|
||||
}
|
||||
|
||||
function size() {
|
||||
return stack.length;
|
||||
}
|
||||
|
||||
initialNotifications.map(push);
|
||||
|
||||
return {
|
||||
size,
|
||||
push,
|
||||
clear,
|
||||
messages,
|
||||
};
|
||||
}
|
||||
|
||||
let browserCache;
|
||||
|
||||
let page_config = null;
|
||||
let mediacms_config = null;
|
||||
|
||||
const mediacms_api_endpoint_url = (k) => mediacms_config.api[k] || null;
|
||||
|
||||
class PageStore extends EventEmitter {
|
||||
constructor(page) {
|
||||
super();
|
||||
|
||||
mediacms_config = mediaCmsConfig(window.MediaCMS);
|
||||
|
||||
browserCache = new BrowserCache(mediacms_config.site.id, 86400); // Keep cache data "fresh" for one day.
|
||||
|
||||
page_config = {
|
||||
mediaAutoPlay: browserCache.get('media-auto-play'),
|
||||
};
|
||||
|
||||
page_config.mediaAutoPlay = null !== page_config.mediaAutoPlay ? page_config.mediaAutoPlay : true;
|
||||
|
||||
this.browserEvents = BrowserEvents();
|
||||
this.browserEvents.doc(this.onDocumentVisibilityChange.bind(this));
|
||||
this.browserEvents.win(this.onWindowResize.bind(this), this.onWindowScroll.bind(this));
|
||||
|
||||
this.notifications = Notifications(
|
||||
void 0 !== window.MediaCMS && void 0 !== window.MediaCMS.notifications ? window.MediaCMS.notifications : []
|
||||
);
|
||||
}
|
||||
|
||||
onDocumentVisibilityChange() {
|
||||
this.emit('document_visibility_change');
|
||||
}
|
||||
|
||||
onWindowScroll() {
|
||||
this.emit('window_scroll');
|
||||
}
|
||||
|
||||
onWindowResize() {
|
||||
this.emit('window_resize');
|
||||
}
|
||||
|
||||
initPage(page) {
|
||||
page_config.currentPage = page;
|
||||
}
|
||||
|
||||
get(type) {
|
||||
let r;
|
||||
|
||||
switch (type) {
|
||||
case 'browser-cache':
|
||||
r = browserCache;
|
||||
break;
|
||||
case 'media-auto-play':
|
||||
r = page_config.mediaAutoPlay;
|
||||
break;
|
||||
case 'config-contents':
|
||||
r = mediacms_config.contents;
|
||||
break;
|
||||
case 'config-enabled':
|
||||
r = mediacms_config.enabled;
|
||||
break;
|
||||
case 'config-media-item':
|
||||
r = mediacms_config.media.item;
|
||||
break;
|
||||
case 'config-options':
|
||||
r = mediacms_config.options;
|
||||
break;
|
||||
case 'config-site':
|
||||
r = mediacms_config.site;
|
||||
break;
|
||||
case 'api-playlists':
|
||||
r = mediacms_api_endpoint_url(type.split('-')[1]);
|
||||
break;
|
||||
case 'notifications-size':
|
||||
r = this.notifications.size();
|
||||
break;
|
||||
case 'notifications':
|
||||
r = this.notifications.messages();
|
||||
this.notifications.clear();
|
||||
break;
|
||||
case 'current-page':
|
||||
r = page_config.currentPage;
|
||||
break;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
actions_handler(action) {
|
||||
switch (action.type) {
|
||||
case 'INIT_PAGE':
|
||||
this.initPage(action.page);
|
||||
this.emit('page_init');
|
||||
break;
|
||||
case 'TOGGLE_AUTO_PLAY':
|
||||
page_config.mediaAutoPlay = !page_config.mediaAutoPlay;
|
||||
browserCache.set('media-auto-play', page_config.mediaAutoPlay);
|
||||
this.emit('switched_media_auto_play');
|
||||
break;
|
||||
case 'ADD_NOTIFICATION':
|
||||
this.notifications.push(action.notification);
|
||||
this.emit('added_notification');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default exportStore(new PageStore(), 'actions_handler');
|
||||
236
frontend/src/static/js/utils/stores/PlaylistPageStore.js
Normal file
236
frontend/src/static/js/utils/stores/PlaylistPageStore.js
Normal file
@@ -0,0 +1,236 @@
|
||||
import React from 'react';
|
||||
import EventEmitter from 'events';
|
||||
import { publishedOnDate, exportStore, getRequest, postRequest, deleteRequest, csrfToken } from '../helpers';
|
||||
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
const PlaylistPageStoreData = {};
|
||||
|
||||
class PlaylistPageStore extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.mediacms_config = mediacmsConfig(window.MediaCMS);
|
||||
|
||||
PlaylistPageStoreData[
|
||||
Object.defineProperty(this, 'id', {
|
||||
value: 'PlaylistPageStoreData_' + Object.keys(PlaylistPageStoreData).length,
|
||||
}).id
|
||||
] = {
|
||||
playlistId: null,
|
||||
data: {},
|
||||
};
|
||||
|
||||
this.data = {
|
||||
savedPlaylist: false,
|
||||
publishDate: new Date(2018, 3, 14, 1, 13, 22, 0),
|
||||
publishDateLabel: null,
|
||||
};
|
||||
|
||||
this.onPlaylistUpdateCompleted = this.onPlaylistUpdateCompleted.bind(this);
|
||||
this.onPlaylistUpdateFailed = this.onPlaylistUpdateFailed.bind(this);
|
||||
|
||||
this.onPlaylistRemovalCompleted = this.onPlaylistRemovalCompleted.bind(this);
|
||||
this.onPlaylistRemovalFailed = this.onPlaylistRemovalFailed.bind(this);
|
||||
}
|
||||
|
||||
loadData() {
|
||||
if (!PlaylistPageStoreData[this.id].playlistId) {
|
||||
console.warn('Invalid playlist id:', PlaylistPageStoreData[this.id].playlistId);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.playlistAPIUrl = this.mediacms_config.api.playlists + '/' + PlaylistPageStoreData[this.id].playlistId;
|
||||
|
||||
this.dataResponse = this.dataResponse.bind(this);
|
||||
this.dataErrorResponse = this.dataErrorResponse.bind(this);
|
||||
getRequest(this.playlistAPIUrl, !1, this.dataResponse, this.dataErrorResponse);
|
||||
}
|
||||
|
||||
dataResponse(response) {
|
||||
if (response && response.data) {
|
||||
PlaylistPageStoreData[this.id].data = response.data;
|
||||
this.emit('loaded_playlist_data');
|
||||
}
|
||||
}
|
||||
|
||||
dataErrorResponse(response) {
|
||||
this.emit('loaded_playlist_error');
|
||||
|
||||
if (void 0 !== response.type) {
|
||||
/*switch( response.type ){
|
||||
case "network":
|
||||
case "private":
|
||||
case "unavailable":
|
||||
MediaPageStoreData[this.id].loadErrorType = response.type;
|
||||
MediaPageStoreData[this.id].loadErrorMessage = void 0 !== response.message ? response.message : "Αn error occurred while loading the media's data";
|
||||
this.emit('loaded_media_error');
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
get(type) {
|
||||
switch (type) {
|
||||
case 'playlistId':
|
||||
return PlaylistPageStoreData[this.id].playlistId || null;
|
||||
break;
|
||||
case 'logged-in-user-playlist':
|
||||
return (
|
||||
!this.mediacms_config.member.is.anonymous &&
|
||||
PlaylistPageStoreData[this.id].data.user === this.mediacms_config.member.username
|
||||
);
|
||||
case 'playlist-media':
|
||||
return PlaylistPageStoreData[this.id].data.playlist_media || [];
|
||||
case 'visibility':
|
||||
return 'public';
|
||||
case 'visibility-icon':
|
||||
switch (this.get('visibility')) {
|
||||
case 'unlisted':
|
||||
return <i className="material-icons">insert_link</i>;
|
||||
case 'private':
|
||||
return <i className="material-icons">lock</i>;
|
||||
}
|
||||
return null;
|
||||
case 'total-items':
|
||||
return PlaylistPageStoreData[this.id].data.playlist_media.length || 0;
|
||||
case 'views-count':
|
||||
return 'N/A';
|
||||
case 'title':
|
||||
return PlaylistPageStoreData[this.id].data.title || null;
|
||||
case 'edit-link':
|
||||
return '#';
|
||||
case 'thumb':
|
||||
if (
|
||||
PlaylistPageStoreData[this.id].data.playlist_media &&
|
||||
PlaylistPageStoreData[this.id].data.playlist_media.length
|
||||
) {
|
||||
return PlaylistPageStoreData[this.id].data.playlist_media[0].thumbnail_url;
|
||||
}
|
||||
return null;
|
||||
case 'description':
|
||||
return PlaylistPageStoreData[this.id].data.description || null;
|
||||
case 'author-username':
|
||||
return PlaylistPageStoreData[this.id].data.user || null; // TODO: Recheck this, is this same with 'author-name'?
|
||||
case 'author-name':
|
||||
return PlaylistPageStoreData[this.id].data.user || null;
|
||||
case 'author-link':
|
||||
return PlaylistPageStoreData[this.id].data.user
|
||||
? this.mediacms_config.site.url + '/user/' + PlaylistPageStoreData[this.id].data.user
|
||||
: null;
|
||||
case 'author-thumb':
|
||||
if (!PlaylistPageStoreData[this.id].data.user_thumbnail_url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
this.mediacms_config.site.url +
|
||||
'/' +
|
||||
PlaylistPageStoreData[this.id].data.user_thumbnail_url.replace(/^\//g, '')
|
||||
);
|
||||
|
||||
case 'saved-playlist':
|
||||
return this.data.savedPlaylist;
|
||||
case 'date-label':
|
||||
if (!PlaylistPageStoreData[this.id].data || !PlaylistPageStoreData[this.id].data.add_date) {
|
||||
return null;
|
||||
}
|
||||
this.data.publishDateLabel =
|
||||
this.data.publishDateLabel ||
|
||||
'Created on ' + publishedOnDate(new Date(PlaylistPageStoreData[this.id].data.add_date), 3);
|
||||
return this.data.publishDateLabel;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
onPlaylistUpdateCompleted(response) {
|
||||
if (response && response.data) {
|
||||
PlaylistPageStoreData[this.id].data.title = response.data.title;
|
||||
PlaylistPageStoreData[this.id].data.description = response.data.description;
|
||||
this.emit('playlist_update_completed', response.data);
|
||||
}
|
||||
}
|
||||
|
||||
onPlaylistUpdateFailed() {
|
||||
this.emit('playlist_update_failed');
|
||||
}
|
||||
|
||||
onPlaylistRemovalCompleted(response) {
|
||||
if (response && void 0 !== response.status && 403 !== response.status) {
|
||||
this.emit('playlist_removal_completed', response);
|
||||
} else {
|
||||
this.onPlaylistRemovalFailed();
|
||||
}
|
||||
}
|
||||
|
||||
onPlaylistRemovalFailed() {
|
||||
this.emit('playlist_removal_failed');
|
||||
}
|
||||
|
||||
actions_handler(action) {
|
||||
switch (action.type) {
|
||||
case 'LOAD_PLAYLIST_DATA':
|
||||
PlaylistPageStoreData[this.id].playlistId =
|
||||
window.MediaCMS.playlistId ||
|
||||
(function (url) {
|
||||
var arr = url.split('/');
|
||||
return arr.length ? arr[arr.length - 1] : null;
|
||||
})(window.location.href);
|
||||
this.loadData();
|
||||
break;
|
||||
case 'TOGGLE_SAVE':
|
||||
this.data.savedPlaylist = !this.data.savedPlaylist;
|
||||
this.emit('saved-updated');
|
||||
break;
|
||||
case 'UPDATE_PLAYLIST':
|
||||
postRequest(
|
||||
this.playlistAPIUrl,
|
||||
{
|
||||
title: action.playlist_data.title,
|
||||
description: action.playlist_data.description,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken(),
|
||||
},
|
||||
},
|
||||
false,
|
||||
this.onPlaylistUpdateCompleted,
|
||||
this.onPlaylistUpdateFailed
|
||||
);
|
||||
break;
|
||||
case 'REMOVE_PLAYLIST':
|
||||
deleteRequest(
|
||||
this.playlistAPIUrl,
|
||||
{
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken(),
|
||||
},
|
||||
},
|
||||
false,
|
||||
this.onPlaylistRemovalCompleted,
|
||||
this.onPlaylistRemovalFailed
|
||||
);
|
||||
break;
|
||||
case 'PLAYLIST_MEDIA_REORDERED':
|
||||
PlaylistPageStoreData[this.id].data.playlist_media = action.playlist_media;
|
||||
this.emit('reordered_media_in_playlist');
|
||||
break;
|
||||
case 'MEDIA_REMOVED_FROM_PLAYLIST':
|
||||
const new_playlist_media = [];
|
||||
let i = 0;
|
||||
while (i < PlaylistPageStoreData[this.id].data.playlist_media.length) {
|
||||
if (action.media_id !== PlaylistPageStoreData[this.id].data.playlist_media[i].url.split('=')[1]) {
|
||||
new_playlist_media.push(PlaylistPageStoreData[this.id].data.playlist_media[i]);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
PlaylistPageStoreData[this.id].data.playlist_media = new_playlist_media;
|
||||
|
||||
this.emit('removed_media_from_playlist');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default exportStore(new PlaylistPageStore(), 'actions_handler');
|
||||
76
frontend/src/static/js/utils/stores/PlaylistViewStore.js
Executable file
76
frontend/src/static/js/utils/stores/PlaylistViewStore.js
Executable file
@@ -0,0 +1,76 @@
|
||||
import EventEmitter from 'events';
|
||||
import { exportStore } from '../helpers';
|
||||
import PageStore from './PageStore';
|
||||
import MediaPageStore from './MediaPageStore';
|
||||
|
||||
class PlaylistViewStore extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.data = {
|
||||
playlistId: null,
|
||||
enabledLoop: null,
|
||||
enabledShuffle: null,
|
||||
savedPlaylist: false,
|
||||
response: null,
|
||||
};
|
||||
|
||||
this.browserCache = PageStore.get('browser-cache');
|
||||
}
|
||||
|
||||
get(type) {
|
||||
switch (type) {
|
||||
case 'logged-in-user-playlist':
|
||||
// TODO: Continue here.
|
||||
return false;
|
||||
case 'enabled-loop':
|
||||
if (null === this.data.playlistId) {
|
||||
this.data.playlistId = MediaPageStore.get('playlist-id');
|
||||
this.data.enabledLoop = this.browserCache.get('loopPlaylist[' + this.data.playlistId + ']');
|
||||
this.data.enabledLoop = null === this.data.enabledLoop ? true : this.data.enabledLoop;
|
||||
}
|
||||
return this.data.enabledLoop;
|
||||
case 'enabled-shuffle':
|
||||
if (null === this.data.playlistId) {
|
||||
this.data.playlistId = MediaPageStore.get('playlist-id');
|
||||
this.data.enabledShuffle = this.browserCache.get('shufflePlaylist[' + this.data.playlistId + ']');
|
||||
this.data.enabledShuffle = null === this.data.enabledShuffle ? false : this.data.enabledShuffle;
|
||||
}
|
||||
return this.data.enabledShuffle;
|
||||
case 'saved-playlist':
|
||||
return this.data.savedPlaylist;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
actions_handler(action) {
|
||||
switch (action.type) {
|
||||
case 'TOGGLE_LOOP':
|
||||
if (null === this.data.playlistId) {
|
||||
this.data.playlistId = MediaPageStore.get('playlist-id');
|
||||
this.data.enabledLoop = this.browserCache.get('loopPlaylist[' + this.data.playlistId + ']');
|
||||
this.data.enabledLoop = null === this.data.enabledLoop ? true : this.data.enabledLoop;
|
||||
}
|
||||
this.data.enabledLoop = !this.data.enabledLoop;
|
||||
this.browserCache.set('loopPlaylist[' + this.data.playlistId + ']', this.data.enabledLoop);
|
||||
this.emit('loop-repeat-updated');
|
||||
break;
|
||||
case 'TOGGLE_SHUFFLE':
|
||||
if (null === this.data.playlistId) {
|
||||
this.data.playlistId = MediaPageStore.get('playlist-id');
|
||||
this.data.enabledShuffle = this.browserCache.get('shufflePlaylist[' + this.data.playlistId + ']');
|
||||
this.data.enabledShuffle = null === this.data.enabledShuffle ? false : this.data.enabledShuffle;
|
||||
}
|
||||
this.data.enabledShuffle = !this.data.enabledShuffle;
|
||||
this.browserCache.set('shufflePlaylist[' + this.data.playlistId + ']', this.data.enabledShuffle);
|
||||
this.emit('shuffle-updated');
|
||||
break;
|
||||
case 'TOGGLE_SAVE':
|
||||
this.data.savedPlaylist = !this.data.savedPlaylist;
|
||||
this.emit('saved-updated');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default exportStore(new PlaylistViewStore(), 'actions_handler');
|
||||
106
frontend/src/static/js/utils/stores/ProfilePageStore.js
Executable file
106
frontend/src/static/js/utils/stores/ProfilePageStore.js
Executable file
@@ -0,0 +1,106 @@
|
||||
import EventEmitter from 'events';
|
||||
import { exportStore, getRequest, deleteRequest, csrfToken } from '../helpers';
|
||||
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
class ProfilePageStore extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
this.mediacms_config = mediacmsConfig(window.MediaCMS);
|
||||
this.authorData = null;
|
||||
this.authorQuery = void 0;
|
||||
this.onDataLoad = this.onDataLoad.bind(this);
|
||||
this.onDataLoadFail = this.onDataLoadFail.bind(this);
|
||||
this.removeProfileResponse = this.removeProfileResponse.bind(this);
|
||||
this.removeProfileFail = this.removeProfileFail.bind(this);
|
||||
this.removingProfile = false;
|
||||
}
|
||||
|
||||
removeProfileResponse(response) {
|
||||
if (response && 204 === response.status) {
|
||||
this.emit('profile_delete', this.authorData.username);
|
||||
}
|
||||
}
|
||||
|
||||
removeProfileFail() {
|
||||
this.emit('profile_delete_fail', this.authorData.username);
|
||||
setTimeout(
|
||||
function (ins) {
|
||||
this.removingProfile = false;
|
||||
},
|
||||
100,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
get(type) {
|
||||
switch (type) {
|
||||
case 'author-data':
|
||||
return this.authorData;
|
||||
case 'author-query':
|
||||
if (void 0 === this.authorQuery) {
|
||||
this.authorQuery = null;
|
||||
|
||||
let getArgs = window.location.search;
|
||||
|
||||
if (getArgs && '' !== getArgs) {
|
||||
getArgs = getArgs.split('?')[1].split('=');
|
||||
|
||||
let i = 0;
|
||||
while (i < getArgs.length) {
|
||||
if ('aq' === getArgs[i]) {
|
||||
this.authorQuery = getArgs[i + 1] || null;
|
||||
break;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.authorQuery;
|
||||
}
|
||||
}
|
||||
|
||||
onDataLoad(response) {
|
||||
if (response && response.data) {
|
||||
this.authorData = response.data;
|
||||
this.authorData.id = this.authorData.username;
|
||||
this.authorData.name = '' !== this.authorData.name ? this.authorData.name : this.authorData.username;
|
||||
this.emit('load-author-data');
|
||||
}
|
||||
}
|
||||
|
||||
onDataLoadFail(response) {
|
||||
// TODO: Continue here.
|
||||
}
|
||||
|
||||
actions_handler(action) {
|
||||
switch (action.type) {
|
||||
case 'REMOVE_PROFILE':
|
||||
if (this.removingProfile) {
|
||||
return;
|
||||
}
|
||||
this.removingProfile = true;
|
||||
let deleteAPIurl = this.mediacms_config.api.users + '/' + this.authorData.username;
|
||||
deleteRequest(
|
||||
deleteAPIurl,
|
||||
{ headers: { 'X-CSRFToken': csrfToken() } },
|
||||
false,
|
||||
this.removeProfileResponse,
|
||||
this.removeProfileFail
|
||||
);
|
||||
break;
|
||||
case 'LOAD_AUTHOR_DATA':
|
||||
getRequest(
|
||||
this.mediacms_config.api.users + '/' + window.MediaCMS.profileId,
|
||||
!1,
|
||||
this.onDataLoad,
|
||||
this.onDataLoadFail
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default exportStore(new ProfilePageStore(), 'actions_handler');
|
||||
102
frontend/src/static/js/utils/stores/SearchFieldStore.js
Executable file
102
frontend/src/static/js/utils/stores/SearchFieldStore.js
Executable file
@@ -0,0 +1,102 @@
|
||||
import EventEmitter from 'events';
|
||||
import { exportStore, getRequest } from '../helpers';
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
function getUrlVars() {
|
||||
var vars = {};
|
||||
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) {
|
||||
vars[key] = value;
|
||||
});
|
||||
return vars;
|
||||
}
|
||||
|
||||
const SearchFieldStoreData = {};
|
||||
|
||||
class SearchFieldStore extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.mediacms_config = mediacmsConfig(window.MediaCMS);
|
||||
|
||||
const urlvars = getUrlVars();
|
||||
const query = urlvars['q'];
|
||||
const categories = urlvars['c'];
|
||||
const tags = urlvars['t'];
|
||||
|
||||
SearchFieldStoreData[
|
||||
Object.defineProperty(this, 'id', {
|
||||
value: 'SearchFieldStoreData_' + Object.keys(SearchFieldStoreData).length,
|
||||
}).id
|
||||
] = {
|
||||
searchQuery: query ? decodeURIComponent(query).replace(/\+/g, ' ') : '',
|
||||
categoriesQuery: categories ? decodeURIComponent(categories).replace(/\+/g, ' ') : '',
|
||||
tagsQuery: tags ? decodeURIComponent(tags).replace(/\+/g, ' ') : '',
|
||||
predictions: [],
|
||||
};
|
||||
this.dataResponse = this.dataResponse.bind(this);
|
||||
}
|
||||
|
||||
dataResponse(response) {
|
||||
if (response && response.data) {
|
||||
let i = 0;
|
||||
|
||||
SearchFieldStoreData[this.id].predictions = [];
|
||||
|
||||
while (i < response.data.length) {
|
||||
SearchFieldStoreData[this.id].predictions[i] = response.data[i].title;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
this.emit(
|
||||
'load_predictions',
|
||||
SearchFieldStoreData[this.id].requestedQuery,
|
||||
SearchFieldStoreData[this.id].predictions
|
||||
);
|
||||
|
||||
if (SearchFieldStoreData[this.id].pendingRequested) {
|
||||
SearchFieldStoreData[this.id].requestedQuery = SearchFieldStoreData[this.id].pendingRequested.query;
|
||||
|
||||
getRequest(SearchFieldStoreData[this.id].pendingRequested.url, !1, this.dataResponse);
|
||||
|
||||
SearchFieldStoreData[this.id].pendingRequested = null;
|
||||
} else {
|
||||
SearchFieldStoreData[this.id].requestedQuery = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get(type) {
|
||||
switch (type) {
|
||||
case 'search-query':
|
||||
return SearchFieldStoreData[this.id].searchQuery;
|
||||
case 'search-categories':
|
||||
return SearchFieldStoreData[this.id].categoriesQuery;
|
||||
case 'search-tags':
|
||||
return SearchFieldStoreData[this.id].tagsQuery;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
actions_handler(action) {
|
||||
switch (action.type) {
|
||||
case 'REQUEST_PREDICTIONS':
|
||||
let q = action.query;
|
||||
let u = this.mediacms_config.api.search.titles + q;
|
||||
|
||||
if (SearchFieldStoreData[this.id].requestedQuery) {
|
||||
if (SearchFieldStoreData[this.id].requestedQuery.q !== q) {
|
||||
SearchFieldStoreData[this.id].pendingRequested = { query: q, url: u };
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
SearchFieldStoreData[this.id].requestedQuery = q;
|
||||
|
||||
getRequest(u, !1, this.dataResponse);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default exportStore(new SearchFieldStore(), 'actions_handler');
|
||||
96
frontend/src/static/js/utils/stores/VideoViewerStore.js
Executable file
96
frontend/src/static/js/utils/stores/VideoViewerStore.js
Executable file
@@ -0,0 +1,96 @@
|
||||
import EventEmitter from 'events';
|
||||
import { exportStore } from '../helpers/';
|
||||
import { BrowserCache } from '../classes/';
|
||||
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
|
||||
let browserCache;
|
||||
|
||||
const _StoreData = {};
|
||||
|
||||
class VideoPlayerStore extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.mediacms_config = mediacmsConfig(window.MediaCMS);
|
||||
|
||||
browserCache = new BrowserCache(this.mediacms_config.site.id, 86400); // Keep cache data "fresh" for one day.
|
||||
|
||||
_StoreData.inTheaterMode = browserCache.get('in-theater-mode');
|
||||
_StoreData.inTheaterMode = null !== _StoreData.inTheaterMode ? _StoreData.inTheaterMode : !1;
|
||||
|
||||
_StoreData.playerVolume = browserCache.get('player-volume');
|
||||
_StoreData.playerVolume =
|
||||
null === _StoreData.playerVolume ? 1 : Math.max(Math.min(Number(_StoreData.playerVolume), 1), 0);
|
||||
|
||||
_StoreData.playerSoundMuted = browserCache.get('player-sound-muted');
|
||||
_StoreData.playerSoundMuted = null !== _StoreData.playerSoundMuted ? _StoreData.playerSoundMuted : !1;
|
||||
|
||||
_StoreData.videoQuality = browserCache.get('video-quality');
|
||||
_StoreData.videoQuality = null !== _StoreData.videoQuality ? _StoreData.videoQuality : 'Auto';
|
||||
|
||||
_StoreData.videoPlaybackSpeed = browserCache.get('video-playback-speed');
|
||||
_StoreData.videoPlaybackSpeed = null !== _StoreData.videoPlaybackSpeed ? _StoreData.videoPlaybackSpeed : !1;
|
||||
}
|
||||
|
||||
get(type) {
|
||||
let r = null;
|
||||
switch (type) {
|
||||
case 'player-volume':
|
||||
r = _StoreData.playerVolume;
|
||||
break;
|
||||
case 'player-sound-muted':
|
||||
r = _StoreData.playerSoundMuted;
|
||||
break;
|
||||
case 'in-theater-mode':
|
||||
r = _StoreData.inTheaterMode;
|
||||
break;
|
||||
case 'video-data':
|
||||
r = _StoreData.videoData;
|
||||
break;
|
||||
case 'video-quality':
|
||||
r = _StoreData.videoQuality;
|
||||
break;
|
||||
case 'video-playback-speed':
|
||||
r = _StoreData.videoPlaybackSpeed;
|
||||
break;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
actions_handler(action) {
|
||||
switch (action.type) {
|
||||
case 'TOGGLE_VIEWER_MODE':
|
||||
_StoreData.inTheaterMode = !_StoreData.inTheaterMode;
|
||||
this.emit('changed_viewer_mode');
|
||||
break;
|
||||
case 'SET_VIEWER_MODE':
|
||||
_StoreData.inTheaterMode = action.inTheaterMode;
|
||||
browserCache.set('in-theater-mode', _StoreData.inTheaterMode);
|
||||
this.emit('changed_viewer_mode');
|
||||
break;
|
||||
case 'SET_PLAYER_VOLUME':
|
||||
_StoreData.playerVolume = action.playerVolume;
|
||||
browserCache.set('player-volume', action.playerVolume);
|
||||
this.emit('changed_player_volume');
|
||||
break;
|
||||
case 'SET_PLAYER_SOUND_MUTED':
|
||||
_StoreData.playerSoundMuted = action.playerSoundMuted;
|
||||
browserCache.set('player-sound-muted', action.playerSoundMuted);
|
||||
this.emit('changed_player_sound_muted');
|
||||
break;
|
||||
case 'SET_VIDEO_QUALITY':
|
||||
_StoreData.videoQuality = action.quality;
|
||||
browserCache.set('video-quality', action.quality);
|
||||
this.emit('changed_video_quality');
|
||||
break;
|
||||
case 'SET_VIDEO_PLAYBACK_SPEED':
|
||||
_StoreData.videoPlaybackSpeed = action.playbackSpeed;
|
||||
browserCache.set('video-playback-speed', action.playbackSpeed);
|
||||
this.emit('changed_video_playback_speed');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default exportStore(new VideoPlayerStore(), 'actions_handler');
|
||||
17
frontend/src/static/js/utils/stores/index.ts
Normal file
17
frontend/src/static/js/utils/stores/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import MediaPageStore from './MediaPageStore';
|
||||
import PageStore from './PageStore';
|
||||
import PlaylistPageStore from './PlaylistPageStore';
|
||||
import PlaylistViewStore from './PlaylistViewStore';
|
||||
import ProfilePageStore from './ProfilePageStore';
|
||||
import SearchFieldStore from './SearchFieldStore';
|
||||
import VideoViewerStore from './VideoViewerStore';
|
||||
|
||||
export {
|
||||
MediaPageStore,
|
||||
PageStore,
|
||||
PlaylistPageStore,
|
||||
PlaylistViewStore,
|
||||
ProfilePageStore,
|
||||
SearchFieldStore,
|
||||
VideoViewerStore,
|
||||
}
|
||||
Reference in New Issue
Block a user