mirror of
https://github.com/mue/mue.git
synced 2026-07-06 16:04:20 +02:00
refactor: Convert pure components to functional components
Co-authored-by: David Ralph <me@davidcralph.co.uk> Co-authored-by: Isaac <contact@eartharoid.me>
This commit is contained in:
@@ -1,29 +1,26 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { MdStar, MdStarBorder } from 'react-icons/md';
|
||||
|
||||
class Favourite extends PureComponent {
|
||||
buttons = {
|
||||
favourited: <MdStar onClick={() => this.favourite()} className="topicons" />,
|
||||
unfavourited: <MdStarBorder onClick={() => this.favourite()} className="topicons" />,
|
||||
function Favourite({ credit, offline, pun, tooltipText }) {
|
||||
const [favourited, setFavourited] = useState(
|
||||
localStorage.getItem('favourite') ? (
|
||||
<MdStar onClick={() => favourite()} className="topicons" />
|
||||
) : (
|
||||
<MdStarBorder onClick={() => favourite()} className="topicons" />
|
||||
),
|
||||
);
|
||||
|
||||
const buttons = {
|
||||
favourited: <MdStar onClick={() => favourite()} className="topicons" />,
|
||||
unfavourited: <MdStarBorder onClick={() => favourite()} className="topicons" />,
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
favourited: localStorage.getItem('favourite')
|
||||
? this.buttons.favourited
|
||||
: this.buttons.unfavourited,
|
||||
};
|
||||
}
|
||||
|
||||
async favourite() {
|
||||
async function favourite() {
|
||||
if (localStorage.getItem('favourite')) {
|
||||
localStorage.removeItem('favourite');
|
||||
this.setState({
|
||||
favourited: this.buttons.unfavourited,
|
||||
});
|
||||
this.props.tooltipText(variables.getMessage('widgets.quote.favourite'));
|
||||
setFavourited(buttons.unfavourited);
|
||||
tooltipText(variables.getMessage('widgets.quote.favourite'));
|
||||
variables.stats.postEvent('feature', 'Background favourite');
|
||||
} else {
|
||||
const type = localStorage.getItem('backgroundType');
|
||||
@@ -57,58 +54,57 @@ class Favourite extends PureComponent {
|
||||
reader.onloadend = () => resolve(reader.result);
|
||||
reader.readAsDataURL(await (await fetch(url)).blob());
|
||||
});
|
||||
|
||||
if (type === 'custom') {
|
||||
localStorage.setItem(
|
||||
'favourite',
|
||||
JSON.stringify({
|
||||
type,
|
||||
url,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
const location = document.getElementById('infoLocation');
|
||||
const camera = document.getElementById('infoCamera');
|
||||
|
||||
localStorage.setItem(
|
||||
'favourite',
|
||||
JSON.stringify({
|
||||
type,
|
||||
url,
|
||||
credit,
|
||||
location: location?.innerText,
|
||||
camera: camera?.innerText,
|
||||
resolution: document.getElementById('infoResolution').textContent || '',
|
||||
offline,
|
||||
pun,
|
||||
}),
|
||||
);
|
||||
|
||||
setFavourited(buttons.favourited);
|
||||
|
||||
tooltipText(variables.getMessage('widgets.quote.unfavourite'));
|
||||
|
||||
variables.stats.postEvent('feature', 'Background unfavourite');
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'custom') {
|
||||
localStorage.setItem(
|
||||
'favourite',
|
||||
JSON.stringify({
|
||||
type,
|
||||
url,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
// photo information now hides information if it isn't sent, unless if photoinformation hover is hidden
|
||||
const location = document.getElementById('infoLocation');
|
||||
const camera = document.getElementById('infoCamera');
|
||||
|
||||
localStorage.setItem(
|
||||
'favourite',
|
||||
JSON.stringify({
|
||||
type,
|
||||
url,
|
||||
credit: this.props.credit || '',
|
||||
location: location?.innerText,
|
||||
camera: camera?.innerText,
|
||||
resolution: document.getElementById('infoResolution').textContent || '',
|
||||
offline: this.props.offline,
|
||||
pun: this.props.pun,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return setFavourited(buttons.favourited);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
favourited: this.buttons.favourited,
|
||||
});
|
||||
this.props.tooltipText(variables.getMessage('widgets.quote.unfavourite'));
|
||||
variables.stats.postEvent('feature', 'Background unfavourite');
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (localStorage.getItem('backgroundType') === 'colour') {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.props.tooltipText(
|
||||
localStorage.getItem('favourite')
|
||||
? variables.getMessage('widgets.quote.unfavourite')
|
||||
: variables.getMessage('widgets.quote.favourite'),
|
||||
);
|
||||
|
||||
return this.state.favourited;
|
||||
if (localStorage.getItem('backgroundType') === 'colour') {
|
||||
return null;
|
||||
}
|
||||
|
||||
tooltipText(
|
||||
localStorage.getItem('favourite')
|
||||
? variables.getMessage('widgets.quote.unfavourite')
|
||||
: variables.getMessage('widgets.quote.favourite'),
|
||||
);
|
||||
|
||||
return favourited;
|
||||
}
|
||||
|
||||
export default Favourite;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent, createRef } from 'react';
|
||||
import { useRef, useState, useEffect } from 'react';
|
||||
|
||||
import { nth, convertTimezone } from 'utils/date';
|
||||
import EventBus from 'utils/eventbus';
|
||||
@@ -8,15 +8,11 @@ import defaults from './options/default';
|
||||
import './greeting.scss';
|
||||
|
||||
const isEventsEnabled = localStorage.getItem('events') !== 'false';
|
||||
export default class Greeting extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
greeting: '',
|
||||
};
|
||||
this.timer = undefined;
|
||||
this.greeting = createRef();
|
||||
}
|
||||
|
||||
function Greeting() {
|
||||
const [greeting, setGreeting] = useState('');
|
||||
const timer = useRef(null);
|
||||
const greetingRef = useRef(null);
|
||||
|
||||
/**
|
||||
* Change the greeting message for the events of Christmas, New Year and Halloween.
|
||||
@@ -25,7 +21,8 @@ export default class Greeting extends PureComponent {
|
||||
* @param {String} message The current greeting message.
|
||||
* @returns The message variable is being returned.
|
||||
*/
|
||||
doEvents(time, message) {
|
||||
|
||||
const doEvents = (time, message) => {
|
||||
if (!isEventsEnabled) {
|
||||
return message;
|
||||
}
|
||||
@@ -49,15 +46,15 @@ export default class Greeting extends PureComponent {
|
||||
* It takes a date object and returns the age of the person in years.
|
||||
* @param {Date} date The date of birth.
|
||||
* @returns The age of the person.
|
||||
*/
|
||||
calculateAge(date) {
|
||||
* */
|
||||
const calculateAge = (date) => {
|
||||
const diff = Date.now() - date.getTime();
|
||||
const birthday = new Date(diff);
|
||||
return Math.abs(birthday.getUTCFullYear() - 1970);
|
||||
}
|
||||
|
||||
getGreeting(time = 60000 - (Date.now() % 60000)) {
|
||||
this.timer = setTimeout(() => {
|
||||
const getGreeting = (time = 60000 - (Date.now() % 60000)) => {
|
||||
timer.current = setTimeout(() => {
|
||||
let now = new Date();
|
||||
const timezone = localStorage.getItem('timezone');
|
||||
if (timezone && timezone !== 'auto') {
|
||||
@@ -84,7 +81,7 @@ export default class Greeting extends PureComponent {
|
||||
if (custom === 'false') {
|
||||
message = '';
|
||||
} else {
|
||||
message = this.doEvents(now, message);
|
||||
message = doEvents(now, message);
|
||||
}
|
||||
|
||||
// Name
|
||||
@@ -108,9 +105,9 @@ export default class Greeting extends PureComponent {
|
||||
const birth = new Date(localStorage.getItem('birthday'));
|
||||
|
||||
if (birth.getDate() === now.getDate() && birth.getMonth() === now.getMonth()) {
|
||||
if (localStorage.getItem('birthdayage') === 'true' && this.calculateAge(birth) !== 0) {
|
||||
if (localStorage.getItem('birthdayage') === 'true' && calculateAge(birth) !== 0) {
|
||||
const text = variables.getMessage('widgets.greeting.birthday').split(' ');
|
||||
message = `${text[0]} ${nth(this.calculateAge(birth))} ${text[1]}`;
|
||||
message = `${text[0]} ${nth(calculateAge(birth))} ${text[1]}`;
|
||||
} else {
|
||||
message = variables.getMessage('widgets.greeting.birthday');
|
||||
}
|
||||
@@ -118,49 +115,45 @@ export default class Greeting extends PureComponent {
|
||||
}
|
||||
|
||||
// Set the state to the greeting string
|
||||
this.setState({
|
||||
greeting: `${message}${name}`,
|
||||
});
|
||||
setGreeting(`${message}${name}`);
|
||||
|
||||
this.getGreeting();
|
||||
getGreeting();
|
||||
}, time);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
useEffect(() => {
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'greeting' || data === 'timezone') {
|
||||
if (localStorage.getItem('greeting') === 'false') {
|
||||
return (this.greeting.current.style.display = 'none');
|
||||
return (greetingRef.current.style.display = 'none');
|
||||
}
|
||||
|
||||
this.timer = null;
|
||||
this.getGreeting(0);
|
||||
timer.current = null;
|
||||
getGreeting(0);
|
||||
|
||||
this.greeting.current.style.display = 'block';
|
||||
this.greeting.current.style.fontSize = `${
|
||||
greetingRef.current.style.display = 'block';
|
||||
greetingRef.current.style.fontSize = `${
|
||||
1.6 * Number((localStorage.getItem('zoomGreeting') || defaults.zoomGreeting) / 100)
|
||||
}em`;
|
||||
}
|
||||
});
|
||||
|
||||
// this comment can apply to all widget zoom features apart from the general one in the Accessibility section
|
||||
// in a nutshell: 1.6 is the current font size, and we do "localstorage || 100" so we don't have to try that 4.0 -> 5.0 thing again
|
||||
this.greeting.current.style.fontSize = `${
|
||||
greetingRef.current.style.fontSize = `${
|
||||
1.6 * Number((localStorage.getItem('zoomGreeting') || defaults.zoomGreeting) / 100)
|
||||
}em`;
|
||||
|
||||
this.getGreeting(0);
|
||||
}
|
||||
getGreeting(0);
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
return () => {
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
}, []);
|
||||
|
||||
render() {
|
||||
return (
|
||||
<span className="greeting" ref={this.greeting}>
|
||||
{this.state.greeting}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span className="greeting" ref={greetingRef}>
|
||||
{greeting}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export default Greeting;
|
||||
@@ -13,6 +13,7 @@ import { Checkbox, Switch, Text } from 'components/Form/Settings';
|
||||
import { TextareaAutosize } from '@mui/material';
|
||||
import { Button } from 'components/Elements';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useTab } from 'components/Elements/MainModal/backend/TabContext';
|
||||
|
||||
import defaults from './default';
|
||||
import defaultEvents from '../events.json';
|
||||
@@ -20,6 +21,7 @@ import defaultEvents from '../events.json';
|
||||
import { MdEventNote, MdAdd, MdCancel, MdRefresh } from 'react-icons/md';
|
||||
|
||||
const GreetingOptions = () => {
|
||||
const { subSection } = useTab();
|
||||
const [customEvents, setCustomEvents] = useState(
|
||||
JSON.parse(localStorage.getItem('customEvents')) || defaultEvents
|
||||
);
|
||||
@@ -297,7 +299,8 @@ const GreetingOptions = () => {
|
||||
return (
|
||||
<>
|
||||
{header}
|
||||
{events ? (
|
||||
{subSection}
|
||||
{subSection === "events" ? (
|
||||
<>
|
||||
<Row>
|
||||
<Content
|
||||
@@ -327,6 +330,7 @@ const GreetingOptions = () => {
|
||||
>
|
||||
<AdditionalOptions />
|
||||
<Section
|
||||
id="events"
|
||||
title={variables.getMessage(`${GREETING_SECTION}.events`)}
|
||||
subtitle={variables.getMessage(`${GREETING_SECTION}.events_description`)}
|
||||
onClick={() => setEvents(true)}
|
||||
|
||||
3
src/features/marketplace/api/index.js
Normal file
3
src/features/marketplace/api/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import sortItems from './sort';
|
||||
|
||||
export { sortItems };
|
||||
42
src/features/marketplace/api/sort.js
Normal file
42
src/features/marketplace/api/sort.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import variables from 'config/variables';
|
||||
|
||||
export default function SortItems(data, method = 'a-z') {
|
||||
//const sort = localStorage.getItem('sortMarketplace') || method;
|
||||
const sort = method;
|
||||
|
||||
variables.stats.postEvent('marketplace', 'sort');
|
||||
|
||||
if (!data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
if (sort === 'a-z') {
|
||||
return data.sort((a, b) => {
|
||||
if (a.display_name < b.display_name) {
|
||||
return -1;
|
||||
}
|
||||
if (a.display_name > b.display_name) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
if (sort === 'z-a') {
|
||||
return data.sort((a, b) => {
|
||||
if (a.display_name > b.display_name) {
|
||||
return -1;
|
||||
}
|
||||
if (a.display_name < b.display_name) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
if (sort === 'newest') {
|
||||
return data.reverse();
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import { Header, CustomActions } from 'components/Layout/Settings';
|
||||
import { Button } from 'components/Elements';
|
||||
|
||||
import { install, uninstall } from 'utils/marketplace';
|
||||
import { sortItems } from '../api';
|
||||
|
||||
export default class Added extends PureComponent {
|
||||
constructor() {
|
||||
@@ -130,42 +131,6 @@ export default class Added extends PureComponent {
|
||||
variables.stats.postEvent('marketplace', 'Uninstall');
|
||||
}
|
||||
|
||||
sortAddons(value, sendEvent) {
|
||||
let installed = JSON.parse(localStorage.getItem('installed'));
|
||||
switch (value) {
|
||||
case 'newest':
|
||||
installed.reverse();
|
||||
break;
|
||||
case 'oldest':
|
||||
break;
|
||||
case 'a-z':
|
||||
installed.sort((a, b) => {
|
||||
if (a.display_name < b.display_name) {
|
||||
return -1;
|
||||
}
|
||||
if (a.display_name > b.display_name) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
break;
|
||||
case 'z-a':
|
||||
installed.sort();
|
||||
installed.reverse();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
installed,
|
||||
});
|
||||
|
||||
if (sendEvent) {
|
||||
variables.stats.postEvent('marketplace', 'Sort');
|
||||
}
|
||||
}
|
||||
|
||||
updateCheck() {
|
||||
let updates = 0;
|
||||
this.state.installed.forEach(async (item) => {
|
||||
@@ -207,7 +172,9 @@ export default class Added extends PureComponent {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.sortAddons(localStorage.getItem('sortAddons'), false);
|
||||
this.setState({
|
||||
installed: sortItems(JSON.parse(localStorage.getItem('installed')), 'newest'),
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -297,7 +264,7 @@ export default class Added extends PureComponent {
|
||||
<Dropdown
|
||||
label={variables.getMessage('modals.main.addons.sort.title')}
|
||||
name="sortAddons"
|
||||
onChange={(value) => this.sortAddons(value)}
|
||||
onChange={(value) => this.setState({ installed: sortItems(this.state.installed, value) })}
|
||||
items={[
|
||||
{
|
||||
value: 'newest',
|
||||
|
||||
@@ -1,475 +1,332 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import {
|
||||
MdWifiOff,
|
||||
MdLocalMall,
|
||||
MdClose,
|
||||
MdSearch,
|
||||
MdOutlineArrowForward,
|
||||
MdLibraryAdd,
|
||||
} from 'react-icons/md';
|
||||
import { MdWifiOff, MdLocalMall, MdOutlineArrowForward, MdLibraryAdd } from 'react-icons/md';
|
||||
|
||||
import ItemPage from './ItemPage';
|
||||
import Items from '../components/Items/Items';
|
||||
import Dropdown from '../../../components/Form/Settings/Dropdown/Dropdown';
|
||||
|
||||
import { Header } from 'components/Layout/Settings';
|
||||
import { Button } from 'components/Elements';
|
||||
|
||||
import { install, uninstall } from 'utils/marketplace';
|
||||
import { install } from 'utils/marketplace';
|
||||
import { sortItems } from '../api';
|
||||
|
||||
class Marketplace extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
items: [],
|
||||
button: '',
|
||||
done: false,
|
||||
item: {},
|
||||
collection: false,
|
||||
filter: '',
|
||||
type: 'all',
|
||||
};
|
||||
this.buttons = {
|
||||
uninstall: (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.manage('uninstall')}
|
||||
icon={<MdClose />}
|
||||
label={variables.getMessage('modals.main.marketplace.product.buttons.remove')}
|
||||
/>
|
||||
),
|
||||
install: (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.manage('install')}
|
||||
icon={<MdLibraryAdd />}
|
||||
label={variables.getMessage('modals.main.marketplace.product.buttons.addtomue')}
|
||||
/>
|
||||
),
|
||||
};
|
||||
this.controller = new AbortController();
|
||||
}
|
||||
function Marketplace() {
|
||||
const [items, setItems] = useState([]);
|
||||
const [done, setDone] = useState(false);
|
||||
const [item, setItem] = useState({});
|
||||
const [collection, setCollection] = useState({});
|
||||
const [filter, setFilter] = useState('');
|
||||
const [type, setType] = useState('all');
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [collectionTitle, setCollectionTitle] = useState('');
|
||||
|
||||
const controller = new AbortController();
|
||||
|
||||
async toggle(pageType, data) {
|
||||
async function toggle(pageType, data) {
|
||||
if (pageType === 'item') {
|
||||
let info;
|
||||
// get item info
|
||||
try {
|
||||
let type = this.props.type;
|
||||
if (type === 'all' || type === 'collections') {
|
||||
type = data.type;
|
||||
}
|
||||
info = await (
|
||||
await fetch(`${variables.constants.API_URL}/marketplace/item/${type}/${data.name}`, {
|
||||
signal: this.controller.signal,
|
||||
})
|
||||
).json();
|
||||
} catch (e) {
|
||||
if (this.controller.signal.aborted === false) {
|
||||
return toast(variables.getMessage('toasts.error'));
|
||||
}
|
||||
}
|
||||
const toggleType = type === 'all' || type === 'collections' ? data.type : type;
|
||||
const item = await (
|
||||
await fetch(`${variables.constants.API_URL}/marketplace/item/${toggleType}/${data.name}`, {
|
||||
signal: controller.signal,
|
||||
})
|
||||
).json();
|
||||
|
||||
if (this.controller.signal.aborted === true) {
|
||||
if (controller.signal.aborted === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if already installed
|
||||
let button = this.buttons.install;
|
||||
let addonInstalled = false;
|
||||
let addonInstalledVersion;
|
||||
|
||||
const installed = JSON.parse(localStorage.getItem('installed'));
|
||||
|
||||
if (installed.some((item) => item.name === info.data.name)) {
|
||||
button = this.buttons.uninstall;
|
||||
if (installed.some((item) => item.name === item.data.name)) {
|
||||
addonInstalled = true;
|
||||
for (let i = 0; i < installed.length; i++) {
|
||||
if (installed[i].name === info.data.name) {
|
||||
if (installed[i].name === item.data.name) {
|
||||
addonInstalledVersion = installed[i].version;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
item: {
|
||||
onCollection: data._onCollection,
|
||||
type: info.data.type,
|
||||
display_name: info.data.name,
|
||||
author: info.data.author,
|
||||
description: info.data.description,
|
||||
//updated: info.updated,
|
||||
version: info.data.version,
|
||||
icon: info.data.screenshot_url,
|
||||
data: info.data,
|
||||
addonInstalled,
|
||||
addonInstalledVersion,
|
||||
api_name: data.name,
|
||||
setItem({
|
||||
onCollection: data._onCollection,
|
||||
type: item.data.type,
|
||||
display_name: item.data.name,
|
||||
author: item.data.author,
|
||||
description: item.data.description,
|
||||
version: item.data.version,
|
||||
icon: item.data.screenshot_url,
|
||||
data: item.data,
|
||||
local: {
|
||||
installed: addonInstalled,
|
||||
version: addonInstalledVersion,
|
||||
},
|
||||
button: button,
|
||||
slug: data.name,
|
||||
});
|
||||
document.querySelector('#modal').scrollTop = 0;
|
||||
variables.stats.postEvent('marketplace-item', `${this.state.item.display_name} viewed`);
|
||||
|
||||
setType('item');
|
||||
|
||||
variables.stats.postEvent('marketplace-item', `${item.display_name} viewed`);
|
||||
} else if (pageType === 'collection') {
|
||||
this.setState({
|
||||
done: false,
|
||||
item: {},
|
||||
});
|
||||
setDone(false);
|
||||
setItem({});
|
||||
|
||||
const collection = await (
|
||||
await fetch(`${variables.constants.API_URL}/marketplace/collection/${data}`, {
|
||||
signal: this.controller.signal,
|
||||
signal: controller.signal,
|
||||
})
|
||||
).json();
|
||||
this.setState({
|
||||
items: collection.data.items,
|
||||
collectionTitle: collection.data.display_name,
|
||||
collectionDescription: collection.data.description,
|
||||
collectionImg: collection.data.img,
|
||||
collection: true,
|
||||
done: true,
|
||||
|
||||
setItems(collection.data.items);
|
||||
setCollection({
|
||||
visible: true,
|
||||
title: collection.data.display_name,
|
||||
description: collection.data.description,
|
||||
img: collection.data.img,
|
||||
});
|
||||
|
||||
setType('collection');
|
||||
} else {
|
||||
this.setState({
|
||||
item: {},
|
||||
});
|
||||
setItem({});
|
||||
setCollection({});
|
||||
setType('normal');
|
||||
}
|
||||
}
|
||||
|
||||
async getItems() {
|
||||
this.setState({
|
||||
done: false,
|
||||
});
|
||||
async function getItems() {
|
||||
setDone(false);
|
||||
const dataURL =
|
||||
this.props.type === 'collections'
|
||||
? variables.constants.API_URL + '/marketplace/collections'
|
||||
: variables.constants.API_URL + '/marketplace/items/' + this.props.type;
|
||||
variables.constants.API_URL +
|
||||
(type === 'collections' ? '/marketplace/collections' : '/marketplace/items/' + type);
|
||||
|
||||
const { data } = await (
|
||||
await fetch(dataURL, {
|
||||
signal: this.controller.signal,
|
||||
signal: controller.signal,
|
||||
})
|
||||
).json();
|
||||
const collections = await (
|
||||
|
||||
if (controller.signal.aborted === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
setItems(sortItems(data, 'z-a'));
|
||||
setDone(true);
|
||||
}
|
||||
|
||||
async function getCollections() {
|
||||
setDone(false);
|
||||
|
||||
const { data } = await (
|
||||
await fetch(variables.constants.API_URL + '/marketplace/collections', {
|
||||
signal: this.controller.signal,
|
||||
signal: controller.signal,
|
||||
})
|
||||
).json();
|
||||
|
||||
if (this.controller.signal.aborted === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sorted = this.sortMarketplace(data, false);
|
||||
|
||||
this.setState({
|
||||
items: sorted.items,
|
||||
sortType: sorted.sortType,
|
||||
oldItems: sorted.items,
|
||||
collections: collections.data,
|
||||
displayedCollection:
|
||||
collections.data[Math.floor(Math.random() * collections.data.length)] || [],
|
||||
done: true,
|
||||
});
|
||||
setItems(data);
|
||||
setDone(true);
|
||||
}
|
||||
|
||||
manage(type) {
|
||||
if (type === 'install') {
|
||||
install(this.state.item.type, this.state.item.data);
|
||||
} else {
|
||||
uninstall(this.state.item.type, this.state.item.display_name);
|
||||
}
|
||||
|
||||
toast(variables.getMessage('toasts.' + type + 'ed'));
|
||||
this.setState({
|
||||
button: type === 'install' ? this.buttons.uninstall : this.buttons.install,
|
||||
});
|
||||
|
||||
variables.stats.postEvent(
|
||||
'marketplace-item',
|
||||
`${this.state.item.display_name} ${type === 'install' ? 'installed' : 'uninstalled'}`,
|
||||
);
|
||||
variables.stats.postEvent('marketplace', type === 'install' ? 'Install' : 'Uninstall');
|
||||
function returnToMain() {
|
||||
setCollection(false);
|
||||
}
|
||||
|
||||
async installCollection() {
|
||||
this.setState({ busy: true });
|
||||
try {
|
||||
const installed = JSON.parse(localStorage.getItem('installed'));
|
||||
for (const item of this.state.items) {
|
||||
if (installed.some((i) => i.name === item.display_name)) continue; // don't install if already installed
|
||||
let { data } = await (
|
||||
await fetch(`${variables.constants.API_URL}/marketplace/item/${item.type}/${item.name}`, {
|
||||
signal: this.controller.signal,
|
||||
})
|
||||
).json();
|
||||
install(data.type, data, false, true);
|
||||
variables.stats.postEvent('marketplace-item', `${item.display_name} installed}`);
|
||||
variables.stats.postEvent('marketplace', 'Install');
|
||||
}
|
||||
toast(variables.getMessage('toasts.installed'));
|
||||
window.location.reload();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast(variables.getMessage('toasts.error'));
|
||||
} finally {
|
||||
this.setState({ busy: false });
|
||||
}
|
||||
}
|
||||
|
||||
sortMarketplace(data, sendEvent) {
|
||||
const value = localStorage.getItem('sortMarketplace') || 'a-z';
|
||||
let items = data || this.state.items;
|
||||
if (!items) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (value) {
|
||||
case 'a-z':
|
||||
// sort by name key alphabetically
|
||||
const sorted = items.sort((a, b) => {
|
||||
if (a.display_name < b.display_name) {
|
||||
return -1;
|
||||
}
|
||||
if (a.display_name > b.display_name) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
items = sorted;
|
||||
break;
|
||||
case 'z-a':
|
||||
items.sort();
|
||||
items.reverse();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (sendEvent) {
|
||||
variables.stats.postEvent('marketplace', 'Sort');
|
||||
}
|
||||
|
||||
return {
|
||||
items: items,
|
||||
sortType: value,
|
||||
};
|
||||
}
|
||||
|
||||
changeSort(value) {
|
||||
localStorage.setItem('sortMarketplace', value);
|
||||
this.setState(this.sortMarketplace(null, true));
|
||||
}
|
||||
|
||||
returnToMain() {
|
||||
this.setState({
|
||||
items: this.state.oldItems,
|
||||
collection: false,
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
useEffect(() => {
|
||||
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.getItems();
|
||||
}
|
||||
getItems();
|
||||
}, []);
|
||||
|
||||
componentWillUnmount() {
|
||||
useEffect(() => {
|
||||
// stop making requests
|
||||
this.controller.abort();
|
||||
return () => controller.abort();
|
||||
}, []);
|
||||
|
||||
const errorMessage = (msg) => {
|
||||
return (
|
||||
<>
|
||||
<div className="flexTopMarketplace">
|
||||
<span className="mainTitle">
|
||||
{variables.getMessage('modals.main.navbar.marketplace')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="emptyItems">
|
||||
<div className="emptyMessage">{msg}</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
|
||||
return errorMessage(
|
||||
<>
|
||||
<MdWifiOff />
|
||||
<span className="title">
|
||||
{variables.getMessage('modals.main.marketplace.offline.title')}
|
||||
</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.marketplace.offline.description')}
|
||||
</span>
|
||||
</>,
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const errorMessage = (msg) => {
|
||||
return (
|
||||
if (done === false) {
|
||||
return errorMessage(
|
||||
<div className="loaderHolder">
|
||||
<div id="loader"></div>
|
||||
<span className="subtitle">{variables.getMessage('modals.main.loading')}</span>
|
||||
</div>,
|
||||
);
|
||||
}
|
||||
|
||||
if (!items || items?.length === 0) {
|
||||
getItems();
|
||||
return errorMessage(
|
||||
<>
|
||||
<MdLocalMall />
|
||||
<span className="title">{variables.getMessage('modals.main.addons.empty.title')}</span>
|
||||
<span className="subtitle">{variables.getMessage('modals.main.marketplace.no_items')}</span>
|
||||
</>,
|
||||
);
|
||||
}
|
||||
|
||||
if (item.display_name) {
|
||||
return (
|
||||
<ItemPage
|
||||
data={item}
|
||||
toggleFunction={(...args) => toggle(...args)}
|
||||
icon={item.screenshot_url}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{collection === true ? (
|
||||
<>
|
||||
<div className="flexTopMarketplace">
|
||||
<Header
|
||||
title={variables.getMessage('modals.main.navbar.marketplace')}
|
||||
secondaryTitle={collectionTitle}
|
||||
report={false}
|
||||
goBack={() => returnToMain()}
|
||||
/>
|
||||
<div
|
||||
className="collectionPage"
|
||||
style={{
|
||||
// backgroundImage: `linear-gradient(to bottom, transparent, black), url('${collectionImg}')`,
|
||||
}}
|
||||
>
|
||||
<div className="nice-tag">
|
||||
{variables.getMessage('modals.main.marketplace.collection')}
|
||||
</div>
|
||||
<div className="content">
|
||||
<span className="mainTitle">{collectionTitle}</span>
|
||||
= </div>
|
||||
|
||||
<Button
|
||||
type="collection"
|
||||
onClick={() => installCollection()}
|
||||
disabled={busy}
|
||||
icon={<MdLibraryAdd />}
|
||||
label={
|
||||
busy
|
||||
? variables.getMessage('modals.main.marketplace.installing')
|
||||
: variables.getMessage('modals.main.marketplace.add_all')
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{/*<div className="flexTopMarketplace">
|
||||
<span className="mainTitle">
|
||||
{variables.getMessage('modals.main.navbar.marketplace')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="emptyItems">
|
||||
<div className="emptyMessage">{msg}</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
|
||||
return errorMessage(
|
||||
<>
|
||||
<MdWifiOff />
|
||||
<span className="title">
|
||||
{variables.getMessage('modals.main.marketplace.offline.title')}
|
||||
</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.marketplace.offline.description')}
|
||||
</span>
|
||||
</>,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.done === false) {
|
||||
return errorMessage(
|
||||
<div className="loaderHolder">
|
||||
<div id="loader"></div>
|
||||
<span className="subtitle">{variables.getMessage('modals.main.loading')}</span>
|
||||
</div>,
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.state.items || this.state.items?.length === 0) {
|
||||
this.getItems();
|
||||
return errorMessage(
|
||||
<>
|
||||
<MdLocalMall />
|
||||
<span className="title">{variables.getMessage('modals.main.addons.empty.title')}</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.marketplace.no_items')}
|
||||
</span>
|
||||
</>,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.item.display_name) {
|
||||
return (
|
||||
<ItemPage
|
||||
data={this.state.item}
|
||||
button={this.state.button}
|
||||
toggleFunction={(...args) => this.toggle(...args)}
|
||||
addonInstalled={this.state.item.addonInstalled}
|
||||
addonInstalledVersion={this.state.item.addonInstalledVersion}
|
||||
icon={this.state.item.screenshot_url}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.state.collection === true ? (
|
||||
<>
|
||||
<Header
|
||||
title={variables.getMessage('modals.main.navbar.marketplace')}
|
||||
secondaryTitle={this.state.collectionTitle}
|
||||
report={false}
|
||||
goBack={() => this.returnToMain()}
|
||||
<div className="headerExtras marketplaceCondition">
|
||||
{type !== 'collections' && (
|
||||
<div>
|
||||
<form className="max-w-md mx-auto relative">
|
||||
<input
|
||||
label={variables.getMessage('widgets.search')}
|
||||
placeholder={variables.getMessage('widgets.search')}
|
||||
name="filter"
|
||||
id="filter"
|
||||
value={filter}
|
||||
onChange={(event) => setFilter(event.target.value)}
|
||||
className="block w-full px-4 py-3 ps-10 text-sm text-gray-900 border border-[#484848] rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-white/5 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-neutral-100"
|
||||
/>
|
||||
<div className="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
|
||||
<MdSearch />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
<Dropdown
|
||||
label={variables.getMessage('modals.main.addons.sort.title')}
|
||||
name="sortMarketplace"
|
||||
onChange={(value) => changeSort(value
|
||||
items)}
|
||||
items={[
|
||||
{
|
||||
value: 'a-z',
|
||||
text: variables.getMessage('modals.main.addons.sort.a_z'),
|
||||
},
|
||||
{
|
||||
value: 'z-a',
|
||||
text: variables.getMessage('modals.main.addons.sort.z_a'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<div
|
||||
className="collectionPage"
|
||||
style={{
|
||||
backgroundImage: `linear-gradient(to bottom, transparent, black), url('${this.state.collectionImg}')`,
|
||||
}}
|
||||
>
|
||||
<div className="nice-tag">
|
||||
{variables.getMessage('modals.main.marketplace.collection')}
|
||||
</div>
|
||||
<div className="content">
|
||||
<span className="mainTitle">{this.state.collectionTitle}</span>
|
||||
<span className="subtitle">{this.state.collectionDescription}</span>
|
||||
</div>
|
||||
|
||||
</div>*/}
|
||||
</>
|
||||
)}
|
||||
|
||||
{type === 'collections' && !collection ? (
|
||||
items.map((item) =>
|
||||
!item.news ? (
|
||||
<div
|
||||
className="collection"
|
||||
style={
|
||||
item.news
|
||||
? { backgroundColor: item.background_colour }
|
||||
: {
|
||||
backgroundImage: `linear-gradient(to left, #000, transparent, #000), url('${item.img}')`,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div className="content">
|
||||
<span className="title">{item.display_name}</span>
|
||||
<span className="subtitle">{item.description}</span>
|
||||
</div>
|
||||
<Button
|
||||
type="collection"
|
||||
onClick={() => this.installCollection()}
|
||||
disabled={this.state.busy}
|
||||
icon={<MdLibraryAdd />}
|
||||
label={
|
||||
this.state.busy
|
||||
? variables.getMessage('modals.main.marketplace.installing')
|
||||
: variables.getMessage('modals.main.marketplace.add_all')
|
||||
}
|
||||
onClick={() => toggle('collection', item.name)}
|
||||
icon={<MdOutlineArrowForward />}
|
||||
label={variables.getMessage('modals.main.marketplace.explore_collection')}
|
||||
iconPlacement="right"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{/*<div className="flexTopMarketplace">
|
||||
<span className="mainTitle">
|
||||
{variables.getMessage('modals.main.navbar.marketplace')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="headerExtras marketplaceCondition">
|
||||
{this.props.type !== 'collections' && (
|
||||
<div>
|
||||
<form className="max-w-md mx-auto relative">
|
||||
<input
|
||||
label={variables.getMessage('widgets.search')}
|
||||
placeholder={variables.getMessage('widgets.search')}
|
||||
name="filter"
|
||||
id="filter"
|
||||
value={this.state.filter}
|
||||
onChange={(event) => this.setState({ filter: event.target.value })}
|
||||
className="block w-full px-4 py-3 ps-10 text-sm text-gray-900 border border-[#484848] rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-white/5 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-neutral-100"
|
||||
/>
|
||||
<div className="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
|
||||
<MdSearch />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
<Dropdown
|
||||
label={variables.getMessage('modals.main.addons.sort.title')}
|
||||
name="sortMarketplace"
|
||||
onChange={(value) => this.changeSort(value)}
|
||||
items={[
|
||||
{
|
||||
value: 'a-z',
|
||||
text: variables.getMessage('modals.main.addons.sort.a_z'),
|
||||
},
|
||||
{
|
||||
value: 'z-a',
|
||||
text: variables.getMessage('modals.main.addons.sort.z_a'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>*/}
|
||||
</>
|
||||
)}
|
||||
|
||||
{this.props.type === 'collections' && !this.state.collection ? (
|
||||
this.state.items.map((item) =>
|
||||
!item.news ? (
|
||||
<div
|
||||
className="collection"
|
||||
style={
|
||||
item.news
|
||||
? { backgroundColor: item.background_colour }
|
||||
: {
|
||||
backgroundImage: `linear-gradient(to left, #000, transparent, #000), url('${item.img}')`,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div className="content">
|
||||
<span className="title">{item.display_name}</span>
|
||||
<span className="subtitle">{item.description}</span>
|
||||
</div>
|
||||
<Button
|
||||
type="collection"
|
||||
onClick={() => this.toggle('collection', item.name)}
|
||||
icon={<MdOutlineArrowForward />}
|
||||
label={variables.getMessage('modals.main.marketplace.explore_collection')}
|
||||
iconPlacement="right"
|
||||
/>
|
||||
</div>
|
||||
) : null,
|
||||
)
|
||||
) : (
|
||||
<Items
|
||||
type={this.state.type}
|
||||
items={this.state.items}
|
||||
collection={this.state.displayedCollection}
|
||||
onCollection={this.state.collection}
|
||||
toggleFunction={(input) => this.toggle('item', input)}
|
||||
collectionFunction={(input) => this.toggle('collection', input)}
|
||||
filter={this.state.filter}
|
||||
showCreateYourOwn={true}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
) : null,
|
||||
)
|
||||
) : (
|
||||
<Items
|
||||
type={type}
|
||||
items={items}
|
||||
toggleFunction={(input) => toggle('item', input)}
|
||||
filter={filter}
|
||||
showCreateYourOwn={true}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Marketplace;
|
||||
|
||||
68
src/features/marketplace/views/CollectionPage.jsx
Normal file
68
src/features/marketplace/views/CollectionPage.jsx
Normal file
@@ -0,0 +1,68 @@
|
||||
function CollectionPage() {
|
||||
async function installCollection() {
|
||||
setBusy(true);
|
||||
try {
|
||||
const installed = JSON.parse(localStorage.getItem('installed'));
|
||||
for (const item of items) {
|
||||
if (installed.some((i) => i.name === item.display_name)) continue; // don't install if already installed
|
||||
let { data } = await (
|
||||
await fetch(`${variables.constants.API_URL}/marketplace/item/${item.type}/${item.name}`, {
|
||||
signal: controller.signal,
|
||||
})
|
||||
).json();
|
||||
install(data.type, data, false, true);
|
||||
variables.stats.postEvent('marketplace-item', `${item.display_name} installed}`);
|
||||
variables.stats.postEvent('marketplace', 'Install');
|
||||
}
|
||||
toast(variables.getMessage('toasts.installed'));
|
||||
window.location.reload();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast(variables.getMessage('toasts.error'));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
function returnToMain() {
|
||||
setCollection(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
title={variables.getMessage('modals.main.navbar.marketplace')}
|
||||
secondaryTitle={collectionTitle}
|
||||
report={false}
|
||||
goBack={() => returnToMain()}
|
||||
/>
|
||||
<div
|
||||
className="collectionPage"
|
||||
style={
|
||||
{
|
||||
// backgroundImage: `linear-gradient(to bottom, transparent, black), url('${collectionImg}')`,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div className="nice-tag">{variables.getMessage('modals.main.marketplace.collection')}</div>
|
||||
<div className="content">
|
||||
<span className="mainTitle">{collectionTitle}</span>={' '}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="collection"
|
||||
onClick={() => installCollection()}
|
||||
disabled={busy}
|
||||
icon={<MdLibraryAdd />}
|
||||
label={
|
||||
busy
|
||||
? variables.getMessage('modals.main.marketplace.installing')
|
||||
: variables.getMessage('modals.main.marketplace.add_all')
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default CollectionPage;
|
||||
@@ -1,41 +1,32 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { MdOutlineExtensionOff } from 'react-icons/md';
|
||||
import { Button } from 'components/Elements';
|
||||
|
||||
export default class Create extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<div className="flexTopMarketplace">
|
||||
<span className="mainTitle">
|
||||
{variables.getMessage('modals.main.addons.create.title')}
|
||||
function Create() {
|
||||
return (
|
||||
<>
|
||||
<div className="flexTopMarketplace">
|
||||
<span className="mainTitle">{variables.getMessage('modals.main.addons.create.title')}</span>
|
||||
</div>
|
||||
<div className="emptyItems">
|
||||
<div className="emptyNewMessage">
|
||||
<MdOutlineExtensionOff />
|
||||
<span className="title">
|
||||
{variables.getMessage('modals.main.addons.create.moved_title')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="emptyItems">
|
||||
<div className="emptyNewMessage">
|
||||
<MdOutlineExtensionOff />
|
||||
<span className="title">
|
||||
{variables.getMessage('modals.main.addons.create.moved_title')}
|
||||
</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.addons.create.moved_description')}
|
||||
</span>
|
||||
<div className="createButtons">
|
||||
<Button
|
||||
type="settings"
|
||||
label={variables.getMessage('modals.main.addons.create.moved_button')}
|
||||
/>
|
||||
</div>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.addons.create.moved_description')}
|
||||
</span>
|
||||
<div className="createButtons">
|
||||
<Button
|
||||
type="settings"
|
||||
label={variables.getMessage('modals.main.addons.create.moved_button')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Create;
|
||||
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
MdTranslate,
|
||||
MdOutlineWarning,
|
||||
MdStyle,
|
||||
MdClose,
|
||||
MdLibraryAdd,
|
||||
} from 'react-icons/md';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
@@ -28,14 +30,32 @@ import Markdown from 'markdown-to-jsx';
|
||||
class ItemPage extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
console.log(this.props)
|
||||
this.state = {
|
||||
showUpdateButton:
|
||||
this.props.addonInstalled === true &&
|
||||
this.props.addonInstalledVersion !== this.props.data.version,
|
||||
this.props.data.local.installed === true && this.props.data.local.version !== this.props.data.version,
|
||||
shareModal: false,
|
||||
count: 5,
|
||||
moreByCurator: [],
|
||||
};
|
||||
this.buttons = {
|
||||
uninstall: (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.manage('uninstall')}
|
||||
icon={<MdClose />}
|
||||
label={variables.getMessage('modals.main.marketplace.product.buttons.remove')}
|
||||
/>
|
||||
),
|
||||
install: (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.manage('install')}
|
||||
icon={<MdLibraryAdd />}
|
||||
label={variables.getMessage('modals.main.marketplace.product.buttons.addtomue')}
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async getCurator(name) {
|
||||
@@ -53,6 +73,10 @@ class ItemPage extends PureComponent {
|
||||
|
||||
componentDidMount() {
|
||||
this.getCurator(this.props.data.author);
|
||||
document.querySelector('#modal').scrollTop = 0;
|
||||
this.setState({
|
||||
button: this.props.data.local.installed ? this.buttons.uninstall : this.buttons.install,
|
||||
})
|
||||
}
|
||||
|
||||
updateAddon() {
|
||||
@@ -82,10 +106,31 @@ class ItemPage extends PureComponent {
|
||||
return nameMappings[name] || name;
|
||||
}
|
||||
|
||||
manage(type) {
|
||||
if (type === 'install') {
|
||||
install(this.props.data.type, this.props.data.data);
|
||||
} else {
|
||||
uninstall(this.props.data.type,this.props.data.display_name);
|
||||
}
|
||||
|
||||
toast(variables.getMessage('toasts.' + type + 'ed'));
|
||||
this.setState({
|
||||
button: type === 'install' ? this.buttons.uninstall : this.buttons.install,
|
||||
});
|
||||
|
||||
variables.stats.postEvent(
|
||||
'marketplace-item',
|
||||
`${this.state.item.display_name} ${type === 'install' ? 'installed' : 'uninstalled'}`,
|
||||
);
|
||||
|
||||
variables.stats.postEvent('marketplace', type === 'install' ? 'Install' : 'Uninstall');
|
||||
}
|
||||
|
||||
render() {
|
||||
const locale = localStorage.getItem('language');
|
||||
const shortLocale = locale.includes('_') ? locale.split('_')[0] : locale;
|
||||
let languageNames = new Intl.DisplayNames([shortLocale], { type: 'language' });
|
||||
|
||||
const convertedType = (() => {
|
||||
const map = {
|
||||
photos: 'photo_packs',
|
||||
@@ -94,6 +139,7 @@ class ItemPage extends PureComponent {
|
||||
};
|
||||
return map[this.props.data.data.type];
|
||||
})();
|
||||
|
||||
const moreByCurator = this.state.moreByCurator
|
||||
.filter((item) => item.type === convertedType && item.name !== this.props.data.data.name)
|
||||
.sort(() => 0.5 - Math.random())
|
||||
@@ -181,9 +227,7 @@ class ItemPage extends PureComponent {
|
||||
onRequestClose={() => this.setState({ shareModal: false })}
|
||||
>
|
||||
<ShareModal
|
||||
data={
|
||||
variables.constants.API_URL + '/marketplace/share/' + btoa(this.props.data.api_name)
|
||||
}
|
||||
data={variables.constants.API_URL + '/marketplace/share/' + btoa(this.props.data.slug)}
|
||||
modalClose={() => this.setState({ shareModal: false })}
|
||||
/>
|
||||
</Modal>
|
||||
@@ -347,7 +391,7 @@ class ItemPage extends PureComponent {
|
||||
}}
|
||||
/>
|
||||
{localStorage.getItem('welcomePreview') !== 'true' ? (
|
||||
this.props.button
|
||||
this.state.button
|
||||
) : (
|
||||
<p style={{ textAlign: 'center' }}>
|
||||
{variables.getMessage(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { MdCancel, MdAdd, MdOutlineTextsms } from 'react-icons/md';
|
||||
import { toast } from 'react-toastify';
|
||||
import { TextareaAutosize } from '@mui/material';
|
||||
@@ -8,142 +8,125 @@ import { Header, Row, Content, Action, PreferencesWrapper } from 'components/Lay
|
||||
import { Button } from 'components/Elements';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
class MessageOptions extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
messages: JSON.parse(localStorage.getItem('messages')) || [],
|
||||
};
|
||||
}
|
||||
function MessageOptions() {
|
||||
const [messages, setMessages] = useState(JSON.parse(localStorage.getItem('messages')) || []);
|
||||
|
||||
reset = () => {
|
||||
const reset = () => {
|
||||
localStorage.setItem('messages', '[]');
|
||||
this.setState({
|
||||
messages: [],
|
||||
});
|
||||
toast(variables.getMessage(this.languagecode, 'toasts.reset'));
|
||||
setMessages([]);
|
||||
toast(variables.getMessage('toasts.reset'));
|
||||
EventBus.emit('refresh', 'message');
|
||||
};
|
||||
|
||||
modifyMessage(type, index) {
|
||||
const messages = this.state.messages;
|
||||
const modifyMessage = (type, index) => {
|
||||
const messages = messages;
|
||||
if (type === 'add') {
|
||||
messages.push('');
|
||||
} else {
|
||||
messages.splice(index, 1);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
messages,
|
||||
});
|
||||
this.forceUpdate();
|
||||
|
||||
setMessages(messages);
|
||||
localStorage.setItem('messages', JSON.stringify(messages));
|
||||
}
|
||||
};
|
||||
|
||||
message(e, text, index) {
|
||||
const message = (e, text, index) => {
|
||||
const result = text === true ? e.target.value : e.target.result;
|
||||
|
||||
const messages = this.state.messages;
|
||||
const messages = messages;
|
||||
messages[index] = result;
|
||||
this.setState({
|
||||
messages,
|
||||
});
|
||||
this.forceUpdate();
|
||||
setMessages(messages);
|
||||
|
||||
localStorage.setItem('messages', JSON.stringify(messages));
|
||||
document.querySelector('.reminder-info').style.display = 'flex';
|
||||
localStorage.setItem('showReminder', true);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const MESSAGE_SECTION = 'modals.main.settings.sections.message';
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
title={variables.getMessage(`${MESSAGE_SECTION}.title`)}
|
||||
setting="message"
|
||||
category="message"
|
||||
element=".message"
|
||||
zoomSetting="zoomMessage"
|
||||
visibilityToggle={true}
|
||||
/>
|
||||
<PreferencesWrapper
|
||||
setting="message"
|
||||
visibilityToggle={true}
|
||||
category="message"
|
||||
zoomSetting="zoomMessage"
|
||||
>
|
||||
<Row final={true}>
|
||||
<Content title={variables.getMessage(`${MESSAGE_SECTION}.messages`)} />
|
||||
<Action>
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.modifyMessage('add')}
|
||||
icon={<MdAdd />}
|
||||
label={variables.getMessage(`${MESSAGE_SECTION}.add`)}
|
||||
/>
|
||||
</Action>
|
||||
</Row>
|
||||
<div className="messagesContainer">
|
||||
{this.state.messages.map((_url, index) => (
|
||||
<div className="messageMap" key={index}>
|
||||
<div className="flexGrow">
|
||||
<div className="icon">
|
||||
<MdOutlineTextsms />
|
||||
</div>
|
||||
<div className="messageText">
|
||||
<span className="subtitle">
|
||||
{variables.getMessage(`${MESSAGE_SECTION}.title`)}
|
||||
</span>
|
||||
<TextareaAutosize
|
||||
value={this.state.messages[index]}
|
||||
placeholder={variables.getMessage(
|
||||
'modals.main.settings.sections.message.content',
|
||||
)}
|
||||
onChange={(e) => this.message(e, true, index)}
|
||||
varient="outlined"
|
||||
style={{ padding: '0' }}
|
||||
/>
|
||||
</div>
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
title={variables.getMessage('modals.main.settings.sections.message.title')}
|
||||
setting="message"
|
||||
category="message"
|
||||
element=".message"
|
||||
zoomSetting="zoomMessage"
|
||||
visibilityToggle={true}
|
||||
/>
|
||||
<PreferencesWrapper
|
||||
setting="message"
|
||||
visibilityToggle={true}
|
||||
category="message"
|
||||
zoomSetting="zoomMessage"
|
||||
>
|
||||
<Row final={true}>
|
||||
<Content title={variables.getMessage('modals.main.settings.sections.message.messages')} />
|
||||
<Action>
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => modifyMessage('add')}
|
||||
icon={<MdAdd />}
|
||||
label={variables.getMessage('modals.main.settings.sections.message.add')}
|
||||
/>
|
||||
</Action>
|
||||
</Row>
|
||||
<div className="messagesContainer">
|
||||
{messages.map((_url, index) => (
|
||||
<div className="messageMap" key={index}>
|
||||
<div className="flexGrow">
|
||||
<div className="icon">
|
||||
<MdOutlineTextsms />
|
||||
</div>
|
||||
<div>
|
||||
<div className="messageAction">
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.modifyMessage('remove', index)}
|
||||
icon={<MdCancel />}
|
||||
label={variables.getMessage('modals.main.marketplace.product.buttons.remove')}
|
||||
/>
|
||||
</div>
|
||||
<div className="messageText">
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.settings.sections.message.title')}
|
||||
</span>
|
||||
<TextareaAutosize
|
||||
value={messages[index]}
|
||||
placeholder={variables.getMessage(
|
||||
'modals.main.settings.sections.message.content',
|
||||
)}
|
||||
onChange={(e) => message(e, true, index)}
|
||||
varient="outlined"
|
||||
style={{ padding: '0' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{this.state.messages.length === 0 && (
|
||||
<div className="photosEmpty">
|
||||
<div className="emptyNewMessage">
|
||||
<MdOutlineTextsms />
|
||||
<span className="title">
|
||||
{variables.getMessage(`${MESSAGE_SECTION}.no_messages`)}
|
||||
</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage(`${MESSAGE_SECTION}.add_some`)}
|
||||
</span>
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.modifyMessage('add')}
|
||||
icon={<MdAdd />}
|
||||
label={variables.getMessage(`${MESSAGE_SECTION}.add`)}
|
||||
/>
|
||||
<div>
|
||||
<div className="messageAction">
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => modifyMessage('remove', index)}
|
||||
icon={<MdCancel />}
|
||||
label={variables.getMessage('modals.main.marketplace.product.buttons.remove')}
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</PreferencesWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
))}
|
||||
</div>
|
||||
{messages.length === 0 && (
|
||||
<div className="photosEmpty">
|
||||
<div className="emptyNewMessage">
|
||||
<MdOutlineTextsms />
|
||||
<span className="title">
|
||||
{variables.getMessage('modals.main.settings.sections.message.no_messages')}
|
||||
</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.settings.sections.message.add_some')}
|
||||
</span>
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => modifyMessage('add')}
|
||||
icon={<MdAdd />}
|
||||
label={variables.getMessage('modals.main.settings.sections.message.add')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</PreferencesWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export { MessageOptions as default, MessageOptions };
|
||||
|
||||
@@ -1,35 +1,22 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import { MainModal } from 'components/Elements';
|
||||
import Navbar from '../../navbar/Navbar';
|
||||
import Preview from '../../helpers/preview/Preview';
|
||||
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import Welcome from 'features/welcome/Welcome';
|
||||
function Modals() {
|
||||
const [main, setMainVisible] = useState(false);
|
||||
const [welcome, setWelcomeVisible] = useState(false);
|
||||
|
||||
export default class Modals extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
mainModal: false,
|
||||
updateModal: false,
|
||||
welcomeModal: false,
|
||||
appsModal: false,
|
||||
preview: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
useEffect(() => {
|
||||
if (
|
||||
localStorage.getItem('showWelcome') !== 'false' &&
|
||||
window.location.search !== '?nointro=true'
|
||||
) {
|
||||
this.setState({
|
||||
welcomeModal: true,
|
||||
});
|
||||
setWelcomeVisible(true);
|
||||
variables.stats.postEvent('modal', 'Opened welcome');
|
||||
}
|
||||
|
||||
@@ -43,56 +30,45 @@ export default class Modals extends PureComponent {
|
||||
|
||||
// hide refresh reminder once the user has refreshed the page
|
||||
localStorage.setItem('showReminder', false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
closeWelcome() {
|
||||
localStorage.setItem('showWelcome', false);
|
||||
this.setState({
|
||||
welcomeModal: false,
|
||||
});
|
||||
EventBus.emit('refresh', 'widgetsWelcomeDone');
|
||||
EventBus.emit('refresh', 'widgets');
|
||||
EventBus.emit('refresh', 'backgroundwelcome');
|
||||
}
|
||||
|
||||
previewWelcome() {
|
||||
localStorage.setItem('showWelcome', false);
|
||||
localStorage.setItem('welcomePreview', true);
|
||||
this.setState({
|
||||
welcomeModal: false,
|
||||
preview: true,
|
||||
});
|
||||
EventBus.emit('refresh', 'widgetsWelcome');
|
||||
}
|
||||
|
||||
toggleModal(type, action) {
|
||||
this.setState({
|
||||
[type]: action,
|
||||
});
|
||||
const toggleModal = (type, action) => {
|
||||
switch (type) {
|
||||
case 'main':
|
||||
setMainVisible(action);
|
||||
break;
|
||||
case 'welcome':
|
||||
setWelcomeVisible(action);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (action !== false) {
|
||||
variables.stats.postEvent('modal', `Opened ${type.replace('Modal', '')}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
{this.state.welcomeModal === false && (
|
||||
<Navbar openModal={(modal) => this.toggleModal(modal, true)} />
|
||||
)}
|
||||
<Modal
|
||||
closeTimeoutMS={300}
|
||||
id="modal"
|
||||
onRequestClose={() => this.toggleModal('mainModal', false)}
|
||||
isOpen={this.state.mainModal}
|
||||
className="Modal mainModal"
|
||||
overlayClassName="Overlay"
|
||||
ariaHideApp={false}
|
||||
>
|
||||
<MainModal modalClose={() => this.toggleModal('mainModal', false)} />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{welcome === false && (
|
||||
<Navbar openModal={(modal) => toggleModal('main', true)} />
|
||||
)}
|
||||
<Modal
|
||||
closeTimeoutMS={300}
|
||||
id="modal"
|
||||
onRequestClose={() => toggleModal('main', false)}
|
||||
isOpen={main}
|
||||
className="Modal mainModal"
|
||||
overlayClassName="Overlay"
|
||||
ariaHideApp={false}
|
||||
>
|
||||
<MainModal modalClose={() => toggleModal('main', false)} />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Modals;
|
||||
@@ -15,6 +15,7 @@ import { FileUpload, Text, Switch, Dropdown } from 'components/Form/Settings';
|
||||
import { ResetModal, Button } from 'components/Elements';
|
||||
|
||||
import { Header, Section, Row, Content, Action } from 'components/Layout/Settings';
|
||||
import { useTab } from 'components/Elements/MainModal/backend/TabContext';
|
||||
|
||||
import time_zones from 'features/time/timezones.json';
|
||||
|
||||
@@ -22,6 +23,7 @@ function AdvancedOptions() {
|
||||
const [resetModal, setResetModal] = useState(false);
|
||||
const [data, setData] = useState(false);
|
||||
const ADVANCED_SECTION = 'modals.main.settings.sections.advanced';
|
||||
const { subSection } = useTab();
|
||||
|
||||
const Data = () => {
|
||||
return localStorage.getItem('welcomePreview') !== 'true' ? (
|
||||
@@ -93,7 +95,7 @@ function AdvancedOptions() {
|
||||
return (
|
||||
<>
|
||||
{header}
|
||||
{data ? (
|
||||
{subSection === "data" ? (
|
||||
<>
|
||||
<Data />
|
||||
<Modal
|
||||
@@ -110,6 +112,7 @@ function AdvancedOptions() {
|
||||
) : (
|
||||
<>
|
||||
<Section
|
||||
id="data"
|
||||
title={variables.getMessage(`${ADVANCED_SECTION}.data`)}
|
||||
subtitle={variables.getMessage(`${ADVANCED_SECTION}.data_subtitle`)}
|
||||
onClick={() => setData(true)}
|
||||
|
||||
@@ -4,6 +4,7 @@ import variables from 'config/variables';
|
||||
|
||||
import { Checkbox, Dropdown, Radio, Slider, Text } from 'components/Form/Settings';
|
||||
import { Header, Section, Row, Content, Action } from 'components/Layout/Settings';
|
||||
import { useTab } from 'components/Elements/MainModal/backend/TabContext';
|
||||
|
||||
import { MdAccessibility } from 'react-icons/md';
|
||||
|
||||
@@ -11,6 +12,7 @@ import values from 'utils/data/slider_values.json';
|
||||
|
||||
function AppearanceOptions() {
|
||||
const [accessibility, setAccessibility] = useState(false);
|
||||
const { subSection } = useTab();
|
||||
|
||||
const ThemeSelection = () => {
|
||||
return (
|
||||
@@ -262,11 +264,12 @@ function AppearanceOptions() {
|
||||
return (
|
||||
<>
|
||||
{header}
|
||||
{accessibility ? (
|
||||
{subSection === "accessibility" ? (
|
||||
<AccessibilityOptions />
|
||||
) : (
|
||||
<>
|
||||
<Section
|
||||
id="accessibility"
|
||||
title={variables.getMessage(
|
||||
'modals.main.settings.sections.appearance.accessibility.title',
|
||||
)}
|
||||
|
||||
@@ -1,56 +1,27 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent, createRef } from 'react';
|
||||
import { useState, createRef, useEffect } from 'react';
|
||||
import Markdown from 'markdown-to-jsx';
|
||||
import { MdOutlineWifiOff } from 'react-icons/md';
|
||||
|
||||
class Changelog extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
title: null,
|
||||
};
|
||||
this.offlineMode = localStorage.getItem('offlineMode') === 'true';
|
||||
this.controller = new AbortController();
|
||||
this.changelog = createRef();
|
||||
}
|
||||
function Changelog() {
|
||||
const [title, setTitle] = useState(null);
|
||||
const [error, setError] = useState(false);
|
||||
const [content, setContent] = useState(null);
|
||||
const [date, setDate] = useState(null);
|
||||
|
||||
parseMarkdown = (text) => {
|
||||
if (typeof text !== 'string') {
|
||||
throw new Error('Input must be a string');
|
||||
}
|
||||
const offlineMode = localStorage.getItem('offlineMode') === 'true';
|
||||
const controller = new AbortController();
|
||||
const changelog = createRef();
|
||||
|
||||
// Replace markdown syntax
|
||||
text = text
|
||||
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
||||
.replace(/^## (.*$)/gm, '<span class="title">$1</span>')
|
||||
.replace(
|
||||
/((http|https):\/\/[^\s]+)/g,
|
||||
'<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>',
|
||||
)
|
||||
// resolve @ to github user link
|
||||
.replace(
|
||||
/@([a-zA-Z0-9-_]+)/g,
|
||||
'<a href="https://github.com/$1" target="_blank" class="changelogAt">@$1</a>',
|
||||
);
|
||||
|
||||
// Replace list items
|
||||
text = text.replace(/^\* (.*$)/gm, '<li>$1</li>');
|
||||
|
||||
// Wrap list items in <ul></ul>
|
||||
text = text.replace(/((<li>.*<\/li>\s*)+)/g, '<ul>$1</ul>');
|
||||
|
||||
return text;
|
||||
};
|
||||
|
||||
async getUpdate() {
|
||||
const getUpdate = async () => {
|
||||
const releases = await fetch(
|
||||
`https://api.github.com/repos/${variables.constants.ORG_NAME}/${variables.constants.REPO_NAME}/releases`,
|
||||
{
|
||||
signal: this.controller.signal,
|
||||
signal: controller.signal,
|
||||
},
|
||||
);
|
||||
|
||||
if (this.controller.signal.aborted === true) {
|
||||
if (controller.signal.aborted === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -63,92 +34,88 @@ class Changelog extends PureComponent {
|
||||
}
|
||||
|
||||
// request the changelog
|
||||
const res = await fetch(release.url, { signal: this.controller.signal });
|
||||
const res = await fetch(release.url, { signal: controller.signal });
|
||||
|
||||
if (res.status === 404) {
|
||||
this.setState({ error: true });
|
||||
setError(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.controller.signal.aborted === true) {
|
||||
if (controller.signal.aborted === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
const changelog = await res.json();
|
||||
this.setState({
|
||||
title: changelog.name,
|
||||
content: changelog.body,
|
||||
date: new Date(changelog.published_at).toLocaleDateString(),
|
||||
});
|
||||
setTitle(changelog.name);
|
||||
setContent(changelog.body);
|
||||
setDate(new Date(changelog.published_at).toLocaleDateString());
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (navigator.onLine === false || this.offlineMode) {
|
||||
useEffect(() => {
|
||||
if (navigator.onLine === false || offlineMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.getUpdate();
|
||||
}
|
||||
getUpdate();
|
||||
}, []);
|
||||
|
||||
componentWillUnmount() {
|
||||
useEffect(() => {
|
||||
// stop making requests
|
||||
this.controller.abort();
|
||||
}
|
||||
|
||||
render() {
|
||||
const errorMessage = (msg) => {
|
||||
return (
|
||||
<div className="emptyItems">
|
||||
<div className="emptyMessage">{msg}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
if (navigator.onLine === false || this.offlineMode) {
|
||||
return errorMessage(
|
||||
<>
|
||||
<MdOutlineWifiOff />
|
||||
<h1>{variables.getMessage('modals.main.marketplace.offline.title')}</h1>
|
||||
<p className="description">
|
||||
{variables.getMessage('modals.main.marketplace.offline.description')}
|
||||
</p>
|
||||
</>,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.error === true) {
|
||||
return errorMessage(
|
||||
<>
|
||||
<MdOutlineWifiOff />
|
||||
<span className="title">{variables.getMessage('modals.main.error_boundary.title')}</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.error_boundary.message')}
|
||||
</span>
|
||||
</>,
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.state.title) {
|
||||
return errorMessage(
|
||||
<div className="loaderHolder">
|
||||
<div id="loader"></div>
|
||||
<span className="subtitle">{variables.getMessage('modals.main.loading')}</span>
|
||||
</div>,
|
||||
);
|
||||
}
|
||||
return () => controller.abort();
|
||||
}, []);
|
||||
|
||||
const errorMessage = (msg) => {
|
||||
return (
|
||||
<article className="changelogtab prose dark:prose-invert" ref={this.changelog}>
|
||||
<div className="not-prose">
|
||||
<span className="mainTitle">{this.state.title}</span>
|
||||
<span className="subtitle">Released on {this.state.date}</span>
|
||||
</div>
|
||||
<Markdown options={{ overrides: { a: { props: { target: '_blank' } } } }}>
|
||||
{this.state.content}
|
||||
</Markdown>
|
||||
</article>
|
||||
<div className="emptyItems">
|
||||
<div className="emptyMessage">{msg}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
if (navigator.onLine === false || offlineMode) {
|
||||
return errorMessage(
|
||||
<>
|
||||
<MdOutlineWifiOff />
|
||||
<h1>{variables.getMessage('modals.main.marketplace.offline.title')}</h1>
|
||||
<p className="description">
|
||||
{variables.getMessage('modals.main.marketplace.offline.description')}
|
||||
</p>
|
||||
</>,
|
||||
);
|
||||
}
|
||||
|
||||
if (error === true) {
|
||||
return errorMessage(
|
||||
<>
|
||||
<MdOutlineWifiOff />
|
||||
<span className="title">{variables.getMessage('modals.main.error_boundary.title')}</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.error_boundary.message')}
|
||||
</span>
|
||||
</>,
|
||||
);
|
||||
}
|
||||
|
||||
if (!title) {
|
||||
return errorMessage(
|
||||
<div className="loaderHolder">
|
||||
<div id="loader"></div>
|
||||
<span className="subtitle">{variables.getMessage('modals.main.loading')}</span>
|
||||
</div>,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<article className="changelogtab prose dark:prose-invert" ref={changelog}>
|
||||
<div className="not-prose">
|
||||
<span className="mainTitle">{title}</span>
|
||||
<span className="subtitle">Released on {date}</span>
|
||||
</div>
|
||||
<Markdown options={{ overrides: { a: { props: { target: '_blank' } } } }}>
|
||||
{content}
|
||||
</Markdown>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
export { Changelog as default, Changelog };
|
||||
|
||||
@@ -29,6 +29,7 @@ const SortableItem = sortableElement(({ value }) => (
|
||||
</li>
|
||||
));
|
||||
|
||||
|
||||
const SortableContainer = sortableContainer(({ children }) => (
|
||||
<ul className="sortableContainer">{children}</ul>
|
||||
));
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
import variables from 'config/variables';
|
||||
import { memo } from 'react';
|
||||
import Tabs from 'components/Elements/MainModal/backend/Tabs';
|
||||
|
||||
import Added from '../../marketplace/views/Added';
|
||||
import Create from '../../marketplace/views/Create';
|
||||
|
||||
function Addons(props) {
|
||||
return (
|
||||
<Tabs changeTab={(type) => props.changeTab(type)} current="addons" modalClose={props.modalClose}>
|
||||
{
|
||||
/*<Tabs changeTab={(type) => props.changeTab(type)} current="addons" modalClose={props.modalClose}>
|
||||
<div label={variables.getMessage('modals.main.addons.added')} name="added">
|
||||
<Added />
|
||||
</div>
|
||||
<div label={variables.getMessage('modals.main.addons.create.title')} name="create">
|
||||
<Create />
|
||||
</div>
|
||||
</Tabs>
|
||||
</Tabs>*/
|
||||
},
|
||||
(
|
||||
<div className="modalTabContent">
|
||||
<Added />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import variables from 'config/variables';
|
||||
import { memo } from 'react';
|
||||
|
||||
import Tabs from '../../../components/Elements/MainModal/backend/Tabs';
|
||||
import MarketplaceTab from '../../marketplace/views/Browse';
|
||||
|
||||
function Marketplace(props) {
|
||||
return (
|
||||
<Tabs changeTab={(type) => props.changeTab(type)} current="marketplace" modalClose={props.modalClose}>
|
||||
{
|
||||
/*<Tabs changeTab={(type) => props.changeTab(type)} current="marketplace" modalClose={props.modalClose}>
|
||||
<div label={variables.getMessage('modals.main.marketplace.all')} name="all">
|
||||
<MarketplaceTab type="all" />
|
||||
</div>
|
||||
@@ -25,7 +25,13 @@ function Marketplace(props) {
|
||||
<div label={variables.getMessage('modals.main.marketplace.collections')} name="collections">
|
||||
<MarketplaceTab type="collections" />
|
||||
</div>
|
||||
</Tabs>
|
||||
</Tabs>*/
|
||||
},
|
||||
(
|
||||
<div className="modalTabContent">
|
||||
<MarketplaceTab type="all" />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import variables from 'config/variables';
|
||||
import { memo } from 'react';
|
||||
|
||||
import Tabs from 'components/Elements/MainModal/backend/Tabs';
|
||||
//import Tabs from 'components/Elements/MainModal/backend/Tabs';
|
||||
import { Tabs } from 'components/Elements/MainModal/backend/newTabs';
|
||||
|
||||
import { NavbarOptions } from 'features/navbar';
|
||||
import { GreetingOptions } from 'features/greeting';
|
||||
@@ -90,13 +91,9 @@ const sections = [
|
||||
|
||||
function Settings(props) {
|
||||
return (
|
||||
<Tabs setSubTab={props.setSubTab} changeTab={(type) => props.changeTab(type)} current="settings" modalClose={props.modalClose}>
|
||||
{sections.map(({ label, name, component: Component }) => (
|
||||
<div key={name} label={variables.getMessage(label)} name={name}>
|
||||
<Component />
|
||||
</div>
|
||||
))}
|
||||
</Tabs>
|
||||
<>
|
||||
<Tabs sections={sections} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PureComponent, Fragment, Suspense, lazy } from 'react';
|
||||
import { Fragment, Suspense, lazy, useState, useEffect } from 'react';
|
||||
|
||||
import Clock from '../../time/Clock';
|
||||
import Greeting from '../../greeting/Greeting';
|
||||
@@ -18,74 +18,61 @@ import defaults from 'config/default';
|
||||
// as seen here it is ridiculously large
|
||||
const Weather = lazy(() => import('../../weather/Weather'));
|
||||
|
||||
export default class Widgets extends PureComponent {
|
||||
online = localStorage.getItem('offlineMode') === 'false';
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
order: JSON.parse(localStorage.getItem('order')) || defaults.order,
|
||||
welcome: localStorage.getItem('showWelcome'),
|
||||
};
|
||||
// widgets we can re-order
|
||||
this.widgets = {
|
||||
time: this.enabled('time') && <Clock />,
|
||||
greeting: this.enabled('greeting') && <Greeting />,
|
||||
quote: this.enabled('quote') && <Quote />,
|
||||
date: this.enabled('date') && <Date />,
|
||||
quicklinks: this.enabled('quicklinksenabled') && this.online ? <QuickLinks /> : null,
|
||||
message: this.enabled('message') && <Message />,
|
||||
};
|
||||
}
|
||||
export function Widgets() {
|
||||
const [order, setOrder] = useState(JSON.parse(localStorage.getItem('order')) || defaults.order);
|
||||
const [welcome, setWelcome] = useState(localStorage.getItem('showWelcome'));
|
||||
|
||||
enabled(key) {
|
||||
const online = localStorage.getItem('offlineMode') === 'false';
|
||||
|
||||
const widgets = {
|
||||
time: enabled('time') && <Clock />,
|
||||
greeting: enabled('greeting') && <Greeting />,
|
||||
quote: enabled('quote') && <Quote />,
|
||||
date: enabled('date') && <Date />,
|
||||
quicklinks: enabled('quicklinksenabled') && online ? <QuickLinks /> : null,
|
||||
message: enabled('message') && <Message />,
|
||||
};
|
||||
|
||||
function enabled(key) {
|
||||
return localStorage.getItem(key) === 'true';
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
useEffect(() => {
|
||||
EventBus.on('refresh', (data) => {
|
||||
switch (data) {
|
||||
case 'widgets':
|
||||
return this.setState({
|
||||
order: JSON.parse(localStorage.getItem('order')) || defaults.order,
|
||||
});
|
||||
setOrder(JSON.parse(localStorage.getItem('order')) || defaults.order);
|
||||
break;
|
||||
case 'widgetsWelcome':
|
||||
this.setState({
|
||||
welcome: localStorage.getItem('showWelcome'),
|
||||
});
|
||||
setWelcome(localStorage.getItem('showWelcome'));
|
||||
localStorage.setItem('showWelcome', true);
|
||||
window.onbeforeunload = () => {
|
||||
localStorage.clear();
|
||||
};
|
||||
break;
|
||||
case 'widgetsWelcomeDone':
|
||||
this.setState({
|
||||
welcome: localStorage.getItem('showWelcome'),
|
||||
});
|
||||
return (window.onbeforeunload = null);
|
||||
setWelcome(localStorage.getItem('showWelcome'));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
return () => EventBus.off('refresh');
|
||||
}, []);
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
// don't show when welcome is there
|
||||
return this.state.welcome !== 'false' ? (
|
||||
<WidgetsLayout />
|
||||
) : (
|
||||
<WidgetsLayout>
|
||||
<Suspense fallback={<></>}>
|
||||
{this.enabled('searchBar') && <Search />}
|
||||
{this.state.order.map((element, key) => (
|
||||
<Fragment key={key}>{this.widgets[element]}</Fragment>
|
||||
))}
|
||||
{this.enabled('weatherEnabled') && this.online ? <Weather /> : null}
|
||||
</Suspense>
|
||||
</WidgetsLayout>
|
||||
);
|
||||
}
|
||||
return welcome !== 'false' ? (
|
||||
<WidgetsLayout />
|
||||
) : (
|
||||
<WidgetsLayout>
|
||||
<Suspense fallback={<></>}>
|
||||
{enabled('searchBar') && <Search />}
|
||||
{order.map((element, key) => (
|
||||
<Fragment key={key}>{widgets[element]}</Fragment>
|
||||
))}
|
||||
{enabled('weatherEnabled') && online ? <Weather /> : null}
|
||||
</Suspense>
|
||||
</WidgetsLayout>
|
||||
);
|
||||
}
|
||||
|
||||
export default Widgets;
|
||||
@@ -1,18 +1,14 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { MdCropFree } from 'react-icons/md';
|
||||
|
||||
import { Tooltip } from 'components/Elements';
|
||||
class Maximise extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
hidden: false,
|
||||
};
|
||||
}
|
||||
|
||||
setAttribute(blur, brightness, filter) {
|
||||
function Maximise(props) {
|
||||
const [hidden, setHidden] = useState(false);
|
||||
|
||||
const setAttribute = (blur, brightness, filter) => {
|
||||
// don't attempt to modify the background if it isn't an image
|
||||
const backgroundType = localStorage.getItem('backgroundType');
|
||||
if (
|
||||
@@ -43,48 +39,35 @@ class Maximise extends PureComponent {
|
||||
: ''
|
||||
};`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
maximise = () => {
|
||||
const maximise = () => {
|
||||
// hide widgets
|
||||
const widgets = document.getElementById('widgets');
|
||||
this.state.hidden === false
|
||||
? (widgets.style.display = 'none')
|
||||
: (widgets.style.display = 'flex');
|
||||
setHidden(!hidden);
|
||||
widgets.style.display = hidden ? 'none' : 'flex';
|
||||
|
||||
if (this.state.hidden === false) {
|
||||
this.setState({
|
||||
hidden: true,
|
||||
});
|
||||
|
||||
this.setAttribute(0, 100);
|
||||
if (hidden === false) {
|
||||
setAttribute(0, 100);
|
||||
variables.stats.postEvent('feature', 'Background maximise');
|
||||
} else {
|
||||
this.setState({
|
||||
hidden: false,
|
||||
});
|
||||
|
||||
this.setAttribute(localStorage.getItem('blur'), localStorage.getItem('brightness'), true);
|
||||
setAttribute(localStorage.getItem('blur'), localStorage.getItem('brightness'), true);
|
||||
variables.stats.postEvent('feature', 'Background unmaximise');
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Tooltip
|
||||
title={variables.getMessage('modals.main.settings.sections.background.buttons.view')}
|
||||
return (
|
||||
<Tooltip title={variables.getMessage('modals.main.settings.sections.background.buttons.view')}>
|
||||
<button
|
||||
className="navbarButton"
|
||||
style={{ fontSize: props.fontSize }}
|
||||
onClick={maximise}
|
||||
aria-label={variables.getMessage('modals.main.settings.sections.background.buttons.view')}
|
||||
>
|
||||
<button
|
||||
className="navbarButton"
|
||||
style={{ fontSize: this.props.fontSize }}
|
||||
onClick={this.maximise}
|
||||
aria-label={variables.getMessage('modals.main.settings.sections.background.buttons.view')}
|
||||
>
|
||||
<MdCropFree className="topicons" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
<MdCropFree className="topicons" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export { Maximise as default, Maximise };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import variables from 'config/variables';
|
||||
import React, { PureComponent } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { MdCancel, MdAdd, MdSource, MdOutlineFormatQuote } from 'react-icons/md';
|
||||
import TextareaAutosize from '@mui/material/TextareaAutosize';
|
||||
|
||||
@@ -13,70 +13,52 @@ import {
|
||||
} from 'components/Layout/Settings';
|
||||
import { Checkbox, Dropdown } from 'components/Form/Settings';
|
||||
import { Button } from 'components/Elements';
|
||||
import { useTab } from 'components/Elements/MainModal/backend/TabContext';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
import EventBus from 'utils/eventbus';
|
||||
import defaults from './default';
|
||||
|
||||
class QuoteOptions extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
quoteType: localStorage.getItem('quoteType') || defaults.quoteType,
|
||||
customQuote: this.getCustom(),
|
||||
sourceSection: false,
|
||||
};
|
||||
}
|
||||
const QuoteOptions = () => {
|
||||
const [quoteType, setQuoteType] = useState(localStorage.getItem('quoteType') || defaults.quoteType);
|
||||
const [customQuote, setCustomQuote] = useState(getCustom());
|
||||
const [sourceSection, setSourceSection] = useState(false);
|
||||
const QUOTE_SECTION = 'modals.main.settings.sections.quote';
|
||||
const { subSection } = useTab();
|
||||
|
||||
resetCustom = () => {
|
||||
useEffect(() => {
|
||||
localStorage.setItem('quoteType', quoteType);
|
||||
}, [quoteType]);
|
||||
|
||||
function resetCustom() {
|
||||
localStorage.setItem('customQuote', '[{"quote": "", "author": ""}]');
|
||||
this.setState({
|
||||
customQuote: [
|
||||
{
|
||||
quote: '',
|
||||
author: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
setCustomQuote([{ quote: '', author: '' }]);
|
||||
toast(variables.getMessage('toasts.reset'));
|
||||
EventBus.emit('refresh', 'background');
|
||||
};
|
||||
}
|
||||
|
||||
customQuote(e, text, index, type) {
|
||||
const result = text === true ? e.target.value : e.target.result;
|
||||
|
||||
const customQuote = this.state.customQuote;
|
||||
customQuote[index][type] = result;
|
||||
this.setState({
|
||||
customQuote,
|
||||
});
|
||||
this.forceUpdate();
|
||||
|
||||
localStorage.setItem('customQuote', JSON.stringify(customQuote));
|
||||
function handleCustomQuote(e, index, type) {
|
||||
const result = e.target.value;
|
||||
const newCustomQuote = [...customQuote];
|
||||
newCustomQuote[index][type] = result;
|
||||
setCustomQuote(newCustomQuote);
|
||||
localStorage.setItem('customQuote', JSON.stringify(newCustomQuote));
|
||||
document.querySelector('.reminder-info').style.display = 'flex';
|
||||
localStorage.setItem('showReminder', true);
|
||||
}
|
||||
|
||||
modifyCustomQuote(type, index) {
|
||||
const customQuote = this.state.customQuote;
|
||||
function modifyCustomQuote(type, index) {
|
||||
let newCustomQuote = [...customQuote];
|
||||
if (type === 'add') {
|
||||
customQuote.push({
|
||||
quote: '',
|
||||
author: '',
|
||||
});
|
||||
newCustomQuote.push({ quote: '', author: '' });
|
||||
} else {
|
||||
customQuote.splice(index, 1);
|
||||
newCustomQuote.splice(index, 1);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
customQuote,
|
||||
});
|
||||
this.forceUpdate();
|
||||
|
||||
localStorage.setItem('customQuote', JSON.stringify(customQuote));
|
||||
setCustomQuote(newCustomQuote);
|
||||
localStorage.setItem('customQuote', JSON.stringify(newCustomQuote));
|
||||
}
|
||||
|
||||
getCustom() {
|
||||
function getCustom() {
|
||||
let data = JSON.parse(localStorage.getItem('customQuote'));
|
||||
if (data === null) {
|
||||
data = [];
|
||||
@@ -84,222 +66,211 @@ class QuoteOptions extends PureComponent {
|
||||
return data;
|
||||
}
|
||||
|
||||
render() {
|
||||
const QUOTE_SECTION = 'modals.main.settings.sections.quote';
|
||||
|
||||
const ButtonOptions = () => {
|
||||
return (
|
||||
<Row>
|
||||
<Content
|
||||
title={variables.getMessage(`${QUOTE_SECTION}.buttons.title`)}
|
||||
subtitle={variables.getMessage('modals.main.settings.sections.quote.buttons.subtitle')}
|
||||
/>
|
||||
<Action>
|
||||
<Checkbox
|
||||
name="copyButton"
|
||||
text={variables.getMessage(`${QUOTE_SECTION}.buttons.copy`)}
|
||||
category="quote"
|
||||
/>
|
||||
<Checkbox
|
||||
name="quoteShareButton"
|
||||
text={variables.getMessage('widgets.quote.share')}
|
||||
category="quote"
|
||||
/>
|
||||
<Checkbox
|
||||
name="favouriteQuoteEnabled"
|
||||
text={variables.getMessage(`${QUOTE_SECTION}.buttons.favourite`)}
|
||||
category="quote"
|
||||
/>
|
||||
</Action>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
const SourceDropdown = () => {
|
||||
return (
|
||||
<Dropdown
|
||||
name="quoteType"
|
||||
label={variables.getMessage('modals.main.settings.sections.background.type.title')}
|
||||
onChange={(value) => this.setState({ quoteType: value })}
|
||||
category="quote"
|
||||
items={[
|
||||
localStorage.getItem('quote_packs') && {
|
||||
value: 'quote_pack',
|
||||
text: variables.getMessage('modals.main.navbar.marketplace'),
|
||||
},
|
||||
{
|
||||
value: 'api',
|
||||
text: variables.getMessage('modals.main.settings.sections.background.type.api'),
|
||||
},
|
||||
{ value: 'custom', text: variables.getMessage(`${QUOTE_SECTION}.custom`) },
|
||||
]}
|
||||
const ButtonOptions = () => {
|
||||
return (
|
||||
<Row>
|
||||
<Content
|
||||
title={variables.getMessage(`${QUOTE_SECTION}.buttons.title`)}
|
||||
subtitle={variables.getMessage('modals.main.settings.sections.quote.buttons.subtitle')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
<Action>
|
||||
<Checkbox
|
||||
name="copyButton"
|
||||
text={variables.getMessage(`${QUOTE_SECTION}.buttons.copy`)}
|
||||
category="quote"
|
||||
/>
|
||||
<Checkbox
|
||||
name="quoteShareButton"
|
||||
text={variables.getMessage('widgets.quote.share')}
|
||||
category="quote"
|
||||
/>
|
||||
<Checkbox
|
||||
name="favouriteQuoteEnabled"
|
||||
text={variables.getMessage(`${QUOTE_SECTION}.buttons.favourite`)}
|
||||
category="quote"
|
||||
/>
|
||||
</Action>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
const AdditionalOptions = () => {
|
||||
return (
|
||||
const SourceDropdown = () => {
|
||||
return (
|
||||
<Dropdown
|
||||
name="quoteType"
|
||||
label={variables.getMessage('modals.main.settings.sections.background.type.title')}
|
||||
onChange={(value) => setQuoteType(value)}
|
||||
category="quote"
|
||||
items={[
|
||||
localStorage.getItem('quote_packs') && {
|
||||
value: 'quote_pack',
|
||||
text: variables.getMessage('modals.main.navbar.marketplace'),
|
||||
},
|
||||
{
|
||||
value: 'api',
|
||||
text: variables.getMessage('modals.main.settings.sections.background.type.api'),
|
||||
},
|
||||
{ value: 'custom', text: variables.getMessage(`${QUOTE_SECTION}.custom`) },
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const AdditionalOptions = () => {
|
||||
return (
|
||||
<Row final={true}>
|
||||
<Content
|
||||
title={variables.getMessage('modals.main.settings.additional_settings')}
|
||||
subtitle={variables.getMessage(`${QUOTE_SECTION}.additional`)}
|
||||
/>
|
||||
<Action>
|
||||
<Checkbox
|
||||
name="authorLink"
|
||||
text={variables.getMessage(`${QUOTE_SECTION}.author_link`)}
|
||||
element=".other"
|
||||
/>
|
||||
<Checkbox
|
||||
name="authorImg"
|
||||
text={variables.getMessage(`${QUOTE_SECTION}.author_img`)}
|
||||
element=".other"
|
||||
/>
|
||||
</Action>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
let customSettings;
|
||||
if (quoteType === 'custom' && subSection === 'source') {
|
||||
customSettings = (
|
||||
<>
|
||||
<Row final={true}>
|
||||
<Content
|
||||
title={variables.getMessage('modals.main.settings.additional_settings')}
|
||||
subtitle={variables.getMessage(`${QUOTE_SECTION}.additional`)}
|
||||
title={variables.getMessage(`${QUOTE_SECTION}.custom`)}
|
||||
subtitle={variables.getMessage(`${QUOTE_SECTION}.custom_subtitle`)}
|
||||
/>
|
||||
<Action>
|
||||
<Checkbox
|
||||
name="authorLink"
|
||||
text={variables.getMessage(`${QUOTE_SECTION}.author_link`)}
|
||||
element=".other"
|
||||
/>
|
||||
<Checkbox
|
||||
name="authorImg"
|
||||
text={variables.getMessage(`${QUOTE_SECTION}.author_img`)}
|
||||
element=".other"
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => modifyCustomQuote('add')}
|
||||
icon={<MdAdd />}
|
||||
label={variables.getMessage(`${QUOTE_SECTION}.add`)}
|
||||
/>
|
||||
</Action>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
let customSettings;
|
||||
if (this.state.quoteType === 'custom' && this.state.sourceSection === true) {
|
||||
customSettings = (
|
||||
<>
|
||||
<Row final={true}>
|
||||
<Content
|
||||
title={variables.getMessage(`${QUOTE_SECTION}.custom`)}
|
||||
subtitle={variables.getMessage(`${QUOTE_SECTION}.custom_subtitle`)}
|
||||
/>
|
||||
<Action>
|
||||
{customQuote.length !== 0 ? (
|
||||
<div className="messagesContainer">
|
||||
{customQuote.map((_url, index) => (
|
||||
<div className="messageMap" key={index}>
|
||||
<div className="icon">
|
||||
<MdOutlineFormatQuote />
|
||||
</div>
|
||||
<div className="messageText">
|
||||
<TextareaAutosize
|
||||
value={customQuote[index].quote}
|
||||
placeholder={variables.getMessage('modals.main.settings.sections.quote.title')}
|
||||
onChange={(e) => handleCustomQuote(e, index, 'quote')}
|
||||
varient="outlined"
|
||||
style={{ fontSize: '22px', fontWeight: 'bold' }}
|
||||
/>
|
||||
<TextareaAutosize
|
||||
value={customQuote[index].author}
|
||||
placeholder={variables.getMessage('modals.main.settings.sections.quote.author')}
|
||||
className="subtitle"
|
||||
onChange={(e) => handleCustomQuote(e, index, 'author')}
|
||||
varient="outlined"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="messageAction">
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => modifyCustomQuote('remove', index)}
|
||||
icon={<MdCancel />}
|
||||
label={variables.getMessage('modals.main.marketplace.product.buttons.remove')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="photosEmpty">
|
||||
<div className="emptyNewMessage">
|
||||
<MdOutlineFormatQuote />
|
||||
<span className="title">{variables.getMessage(`${QUOTE_SECTION}.no_quotes`)}</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.settings.sections.message.add_some')}
|
||||
</span>
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.modifyCustomQuote('add')}
|
||||
onClick={() => modifyCustomQuote('add')}
|
||||
icon={<MdAdd />}
|
||||
label={variables.getMessage(`${QUOTE_SECTION}.add`)}
|
||||
/>
|
||||
</Action>
|
||||
</Row>
|
||||
|
||||
{this.state.customQuote.length !== 0 ? (
|
||||
<div className="messagesContainer">
|
||||
{this.state.customQuote.map((_url, index) => (
|
||||
<div className="messageMap">
|
||||
<div className="icon">
|
||||
<MdOutlineFormatQuote />
|
||||
</div>
|
||||
<div className="messageText">
|
||||
<TextareaAutosize
|
||||
value={this.state.customQuote[index].quote}
|
||||
placeholder={variables.getMessage(
|
||||
'modals.main.settings.sections.quote.title',
|
||||
)}
|
||||
onChange={(e) => this.customQuote(e, true, index, 'quote')}
|
||||
varient="outlined"
|
||||
style={{ fontSize: '22px', fontWeight: 'bold' }}
|
||||
/>
|
||||
<TextareaAutosize
|
||||
value={this.state.customQuote[index].author}
|
||||
placeholder={variables.getMessage(
|
||||
'modals.main.settings.sections.quote.author',
|
||||
)}
|
||||
className="subtitle"
|
||||
onChange={(e) => this.customQuote(e, true, index, 'author')}
|
||||
varient="outlined"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="messageAction">
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.modifyCustomQuote('remove', index)}
|
||||
icon={<MdCancel />}
|
||||
label={variables.getMessage(
|
||||
'modals.main.marketplace.product.buttons.remove',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="photosEmpty">
|
||||
<div className="emptyNewMessage">
|
||||
<MdOutlineFormatQuote />
|
||||
<span className="title">{variables.getMessage(`${QUOTE_SECTION}.no_quotes`)}</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.settings.sections.message.add_some')}
|
||||
</span>
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.modifyCustomQuote('add')}
|
||||
icon={<MdAdd />}
|
||||
label={variables.getMessage(`${QUOTE_SECTION}.add`)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
// api
|
||||
customSettings = <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.state.sourceSection ? (
|
||||
<Header
|
||||
title={variables.getMessage(`${QUOTE_SECTION}.title`)}
|
||||
secondaryTitle={variables.getMessage(
|
||||
'modals.main.settings.sections.background.source.title',
|
||||
)}
|
||||
goBack={() => this.setState({ sourceSection: false })}
|
||||
report={false}
|
||||
/>
|
||||
) : (
|
||||
<Header
|
||||
title={variables.getMessage(`${QUOTE_SECTION}.title`)}
|
||||
setting="quote"
|
||||
category="quote"
|
||||
element=".quotediv"
|
||||
zoomSetting="zoomQuote"
|
||||
visibilityToggle={true}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{this.state.sourceSection && (
|
||||
<Row final={true}>
|
||||
<Content
|
||||
title={variables.getMessage('modals.main.settings.sections.background.source.title')}
|
||||
subtitle={variables.getMessage(`${QUOTE_SECTION}.source_subtitle`)}
|
||||
/>
|
||||
<Action>
|
||||
<SourceDropdown />
|
||||
</Action>
|
||||
</Row>
|
||||
)}
|
||||
{!this.state.sourceSection && (
|
||||
<PreferencesWrapper
|
||||
setting="quote"
|
||||
zoomSetting="zoomQuote"
|
||||
category="quote"
|
||||
visibilityToggle={true}
|
||||
>
|
||||
<Section
|
||||
icon={<MdSource />}
|
||||
title={variables.getMessage('modals.main.settings.sections.background.source.title')}
|
||||
subtitle={variables.getMessage(`${QUOTE_SECTION}.source_subtitle`)}
|
||||
onClick={() => this.setState({ sourceSection: true })}
|
||||
>
|
||||
<SourceDropdown />
|
||||
</Section>
|
||||
<ButtonOptions />
|
||||
<AdditionalOptions />
|
||||
</PreferencesWrapper>
|
||||
)}
|
||||
{customSettings}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
// api
|
||||
customSettings = <></>;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{subSection === 'source' ? (
|
||||
<Header
|
||||
title={variables.getMessage(`${QUOTE_SECTION}.title`)}
|
||||
secondaryTitle={variables.getMessage('modals.main.settings.sections.background.source.title')}
|
||||
goBack={() => setSourceSection(false)}
|
||||
report={false}
|
||||
/>
|
||||
) : (
|
||||
<Header
|
||||
title={variables.getMessage(`${QUOTE_SECTION}.title`)}
|
||||
setting="quote"
|
||||
category="quote"
|
||||
element=".quotediv"
|
||||
zoomSetting="zoomQuote"
|
||||
visibilityToggle={true}
|
||||
/>
|
||||
)}
|
||||
{subSection === 'source' && (
|
||||
<Row final={true}>
|
||||
<Content
|
||||
title={variables.getMessage('modals.main.settings.sections.background.source.title')}
|
||||
subtitle={variables.getMessage(`${QUOTE_SECTION}.source_subtitle`)}
|
||||
/>
|
||||
<Action>
|
||||
<SourceDropdown />
|
||||
</Action>
|
||||
</Row>
|
||||
)}
|
||||
{subSection !== 'source' && (
|
||||
<PreferencesWrapper
|
||||
setting="quote"
|
||||
zoomSetting="zoomQuote"
|
||||
category="quote"
|
||||
visibilityToggle={true}
|
||||
>
|
||||
<Section
|
||||
id="source"
|
||||
icon={<MdSource />}
|
||||
title={variables.getMessage('modals.main.settings.sections.background.source.title')}
|
||||
subtitle={variables.getMessage(`${QUOTE_SECTION}.source_subtitle`)}
|
||||
onClick={() => setSourceSection(true)}
|
||||
>
|
||||
<SourceDropdown />
|
||||
</Section>
|
||||
<ButtonOptions />
|
||||
<AdditionalOptions />
|
||||
</PreferencesWrapper>
|
||||
)}
|
||||
{customSettings}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export { QuoteOptions as default, QuoteOptions };
|
||||
|
||||
@@ -37,7 +37,8 @@ function WelcomeModal() {
|
||||
useEffect(() => {
|
||||
setFirstRender(false);
|
||||
const welcomeTab = localStorage.getItem('welcomeTab');
|
||||
const randomQuote = offline_quotes[Math.floor(Math.random() * offline_quotes.length)].quote;
|
||||
const usableQuotes = offline_quotes.filter((q) => q.quote.length <= 100);
|
||||
const randomQuote = usableQuotes[Math.floor(Math.random() * usableQuotes.length)].quote;
|
||||
setQuote(randomQuote);
|
||||
if (welcomeTab) {
|
||||
const tab = Number(welcomeTab);
|
||||
@@ -116,29 +117,31 @@ function WelcomeModal() {
|
||||
|
||||
const Navigation = () => {
|
||||
return (
|
||||
<div className="welcomeButtons">
|
||||
{currentTab !== 0 ? (
|
||||
<div className="welcomeButtons z-50 backdrop-blur-sm absolute bottom-0 right-0 w-1/2">
|
||||
<div className="flex justify-end gap-5 p-6">
|
||||
{currentTab !== 0 ? (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => prevTab()}
|
||||
icon={<MdArrowBackIosNew />}
|
||||
label={variables.getMessage('modals.welcome.buttons.previous')}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => modalSkip()}
|
||||
icon={<MdOutlinePreview />}
|
||||
label={variables.getMessage('modals.welcome.buttons.preview')}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => prevTab()}
|
||||
icon={<MdArrowBackIosNew />}
|
||||
label={variables.getMessage('modals.welcome.buttons.previous')}
|
||||
onClick={() => nextTab()}
|
||||
icon={<MdArrowForwardIos />}
|
||||
label={buttonText}
|
||||
iconPlacement={'right'}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => modalSkip()}
|
||||
icon={<MdOutlinePreview />}
|
||||
label={variables.getMessage('modals.welcome.buttons.preview')}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => nextTab()}
|
||||
icon={<MdArrowForwardIos />}
|
||||
label={buttonText}
|
||||
iconPlacement={'right'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -196,17 +199,17 @@ function WelcomeModal() {
|
||||
transition={{ type: 'spring', duration: 3 }}
|
||||
>
|
||||
<motion.span
|
||||
className="text-6xl font-bold max-w-[50%] leading-normal"
|
||||
className="text-6xl font-bold mx-auto max-w-[75%] 2xl:max-w-[50%] leading-tight lg:leading-snug"
|
||||
initial={{ opacity: 0, y: 100 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: 0 }}
|
||||
transition={{ type: 'spring', duration: 2 }}
|
||||
key={currentTab}
|
||||
>
|
||||
<mark className="bg-[#ff6b6b]">"{quote}"</mark>
|
||||
<mark className="bg-transparent text-white shadow-black text-shadow-lg">"{quote}"</mark>
|
||||
</motion.span>
|
||||
</motion.div>
|
||||
<div className="welcomeCredit">
|
||||
<div className="absolute bottom-4 left-4 text-white">
|
||||
{variables.getMessage('widgets.background.credit')}{' '}
|
||||
<motion.span
|
||||
initial={{ opacity: 0, x: -100 }}
|
||||
@@ -231,7 +234,7 @@ function WelcomeModal() {
|
||||
transition={{ type: 'tween', duration: 0.8 }}
|
||||
style={{ position: 'absolute', height: '100%', width: '50%' }}
|
||||
>
|
||||
<div style={{ height: '100%', overflow: 'auto' }}>{tabs[currentTab]}</div>
|
||||
<div className="h-full overflow-auto p-8">{tabs[currentTab]}</div>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
<Navigation />
|
||||
|
||||
@@ -12,12 +12,6 @@
|
||||
background-color: t($modal-background);
|
||||
}
|
||||
|
||||
.welcomeCredit {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
left: 1rem;
|
||||
}
|
||||
|
||||
.MuiFormControlLabel-root {
|
||||
margin-right: 0;
|
||||
}
|
||||
@@ -331,19 +325,6 @@ a.privacy {
|
||||
}
|
||||
|
||||
.welcomeButtons {
|
||||
z-index: 999;
|
||||
backdrop-filter: blur(2px);
|
||||
backdrop-filter: blur(2px);
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
padding: 25px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 20px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
|
||||
button {
|
||||
@include modal-button(standard);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user