mirror of https://github.com/mue/mue.git
[New Feature] Created a "Apps" section to add more frequently visited website links like in Chrome. (#622)
* added apps links section to the navbar with settings to add and edit links in under navbar settings * translated to english US
This commit is contained in:
parent
59e721d663
commit
0d77508f4c
|
@ -10,6 +10,8 @@ import EventBus from 'modules/helpers/eventbus';
|
|||
|
||||
import Welcome from './welcome/Welcome';
|
||||
|
||||
import Apps from './apps/Apps';
|
||||
|
||||
export default class Modals extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -17,6 +19,7 @@ export default class Modals extends PureComponent {
|
|||
mainModal: false,
|
||||
updateModal: false,
|
||||
welcomeModal: false,
|
||||
appsModal: false,
|
||||
preview: false,
|
||||
};
|
||||
}
|
||||
|
@ -75,6 +78,9 @@ export default class Modals extends PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const navZoom = localStorage.getItem('zoomNavbar');
|
||||
const appsInfo = JSON.parse(localStorage.getItem('applinks'));
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.state.welcomeModal === false && (
|
||||
|
@ -102,6 +108,27 @@ export default class Modals extends PureComponent {
|
|||
>
|
||||
<Welcome modalClose={() => this.closeWelcome()} modalSkip={() => this.previewWelcome()} />
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
closeTimeoutMS={300}
|
||||
onRequestClose={() => this.toggleModal('appsModal', false)}
|
||||
isOpen={this.state.appsModal}
|
||||
className="Modal appsmodal"
|
||||
overlayClassName="Overlay"
|
||||
shouldCloseOnOverlayClick={true}
|
||||
ariaHideApp={false}
|
||||
style={{
|
||||
content: {
|
||||
position: 'absolute',
|
||||
right: '1rem',
|
||||
top: `calc(1rem + ${55 + Math.ceil((navZoom / 20) * (navZoom * 0.01))}px)`,
|
||||
overflow: 'visible',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Apps appsInfo={appsInfo} />
|
||||
</Modal>
|
||||
|
||||
{this.state.preview && <Preview setup={() => window.location.reload()} />}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import Tooltip from 'components/helpers/tooltip/Tooltip';
|
||||
import './scss/index.scss';
|
||||
import { MdLinkOff } from 'react-icons/md';
|
||||
|
||||
const Apps = ({ appsInfo }) => {
|
||||
return (
|
||||
<div className="appsShortcutContainer">
|
||||
{appsInfo.length > 0 ? (
|
||||
appsInfo.map((info, i) => (
|
||||
<Tooltip
|
||||
title={info.name.split(' ')[0]}
|
||||
subtitle={info.name.split(' ').slice(1).join(' ')}
|
||||
key={i}
|
||||
>
|
||||
<a href={info.url} className="appsIcon">
|
||||
<img
|
||||
src={
|
||||
info.icon === ''
|
||||
? `https://icon.horse/icon/ ${info.url.replace('https://', '').replace('http://', '')}`
|
||||
: info.icon
|
||||
}
|
||||
width="40px"
|
||||
height="40px"
|
||||
alt="Google"
|
||||
/>
|
||||
<span>{info.name}</span>
|
||||
</a>
|
||||
</Tooltip>
|
||||
))
|
||||
) : (
|
||||
<div className="noAppsContainer">
|
||||
<h3>
|
||||
No app links found
|
||||
<MdLinkOff />
|
||||
</h3>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Apps;
|
|
@ -0,0 +1,77 @@
|
|||
@import 'scss/variables';
|
||||
|
||||
$appsWidth: 21rem;
|
||||
|
||||
.appsShortcutContainer {
|
||||
max-height: 35rem;
|
||||
overflow-y: auto;
|
||||
// scrollbar-width: thin;
|
||||
border-radius: 0.8em;
|
||||
padding: 1.2em;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, auto);
|
||||
grid-auto-rows: 100px;
|
||||
gap: 10px;
|
||||
place-items: center;
|
||||
|
||||
@include themed {
|
||||
background: t($modal-secondaryColour);
|
||||
}
|
||||
|
||||
.noAppsContainer {
|
||||
h3 {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
svg {
|
||||
font-size: 30px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.appsIcon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
text-decoration: none;
|
||||
padding: 10px;
|
||||
border-radius: 0.8em;
|
||||
cursor: pointer;
|
||||
width: 5rem;
|
||||
height: 4.7rem;
|
||||
transition: 0.5s;
|
||||
|
||||
img {
|
||||
border-radius: 0.6rem;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
span {
|
||||
white-space: initial;
|
||||
}
|
||||
|
||||
height: max-content;
|
||||
}
|
||||
|
||||
@include themed {
|
||||
color: t($color);
|
||||
|
||||
&:hover {
|
||||
background: t($modal-sidebarActive);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -170,12 +170,6 @@ h5 {
|
|||
gap: 20px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.link {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.marketplaceCondition {
|
||||
|
|
|
@ -2,16 +2,113 @@ import variables from 'modules/variables';
|
|||
|
||||
import { useState, memo } from 'react';
|
||||
|
||||
import Modal from 'react-modal';
|
||||
import { MdAddLink } from 'react-icons/md';
|
||||
|
||||
import AddModal from './quicklinks/AddModal';
|
||||
|
||||
import Checkbox from '../Checkbox';
|
||||
import Dropdown from '../Dropdown';
|
||||
|
||||
import SettingsItem from '../SettingsItem';
|
||||
import Header from '../Header';
|
||||
import { getTitleFromUrl, isValidUrl } from './utils/utils';
|
||||
import QuickLink from './quicklinks/QuickLink';
|
||||
|
||||
function Navbar() {
|
||||
const [showRefreshOptions, setShowRefreshOptions] = useState(
|
||||
localStorage.getItem('refresh') === 'true',
|
||||
);
|
||||
const [appsModalInfo, setAppsModalInfo] = useState({
|
||||
newLink: false,
|
||||
edit: false,
|
||||
items: JSON.parse(localStorage.getItem('applinks')),
|
||||
urlError: '',
|
||||
iconError: '',
|
||||
editData: null,
|
||||
});
|
||||
|
||||
const addLink = async (name, url, icon) => {
|
||||
const data = JSON.parse(localStorage.getItem('applinks'));
|
||||
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
url = 'http://' + url;
|
||||
}
|
||||
|
||||
if (url.length <= 0 || isValidUrl(url) === false) {
|
||||
return setAppsModalInfo((oldState) => ({
|
||||
...oldState,
|
||||
urlError: variables.getMessage('widgets.quicklinks.url_error'),
|
||||
}));
|
||||
}
|
||||
|
||||
if (icon.length > 0 && isValidUrl(icon) === false) {
|
||||
return this.setState((oldState) => ({
|
||||
...oldState,
|
||||
iconError: variables.getMessage('widgets.quicklinks.url_error'),
|
||||
}));
|
||||
}
|
||||
|
||||
data.push({
|
||||
name: name || (await getTitleFromUrl(url)),
|
||||
url,
|
||||
icon: icon || '',
|
||||
key: Math.random().toString(36).substring(7) + 1,
|
||||
});
|
||||
|
||||
localStorage.setItem('applinks', JSON.stringify(data));
|
||||
|
||||
setAppsModalInfo({
|
||||
newLink: false,
|
||||
edit: false,
|
||||
items: data,
|
||||
urlError: '',
|
||||
iconError: '',
|
||||
});
|
||||
|
||||
variables.stats.postEvent('feature', 'App link add');
|
||||
};
|
||||
|
||||
const startEditLink = (data) => {
|
||||
setAppsModalInfo((oldState) => ({
|
||||
...oldState,
|
||||
edit: true,
|
||||
editData: data,
|
||||
}));
|
||||
};
|
||||
|
||||
const editLink = async (og, name, url, icon) => {
|
||||
const data = JSON.parse(localStorage.getItem('applinks'));
|
||||
const dataobj = data.find((i) => i.key === og.key);
|
||||
dataobj.name = name || (await getTitleFromUrl(url));
|
||||
dataobj.url = url;
|
||||
dataobj.icon = icon || '';
|
||||
|
||||
localStorage.setItem('applinks', JSON.stringify(data));
|
||||
|
||||
setAppsModalInfo((oldState) => ({
|
||||
...oldState,
|
||||
items: data,
|
||||
edit: false,
|
||||
newLink: false,
|
||||
}));
|
||||
};
|
||||
|
||||
const deleteLink = (key, event) => {
|
||||
event.preventDefault();
|
||||
|
||||
// remove link from array
|
||||
const data = JSON.parse(localStorage.getItem('applinks')).filter((i) => i.key !== key);
|
||||
|
||||
localStorage.setItem('applinks', JSON.stringify(data));
|
||||
|
||||
setAppsModalInfo((oldState) => ({
|
||||
...oldState,
|
||||
items: data,
|
||||
}));
|
||||
|
||||
variables.stats.postEvent('feature', 'App link delete');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -27,7 +124,7 @@ function Navbar() {
|
|||
subtitle={variables.getMessage(
|
||||
'modals.main.settings.sections.appearance.navbar.additional',
|
||||
)}
|
||||
final={!showRefreshOptions}
|
||||
final={false}
|
||||
>
|
||||
<Checkbox
|
||||
name="navbarHover"
|
||||
|
@ -55,6 +152,12 @@ function Navbar() {
|
|||
text={variables.getMessage('widgets.navbar.todo.title')}
|
||||
category="navbar"
|
||||
/>
|
||||
|
||||
<Checkbox
|
||||
name="appsEnabled"
|
||||
text={variables.getMessage('widgets.navbar.apps.title')}
|
||||
category="navbar"
|
||||
/>
|
||||
</SettingsItem>
|
||||
{showRefreshOptions && (
|
||||
<SettingsItem
|
||||
|
@ -62,7 +165,7 @@ function Navbar() {
|
|||
subtitle={variables.getMessage(
|
||||
'modals.main.settings.sections.appearance.navbar.refresh_subtitle',
|
||||
)}
|
||||
final={true}
|
||||
final={false}
|
||||
>
|
||||
<Dropdown name="refreshOption" category="navbar">
|
||||
<option value="page">
|
||||
|
@ -83,6 +186,52 @@ function Navbar() {
|
|||
</Dropdown>
|
||||
</SettingsItem>
|
||||
)}
|
||||
|
||||
<SettingsItem
|
||||
title={variables.getMessage('widgets.navbar.apps.title')}
|
||||
subtitle={variables.getMessage(
|
||||
'modals.main.settings.sections.appearance.navbar.apps_subtitle',
|
||||
)}
|
||||
final={true}
|
||||
>
|
||||
<button onClick={() => setAppsModalInfo((oldState) => ({ ...oldState, newLink: true }))}>
|
||||
{variables.getMessage('modals.main.settings.sections.quicklinks.add_link')}
|
||||
<MdAddLink />
|
||||
</button>
|
||||
</SettingsItem>
|
||||
|
||||
<div className="messagesContainer">
|
||||
{appsModalInfo.items.map((item, i) => (
|
||||
<QuickLink
|
||||
key={i}
|
||||
item={item}
|
||||
startEditLink={() => startEditLink(item)}
|
||||
deleteLink={(key, e) => deleteLink(key, e)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
closeTimeoutMS={100}
|
||||
onRequestClose={() =>
|
||||
setAppsModalInfo((oldState) => ({ ...oldState, newLink: false, edit: false }))
|
||||
}
|
||||
isOpen={appsModalInfo.edit || appsModalInfo.newLink}
|
||||
className="Modal resetmodal mainModal"
|
||||
overlayClassName="Overlay resetoverlay"
|
||||
ariaHideApp={false}
|
||||
>
|
||||
<AddModal
|
||||
urlError={appsModalInfo.urlError}
|
||||
addLink={(name, url, icon) => addLink(name, url, icon)}
|
||||
editLink={(og, name, url, icon) => editLink(og, name, url, icon)}
|
||||
edit={appsModalInfo.edit}
|
||||
editData={appsModalInfo.editData}
|
||||
closeModal={() =>
|
||||
setAppsModalInfo((oldState) => ({ ...oldState, newLink: false, edit: false }))
|
||||
}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import SettingsItem from '../SettingsItem';
|
|||
import AddModal from './quicklinks/AddModal';
|
||||
|
||||
import EventBus from 'modules/helpers/eventbus';
|
||||
import QuickLink from './quicklinks/QuickLink';
|
||||
import { getTitleFromUrl, isValidUrl } from './utils/utils';
|
||||
|
||||
export default class QuickLinks extends PureComponent {
|
||||
constructor() {
|
||||
|
@ -42,28 +44,24 @@ export default class QuickLinks extends PureComponent {
|
|||
async addLink(name, url, icon) {
|
||||
const data = JSON.parse(localStorage.getItem('quicklinks'));
|
||||
|
||||
// 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,6}\b([-a-zA-Z0-9()!@:%_.~#?&=]*)/;
|
||||
if (url.length <= 0 || urlRegex.test(url) === false) {
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
url = 'http://' + url;
|
||||
}
|
||||
|
||||
if (url.length <= 0 || isValidUrl(url) === false) {
|
||||
return this.setState({
|
||||
urlError: variables.getMessage('widgets.quicklinks.url_error'),
|
||||
});
|
||||
}
|
||||
|
||||
if (icon.length > 0 && urlRegex.test(icon) === false) {
|
||||
if (icon.length > 0 && isValidUrl(icon) === false) {
|
||||
return this.setState({
|
||||
iconError: variables.getMessage('widgets.quicklinks.url_error'),
|
||||
});
|
||||
}
|
||||
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
url = 'http://' + url;
|
||||
}
|
||||
|
||||
data.push({
|
||||
name: name || (await this.getTitle(url)),
|
||||
name: name || (await getTitleFromUrl(url)),
|
||||
url,
|
||||
icon: icon || '',
|
||||
key: Math.random().toString(36).substring(7) + 1,
|
||||
|
@ -92,7 +90,7 @@ export default class QuickLinks extends PureComponent {
|
|||
async editLink(og, name, url, icon) {
|
||||
const data = JSON.parse(localStorage.getItem('quicklinks'));
|
||||
const dataobj = data.find((i) => i.key === og.key);
|
||||
dataobj.name = name || (await this.getTitle(url));
|
||||
dataobj.name = name || (await getTitleFromUrl(url));
|
||||
dataobj.url = url;
|
||||
dataobj.icon = icon || '';
|
||||
|
||||
|
@ -105,24 +103,6 @@ export default class QuickLinks extends PureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
async getTitle(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;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'quicklinks') {
|
||||
|
@ -144,77 +124,6 @@ export default class QuickLinks extends PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
let target,
|
||||
rel = null;
|
||||
if (localStorage.getItem('quicklinksnewtab') === 'true') {
|
||||
target = '_blank';
|
||||
rel = 'noopener noreferrer';
|
||||
}
|
||||
|
||||
const useText = localStorage.getItem('quicklinksText') === 'true';
|
||||
|
||||
const quickLink = (item) => {
|
||||
if (useText) {
|
||||
return (
|
||||
<a
|
||||
className="quicklinkstext"
|
||||
key={item.key}
|
||||
onContextMenu={(e) => this.deleteLink(item.key, e)}
|
||||
href={item.url}
|
||||
target={target}
|
||||
rel={rel}
|
||||
draggable={false}
|
||||
>
|
||||
{item.name}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
const img =
|
||||
item.icon ||
|
||||
'https://icon.horse/icon/ ' + item.url.replace('https://', '').replace('http://', '');
|
||||
|
||||
const link = (
|
||||
<div className="messageMap" key={item.key}>
|
||||
<div className="icon">
|
||||
<img
|
||||
src={img}
|
||||
alt={item.name}
|
||||
draggable={false}
|
||||
style={{ height: '30px', width: '30px' }}
|
||||
/>
|
||||
</div>
|
||||
<div className="messageText">
|
||||
<div className="title">{item.name}</div>
|
||||
<div className="subtitle">
|
||||
<a
|
||||
className="quicklinknostyle"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={item.url}
|
||||
>
|
||||
{item.url}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="messageAction">
|
||||
<button className="deleteButton" onClick={() => this.startEditLink(item)}>
|
||||
{variables.getMessage('modals.main.settings.sections.quicklinks.edit')}
|
||||
<MdEdit />
|
||||
</button>
|
||||
<button className="deleteButton" onClick={(e) => this.deleteLink(item.key, e)}>
|
||||
{variables.getMessage('modals.main.marketplace.product.buttons.remove')}
|
||||
<MdCancel />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return link;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
|
@ -292,7 +201,14 @@ export default class QuickLinks extends PureComponent {
|
|||
)}
|
||||
|
||||
<div className="messagesContainer" ref={this.quicklinksContainer}>
|
||||
{this.state.items.map((item) => quickLink(item))}
|
||||
{this.state.items.map((item, i) => (
|
||||
<QuickLink
|
||||
key={i}
|
||||
item={item}
|
||||
startEditLink={() => this.startEditLink(item)}
|
||||
deleteLink={(key, e) => this.deleteLink(key, e)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<Modal
|
||||
closeTimeoutMS={100}
|
||||
|
@ -308,7 +224,9 @@ export default class QuickLinks extends PureComponent {
|
|||
editLink={(og, name, url, icon) => this.editLink(og, name, url, icon)}
|
||||
edit={this.state.edit}
|
||||
editData={this.state.editData}
|
||||
closeModal={() => this.setState({ showAddModal: false, urlError: '', iconError: '' })}
|
||||
closeModal={() =>
|
||||
this.setState({ showAddModal: false, urlError: '', iconError: '', edit: false })
|
||||
}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import variables from 'modules/variables';
|
||||
|
||||
import { MdEdit, MdCancel } from 'react-icons/md';
|
||||
|
||||
const QuickLink = ({ item, deleteLink, startEditLink }) => {
|
||||
let target,
|
||||
rel = null;
|
||||
if (localStorage.getItem('quicklinksnewtab') === 'true') {
|
||||
target = '_blank';
|
||||
rel = 'noopener noreferrer';
|
||||
}
|
||||
|
||||
const useText = localStorage.getItem('quicklinksText') === 'true';
|
||||
|
||||
if (useText) {
|
||||
return (
|
||||
<a
|
||||
className="quicklinkstext"
|
||||
onContextMenu={(e) => deleteLink(item.key, e)}
|
||||
href={item.url}
|
||||
target={target}
|
||||
rel={rel}
|
||||
draggable={false}
|
||||
>
|
||||
{item.name}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
const img =
|
||||
item.icon ||
|
||||
'https://icon.horse/icon/ ' + item.url.replace('https://', '').replace('http://', '');
|
||||
|
||||
return (
|
||||
<div className="messageMap">
|
||||
<div className="icon">
|
||||
<img
|
||||
src={img}
|
||||
alt={item.name}
|
||||
draggable={false}
|
||||
style={{ height: '30px', width: '30px' }}
|
||||
/>
|
||||
</div>
|
||||
<div className="messageText">
|
||||
<div className="title">{item.name}</div>
|
||||
<div className="subtitle">
|
||||
<a className="quicklinknostyle" target="_blank" rel="noopener noreferrer" href={item.url}>
|
||||
{item.url}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="messageAction">
|
||||
<button className="deleteButton" onClick={() => startEditLink(item)}>
|
||||
{variables.getMessage('modals.main.settings.sections.quicklinks.edit')}
|
||||
<MdEdit />
|
||||
</button>
|
||||
<button className="deleteButton" onClick={(e) => deleteLink(item.key, e)}>
|
||||
{variables.getMessage('modals.main.marketplace.product.buttons.remove')}
|
||||
<MdCancel />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default QuickLink;
|
|
@ -0,0 +1,28 @@
|
|||
const getTitleFromUrl = async (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;
|
||||
};
|
||||
|
||||
const 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,6}\b([-a-zA-Z0-9()!@:%_.~#?&=]*)/;
|
||||
|
||||
return urlRegex.test(url);
|
||||
};
|
||||
|
||||
export { getTitleFromUrl, isValidUrl };
|
|
@ -206,7 +206,6 @@ function PhotoInformation({ info, url, api }) {
|
|||
>
|
||||
<div className={photoMapClassList}>
|
||||
{useMapIcon || photoMap() === null ? <MdLocationOn /> : ''}
|
||||
<h1>{photoMap}</h1>
|
||||
{photoMap()}
|
||||
</div>
|
||||
{showingPhotoMap && (
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import variables from 'modules/variables';
|
||||
import { PureComponent, createRef } from 'react';
|
||||
|
||||
import { MdRefresh, MdSettings } from 'react-icons/md';
|
||||
import { MdRefresh, MdSettings, MdOutlineApps } from 'react-icons/md';
|
||||
|
||||
import Notes from './Notes';
|
||||
import Todo from './Todo';
|
||||
|
@ -21,6 +21,7 @@ class Navbar extends PureComponent {
|
|||
refreshText: '',
|
||||
refreshEnabled: localStorage.getItem('refresh'),
|
||||
refreshOption: localStorage.getItem('refreshOption') || '',
|
||||
appsOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -123,6 +124,21 @@ class Navbar extends PureComponent {
|
|||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{localStorage.getItem('appsEnabled') === 'true' && (
|
||||
<>
|
||||
<Tooltip title={variables.getMessage('widgets.navbar.apps.title')}>
|
||||
<button
|
||||
style={{ fontSize: this.state.zoomFontSize }}
|
||||
onClick={() => this.props.openModal('appsModal')}
|
||||
id="appsShortcutBtn"
|
||||
>
|
||||
<MdOutlineApps className="topicons" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Tooltip
|
||||
title={variables.getMessage('modals.main.navbar.settings', {
|
||||
type: variables.getMessage(
|
||||
|
|
|
@ -81,3 +81,13 @@
|
|||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.appsmodal {
|
||||
width: fit-content;
|
||||
padding: 10px;
|
||||
border-radius: 1em;
|
||||
|
||||
@include themed {
|
||||
background: t($modal-sidebarActive);
|
||||
}
|
||||
}
|
|
@ -262,5 +262,13 @@
|
|||
{
|
||||
"name": "photoMap",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "appsEnabled",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"name": "applinks",
|
||||
"value": "[]"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -136,6 +136,15 @@ export function loadSettings(hotreload) {
|
|||
);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
|
|
@ -67,6 +67,10 @@
|
|||
"pin": "Pin",
|
||||
"add": "Add",
|
||||
"no_todos": "No Todos"
|
||||
},
|
||||
"apps": {
|
||||
"title": "Apps",
|
||||
"no_apps": "No app links found"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -361,7 +365,8 @@
|
|||
"refresh_options": {
|
||||
"none": "None",
|
||||
"page": "Page"
|
||||
}
|
||||
},
|
||||
"apps_subtitle": "Create a shortcut of your other commonly used websites."
|
||||
},
|
||||
"font": {
|
||||
"title": "Font",
|
||||
|
|
|
@ -67,6 +67,10 @@
|
|||
"pin": "Pin",
|
||||
"add": "Add",
|
||||
"no_todos": "No Todos"
|
||||
},
|
||||
"apps": {
|
||||
"title": "Apps",
|
||||
"no_apps": "No app links found"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -361,7 +365,8 @@
|
|||
"refresh_options": {
|
||||
"none": "None",
|
||||
"page": "Page"
|
||||
}
|
||||
},
|
||||
"apps_subtitle": "Create a shortcut of your other commonly used websites."
|
||||
},
|
||||
"font": {
|
||||
"title": "Font",
|
||||
|
|
Loading…
Reference in New Issue