mirror of
https://github.com/mediacms-io/mediacms.git
synced 2026-03-22 12:33:11 -04:00
refactor(frontend): convert contexts layer to TS and align page integration
This commit is contained in:
@@ -55,7 +55,7 @@ export const HistoryPage: React.FC = () => {
|
|||||||
const anonymousPage = isAnonymous || !PageStore.get('config-options').pages.profile.includeHistory;
|
const anonymousPage = isAnonymous || !PageStore.get('config-options').pages.profile.includeHistory;
|
||||||
|
|
||||||
if (!anonymousPage) {
|
if (!anonymousPage) {
|
||||||
addClassname(document.getElementById('page-history'), 'profile-page-history');
|
addClassname(document.getElementById('page-history')!, 'profile-page-history');
|
||||||
window.MediaCMS.profileId = username;
|
window.MediaCMS.profileId = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export const HomePage: React.FC<HomePageProps> = ({
|
|||||||
<MediaListRow
|
<MediaListRow
|
||||||
title={featured_title}
|
title={featured_title}
|
||||||
style={!visibleFeatured ? { display: 'none' } : undefined}
|
style={!visibleFeatured ? { display: 'none' } : undefined}
|
||||||
viewAllLink={featured_view_all_link ? links.featured : null}
|
viewAllLink={featured_view_all_link ? links.featured : undefined}
|
||||||
>
|
>
|
||||||
<InlineSliderItemListAsync
|
<InlineSliderItemListAsync
|
||||||
requestUrl={apiUrl.featured}
|
requestUrl={apiUrl.featured}
|
||||||
@@ -93,7 +93,7 @@ export const HomePage: React.FC<HomePageProps> = ({
|
|||||||
<MediaListRow
|
<MediaListRow
|
||||||
title={recommended_title}
|
title={recommended_title}
|
||||||
style={!visibleRecommended ? { display: 'none' } : undefined}
|
style={!visibleRecommended ? { display: 'none' } : undefined}
|
||||||
viewAllLink={recommended_view_all_link ? links.recommended : null}
|
viewAllLink={recommended_view_all_link ? links.recommended : undefined}
|
||||||
>
|
>
|
||||||
<InlineSliderItemListAsync
|
<InlineSliderItemListAsync
|
||||||
requestUrl={apiUrl.recommended}
|
requestUrl={apiUrl.recommended}
|
||||||
@@ -108,7 +108,7 @@ export const HomePage: React.FC<HomePageProps> = ({
|
|||||||
<MediaListRow
|
<MediaListRow
|
||||||
title={latest_title}
|
title={latest_title}
|
||||||
style={!visibleLatest ? { display: 'none' } : undefined}
|
style={!visibleLatest ? { display: 'none' } : undefined}
|
||||||
viewAllLink={latest_view_all_link ? links.latest : null}
|
viewAllLink={latest_view_all_link ? links.latest : undefined}
|
||||||
>
|
>
|
||||||
<ItemListAsync
|
<ItemListAsync
|
||||||
pageItems={30}
|
pageItems={30}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export const LikedMediaPage: React.FC = () => {
|
|||||||
const anonymousPage = isAnonymous || !PageStore.get('config-options').pages.profile.includeLikedMedia;
|
const anonymousPage = isAnonymous || !PageStore.get('config-options').pages.profile.includeLikedMedia;
|
||||||
|
|
||||||
if (!anonymousPage) {
|
if (!anonymousPage) {
|
||||||
addClassname(document.getElementById('page-liked'), 'profile-page-liked');
|
addClassname(document.getElementById('page-liked')!, 'profile-page-liked');
|
||||||
window.MediaCMS.profileId = username;
|
window.MediaCMS.profileId = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
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;
|
|
||||||
5
frontend/src/static/js/utils/contexts/ApiUrlContext.ts
Normal file
5
frontend/src/static/js/utils/contexts/ApiUrlContext.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
import { config as mediacmsConfig } from '../settings/config';
|
||||||
|
|
||||||
|
export const ApiUrlContext = createContext(mediacmsConfig(window.MediaCMS).api);
|
||||||
|
export const ApiUrlConsumer = ApiUrlContext.Consumer;
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
import React, { createContext } from 'react';
|
|
||||||
import { config as mediacmsConfig } from '../settings/config.js';
|
|
||||||
import { translateString } from '../../utils/helpers/';
|
|
||||||
|
|
||||||
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: translateString('Upload media'),
|
|
||||||
itemAttr: {
|
|
||||||
className: 'visible-only-in-small',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (user.pages.media) {
|
|
||||||
items.push({
|
|
||||||
link: user.pages.media,
|
|
||||||
icon: 'video_library',
|
|
||||||
text: translateString('My media'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
items.push({
|
|
||||||
link: links.signout,
|
|
||||||
icon: 'exit_to_app',
|
|
||||||
text: translateString('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: translateString('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: translateString('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: translateString('Edit profile'),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (user.can.changePassword) {
|
|
||||||
items.push({
|
|
||||||
link: links.changePassword,
|
|
||||||
icon: 'lock',
|
|
||||||
text: translateString('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;
|
|
||||||
130
frontend/src/static/js/utils/contexts/HeaderContext.ts
Normal file
130
frontend/src/static/js/utils/contexts/HeaderContext.ts
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
import { config as mediacmsConfig } from '../settings/config';
|
||||||
|
import { translateString } from '../helpers';
|
||||||
|
|
||||||
|
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: translateString('Upload media'),
|
||||||
|
itemAttr: {
|
||||||
|
className: 'visible-only-in-small',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (user.pages.media) {
|
||||||
|
items.push({
|
||||||
|
link: user.pages.media,
|
||||||
|
icon: 'video_library',
|
||||||
|
text: translateString('My media'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
link: links.signout,
|
||||||
|
icon: 'exit_to_app',
|
||||||
|
text: translateString('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: translateString('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: translateString('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: translateString('Edit profile'),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (user.can.changePassword) {
|
||||||
|
items.push({
|
||||||
|
link: links.changePassword,
|
||||||
|
icon: 'lock',
|
||||||
|
text: translateString('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;
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
import React, { createContext, ReactNode, useContext, useEffect, useMemo, useState } from 'react';
|
||||||
import { BrowserCache } from '../classes/';
|
import { BrowserCache } from '../classes';
|
||||||
import { PageStore } from '../stores/';
|
import { PageStore } from '../stores';
|
||||||
import { addClassname, removeClassname, inEmbeddedApp } from '../helpers/';
|
import { addClassname, removeClassname, inEmbeddedApp } from '../helpers';
|
||||||
import SiteContext from './SiteContext';
|
import SiteContext from './SiteContext';
|
||||||
|
|
||||||
let slidingSidebarTimeout;
|
let slidingSidebarTimeout: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
function onSidebarVisibilityChange(visibleSidebar) {
|
function onSidebarVisibilityChange(visibleSidebar: boolean) {
|
||||||
clearTimeout(slidingSidebarTimeout);
|
if (slidingSidebarTimeout) {
|
||||||
|
clearTimeout(slidingSidebarTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
addClassname(document.body, 'sliding-sidebar');
|
addClassname(document.body, 'sliding-sidebar');
|
||||||
|
|
||||||
@@ -39,18 +41,29 @@ function onSidebarVisibilityChange(visibleSidebar) {
|
|||||||
}, 20);
|
}, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LayoutContext = createContext();
|
export const LayoutContext = createContext({
|
||||||
|
enabledSidebar: true,
|
||||||
|
visibleSidebar: true,
|
||||||
|
setVisibleSidebar: (_: boolean) => {},
|
||||||
|
visibleMobileSearch: false,
|
||||||
|
toggleMobileSearch: () => {},
|
||||||
|
toggleSidebar: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
export const LayoutProvider = ({ children }) => {
|
export const LayoutProvider = ({ children }: { children: ReactNode }) => {
|
||||||
const site = useContext(SiteContext);
|
const site = useContext(SiteContext);
|
||||||
const cache = new BrowserCache('MediaCMS[' + site.id + '][layout]', 86400);
|
const cache = BrowserCache('MediaCMS[' + site.id + '][layout]', 86400);
|
||||||
|
|
||||||
const isMediaPage = useMemo(() => PageStore.get('current-page') === 'media', []);
|
const isMediaPage = useMemo(() => PageStore.get('current-page') === 'media', []);
|
||||||
const isEmbeddedApp = useMemo(() => inEmbeddedApp(), []);
|
const isEmbeddedApp = useMemo(() => inEmbeddedApp(), []);
|
||||||
|
|
||||||
const enabledSidebar = Boolean(document.getElementById('app-sidebar') || document.querySelector('.page-sidebar'));
|
const enabledSidebar = Boolean(document.getElementById('app-sidebar') || document.querySelector('.page-sidebar'));
|
||||||
|
|
||||||
const [visibleSidebar, setVisibleSidebar] = useState(cache.get('visible-sidebar'));
|
const [visibleSidebar, setVisibleSidebar] = useState<boolean>(
|
||||||
|
cache instanceof Error
|
||||||
|
? true // @todo: Check this again
|
||||||
|
: cache.get('visible-sidebar')
|
||||||
|
);
|
||||||
const [visibleMobileSearch, setVisibleMobileSearch] = useState(false);
|
const [visibleMobileSearch, setVisibleMobileSearch] = useState(false);
|
||||||
|
|
||||||
const toggleMobileSearch = () => {
|
const toggleMobileSearch = () => {
|
||||||
@@ -71,7 +84,9 @@ export const LayoutProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isEmbeddedApp && !isMediaPage && 1023 < window.innerWidth) {
|
if (!isEmbeddedApp && !isMediaPage && 1023 < window.innerWidth) {
|
||||||
cache.set('visible-sidebar', visibleSidebar);
|
if (!(cache instanceof Error)) {
|
||||||
|
cache.set('visible-sidebar', visibleSidebar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [isEmbeddedApp, isMediaPage, visibleSidebar]);
|
}, [isEmbeddedApp, isMediaPage, visibleSidebar]);
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
import { config as mediacmsConfig } from '../settings/config.js';
|
import { config as mediacmsConfig } from '../settings/config';
|
||||||
|
|
||||||
export const LinksContext = createContext(mediacmsConfig(window.MediaCMS).url);
|
export const LinksContext = createContext(mediacmsConfig(window.MediaCMS).url);
|
||||||
export const LinksConsumer = LinksContext.Consumer;
|
export const LinksConsumer = LinksContext.Consumer;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
import { config as mediacmsConfig } from '../settings/config.js';
|
import { config as mediacmsConfig } from '../settings/config';
|
||||||
|
|
||||||
export const MemberContext = createContext(mediacmsConfig(window.MediaCMS).member);
|
export const MemberContext = createContext(mediacmsConfig(window.MediaCMS).member);
|
||||||
export const MemberConsumer = MemberContext.Consumer;
|
export const MemberConsumer = MemberContext.Consumer;
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import React, { createContext } from 'react';
|
|
||||||
import { config as mediacmsConfig } from '../settings/config.js';
|
|
||||||
|
|
||||||
export const PlaylistsContext = createContext(mediacmsConfig(window.MediaCMS).playlists);
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
import { config as mediacmsConfig } from '../settings/config';
|
||||||
|
|
||||||
|
export const PlaylistsContext = createContext(mediacmsConfig(window.MediaCMS).playlists);
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import React, { createContext } from 'react';
|
|
||||||
import { config as mediacmsConfig } from '../settings/config.js';
|
|
||||||
|
|
||||||
export const ShareOptionsContext = createContext(mediacmsConfig(window.MediaCMS).media.share.options);
|
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
import { config as mediacmsConfig } from '../settings/config';
|
||||||
|
|
||||||
|
export const ShareOptionsContext = createContext(mediacmsConfig(window.MediaCMS).media.share.options);
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
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;
|
|
||||||
5
frontend/src/static/js/utils/contexts/SidebarContext.ts
Normal file
5
frontend/src/static/js/utils/contexts/SidebarContext.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
import { config as mediacmsConfig } from '../settings/config';
|
||||||
|
|
||||||
|
export const SidebarContext = createContext(mediacmsConfig(window.MediaCMS).sidebar);
|
||||||
|
export const SidebarConsumer = SidebarContext.Consumer;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
import { config as mediacmsConfig } from '../settings/config.js';
|
import { config as mediacmsConfig } from '../settings/config';
|
||||||
|
|
||||||
export const SiteContext = createContext(mediacmsConfig(window.MediaCMS).site);
|
export const SiteContext = createContext(mediacmsConfig(window.MediaCMS).site);
|
||||||
export const SiteConsumer = SiteContext.Consumer;
|
export const SiteConsumer = SiteContext.Consumer;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import React, { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
import { config as mediacmsConfig } from '../settings/config.js';
|
import { config as mediacmsConfig } from '../settings/config';
|
||||||
|
|
||||||
const notifications = mediacmsConfig(window.MediaCMS).notifications.messages;
|
const notifications = mediacmsConfig(window.MediaCMS).notifications.messages;
|
||||||
|
|
||||||
const texts = {
|
const texts = {
|
||||||
notifications,
|
notifications,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TextsContext = createContext(texts);
|
export const TextsContext = createContext(texts);
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
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;
|
|
||||||
95
frontend/src/static/js/utils/contexts/ThemeContext.tsx
Normal file
95
frontend/src/static/js/utils/contexts/ThemeContext.tsx
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import React, { createContext, ReactNode, useContext, useEffect, useState } from 'react';
|
||||||
|
import { GlobalMediaCMS } from '../../types';
|
||||||
|
import { BrowserCache } from '../classes';
|
||||||
|
import { addClassname, removeClassname, supportsSvgAsImg } from '../helpers';
|
||||||
|
import { config as mediacmsConfig } from '../settings/config';
|
||||||
|
import SiteContext from './SiteContext';
|
||||||
|
|
||||||
|
const config = mediacmsConfig(window.MediaCMS);
|
||||||
|
|
||||||
|
function initLogo(logo: GlobalMediaCMS['site']['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: string | undefined, defaultValue: GlobalMediaCMS['site']['theme']['mode']) {
|
||||||
|
return 'light' === cachedValue || 'dark' === cachedValue ? cachedValue : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ThemeContext = createContext({
|
||||||
|
logo: initLogo(config.theme.logo)[config.theme.mode],
|
||||||
|
currentThemeMode: config.theme.mode,
|
||||||
|
changeThemeMode: () => {},
|
||||||
|
themeModeSwitcher: config.theme.switch,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ThemeProvider = ({ children }: { children: ReactNode }) => {
|
||||||
|
const site = useContext(SiteContext);
|
||||||
|
|
||||||
|
const cache = BrowserCache('MediaCMS[' + site.id + '][theme]', 86400);
|
||||||
|
|
||||||
|
const [themeMode, setThemeMode] = useState(
|
||||||
|
initMode(cache instanceof Error ? undefined : 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');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cache instanceof Error)) {
|
||||||
|
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;
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
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;
|
|
||||||
28
frontend/src/static/js/utils/contexts/UserContext.tsx
Normal file
28
frontend/src/static/js/utils/contexts/UserContext.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { createContext, ReactNode } from 'react';
|
||||||
|
import { config as mediacmsConfig } from '../settings/config';
|
||||||
|
|
||||||
|
const member = mediacmsConfig(window.MediaCMS).member;
|
||||||
|
export const UserContext = createContext({
|
||||||
|
isAnonymous: member.is.anonymous,
|
||||||
|
username: member.username,
|
||||||
|
thumbnail: member.thumbnail,
|
||||||
|
userCan: member.can,
|
||||||
|
pages: member.pages,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function UserProvider({ children }: { children: ReactNode }) {
|
||||||
|
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;
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useBulkActions } from '../hooks/useBulkActions';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Higher-Order Component that provides bulk actions functionality
|
|
||||||
* to class components via props
|
|
||||||
*/
|
|
||||||
export function withBulkActions(WrappedComponent) {
|
|
||||||
return function WithBulkActionsComponent(props) {
|
|
||||||
const bulkActions = useBulkActions();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<WrappedComponent
|
|
||||||
{...props}
|
|
||||||
bulkActions={bulkActions}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
15
frontend/src/static/js/utils/hoc/withBulkActions.tsx
Normal file
15
frontend/src/static/js/utils/hoc/withBulkActions.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useBulkActions } from '../hooks/useBulkActions';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Higher-Order Component that provides bulk actions functionality
|
||||||
|
* to class components via props
|
||||||
|
*/
|
||||||
|
export function withBulkActions<P extends { bulkActions: ReturnType<typeof useBulkActions> }>(
|
||||||
|
WrappedComponent: React.ComponentType<P>
|
||||||
|
) {
|
||||||
|
return function WithBulkActionsComponent(props: Omit<P, 'bulkActions'>) {
|
||||||
|
const bulkActions = useBulkActions();
|
||||||
|
return <WrappedComponent {...(props as P)} bulkActions={bulkActions} />;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -18,10 +18,6 @@ jest.mock('../../../src/static/js/utils/classes/', () => ({
|
|||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('../../../src/static/js/utils/dispatcher.js', () => ({
|
|
||||||
register: jest.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('../../../src/static/js/utils/settings/config', () => ({
|
jest.mock('../../../src/static/js/utils/settings/config', () => ({
|
||||||
config: jest.fn(() => jest.requireActual('../../tests-constants').sampleMediaCMSConfig),
|
config: jest.fn(() => jest.requireActual('../../tests-constants').sampleMediaCMSConfig),
|
||||||
}));
|
}));
|
||||||
@@ -54,7 +50,7 @@ describe('utils/hooks', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Returns undefined value when used without a Provider', () => {
|
test('Returns default context value when used without a Provider', () => {
|
||||||
let received: any = 'init';
|
let received: any = 'init';
|
||||||
|
|
||||||
const Comp: React.FC = () => {
|
const Comp: React.FC = () => {
|
||||||
@@ -64,7 +60,14 @@ describe('utils/hooks', () => {
|
|||||||
|
|
||||||
render(<Comp />);
|
render(<Comp />);
|
||||||
|
|
||||||
expect(received).toBe(undefined);
|
expect(received).toStrictEqual({
|
||||||
|
enabledSidebar: true,
|
||||||
|
visibleSidebar: true,
|
||||||
|
visibleMobileSearch: false,
|
||||||
|
setVisibleSidebar: expect.any(Function),
|
||||||
|
toggleMobileSearch: expect.any(Function),
|
||||||
|
toggleSidebar: expect.any(Function),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Toggle sidebar', () => {
|
test('Toggle sidebar', () => {
|
||||||
|
|||||||
@@ -12,10 +12,6 @@ jest.mock('../../../src/static/js/utils/classes/', () => ({
|
|||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('../../../src/static/js/utils/dispatcher.js', () => ({
|
|
||||||
register: jest.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
function getRenderers(ThemeProvider: React.FC<{ children: React.ReactNode }>, useTheme: typeof useThemeHook) {
|
function getRenderers(ThemeProvider: React.FC<{ children: React.ReactNode }>, useTheme: typeof useThemeHook) {
|
||||||
const data: { current: any } = { current: undefined };
|
const data: { current: any } = { current: undefined };
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user