mirror of https://github.com/mue/mue.git
refactor(modules): second part of moving files around, changing layout etc
This commit is contained in:
parent
618b5fe466
commit
294b3830bf
|
@ -6,9 +6,9 @@ import Background from 'features/widgets/background/Background';
|
|||
import Widgets from 'features/widgets/Widgets';
|
||||
import Modals from 'features/modals/Modals';
|
||||
|
||||
import { loadSettings, moveSettings } from 'utils/helpers/settings';
|
||||
import { loadSettings, moveSettings } from 'utils/settings';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
export default class App extends PureComponent {
|
||||
componentDidMount() {
|
||||
|
|
|
@ -2,7 +2,7 @@ import variables from 'config/variables';
|
|||
import { PureComponent } from 'react';
|
||||
import { Checkbox as CheckboxUI, FormControlLabel } from '@mui/material';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
class Checkbox extends PureComponent {
|
||||
constructor(props) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import variables from 'config/variables';
|
|||
import { PureComponent, createRef } from 'react';
|
||||
import { InputLabel, MenuItem, FormControl, Select } from '@mui/material';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
class Dropdown extends PureComponent {
|
||||
constructor(props) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import variables from 'config/variables';
|
|||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { compressAccurately, filetoDataURL } from 'image-conversion';
|
||||
import { videoCheck } from 'utils/helpers/background/widget';
|
||||
import { videoCheck } from 'utils/background';
|
||||
|
||||
class FileUpload extends PureComponent {
|
||||
componentDidMount() {
|
||||
|
|
|
@ -8,8 +8,8 @@ import {
|
|||
FormLabel,
|
||||
} from '@mui/material';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import { translations } from 'utils/translations';
|
||||
import EventBus from 'utils/eventbus';
|
||||
import { translations } from 'lib/translations';
|
||||
|
||||
class Radio extends PureComponent {
|
||||
constructor(props) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { toast } from 'react-toastify';
|
|||
import { Slider } from '@mui/material';
|
||||
import { MdRefresh } from 'react-icons/md';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
class SliderComponent extends PureComponent {
|
||||
constructor(props) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import variables from 'config/variables';
|
|||
import { PureComponent } from 'react';
|
||||
import { Switch as SwitchUI, FormControlLabel } from '@mui/material';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
class Switch extends PureComponent {
|
||||
constructor(props) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { toast } from 'react-toastify';
|
|||
import { TextField } from '@mui/material';
|
||||
import { MdRefresh } from 'react-icons/md';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
class Text extends PureComponent {
|
||||
constructor(props) {
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
MdOutlineVisibility,
|
||||
MdOutlineKeyboardArrowRight,
|
||||
} from 'react-icons/md';
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
import { Button } from 'components/Elements';
|
||||
|
||||
export const CustomActions = ({ children }) => {
|
||||
|
|
|
@ -3,8 +3,8 @@ import { Row, Content, Action } from '../Item/SettingsItem';
|
|||
import variables from 'config/variables';
|
||||
import Slider from '../../../Form/Settings/Slider/Slider';
|
||||
|
||||
import { values } from 'utils/helpers/settings/modals';
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import values from 'utils/data/slider_values.json';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
const PreferencesWrapper = ({ children, ...props }) => {
|
||||
const [shown, setShown] = useState(localStorage.getItem(props.setting) === 'true');
|
||||
|
@ -32,7 +32,7 @@ const PreferencesWrapper = ({ children, ...props }) => {
|
|||
max="400"
|
||||
default="100"
|
||||
display="%"
|
||||
marks={values('zoom')}
|
||||
marks={values.zoom}
|
||||
category={props.zoomCategory || props.category}
|
||||
/>
|
||||
</Action>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as constants from 'config/constants';
|
||||
import Stats from 'utils/helpers/stats';
|
||||
import Stats from 'utils/stats';
|
||||
|
||||
const variables = {
|
||||
language: {},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { PureComponent } from 'react';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import './autocomplete.scss';
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import Main from './main/Main';
|
|||
import Navbar from '../widgets/navbar/Navbar';
|
||||
import Preview from '../helpers/preview/Preview';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import Welcome from './welcome/Welcome';
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import Modal from 'react-modal';
|
|||
import { Header } from 'components/Layout/Settings';
|
||||
import { Button } from 'components/Elements';
|
||||
|
||||
import { install, uninstall } from 'utils/helpers/marketplace';
|
||||
import { install, uninstall } from 'utils/marketplace';
|
||||
|
||||
import { ShareModal } from 'components/Elements';
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import Dropdown from '../../../../../components/Form/Settings/Dropdown/Dropdown'
|
|||
import { Header, CustomActions } from 'components/Layout/Settings';
|
||||
import { Button } from 'components/Elements';
|
||||
|
||||
import { install, uninstall, urlParser } from 'utils/helpers/marketplace';
|
||||
import { install, uninstall, urlParser } from 'utils/marketplace';
|
||||
|
||||
export default class Added extends PureComponent {
|
||||
constructor() {
|
||||
|
|
|
@ -16,7 +16,7 @@ import Dropdown from '../../../../../components/Form/Settings/Dropdown/Dropdown'
|
|||
import { Header } from 'components/Layout/Settings';
|
||||
import { Button } from 'components/Elements';
|
||||
|
||||
import { install, urlParser, uninstall } from 'utils/helpers/marketplace';
|
||||
import { install, urlParser, uninstall } from 'utils/marketplace';
|
||||
|
||||
class Marketplace extends PureComponent {
|
||||
constructor() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { memo } from 'react';
|
||||
import variables from 'config/variables';
|
||||
import { MdClose, MdRestartAlt } from 'react-icons/md';
|
||||
import { setDefaultSettings } from 'utils/helpers/settings';
|
||||
import { setDefaultSettings } from 'utils/settings';
|
||||
import { Tooltip } from 'components/Elements';
|
||||
function ResetModal({ modalClose }) {
|
||||
const reset = () => {
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
MdDataUsage,
|
||||
} from 'react-icons/md';
|
||||
|
||||
import { exportSettings, importSettings } from 'utils/helpers/settings/modals';
|
||||
import { exportSettings, importSettings } from 'utils/settings';
|
||||
|
||||
import { FileUpload, Text, Switch, Dropdown } from 'components/Form/Settings';
|
||||
import ResetModal from '../ResetModal';
|
||||
|
|
|
@ -7,7 +7,7 @@ import { Header, Section, Row, Content, Action } from 'components/Layout/Setting
|
|||
|
||||
import { MdAccessibility } from 'react-icons/md';
|
||||
|
||||
import { values } from 'utils/helpers/settings/modals';
|
||||
import values from 'utils/data/slider_values.json';
|
||||
|
||||
function AppearanceSettings() {
|
||||
const [accessibility, setAccessibility] = useState(false);
|
||||
|
@ -226,7 +226,7 @@ function AppearanceSettings() {
|
|||
step="100"
|
||||
min="500"
|
||||
max="5000"
|
||||
marks={values('toast')}
|
||||
marks={values.toast}
|
||||
display={
|
||||
' ' +
|
||||
variables.getMessage(
|
||||
|
|
|
@ -4,8 +4,8 @@ import Checkbox from '../../../../../components/Form/Settings/Checkbox/Checkbox'
|
|||
import Slider from '../../../../../components/Form/Settings/Slider/Slider';
|
||||
import { TextField } from '@mui/material';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import { values } from 'utils/helpers/settings/modals';
|
||||
import EventBus from 'utils/eventbus';
|
||||
import values from 'utils/data/slider_values.json';
|
||||
|
||||
import { Row, Content, Action } from '../../../../../components/Layout/Settings/Item/SettingsItem';
|
||||
|
||||
|
@ -34,7 +34,7 @@ function ExperimentalSettings() {
|
|||
max="5000"
|
||||
default="0"
|
||||
step="100"
|
||||
marks={values('experimental')}
|
||||
marks={values.experimental}
|
||||
element=".other"
|
||||
/>
|
||||
<p style={{ textAlign: 'left' }}>Send Event</p>
|
||||
|
|
|
@ -5,7 +5,7 @@ import { toast } from 'react-toastify';
|
|||
import { TextareaAutosize } from '@mui/material';
|
||||
|
||||
import { Header, Row, Content, Action, PreferencesWrapper } from 'components/Layout/Settings';
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
export default class Message extends PureComponent {
|
||||
constructor() {
|
||||
|
|
|
@ -13,7 +13,7 @@ import { Button } from 'components/Elements';
|
|||
|
||||
import { Row, Content, Action } from '../../../../../components/Layout/Settings/Item/SettingsItem';
|
||||
import { Header } from 'components/Layout/Settings';
|
||||
import { getTitleFromUrl, isValidUrl } from 'utils/helpers/settings/modals';
|
||||
import { getTitleFromUrl, isValidUrl } from 'utils/links';
|
||||
import QuickLink from './quicklinks/QuickLink';
|
||||
|
||||
function Navbar() {
|
||||
|
|
|
@ -11,7 +11,7 @@ import QuickLinks from './overview_skeletons/QuickLinks';
|
|||
import Date from './overview_skeletons/Date';
|
||||
import Message from './overview_skeletons/Message';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
const widget_name = {
|
||||
greeting: variables.getMessage('modals.main.settings.sections.greeting.title'),
|
||||
|
|
|
@ -8,9 +8,9 @@ import Modal from 'react-modal';
|
|||
|
||||
import AddModal from './quicklinks/AddModal';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
import QuickLink from './quicklinks/QuickLink';
|
||||
import { getTitleFromUrl, isValidUrl } from 'utils/helpers/settings/modals';
|
||||
import { getTitleFromUrl, isValidUrl } from 'utils/links';
|
||||
|
||||
export default class QuickLinks extends PureComponent {
|
||||
constructor() {
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
import { Checkbox, Dropdown } from 'components/Form/Settings';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
export default class QuoteSettings extends PureComponent {
|
||||
constructor() {
|
||||
|
|
|
@ -6,7 +6,7 @@ import { MenuItem, TextField } from '@mui/material';
|
|||
import { Header, Row, Content, Action, PreferencesWrapper } from 'components/Layout/Settings';
|
||||
import { Dropdown, Checkbox } from 'components/Form/Settings';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import searchEngines from 'features/widgets/search/search_engines.json';
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import { toast } from 'react-toastify';
|
|||
import { Button } from 'components/Elements';
|
||||
import { Header, CustomActions } from 'components/Layout/Settings';
|
||||
|
||||
import { saveFile } from 'utils/helpers/settings/modals';
|
||||
import { saveFile } from 'utils/saveFile';
|
||||
|
||||
import achievementsData from 'utils/data/achievements.json';
|
||||
import translations from 'i18n/locales/achievements/index';
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { MenuItem } from '@mui/material';
|
||||
import { MdSource, MdOutlineKeyboardArrowRight, MdOutlineAutoAwesome } from 'react-icons/md';
|
||||
|
||||
import { Header } from 'components/Layout/Settings';
|
||||
|
@ -19,7 +18,7 @@ import Text from '../../../../../../components/Form/Settings/Text/Text';
|
|||
import ColourSettings from './Colour';
|
||||
import CustomSettings from './Custom';
|
||||
|
||||
import { values } from 'utils/helpers/settings/modals';
|
||||
import values from 'utils/data/slider_values.json';
|
||||
|
||||
export default class BackgroundSettings extends PureComponent {
|
||||
constructor() {
|
||||
|
@ -542,7 +541,7 @@ export default class BackgroundSettings extends PureComponent {
|
|||
max="100"
|
||||
default="0"
|
||||
display="%"
|
||||
marks={values('background')}
|
||||
marks={values.background}
|
||||
category="background"
|
||||
element="#backgroundImage"
|
||||
/>
|
||||
|
@ -555,7 +554,7 @@ export default class BackgroundSettings extends PureComponent {
|
|||
max="100"
|
||||
default="90"
|
||||
display="%"
|
||||
marks={values('background')}
|
||||
marks={values.background}
|
||||
category="background"
|
||||
element="#backgroundImage"
|
||||
/>
|
||||
|
@ -616,7 +615,7 @@ export default class BackgroundSettings extends PureComponent {
|
|||
max="100"
|
||||
default="0"
|
||||
display="%"
|
||||
marks={values('background')}
|
||||
marks={values.background}
|
||||
category="background"
|
||||
element="#backgroundImage"
|
||||
/>
|
||||
|
|
|
@ -8,8 +8,7 @@ import {
|
|||
Action,
|
||||
} from '../../../../../../components/Layout/Settings/Item/SettingsItem';
|
||||
|
||||
import hexToRgb from 'utils/helpers/background/hexToRgb';
|
||||
import rgbToHex from 'utils/helpers/background/rgbToHex';
|
||||
import { hexToRgb, rgbToHex } from 'utils/background/gradient';
|
||||
|
||||
//import '@muetab/react-color-gradient-picker/dist/index.css';
|
||||
import '../../../scss/settings/react-color-picker-gradient-picker-custom-styles.scss';
|
||||
|
|
|
@ -9,9 +9,9 @@ import {
|
|||
MdOutlineFileUpload,
|
||||
MdFolder,
|
||||
} from 'react-icons/md';
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
import { compressAccurately, filetoDataURL } from 'image-conversion';
|
||||
import { videoCheck } from 'utils/helpers/background/widget';
|
||||
import { videoCheck } from 'utils/background';
|
||||
|
||||
import Checkbox from '../../../../../../components/Form/Settings/Checkbox/Checkbox';
|
||||
import FileUpload from '../../../../../../components/Form/Settings/FileUpload/FileUpload';
|
||||
|
|
|
@ -2,7 +2,7 @@ import variables from 'config/variables';
|
|||
import { useState } from 'react';
|
||||
import { FileUpload } from 'components/Form/Settings';
|
||||
import { MdCloudUpload } from 'react-icons/md';
|
||||
import { importSettings as importSettingsFunction } from 'utils/helpers/settings/modals';
|
||||
import { importSettings as importSettingsFunction } from 'utils/settings';
|
||||
import { Header } from '../components/Layout';
|
||||
import default_settings from 'utils/data/default_settings.json';
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import variables from 'config/variables';
|
||||
import { useState } from 'react';
|
||||
import { MdAutoAwesome, MdLightMode, MdDarkMode } from 'react-icons/md';
|
||||
import { loadSettings } from 'utils/helpers/settings';
|
||||
import { loadSettings } from 'utils/settings';
|
||||
import { Header } from '../components/Layout';
|
||||
|
||||
function ThemeSelection() {
|
||||
|
|
|
@ -2,7 +2,7 @@ import variables from 'config/variables';
|
|||
import { PureComponent } from 'react';
|
||||
import { MdArrowBackIosNew, MdArrowForwardIos, MdOutlinePreview } from 'react-icons/md';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import { ProgressBar } from './components/Elements';
|
||||
import { Button } from 'components/Elements';
|
||||
|
|
|
@ -15,8 +15,7 @@ import {
|
|||
import { FaDiscord, FaGithub } from 'react-icons/fa';
|
||||
import { Radio, Checkbox, FileUpload } from 'components/Form/Settings';
|
||||
|
||||
import { loadSettings } from 'utils/helpers/settings';
|
||||
import { importSettings } from 'utils/helpers/settings/modals';
|
||||
import { loadSettings, importSettings } from 'utils/settings';
|
||||
|
||||
import default_settings from 'utils/data/default_settings.json';
|
||||
import languages from '@/i18n/languages.json';
|
||||
|
|
|
@ -9,7 +9,7 @@ import Date from './time/Date';
|
|||
import Message from './message/Message';
|
||||
import { WidgetsLayout } from 'components/Layout';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
// weather is lazy loaded due to the size of the weather icons module
|
||||
// since we're using react-icons this might not be accurate,
|
||||
|
|
|
@ -5,17 +5,18 @@ import { PureComponent } from 'react';
|
|||
|
||||
import PhotoInformation from './PhotoInformation';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
import {
|
||||
videoCheck,
|
||||
offlineBackground,
|
||||
getGradient,
|
||||
randomColourStyleBuilder,
|
||||
} from 'utils/helpers/background/widget';
|
||||
supportsAVIF
|
||||
} from 'utils/background';
|
||||
|
||||
import { getGradient } from 'utils/background/gradient';
|
||||
|
||||
import './scss/index.scss';
|
||||
import { decodeBlurHash } from 'fast-blurhash';
|
||||
import { supportsAVIF } from 'utils/helpers/background/avif';
|
||||
|
||||
export default class Background extends PureComponent {
|
||||
constructor() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import variables from 'config/variables';
|
||||
import { memo } from 'react';
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
import { Tooltip } from 'components/Elements';
|
||||
|
||||
import { MdClose, MdDone } from 'react-icons/md';
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import variables from 'config/variables';
|
||||
import { PureComponent, createRef } from 'react';
|
||||
|
||||
import { nth, convertTimezone } from 'utils/helpers/date';
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import { nth, convertTimezone } from 'utils/date';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import './greeting.scss';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useState, useEffect, useRef } from 'react';
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
import './message.scss';
|
||||
|
||||
const Message = () => {
|
||||
|
|
|
@ -6,7 +6,7 @@ import { MdPlaylistRemove, MdOutlineApps } from 'react-icons/md';
|
|||
import { Tooltip } from 'components/Elements';
|
||||
|
||||
import { shift, useFloating } from '@floating-ui/react-dom';
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
class Apps extends PureComponent {
|
||||
constructor() {
|
||||
|
|
|
@ -8,7 +8,7 @@ import Todo from './Todo';
|
|||
import Apps from './Apps';
|
||||
import Maximise from '../background/Maximise';
|
||||
import { Tooltip } from 'components/Elements';
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import './scss/index.scss';
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ import TextareaAutosize from '@mui/material/TextareaAutosize';
|
|||
import { toast } from 'react-toastify';
|
||||
import { Tooltip } from 'components/Elements';
|
||||
|
||||
import { saveFile } from 'utils/helpers/settings/modals';
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import { saveFile } from 'utils/saveFile';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
class Notes extends PureComponent {
|
||||
constructor() {
|
||||
|
|
|
@ -15,7 +15,7 @@ import { Tooltip } from 'components/Elements';
|
|||
import Checkbox from '@mui/material/Checkbox';
|
||||
import { shift, useFloating } from '@floating-ui/react-dom';
|
||||
import { sortableContainer, sortableElement, sortableHandle } from '@muetab/react-sortable-hoc';
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
const SortableItem = sortableElement(({ value }) => <div>{value}</div>);
|
||||
const SortableContainer = sortableContainer(({ children }) => <div>{children}</div>);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { PureComponent, createRef } from 'react';
|
||||
import { Tooltip } from 'components/Elements';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import './quicklinks.scss';
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ import { ShareModal } from 'components/Elements';
|
|||
|
||||
import offline_quotes from './offline_quotes.json';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import './quote.scss';
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { FaYandex } from 'react-icons/fa';
|
|||
import { Tooltip } from 'components/Elements';
|
||||
import AutocompleteInput from 'features/helpers/autocomplete/Autocomplete';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import './search.scss';
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { PureComponent, Suspense, lazy } from 'react';
|
||||
|
||||
import { convertTimezone } from 'utils/helpers/date';
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import { convertTimezone } from 'utils/date';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import './clock.scss';
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import variables from 'config/variables';
|
||||
import { PureComponent, createRef } from 'react';
|
||||
|
||||
import { nth, convertTimezone } from 'utils/helpers/date';
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import { nth, convertTimezone } from 'utils/date';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import './date.scss';
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { PureComponent } from 'react';
|
|||
import WeatherIcon from './WeatherIcon';
|
||||
import Expanded from './Expanded';
|
||||
|
||||
import EventBus from 'utils/helpers/eventbus';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import './weather.scss';
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import './scss/index.scss';
|
|||
// the toast css is based on default so we need to import it
|
||||
import 'react-toastify/dist/ReactToastify.min.css';
|
||||
|
||||
import { initTranslations } from 'utils/translations';
|
||||
import { initTranslations } from 'lib/translations';
|
||||
|
||||
const languagecode = localStorage.getItem('language') || 'en_GB';
|
||||
variables.language = initTranslations(languagecode);
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import offlineImages from 'utils/data/offline_images.json';
|
||||
|
||||
/**
|
||||
* It gets a random photographer from the offlineImages.json file, then gets a random image from that
|
||||
* photographer, and returns an object with the image's URL, type, and photoInfo.
|
||||
* </code>
|
||||
* @param type - 'background' or 'thumbnail'
|
||||
* @returns An object with the following properties:
|
||||
* url: A string that is the path to the image.
|
||||
* type: A string that is the type of image.
|
||||
* photoInfo: An object with the following properties:
|
||||
* offline: A boolean that is true.
|
||||
* credit: A string that is the name of the photographer.
|
||||
*/
|
||||
export function offlineBackground(type) {
|
||||
const photographers = Object.keys(offlineImages);
|
||||
const photographer = photographers[Math.floor(Math.random() * photographers.length)];
|
||||
|
||||
const randomImage =
|
||||
offlineImages[photographer].photo[
|
||||
Math.floor(Math.random() * offlineImages[photographer].photo.length)
|
||||
];
|
||||
|
||||
const object = {
|
||||
url: `./offline-images/${randomImage}.webp`,
|
||||
type,
|
||||
photoInfo: {
|
||||
offline: true,
|
||||
credit: photographer,
|
||||
},
|
||||
};
|
||||
|
||||
localStorage.setItem('currentBackground', JSON.stringify(object));
|
||||
return object;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* It takes a gradient object and returns a style object
|
||||
* @returns An object with two properties: type and style.
|
||||
*/
|
||||
export function gradientStyleBuilder({ type, angle, gradient }) {
|
||||
// Note: Append the gradient for additional browser support.
|
||||
const steps = gradient?.map((v) => `${v.colour} ${v.stop}%`);
|
||||
const grad = `background: ${type}-gradient(${type === 'linear' ? `${angle}deg,` : ''}${steps})`;
|
||||
|
||||
return {
|
||||
type: 'colour',
|
||||
style: `background:${gradient[0]?.colour};${grad}`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* It gets the gradient settings from localStorage, parses it, and returns the gradient style.
|
||||
* @returns A string.
|
||||
*/
|
||||
export function getGradient() {
|
||||
const customBackgroundColour = localStorage.getItem('customBackgroundColour') || {
|
||||
angle: '180',
|
||||
gradient: [{ colour: '#ffb032', stop: 0 }],
|
||||
type: 'linear',
|
||||
};
|
||||
|
||||
let gradientSettings = '';
|
||||
try {
|
||||
gradientSettings = JSON.parse(customBackgroundColour);
|
||||
} catch (e) {
|
||||
const hexColorRegex = /#[0-9a-fA-F]{6}/s;
|
||||
if (hexColorRegex.exec(customBackgroundColour)) {
|
||||
// Colour used to be simply a hex colour or a NULL value before it was a JSON object. This automatically upgrades the hex colour value to the new standard. (NULL would not trigger an exception)
|
||||
gradientSettings = {
|
||||
type: 'linear',
|
||||
angle: '180',
|
||||
gradient: [{ colour: customBackgroundColour, stop: 0 }],
|
||||
};
|
||||
localStorage.setItem('customBackgroundColour', JSON.stringify(gradientSettings));
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof gradientSettings === 'object' && gradientSettings !== null) {
|
||||
return gradientStyleBuilder(gradientSettings);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import rgbToHsv from './rgbToHsv';
|
||||
import setRgba from './setRgba';
|
||||
import { setRGBA } from './setRgba';
|
||||
|
||||
const hexRegexp = /(^#{0,1}[0-9A-F]{6}$)|(^#{0,1}[0-9A-F]{3}$)|(^#{0,1}[0-9A-F]{8}$)/i;
|
||||
const regexp = /([0-9A-F])([0-9A-F])([0-9A-F])/i;
|
||||
|
@ -26,7 +26,7 @@ export default function hexToRgb(value) {
|
|||
const blue = parseInt(value.substr(4, 2), 16);
|
||||
const alpha = parseInt(value.substr(6, 2), 16) / 255;
|
||||
|
||||
const color = setRgba(red, green, blue, alpha);
|
||||
const color = setRGBA(red, green, blue, alpha);
|
||||
const hsv = rgbToHsv({ ...color });
|
||||
|
||||
return {
|
|
@ -0,0 +1,17 @@
|
|||
import { getGradient, gradientStyleBuilder } from './getGradient';
|
||||
|
||||
import hexToRgb from './hexToRgb';
|
||||
import rgbToHex from './rgbToHex';
|
||||
import rgbToHsv from './rgbToHsv';
|
||||
|
||||
import { setRGBA, isValidRGBValue } from './setRgba';
|
||||
|
||||
export {
|
||||
getGradient,
|
||||
gradientStyleBuilder,
|
||||
hexToRgb,
|
||||
rgbToHex,
|
||||
rgbToHsv,
|
||||
setRGBA,
|
||||
isValidRGBValue,
|
||||
};
|
|
@ -3,7 +3,7 @@
|
|||
* @param value - The value to check.
|
||||
* @returns A function that takes a value and returns a boolean.
|
||||
*/
|
||||
const isValidRGBValue = (value) => {
|
||||
export function isValidRGBValue(value) {
|
||||
return typeof value === 'number' && Number.isNaN(value) === false && value >= 0 && value <= 255;
|
||||
};
|
||||
|
||||
|
@ -18,7 +18,7 @@ const isValidRGBValue = (value) => {
|
|||
* @param alpha - The alpha value of the color.
|
||||
* @returns An object with the properties red, green, blue, and alpha.
|
||||
*/
|
||||
export default function setRGBA(red, green, blue, alpha) {
|
||||
export function setRGBA(red, green, blue, alpha) {
|
||||
if (isValidRGBValue(red) && isValidRGBValue(green) && isValidRGBValue(blue)) {
|
||||
const color = {
|
||||
red: red | 0,
|
|
@ -0,0 +1,6 @@
|
|||
import { supportsAVIF } from './avif';
|
||||
import { offlineBackground } from './getOfflineImage';
|
||||
import { randomColourStyleBuilder } from './randomColour';
|
||||
import videoCheck from './videoCheck';
|
||||
|
||||
export { supportsAVIF, offlineBackground, randomColourStyleBuilder, videoCheck };
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* It returns a random colour or random gradient as a style object
|
||||
* @param type - The type of the style. This is used to determine which style builder to use.
|
||||
* @returns An object with two properties: type and style.
|
||||
*/
|
||||
export function randomColourStyleBuilder(type) {
|
||||
// randomColour based on https://stackoverflow.com/a/5092872
|
||||
const randomColour = () =>
|
||||
'#000000'.replace(/0/g, () => {
|
||||
return (~~(Math.random() * 16)).toString(16);
|
||||
});
|
||||
let style = `background:${randomColour()};`;
|
||||
|
||||
if (type === 'random_gradient') {
|
||||
const directions = [
|
||||
'to right',
|
||||
'to left',
|
||||
'to bottom',
|
||||
'to top',
|
||||
'to bottom right',
|
||||
'to bottom left',
|
||||
'to top right',
|
||||
'to top left',
|
||||
];
|
||||
style = `background:linear-gradient(${
|
||||
directions[Math.floor(Math.random() * directions.length)]
|
||||
}, ${randomColour()}, ${randomColour()});`;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'colour',
|
||||
style,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* If the URL starts with `data:video/` or ends with `.mp4`, `.webm`, or `.ogg`, then it's a video.
|
||||
* @param url - The URL of the file to be checked.
|
||||
* @returns A function that takes a url and returns a boolean.
|
||||
*/
|
||||
export default function videoCheck(url) {
|
||||
return (
|
||||
url.startsWith('data:video/') ||
|
||||
url.endsWith('.mp4') ||
|
||||
url.endsWith('.webm') ||
|
||||
url.endsWith('.ogg')
|
||||
);
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"zoom": [
|
||||
{ "value": 10, "label": "0.1x" },
|
||||
{ "value": 100, "label": "1x" },
|
||||
{ "value": 200, "label": "2x" },
|
||||
{ "value": 400, "label": "4x" }
|
||||
],
|
||||
"toast": [
|
||||
{ "value": 500, "label": "0.5s" },
|
||||
{ "value": 1000, "label": "1s" },
|
||||
{ "value": 1500, "label": "1.5s" },
|
||||
{ "value": 2000, "label": "2s" },
|
||||
{ "value": 2500, "label": "2.5s" },
|
||||
{ "value": 3000, "label": "3s" },
|
||||
{ "value": 4000, "label": "4s" },
|
||||
{ "value": 5000, "label": "5s" }
|
||||
],
|
||||
"background": [
|
||||
{ "value": 0, "label": "0%" },
|
||||
{ "value": 25, "label": "25%" },
|
||||
{ "value": 50, "label": "50%" },
|
||||
{ "value": 75, "label": "75%" },
|
||||
{ "value": 100, "label": "100%" }
|
||||
],
|
||||
"experimental": [
|
||||
{ "value": 0, "label": "0s" },
|
||||
{ "value": 500, "label": "0.5s" },
|
||||
{ "value": 1000, "label": "1s" },
|
||||
{ "value": 1500, "label": "1.5s" },
|
||||
{ "value": 2000, "label": "2s" },
|
||||
{ "value": 2500, "label": "2.5s" },
|
||||
{ "value": 3000, "label": "3s" },
|
||||
{ "value": 4000, "label": "4s" },
|
||||
{ "value": 5000, "label": "5s" }
|
||||
]
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
// todo: maybe move stuff
|
||||
/**
|
||||
* If the number is between 3 and 20, return the number with the suffix "th". Otherwise, return the
|
||||
* number with the suffix "st", "nd", "rd", or "th" depending on the last digit of the number
|
|
@ -1,132 +0,0 @@
|
|||
// since there is so much code in the component, we have moved it to a separate file
|
||||
import offlineImages from 'utils/data/offline_images.json';
|
||||
|
||||
/**
|
||||
* If the URL starts with `data:video/` or ends with `.mp4`, `.webm`, or `.ogg`, then it's a video.
|
||||
* @param url - The URL of the file to be checked.
|
||||
* @returns A function that takes a url and returns a boolean.
|
||||
*/
|
||||
export function videoCheck(url) {
|
||||
return (
|
||||
url.startsWith('data:video/') ||
|
||||
url.endsWith('.mp4') ||
|
||||
url.endsWith('.webm') ||
|
||||
url.endsWith('.ogg')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* It gets a random photographer from the offlineImages.json file, then gets a random image from that
|
||||
* photographer, and returns an object with the image's URL, type, and photoInfo.
|
||||
* </code>
|
||||
* @param type - 'background' or 'thumbnail'
|
||||
* @returns An object with the following properties:
|
||||
* url: A string that is the path to the image.
|
||||
* type: A string that is the type of image.
|
||||
* photoInfo: An object with the following properties:
|
||||
* offline: A boolean that is true.
|
||||
* credit: A string that is the name of the photographer.
|
||||
*/
|
||||
export function offlineBackground(type) {
|
||||
const photographers = Object.keys(offlineImages);
|
||||
const photographer = photographers[Math.floor(Math.random() * photographers.length)];
|
||||
|
||||
const randomImage =
|
||||
offlineImages[photographer].photo[
|
||||
Math.floor(Math.random() * offlineImages[photographer].photo.length)
|
||||
];
|
||||
|
||||
const object = {
|
||||
url: `./offline-images/${randomImage}.webp`,
|
||||
type,
|
||||
photoInfo: {
|
||||
offline: true,
|
||||
credit: photographer,
|
||||
},
|
||||
};
|
||||
|
||||
localStorage.setItem('currentBackground', JSON.stringify(object));
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes a gradient object and returns a style object
|
||||
* @returns An object with two properties: type and style.
|
||||
*/
|
||||
function gradientStyleBuilder({ type, angle, gradient }) {
|
||||
// Note: Append the gradient for additional browser support.
|
||||
const steps = gradient?.map((v) => `${v.colour} ${v.stop}%`);
|
||||
const grad = `background: ${type}-gradient(${type === 'linear' ? `${angle}deg,` : ''}${steps})`;
|
||||
|
||||
return {
|
||||
type: 'colour',
|
||||
style: `background:${gradient[0]?.colour};${grad}`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* It gets the gradient settings from localStorage, parses it, and returns the gradient style.
|
||||
* @returns A string.
|
||||
*/
|
||||
export function getGradient() {
|
||||
const customBackgroundColour = localStorage.getItem('customBackgroundColour') || {
|
||||
angle: '180',
|
||||
gradient: [{ colour: '#ffb032', stop: 0 }],
|
||||
type: 'linear',
|
||||
};
|
||||
|
||||
let gradientSettings = '';
|
||||
try {
|
||||
gradientSettings = JSON.parse(customBackgroundColour);
|
||||
} catch (e) {
|
||||
const hexColorRegex = /#[0-9a-fA-F]{6}/s;
|
||||
if (hexColorRegex.exec(customBackgroundColour)) {
|
||||
// Colour used to be simply a hex colour or a NULL value before it was a JSON object. This automatically upgrades the hex colour value to the new standard. (NULL would not trigger an exception)
|
||||
gradientSettings = {
|
||||
type: 'linear',
|
||||
angle: '180',
|
||||
gradient: [{ colour: customBackgroundColour, stop: 0 }],
|
||||
};
|
||||
localStorage.setItem('customBackgroundColour', JSON.stringify(gradientSettings));
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof gradientSettings === 'object' && gradientSettings !== null) {
|
||||
return gradientStyleBuilder(gradientSettings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a random colour or random gradient as a style object
|
||||
* @param type - The type of the style. This is used to determine which style builder to use.
|
||||
* @returns An object with two properties: type and style.
|
||||
*/
|
||||
export function randomColourStyleBuilder(type) {
|
||||
// randomColour based on https://stackoverflow.com/a/5092872
|
||||
const randomColour = () =>
|
||||
'#000000'.replace(/0/g, () => {
|
||||
return (~~(Math.random() * 16)).toString(16);
|
||||
});
|
||||
let style = `background:${randomColour()};`;
|
||||
|
||||
if (type === 'random_gradient') {
|
||||
const directions = [
|
||||
'to right',
|
||||
'to left',
|
||||
'to bottom',
|
||||
'to top',
|
||||
'to bottom right',
|
||||
'to bottom left',
|
||||
'to top right',
|
||||
'to top left',
|
||||
];
|
||||
style = `background:linear-gradient(${
|
||||
directions[Math.floor(Math.random() * directions.length)]
|
||||
}, ${randomColour()}, ${randomColour()});`;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'colour',
|
||||
style,
|
||||
};
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
import EventBus from './eventbus';
|
||||
|
||||
function showReminder() {
|
||||
document.querySelector('.reminder-info').style.display = 'flex';
|
||||
localStorage.setItem('showReminder', true);
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// based on https://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links
|
||||
export function urlParser(input) {
|
||||
const urlPattern =
|
||||
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,63}\b([-a-zA-Z0-9()!@:%_+.~#?&//=]*)/g;
|
||||
const emailPattern = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g;
|
||||
|
||||
const replaceUrl = (url) => `<br/><a class="link" href="${url}" target="_blank">${url}</a>`;
|
||||
const replaceEmail = (email) => `<a class="link" href="mailto:${email}">${email}</a>`;
|
||||
|
||||
const replacedUrls = input.replace(urlPattern, replaceUrl);
|
||||
const replacedEmails = replacedUrls.replace(emailPattern, replaceEmail);
|
||||
return replacedEmails;
|
||||
}
|
||||
|
||||
export function install(type, input, sideload) {
|
||||
switch (type) {
|
||||
case 'settings':
|
||||
localStorage.removeItem('backup_settings');
|
||||
|
||||
let oldSettings = [];
|
||||
Object.keys(localStorage).forEach((key) => {
|
||||
oldSettings.push({
|
||||
name: key,
|
||||
value: localStorage.getItem(key),
|
||||
});
|
||||
});
|
||||
|
||||
localStorage.setItem('backup_settings', JSON.stringify(oldSettings));
|
||||
Object.keys(input.settings).forEach((key) => {
|
||||
localStorage.setItem(key, input.settings[key]);
|
||||
});
|
||||
showReminder();
|
||||
break;
|
||||
|
||||
case 'photos':
|
||||
const currentPhotos = JSON.parse(localStorage.getItem('photo_packs')) || [];
|
||||
input.photos.forEach((photo) => {
|
||||
currentPhotos.push(photo);
|
||||
});
|
||||
localStorage.setItem('photo_packs', JSON.stringify(currentPhotos));
|
||||
|
||||
if (localStorage.getItem('backgroundType') !== 'photo_pack') {
|
||||
localStorage.setItem('oldBackgroundType', localStorage.getItem('backgroundType'));
|
||||
}
|
||||
localStorage.setItem('backgroundType', 'photo_pack');
|
||||
localStorage.removeItem('backgroundchange');
|
||||
EventBus.emit('refresh', 'background');
|
||||
// TODO: make this legitimately good and work without a reload - currently we just refresh
|
||||
sleep(4000);
|
||||
window.location.reload();
|
||||
break;
|
||||
|
||||
case 'quotes':
|
||||
const currentQuotes = JSON.parse(localStorage.getItem('quote_packs')) || [];
|
||||
input.quotes.forEach((quote) => {
|
||||
currentQuotes.push(quote);
|
||||
});
|
||||
localStorage.setItem('quote_packs', JSON.stringify(currentQuotes));
|
||||
|
||||
if (localStorage.getItem('quoteType') !== 'quote_pack') {
|
||||
localStorage.setItem('oldQuoteType', localStorage.getItem('quoteType'));
|
||||
}
|
||||
localStorage.setItem('quoteType', 'quote_pack');
|
||||
localStorage.removeItem('quotechange');
|
||||
EventBus.emit('refresh', 'quote');
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const installed = JSON.parse(localStorage.getItem('installed'));
|
||||
|
||||
if (sideload) {
|
||||
installed.push({
|
||||
content: {
|
||||
updated: 'Unpublished',
|
||||
data: input,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
installed.push(input);
|
||||
}
|
||||
|
||||
localStorage.setItem('installed', JSON.stringify(installed));
|
||||
}
|
||||
|
||||
export function uninstall(type, name) {
|
||||
let installedContents, packContents;
|
||||
switch (type) {
|
||||
case 'settings':
|
||||
const oldSettings = JSON.parse(localStorage.getItem('backup_settings'));
|
||||
localStorage.clear();
|
||||
oldSettings.forEach((item) => {
|
||||
localStorage.setItem(item.name, item.value);
|
||||
});
|
||||
showReminder();
|
||||
break;
|
||||
|
||||
case 'quotes':
|
||||
installedContents = JSON.parse(localStorage.getItem('quote_packs'));
|
||||
packContents = JSON.parse(localStorage.getItem('installed')).find(
|
||||
(content) => content.name === name,
|
||||
);
|
||||
installedContents.forEach((item, index) => {
|
||||
const exists = packContents.quotes.find(
|
||||
(content) => content.quote === item.quote || content.author === item.author,
|
||||
);
|
||||
if (exists !== undefined) {
|
||||
installedContents.splice(index, 1);
|
||||
}
|
||||
});
|
||||
localStorage.setItem('quote_packs', JSON.stringify(installedContents));
|
||||
if (installedContents.length === 0) {
|
||||
localStorage.setItem('quoteType', localStorage.getItem('oldQuoteType') || 'api');
|
||||
localStorage.removeItem('oldQuoteType');
|
||||
localStorage.removeItem('quote_packs');
|
||||
}
|
||||
localStorage.removeItem('quotechange');
|
||||
EventBus.emit('refresh', 'marketplacequoteuninstall');
|
||||
break;
|
||||
|
||||
case 'photos':
|
||||
installedContents = JSON.parse(localStorage.getItem('photo_packs'));
|
||||
packContents = JSON.parse(localStorage.getItem('installed')).find(
|
||||
(content) => content.name === name,
|
||||
);
|
||||
installedContents.forEach((item, index) => {
|
||||
const exists = packContents.photos.find((content) => content.photo === item.photo);
|
||||
if (exists !== undefined) {
|
||||
installedContents.splice(index, 1);
|
||||
}
|
||||
});
|
||||
localStorage.setItem('photo_packs', JSON.stringify(installedContents));
|
||||
if (installedContents.length === 0) {
|
||||
localStorage.setItem('backgroundType', localStorage.getItem('oldBackgroundType') || 'api');
|
||||
localStorage.removeItem('oldBackgroundType');
|
||||
localStorage.removeItem('photo_packs');
|
||||
}
|
||||
localStorage.removeItem('backgroundchange');
|
||||
EventBus.emit('refresh', 'marketplacebackgrounduninstall');
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
let installed = JSON.parse(localStorage.getItem('installed'));
|
||||
for (let i = 0; i < installed.length; i++) {
|
||||
if (installed[i].name === name) {
|
||||
installed.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem('installed', JSON.stringify(installed));
|
||||
}
|
|
@ -1,199 +0,0 @@
|
|||
import variables from 'config/variables';
|
||||
import experimentalInit from '../experimental';
|
||||
|
||||
import defaultSettings from 'utils/data/default_settings.json';
|
||||
import languages from '@/i18n/languages.json';
|
||||
|
||||
/**
|
||||
* It sets the default settings for the extension
|
||||
* @param reset - boolean
|
||||
*/
|
||||
export function setDefaultSettings(reset) {
|
||||
localStorage.clear();
|
||||
defaultSettings.forEach((element) => localStorage.setItem(element.name, element.value));
|
||||
|
||||
// Languages
|
||||
const languageCodes = languages.map(({ value }) => value);
|
||||
const browserLanguage =
|
||||
(navigator.languages &&
|
||||
navigator.languages.find((lang) => lang.replace('-', '_') && languageCodes.includes(lang))) ||
|
||||
navigator.language.replace('-', '_');
|
||||
|
||||
if (languageCodes.includes(browserLanguage)) {
|
||||
localStorage.setItem('language', browserLanguage);
|
||||
} else {
|
||||
localStorage.setItem('language', 'en_GB');
|
||||
}
|
||||
|
||||
localStorage.setItem('tabName', variables.getMessage('tabname'));
|
||||
|
||||
if (reset) {
|
||||
localStorage.setItem('showWelcome', false);
|
||||
}
|
||||
|
||||
// finally we set this to true so it doesn't run the function on every load
|
||||
localStorage.setItem('firstRun', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* It loads the settings from localStorage and applies them to the page.
|
||||
* @param hotreload - boolean
|
||||
*/
|
||||
export function loadSettings(hotreload) {
|
||||
switch (localStorage.getItem('theme')) {
|
||||
case 'dark':
|
||||
document.body.classList.add('dark');
|
||||
document.body.classList.remove('light');
|
||||
break;
|
||||
case 'auto':
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
document.body.classList.add('dark');
|
||||
} else {
|
||||
document.body.classList.remove('dark');
|
||||
document.body.classList.add('light');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
document.body.classList.add('light');
|
||||
document.body.classList.remove('dark');
|
||||
}
|
||||
|
||||
document.title = localStorage.getItem('tabName') || variables.getMessage('tabname');
|
||||
|
||||
if (hotreload === true) {
|
||||
// remove old custom stuff and add new
|
||||
const custom = ['customcss', 'customfont'];
|
||||
custom.forEach((element) => {
|
||||
try {
|
||||
document.head.removeChild(document.getElementById(element));
|
||||
} catch (e) {
|
||||
// Disregard exception if custom stuff doesn't exist
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (localStorage.getItem('animations') === 'false') {
|
||||
document.body.classList.add('no-animations');
|
||||
} else {
|
||||
document.body.classList.remove('no-animations');
|
||||
}
|
||||
|
||||
// technically, this is text SHADOW, and not BORDER
|
||||
// however it's a mess and we'll just leave it at this for now
|
||||
const textBorder = localStorage.getItem('textBorder');
|
||||
// enable/disable old text border from before redesign
|
||||
if (textBorder === 'true') {
|
||||
const elements = ['greeting', 'clock', 'quote', 'quoteauthor', 'date'];
|
||||
elements.forEach((element) => {
|
||||
try {
|
||||
document.querySelector('.' + element).classList.add('textBorder');
|
||||
} catch (e) {
|
||||
// Disregard exception
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const elements = ['greeting', 'clock', 'quote', 'quoteauthor', 'date'];
|
||||
elements.forEach((element) => {
|
||||
try {
|
||||
document.querySelector('.' + element).classList.remove('textBorder');
|
||||
} catch (e) {
|
||||
// Disregard exception
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// remove actual default shadow
|
||||
if (textBorder === 'none') {
|
||||
document.getElementById('center').classList.add('no-textBorder');
|
||||
} else {
|
||||
document.getElementById('center').classList.remove('no-textBorder');
|
||||
}
|
||||
|
||||
const css = localStorage.getItem('customcss');
|
||||
if (css) {
|
||||
document.head.insertAdjacentHTML('beforeend', '<style id="customcss">' + css + '</style>');
|
||||
}
|
||||
|
||||
const font = localStorage.getItem('font');
|
||||
if (font) {
|
||||
let url = '';
|
||||
if (localStorage.getItem('fontGoogle') === 'true') {
|
||||
url = `@import url('https://fonts.googleapis.com/css2?family=${font}&display=swap');`;
|
||||
}
|
||||
|
||||
document.head.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
`
|
||||
<style id='customfont'>
|
||||
${url}
|
||||
* {
|
||||
font-family: '${font}', 'Lexend Deca', 'Montserrat', sans-serif !important;
|
||||
font-weight: ${localStorage.getItem('fontweight')};
|
||||
font-style: ${localStorage.getItem('fontstyle')};
|
||||
}
|
||||
</style>
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
// If the extension got updated and the new app links default settings
|
||||
// were not set, set them
|
||||
if (localStorage.getItem('applinks') === null) {
|
||||
localStorage.setItem('applinks', JSON.stringify([]));
|
||||
}
|
||||
if (localStorage.getItem('appsEnabled') === null) {
|
||||
localStorage.setItem('showWelcome', false);
|
||||
}
|
||||
|
||||
// everything below this shouldn't run on a hot reload event
|
||||
if (hotreload === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (localStorage.getItem('experimental') === 'true') {
|
||||
experimentalInit();
|
||||
}
|
||||
|
||||
// easter egg
|
||||
console.log(`
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-${new Date().getFullYear()} The Mue Authors ██
|
||||
██ GitHub: https://github.com/mue/mue ██
|
||||
██ ██
|
||||
██ Thank you for using Mue! ██
|
||||
██ Feedback: hello@muetab.com ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all of the current settings, resets them, sets the defaults and then overrides
|
||||
* the new settings with the old saved messages where they exist.
|
||||
* @returns the result of the setDefaultSettings() function.
|
||||
*/
|
||||
export function moveSettings() {
|
||||
const currentSettings = Object.keys(localStorage);
|
||||
if (currentSettings.length === 0) {
|
||||
return this.setDefaultSettings();
|
||||
}
|
||||
|
||||
const settings = {};
|
||||
currentSettings.forEach((key) => {
|
||||
settings[key] = localStorage.getItem(key);
|
||||
});
|
||||
|
||||
localStorage.clear();
|
||||
setDefaultSettings();
|
||||
|
||||
Object.keys(settings).forEach((key) => {
|
||||
localStorage.setItem(key, settings[key]);
|
||||
});
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
import variables from 'config/variables';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
/**
|
||||
* It creates a link to a file, and then clicks it
|
||||
* @param data - the data you want to save
|
||||
* @param [filename=file] - the name of the file to be saved
|
||||
* @param [type=text/json] - the type of file you want to save.
|
||||
*/
|
||||
export function saveFile(data, filename = 'file', type = 'text/json') {
|
||||
if (typeof data === 'object') {
|
||||
data = JSON.stringify(data, undefined, 4);
|
||||
}
|
||||
|
||||
const blob = new Blob([data], { type });
|
||||
|
||||
const event = document.createEvent('MouseEvents');
|
||||
const a = document.createElement('a');
|
||||
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.download = filename;
|
||||
a.dataset.downloadurl = [type, a.download, a.href].join(':');
|
||||
|
||||
// i need to see what all this actually does, i think wessel wrote this function
|
||||
event.initMouseEvent(
|
||||
'click',
|
||||
true,
|
||||
false,
|
||||
window,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
null,
|
||||
);
|
||||
a.dispatchEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes all the settings from localStorage and saves them to a file
|
||||
*/
|
||||
export function exportSettings() {
|
||||
const settings = {};
|
||||
|
||||
Object.keys(localStorage).forEach((key) => {
|
||||
settings[key] = localStorage.getItem(key);
|
||||
});
|
||||
|
||||
let date = new Date();
|
||||
// Format the date as YYYY-MM-DD_HH-MM-SS
|
||||
let formattedDate = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}_${date.getHours().toString().padStart(2, '0')}-${date.getMinutes().toString().padStart(2, '0')}-${date.getSeconds().toString().padStart(2, '0')}`;
|
||||
let filename = `mue_settings_backup_${formattedDate}.json`;
|
||||
saveFile(settings, filename);
|
||||
variables.stats.postEvent('tab', 'Settings exported');
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes a JSON file of Mue settings, parses it, and then sets the localStorage values to the values in the
|
||||
* file.
|
||||
* @param e - The JSON settings string to import
|
||||
*/
|
||||
export function importSettings(e) {
|
||||
const content = JSON.parse(e);
|
||||
|
||||
Object.keys(content).forEach((key) => {
|
||||
localStorage.setItem(key, content[key]);
|
||||
});
|
||||
|
||||
toast(variables.getMessage('toasts.imported'));
|
||||
variables.stats.postEvent('tab', 'Settings imported');
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns an array of objects with a value and label property for the Mue sliders.
|
||||
* @param type - The type of slider you want to use.
|
||||
* @returns An object with keys of either zoom, toast, background or experimental.
|
||||
*/
|
||||
export function values(type) {
|
||||
const marks = {
|
||||
zoom: [
|
||||
{ value: 10, label: '0.1x' },
|
||||
{ value: 100, label: '1x' },
|
||||
{ value: 200, label: '2x' },
|
||||
{ value: 400, label: '4x' },
|
||||
],
|
||||
toast: [
|
||||
{ value: 500, label: '0.5s' },
|
||||
{ value: 1000, label: '1s' },
|
||||
{ value: 1500, label: '1.5s' },
|
||||
{ value: 2000, label: '2s' },
|
||||
{ value: 2500, label: '2.5s' },
|
||||
{ value: 3000, label: '3s' },
|
||||
{ value: 4000, label: '4s' },
|
||||
{ value: 5000, label: '5s' },
|
||||
],
|
||||
background: [
|
||||
{ value: 0, label: '0%' },
|
||||
{ value: 25, label: '25%' },
|
||||
{ value: 50, label: '50%' },
|
||||
{ value: 75, label: '75%' },
|
||||
{ value: 100, label: '100%' },
|
||||
],
|
||||
experimental: [
|
||||
{ value: 0, label: '0s' },
|
||||
{ value: 500, label: '0.5s' },
|
||||
{ value: 1000, label: '1s' },
|
||||
{ value: 1500, label: '1.5s' },
|
||||
{ value: 2000, label: '2s' },
|
||||
{ value: 2500, label: '2.5s' },
|
||||
{ value: 3000, label: '3s' },
|
||||
{ value: 4000, label: '4s' },
|
||||
{ value: 5000, label: '5s' },
|
||||
],
|
||||
};
|
||||
|
||||
return marks[type] || [];
|
||||
}
|
||||
|
||||
export async function getTitleFromUrl(url) {
|
||||
let title;
|
||||
try {
|
||||
let response = await fetch(url);
|
||||
if (response.redirected) {
|
||||
response = await fetch(response.url);
|
||||
}
|
||||
const html = await response.text();
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, 'text/html');
|
||||
title = doc.title;
|
||||
} catch (e) {
|
||||
title = url;
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
export function isValidUrl(url) {
|
||||
// regex: https://ihateregex.io/expr/url/
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const urlRegex =
|
||||
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._~#=]{1,256}\.[a-zA-Z0-9()]{1,63}\b([-a-zA-Z0-9()!@:%_.~#?&=]*)/;
|
||||
|
||||
return urlRegex.test(url);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
export async function getTitleFromUrl(url) {
|
||||
let title;
|
||||
try {
|
||||
let response = await fetch(url);
|
||||
if (response.redirected) {
|
||||
response = await fetch(response.url);
|
||||
}
|
||||
const html = await response.text();
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, 'text/html');
|
||||
title = doc.title;
|
||||
} catch (e) {
|
||||
title = url;
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
import { getTitleFromUrl } from "./getTitleFromUrl";
|
||||
import { isValidUrl } from "./isValidUrl";
|
||||
|
||||
export { getTitleFromUrl, isValidUrl };
|
|
@ -0,0 +1,8 @@
|
|||
export function isValidUrl(url) {
|
||||
// regex: https://ihateregex.io/expr/url/
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const urlRegex =
|
||||
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._~#=]{1,256}\.[a-zA-Z0-9()]{1,63}\b([-a-zA-Z0-9()!@:%_.~#?&=]*)/;
|
||||
|
||||
return urlRegex.test(url);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import { install } from './install';
|
||||
import { uninstall } from './uninstall';
|
||||
import { urlParser } from './urlParser';
|
||||
|
||||
export { install, uninstall, urlParser };
|
|
@ -0,0 +1,84 @@
|
|||
import EventBus from 'utils/eventbus';
|
||||
|
||||
// todo: relocate these 2 functions
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function showReminder() {
|
||||
document.querySelector('.reminder-info').style.display = 'flex';
|
||||
localStorage.setItem('showReminder', true);
|
||||
}
|
||||
|
||||
export function install(type, input, sideload) {
|
||||
switch (type) {
|
||||
case 'settings':
|
||||
localStorage.removeItem('backup_settings');
|
||||
|
||||
let oldSettings = [];
|
||||
Object.keys(localStorage).forEach((key) => {
|
||||
oldSettings.push({
|
||||
name: key,
|
||||
value: localStorage.getItem(key),
|
||||
});
|
||||
});
|
||||
|
||||
localStorage.setItem('backup_settings', JSON.stringify(oldSettings));
|
||||
Object.keys(input.settings).forEach((key) => {
|
||||
localStorage.setItem(key, input.settings[key]);
|
||||
});
|
||||
showReminder();
|
||||
break;
|
||||
|
||||
case 'photos':
|
||||
const currentPhotos = JSON.parse(localStorage.getItem('photo_packs')) || [];
|
||||
input.photos.forEach((photo) => {
|
||||
currentPhotos.push(photo);
|
||||
});
|
||||
localStorage.setItem('photo_packs', JSON.stringify(currentPhotos));
|
||||
|
||||
if (localStorage.getItem('backgroundType') !== 'photo_pack') {
|
||||
localStorage.setItem('oldBackgroundType', localStorage.getItem('backgroundType'));
|
||||
}
|
||||
localStorage.setItem('backgroundType', 'photo_pack');
|
||||
localStorage.removeItem('backgroundchange');
|
||||
EventBus.emit('refresh', 'background');
|
||||
// TODO: make this legitimately good and work without a reload - currently we just refresh
|
||||
sleep(4000);
|
||||
window.location.reload();
|
||||
break;
|
||||
|
||||
case 'quotes':
|
||||
const currentQuotes = JSON.parse(localStorage.getItem('quote_packs')) || [];
|
||||
input.quotes.forEach((quote) => {
|
||||
currentQuotes.push(quote);
|
||||
});
|
||||
localStorage.setItem('quote_packs', JSON.stringify(currentQuotes));
|
||||
|
||||
if (localStorage.getItem('quoteType') !== 'quote_pack') {
|
||||
localStorage.setItem('oldQuoteType', localStorage.getItem('quoteType'));
|
||||
}
|
||||
localStorage.setItem('quoteType', 'quote_pack');
|
||||
localStorage.removeItem('quotechange');
|
||||
EventBus.emit('refresh', 'quote');
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const installed = JSON.parse(localStorage.getItem('installed'));
|
||||
|
||||
if (sideload) {
|
||||
installed.push({
|
||||
content: {
|
||||
updated: 'Unpublished',
|
||||
data: input,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
installed.push(input);
|
||||
}
|
||||
|
||||
localStorage.setItem('installed', JSON.stringify(installed));
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
import EventBus from 'utils/eventbus';
|
||||
|
||||
// todo: relocate this function
|
||||
function showReminder() {
|
||||
document.querySelector('.reminder-info').style.display = 'flex';
|
||||
localStorage.setItem('showReminder', true);
|
||||
}
|
||||
|
||||
export function uninstall(type, name) {
|
||||
let installedContents, packContents;
|
||||
switch (type) {
|
||||
case 'settings':
|
||||
const oldSettings = JSON.parse(localStorage.getItem('backup_settings'));
|
||||
localStorage.clear();
|
||||
oldSettings.forEach((item) => {
|
||||
localStorage.setItem(item.name, item.value);
|
||||
});
|
||||
showReminder();
|
||||
break;
|
||||
|
||||
case 'quotes':
|
||||
installedContents = JSON.parse(localStorage.getItem('quote_packs'));
|
||||
packContents = JSON.parse(localStorage.getItem('installed')).find(
|
||||
(content) => content.name === name,
|
||||
);
|
||||
installedContents.forEach((item, index) => {
|
||||
const exists = packContents.quotes.find(
|
||||
(content) => content.quote === item.quote || content.author === item.author,
|
||||
);
|
||||
if (exists !== undefined) {
|
||||
installedContents.splice(index, 1);
|
||||
}
|
||||
});
|
||||
localStorage.setItem('quote_packs', JSON.stringify(installedContents));
|
||||
if (installedContents.length === 0) {
|
||||
localStorage.setItem('quoteType', localStorage.getItem('oldQuoteType') || 'api');
|
||||
localStorage.removeItem('oldQuoteType');
|
||||
localStorage.removeItem('quote_packs');
|
||||
}
|
||||
localStorage.removeItem('quotechange');
|
||||
EventBus.emit('refresh', 'marketplacequoteuninstall');
|
||||
break;
|
||||
|
||||
case 'photos':
|
||||
installedContents = JSON.parse(localStorage.getItem('photo_packs'));
|
||||
packContents = JSON.parse(localStorage.getItem('installed')).find(
|
||||
(content) => content.name === name,
|
||||
);
|
||||
installedContents.forEach((item, index) => {
|
||||
const exists = packContents.photos.find((content) => content.photo === item.photo);
|
||||
if (exists !== undefined) {
|
||||
installedContents.splice(index, 1);
|
||||
}
|
||||
});
|
||||
localStorage.setItem('photo_packs', JSON.stringify(installedContents));
|
||||
if (installedContents.length === 0) {
|
||||
localStorage.setItem('backgroundType', localStorage.getItem('oldBackgroundType') || 'api');
|
||||
localStorage.removeItem('oldBackgroundType');
|
||||
localStorage.removeItem('photo_packs');
|
||||
}
|
||||
localStorage.removeItem('backgroundchange');
|
||||
EventBus.emit('refresh', 'marketplacebackgrounduninstall');
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
let installed = JSON.parse(localStorage.getItem('installed'));
|
||||
for (let i = 0; i < installed.length; i++) {
|
||||
if (installed[i].name === name) {
|
||||
installed.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem('installed', JSON.stringify(installed));
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// based on https://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links
|
||||
export function urlParser(input) {
|
||||
const urlPattern =
|
||||
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,63}\b([-a-zA-Z0-9()!@:%_+.~#?&//=]*)/g;
|
||||
const emailPattern = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g;
|
||||
|
||||
const replaceUrl = (url) => `<br/><a class="link" href="${url}" target="_blank">${url}</a>`;
|
||||
const replaceEmail = (email) => `<a class="link" href="mailto:${email}">${email}</a>`;
|
||||
|
||||
const replacedUrls = input.replace(urlPattern, replaceUrl);
|
||||
const replacedEmails = replacedUrls.replace(emailPattern, replaceEmail);
|
||||
return replacedEmails;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* It creates a link to a file, and then clicks it
|
||||
* @param data - the data you want to save
|
||||
* @param [filename=file] - the name of the file to be saved
|
||||
* @param [type=text/json] - the type of file you want to save.
|
||||
*/
|
||||
export function saveFile(data, filename = 'file', type = 'text/json') {
|
||||
if (typeof data === 'object') {
|
||||
data = JSON.stringify(data, undefined, 4);
|
||||
}
|
||||
|
||||
const blob = new Blob([data], { type });
|
||||
|
||||
const event = document.createEvent('MouseEvents');
|
||||
const a = document.createElement('a');
|
||||
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.download = filename;
|
||||
a.dataset.downloadurl = [type, a.download, a.href].join(':');
|
||||
|
||||
// i need to see what all this actually does, i think wessel wrote this function
|
||||
event.initMouseEvent(
|
||||
'click',
|
||||
true,
|
||||
false,
|
||||
window,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
null,
|
||||
);
|
||||
a.dispatchEvent(event);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import defaultSettings from 'utils/data/default_settings.json';
|
||||
import languages from 'i18n/languages.json';
|
||||
import variables from 'config/variables';
|
||||
|
||||
/**
|
||||
* It sets the default settings for the extension
|
||||
* @param reset - boolean
|
||||
*/
|
||||
export function setDefaultSettings(reset) {
|
||||
localStorage.clear();
|
||||
defaultSettings.forEach((element) => localStorage.setItem(element.name, element.value));
|
||||
|
||||
// Languages
|
||||
const languageCodes = languages.map(({ value }) => value);
|
||||
const browserLanguage =
|
||||
(navigator.languages &&
|
||||
navigator.languages.find((lang) => lang.replace('-', '_') && languageCodes.includes(lang))) ||
|
||||
navigator.language.replace('-', '_');
|
||||
|
||||
if (languageCodes.includes(browserLanguage)) {
|
||||
localStorage.setItem('language', browserLanguage);
|
||||
} else {
|
||||
localStorage.setItem('language', 'en_GB');
|
||||
}
|
||||
|
||||
localStorage.setItem('tabName', variables.getMessage('tabname'));
|
||||
|
||||
if (reset) {
|
||||
localStorage.setItem('showWelcome', false);
|
||||
}
|
||||
|
||||
// finally we set this to true so it doesn't run the function on every load
|
||||
localStorage.setItem('firstRun', true);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import { saveFile } from 'utils/saveFile';
|
||||
import variables from 'config/variables';
|
||||
|
||||
/**
|
||||
* It takes all the settings from localStorage and saves them to a file
|
||||
*/
|
||||
export function exportSettings() {
|
||||
const settings = {};
|
||||
|
||||
Object.keys(localStorage).forEach((key) => {
|
||||
settings[key] = localStorage.getItem(key);
|
||||
});
|
||||
|
||||
let date = new Date();
|
||||
// Format the date as YYYY-MM-DD_HH-MM-SS
|
||||
let formattedDate = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}_${date.getHours().toString().padStart(2, '0')}-${date.getMinutes().toString().padStart(2, '0')}-${date.getSeconds().toString().padStart(2, '0')}`;
|
||||
let filename = `mue_settings_backup_${formattedDate}.json`;
|
||||
saveFile(settings, filename);
|
||||
variables.stats.postEvent('tab', 'Settings exported');
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import { toast } from 'react-toastify';
|
||||
import variables from 'config/variables';
|
||||
|
||||
/**
|
||||
* It takes a JSON file of Mue settings, parses it, and then sets the localStorage values to the values in the
|
||||
* file.
|
||||
* @param e - The JSON settings string to import
|
||||
*/
|
||||
export function importSettings(e) {
|
||||
const content = JSON.parse(e);
|
||||
|
||||
Object.keys(content).forEach((key) => {
|
||||
localStorage.setItem(key, content[key]);
|
||||
});
|
||||
|
||||
toast(variables.getMessage('toasts.imported'));
|
||||
variables.stats.postEvent('tab', 'Settings imported');
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { setDefaultSettings } from './default';
|
||||
import { exportSettings } from './export';
|
||||
import { importSettings } from './import';
|
||||
import { loadSettings } from './load';
|
||||
import { moveSettings } from './move';
|
||||
|
||||
export { setDefaultSettings, exportSettings, importSettings, loadSettings, moveSettings };
|
|
@ -0,0 +1,141 @@
|
|||
import variables from "config/variables";
|
||||
import ExperimentalInit from "utils/experimental";
|
||||
|
||||
/**
|
||||
* It loads the settings from localStorage and applies them to the page.
|
||||
* @param hotreload - boolean
|
||||
*/
|
||||
export function loadSettings(hotreload) {
|
||||
switch (localStorage.getItem('theme')) {
|
||||
case 'dark':
|
||||
document.body.classList.add('dark');
|
||||
document.body.classList.remove('light');
|
||||
break;
|
||||
case 'auto':
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
document.body.classList.add('dark');
|
||||
} else {
|
||||
document.body.classList.remove('dark');
|
||||
document.body.classList.add('light');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
document.body.classList.add('light');
|
||||
document.body.classList.remove('dark');
|
||||
}
|
||||
|
||||
document.title = localStorage.getItem('tabName') || variables.getMessage('tabname');
|
||||
|
||||
if (hotreload === true) {
|
||||
// remove old custom stuff and add new
|
||||
const custom = ['customcss', 'customfont'];
|
||||
custom.forEach((element) => {
|
||||
try {
|
||||
document.head.removeChild(document.getElementById(element));
|
||||
} catch (e) {
|
||||
// Disregard exception if custom stuff doesn't exist
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (localStorage.getItem('animations') === 'false') {
|
||||
document.body.classList.add('no-animations');
|
||||
} else {
|
||||
document.body.classList.remove('no-animations');
|
||||
}
|
||||
|
||||
// technically, this is text SHADOW, and not BORDER
|
||||
// however it's a mess and we'll just leave it at this for now
|
||||
const textBorder = localStorage.getItem('textBorder');
|
||||
// enable/disable old text border from before redesign
|
||||
if (textBorder === 'true') {
|
||||
const elements = ['greeting', 'clock', 'quote', 'quoteauthor', 'date'];
|
||||
elements.forEach((element) => {
|
||||
try {
|
||||
document.querySelector('.' + element).classList.add('textBorder');
|
||||
} catch (e) {
|
||||
// Disregard exception
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const elements = ['greeting', 'clock', 'quote', 'quoteauthor', 'date'];
|
||||
elements.forEach((element) => {
|
||||
try {
|
||||
document.querySelector('.' + element).classList.remove('textBorder');
|
||||
} catch (e) {
|
||||
// Disregard exception
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// remove actual default shadow
|
||||
if (textBorder === 'none') {
|
||||
document.getElementById('center').classList.add('no-textBorder');
|
||||
} else {
|
||||
document.getElementById('center').classList.remove('no-textBorder');
|
||||
}
|
||||
|
||||
const css = localStorage.getItem('customcss');
|
||||
if (css) {
|
||||
document.head.insertAdjacentHTML('beforeend', '<style id="customcss">' + css + '</style>');
|
||||
}
|
||||
|
||||
const font = localStorage.getItem('font');
|
||||
if (font) {
|
||||
let url = '';
|
||||
if (localStorage.getItem('fontGoogle') === 'true') {
|
||||
url = `@import url('https://fonts.googleapis.com/css2?family=${font}&display=swap');`;
|
||||
}
|
||||
|
||||
document.head.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
`
|
||||
<style id='customfont'>
|
||||
${url}
|
||||
* {
|
||||
font-family: '${font}', 'Lexend Deca', 'Montserrat', sans-serif !important;
|
||||
font-weight: ${localStorage.getItem('fontweight')};
|
||||
font-style: ${localStorage.getItem('fontstyle')};
|
||||
}
|
||||
</style>
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
// If the extension got updated and the new app links default settings
|
||||
// were not set, set them
|
||||
if (localStorage.getItem('applinks') === null) {
|
||||
localStorage.setItem('applinks', JSON.stringify([]));
|
||||
}
|
||||
if (localStorage.getItem('appsEnabled') === null) {
|
||||
localStorage.setItem('showWelcome', false);
|
||||
}
|
||||
|
||||
// everything below this shouldn't run on a hot reload event
|
||||
if (hotreload === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (localStorage.getItem('experimental') === 'true') {
|
||||
ExperimentalInit();
|
||||
}
|
||||
|
||||
// easter egg
|
||||
console.log(`
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-${new Date().getFullYear()} The Mue Authors ██
|
||||
██ GitHub: https://github.com/mue/mue ██
|
||||
██ ██
|
||||
██ Thank you for using Mue! ██
|
||||
██ Feedback: hello@muetab.com ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
`);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import { setDefaultSettings } from "./default";
|
||||
|
||||
/**
|
||||
* Saves all of the current settings, resets them, sets the defaults and then overrides
|
||||
* the new settings with the old saved messages where they exist.
|
||||
* @returns the result of the setDefaultSettings() function.
|
||||
*/
|
||||
export function moveSettings() {
|
||||
const currentSettings = Object.keys(localStorage);
|
||||
if (currentSettings.length === 0) {
|
||||
return this.setDefaultSettings();
|
||||
}
|
||||
|
||||
const settings = {};
|
||||
currentSettings.forEach((key) => {
|
||||
settings[key] = localStorage.getItem(key);
|
||||
});
|
||||
|
||||
localStorage.clear();
|
||||
setDefaultSettings();
|
||||
|
||||
Object.keys(settings).forEach((key) => {
|
||||
localStorage.setItem(key, settings[key]);
|
||||
});
|
||||
}
|
|
@ -99,12 +99,13 @@ export default defineConfig(({ command, mode }) => {
|
|||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
i18n: path.resolve(__dirname, './src/i18n'),
|
||||
features: path.resolve(__dirname, './src/features'),
|
||||
components: path.resolve(__dirname, './src/components'),
|
||||
utils: path.resolve(__dirname, './src/utils'),
|
||||
translations: path.resolve(__dirname, './src/i18n/locales'),
|
||||
config: path.resolve(__dirname, './src/config'),
|
||||
features: path.resolve(__dirname, './src/features'),
|
||||
lib: path.resolve(__dirname, './src/lib'),
|
||||
scss: path.resolve(__dirname, './src/scss'),
|
||||
translations: path.resolve(__dirname, './src/i18n/locales'),
|
||||
utils: path.resolve(__dirname, './src/utils'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue