feat: update addons, sideload error modal, show reminder for preset settings

This commit is contained in:
David Ralph 2021-09-05 20:52:26 +01:00
parent 5baf067ff1
commit c63a13a4c2
11 changed files with 189 additions and 79 deletions

View File

@ -24,6 +24,8 @@ const renderLoader = () => (
export default function MainModal({ modalClose }) {
const language = window.language.modals.main.navbar;
const { reminder } = window.language.modals.main.settings;
const display = (localStorage.getItem('showReminder') === 'true') ? 'block' : 'none';
return (
<>
@ -45,6 +47,11 @@ export default function MainModal({ modalClose }) {
</Suspense>
</div>
</Tabs>
<div className='reminder-info' style={{ display }}>
<h1>{reminder.title}</h1>
<p>{reminder.message}</p>
<button className='pinNote' onClick={() => window.location.reload()}>{window.language.modals.main.error_boundary.refresh}</button>
</div>
</>
);
}

View File

@ -1,17 +1,30 @@
import { PureComponent } from 'react';
import { PureComponent, Fragment } from 'react';
import { toast } from 'react-toastify';
import { ArrowBack } from '@material-ui/icons';
import Modal from 'react-modal';
import { install, uninstall } from 'modules/helpers/marketplace';
import Lightbox from './Lightbox';
export default class Item extends PureComponent {
constructor() {
super();
constructor(props) {
super(props);
this.state = {
showLightbox: false
showLightbox: false,
showUpdateButton: (this.props.addonInstalled === true && this.props.addonInstalledVersion !== this.props.data.version)
};
}
updateAddon() {
uninstall(this.props.data.type, this.props.data.display_name);
install(this.props.data.type, this.props.data);
toast('Successfully updated!');
this.setState({
showUpdateButton: false
});
}
render() {
const language = window.language.modals.main.marketplace.product;
@ -36,6 +49,18 @@ export default class Item extends PureComponent {
if (!this.props.data.icon) {
iconsrc = null;
}
let updateButton;
if (this.state.showUpdateButton) {
updateButton = (
<Fragment key='update'>
<br/><br/>
<button className='removeFromMue' onClick={() => this.updateAddon()}>
Update Add-on
</button>
</Fragment>
);
}
return (
<div id='item'>
@ -44,13 +69,14 @@ export default class Item extends PureComponent {
<br/>
<h1>{this.props.data.display_name}</h1>
{this.props.button}
{updateButton}
<br/><br/>
{iconsrc ? <img alt='product' draggable='false' src={iconsrc} onClick={() => this.setState({ showLightbox: true })}/> : null}
<div className='side'>
<div className='productInformation'>
<ul>
<li className='header'>{language.version}</li>
<li>{this.props.data.version}</li>
{updateButton ? <li>{this.props.data.version} (Installed: {this.props.data.addonInstalledVersion})</li> : <li>{this.props.data.version}</li>}
<br/>
<li className='header'>{language.author}</li>
<li>{this.props.data.author}</li>

View File

@ -0,0 +1,13 @@
export default function SideloadFailedModal({ modalClose, reason }) {
return (
<>
<h1 style={{ textAlign: 'center' }}>Error</h1>
<span>Failed to sideload addon</span>
<br/><br/>
<span>{reason}</span>
<div className='resetfooter'>
<button className='import' onClick={modalClose}>Close</button>
</div>
</>
);
}

View File

@ -44,11 +44,20 @@ export default class Marketplace extends PureComponent {
// 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;
addonInstalled = true;
for (let i = 0; i < installed.length; i++) {
if (installed[i].name === info.data.name) {
addonInstalledVersion = installed[i].version;
break;
}
}
}
this.setState({
@ -60,7 +69,9 @@ export default class Marketplace extends PureComponent {
//updated: info.updated,
version: info.data.version,
icon: info.data.screenshot_url,
data: info.data
data: info.data,
addonInstalled,
addonInstalledVersion
},
button: button
});
@ -200,7 +211,7 @@ export default class Marketplace extends PureComponent {
}
if (this.state.item.display_name) {
return <Item data={this.state.item} button={this.state.button} toggleFunction={() => this.toggle()}/>;
return <Item data={this.state.item} button={this.state.button} toggleFunction={() => this.toggle()} addonInstalled={this.state.item.addonInstalled} addonInstalledVersion={this.state.item.addonInstalledVersion}/>;
}
return (

View File

@ -1,25 +1,64 @@
import { PureComponent } from 'react';
import { LocalMall } from '@material-ui/icons';
import { toast } from 'react-toastify';
import Modal from 'react-modal';
import SideloadFailedModal from '../SideloadFailedModal';
import FileUpload from '../../settings/FileUpload';
import { install } from 'modules/helpers/marketplace';
export default function Sideload() {
const installAddon = (input) => {
export default class Sideload extends PureComponent {
constructor(props) {
super(props);
this.state = {
showFailed: false,
failedReason: ''
}
}
installAddon(input) {
let failedReason = '';
if (!input.name) {
failedReason = 'No name provided';
} else if (!input.author) {
failedReason = 'No author provided';
} else if (!input.type) {
failedReason = 'No type provided';
} else if (!input.version) {
failedReason = 'No version provided';
} else if (input.type === 'photos' && (!input.photos || !input.photos.length || !input.photos[0].url.default || !input.photos[0].photographer || !input.photos[0].location)) {
failedReason = 'Invalid photos object';
} else if (input.type === 'quotes' && (!input.quotes || !input.quotes.length || !input.quotes[0].quote || !input.quotes[0].author)) {
failedReason = 'Invalid quotes object';
}
if (failedReason !== '') {
return this.setState({
failedReason,
showFailed: true
});
}
install(input.type, input);
toast(window.language.toasts.installed);
window.stats.postEvent('marketplace', 'Sideload');
};
return (
<div className='emptyitems'>
<div className='emptyMessage'>
<FileUpload id='file-input' type='settings' accept='application/json' loadFunction={(e) => installAddon(JSON.parse(e.target.result))} />
<LocalMall/>
<h1>{window.language.modals.main.addons.sideload}</h1>
<button className='addToMue sideload' onClick={() => document.getElementById('file-input').click()}>{window.language.modals.main.settings.sections.background.source.upload}</button>
}
render() {
return (
<div className='emptyitems'>
<div className='emptyMessage'>
<FileUpload id='file-input' type='settings' accept='application/json' loadFunction={(e) => this.installAddon(JSON.parse(e.target.result))} />
<LocalMall/>
<h1>{window.language.modals.main.addons.sideload}</h1>
<button className='addToMue sideload' onClick={() => document.getElementById('file-input').click()}>{window.language.modals.main.settings.sections.background.source.upload}</button>
</div>
<Modal closeTimeoutMS={100} onRequestClose={() => this.setState({ showFailed: false })} isOpen={this.state.showFailed} className='Modal resetmodal mainModal sideloadModal' overlayClassName='Overlay resetoverlay' ariaHideApp={false}>
<SideloadFailedModal modalClose={() => this.setState({ showFailed: false })} reason={this.state.failedReason}/>
</Modal>
</div>
</div>
);
);
}
}

View File

@ -116,3 +116,14 @@ p.author {
width: auto;
cursor: pointer;
}
/* sideload failed modal */
.sideloadModal {
min-width: 250px;
max-width: 250px;
overflow-x: hidden;
button {
margin-left: -50px;
}
}

View File

@ -18,8 +18,7 @@ import Stats from '../settings/sections/Stats';
import Keybinds from '../settings/sections/Keybinds';
export default function Settings() {
const { reminder, sections } = window.language.modals.main.settings;
const display = (localStorage.getItem('showReminder') === 'true') ? 'block' : 'none';
const { sections } = window.language.modals.main.settings;
return (
<>
@ -41,11 +40,6 @@ export default function Settings() {
<div label={sections.changelog} name='changelog'><Changelog/></div>
<div label={sections.about.title} name='about'><About/></div>
</Tabs>
<div className='reminder-info' style={{ display }}>
<h1>{reminder.title}</h1>
<p>{reminder.message}</p>
<button className='pinNote' onClick={() => window.location.reload()}>{window.language.modals.main.error_boundary.refresh}</button>
</div>
</>
);
}

View File

@ -1,47 +1,16 @@
import EventBus from './eventbus';
function showReminder() {
document.querySelector('.reminder-info').style.display = 'block';
localStorage.setItem('showReminder', true);
}
// 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,6}\b([-a-zA-Z0-9()!@:%_+.~#?&//=]*)/;
return input.replace(urlPattern, '<a href="$&" target="_blank">$&</a>');
}
export function uninstall(type, name) {
switch (type) {
case 'settings':
const oldSettings = JSON.parse(localStorage.getItem('backup_settings'));
localStorage.clear();
oldSettings.forEach((item) => {
localStorage.setItem(item.name, item.value);
});
break;
case 'quotes':
localStorage.removeItem('quote_packs');
localStorage.removeItem('quoteAPI');
localStorage.setItem('quoteType', localStorage.getItem('oldQuoteType'));
localStorage.removeItem('oldQuoteType');
EventBus.dispatch('refresh', 'marketplacequoteuninstall');
break;
case 'photos':
localStorage.removeItem('photo_packs');
localStorage.setItem('backgroundType', localStorage.getItem('oldBackgroundType'));
localStorage.removeItem('oldBackgroundType');
EventBus.dispatch('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));
}
export function install(type, input, sideload) {
switch (type) {
case 'settings':
@ -59,6 +28,7 @@ export function install(type, input, sideload) {
Object.keys(input.settings).forEach((key) => {
localStorage.setItem(key, input.settings[key]);
});
showReminder();
break;
case 'photos':
@ -95,5 +65,43 @@ export function install(type, input, sideload) {
installed.push(input);
}
localStorage.setItem('installed', JSON.stringify(installed));
}
export function uninstall(type, name) {
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':
localStorage.removeItem('quote_packs');
localStorage.removeItem('quoteAPI');
localStorage.setItem('quoteType', localStorage.getItem('oldQuoteType'));
localStorage.removeItem('oldQuoteType');
EventBus.dispatch('refresh', 'marketplacequoteuninstall');
break;
case 'photos':
localStorage.removeItem('photo_packs');
localStorage.setItem('backgroundType', localStorage.getItem('oldBackgroundType'));
localStorage.removeItem('oldBackgroundType');
EventBus.dispatch('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));
};

View File

@ -35,13 +35,3 @@ $modal-link-dark: map-get($modal, 'modal-link-dark');
--photo-info: #{$photo-info-dark};
--modal-link: #{$modal-link-dark};
}
.no-animations {
.ReactModal__Content,
button,
svg,
input[type=text],
.MuiSwitch-switchBase {
transition: none !important;
}
}

View File

@ -34,10 +34,6 @@ body {
text-shadow: 0 0 25px rgba(0, 0, 0, 0.3);
}
.textBorder {
filter: drop-shadow(var(--shadow-shift) var(--shadow-shift) 0 #111c);
}
::placeholder {
color: map-get($theme-colours, 'main');
opacity: 1;
@ -53,7 +49,22 @@ body {
color: map-get($theme-colours, 'main-text-color');
}
// fonts imported from fontsource
/* accessibility */
.textBorder {
filter: drop-shadow(var(--shadow-shift) var(--shadow-shift) 0 #111c);
}
.no-animations {
.ReactModal__Content,
button,
svg,
input[type=text],
.MuiSwitch-switchBase {
transition: none !important;
}
}
/* fonts (imported from fontsource) */
@font-face {
font-family: 'Lexend Deca';
font-style: normal;

View File

@ -16,7 +16,7 @@ module.exports = {
use: ['babel-loader']
},
{
test: /\.(sa|sc|c)ss$/,
test: /\.(|sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,