mirror of
https://github.com/pawelmalak/flame.git
synced 2025-07-19 11:39:36 +02:00
Merge pull request #84 from pawelmalak/local-search
Added searching (filtering) of local apps and bookmarks
This commit is contained in:
commit
43f38a2f44
17 changed files with 421 additions and 4819 deletions
|
@ -32,7 +32,7 @@ git clone https://github.com/pawelmalak/flame
|
||||||
cd flame
|
cd flame
|
||||||
|
|
||||||
# run only once
|
# run only once
|
||||||
npm run dev-init
|
npm run dev:init
|
||||||
|
|
||||||
# start backend and frontend development servers
|
# start backend and frontend development servers
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|
|
@ -7,6 +7,7 @@ import AppCard from '../AppCard/AppCard';
|
||||||
interface ComponentProps {
|
interface ComponentProps {
|
||||||
apps: App[];
|
apps: App[];
|
||||||
totalApps?: number;
|
totalApps?: number;
|
||||||
|
searching: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AppGrid = (props: ComponentProps): JSX.Element => {
|
const AppGrid = (props: ComponentProps): JSX.Element => {
|
||||||
|
@ -16,26 +17,37 @@ const AppGrid = (props: ComponentProps): JSX.Element => {
|
||||||
apps = (
|
apps = (
|
||||||
<div className={classes.AppGrid}>
|
<div className={classes.AppGrid}>
|
||||||
{props.apps.map((app: App): JSX.Element => {
|
{props.apps.map((app: App): JSX.Element => {
|
||||||
return <AppCard
|
return <AppCard key={app.id} app={app} />;
|
||||||
key={app.id}
|
|
||||||
app={app}
|
|
||||||
/>
|
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
} else {
|
} else {
|
||||||
if (props.totalApps) {
|
if (props.totalApps) {
|
||||||
|
if (props.searching) {
|
||||||
apps = (
|
apps = (
|
||||||
<p className={classes.AppsMessage}>There are no pinned applications. You can pin them from the <Link to='/applications'>/applications</Link> menu</p>
|
<p className={classes.AppsMessage}>
|
||||||
|
No apps match your search criteria
|
||||||
|
</p>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
apps = (
|
apps = (
|
||||||
<p className={classes.AppsMessage}>You don't have any applications. You can add a new one from <Link to='/applications'>/applications</Link> menu</p>
|
<p className={classes.AppsMessage}>
|
||||||
|
There are no pinned applications. You can pin them from the{' '}
|
||||||
|
<Link to="/applications">/applications</Link> menu
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
apps = (
|
||||||
|
<p className={classes.AppsMessage}>
|
||||||
|
You don't have any applications. You can add a new one from{' '}
|
||||||
|
<Link to="/applications">/applications</Link> menu
|
||||||
|
</p>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return apps;
|
return apps;
|
||||||
}
|
};
|
||||||
|
|
||||||
export default AppGrid;
|
export default AppGrid;
|
|
@ -27,14 +27,11 @@ interface ComponentProps {
|
||||||
getApps: Function;
|
getApps: Function;
|
||||||
apps: App[];
|
apps: App[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
searching: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Apps = (props: ComponentProps): JSX.Element => {
|
const Apps = (props: ComponentProps): JSX.Element => {
|
||||||
const {
|
const { getApps, apps, loading, searching = false } = props;
|
||||||
getApps,
|
|
||||||
apps,
|
|
||||||
loading
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const [modalIsOpen, setModalIsOpen] = useState(false);
|
const [modalIsOpen, setModalIsOpen] = useState(false);
|
||||||
const [isInEdit, setIsInEdit] = useState(false);
|
const [isInEdit, setIsInEdit] = useState(false);
|
||||||
|
@ -47,8 +44,8 @@ const Apps = (props: ComponentProps): JSX.Element => {
|
||||||
orderId: 0,
|
orderId: 0,
|
||||||
id: 0,
|
id: 0,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date()
|
updatedAt: new Date(),
|
||||||
})
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (apps.length === 0) {
|
if (apps.length === 0) {
|
||||||
|
@ -59,63 +56,57 @@ const Apps = (props: ComponentProps): JSX.Element => {
|
||||||
const toggleModal = (): void => {
|
const toggleModal = (): void => {
|
||||||
setModalIsOpen(!modalIsOpen);
|
setModalIsOpen(!modalIsOpen);
|
||||||
setIsInUpdate(false);
|
setIsInUpdate(false);
|
||||||
}
|
};
|
||||||
|
|
||||||
const toggleEdit = (): void => {
|
const toggleEdit = (): void => {
|
||||||
setIsInEdit(!isInEdit);
|
setIsInEdit(!isInEdit);
|
||||||
setIsInUpdate(false);
|
setIsInUpdate(false);
|
||||||
}
|
};
|
||||||
|
|
||||||
const toggleUpdate = (app: App): void => {
|
const toggleUpdate = (app: App): void => {
|
||||||
setAppInUpdate(app);
|
setAppInUpdate(app);
|
||||||
setIsInUpdate(true);
|
setIsInUpdate(true);
|
||||||
setModalIsOpen(true);
|
setModalIsOpen(true);
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Modal isOpen={modalIsOpen} setIsOpen={setModalIsOpen}>
|
<Modal isOpen={modalIsOpen} setIsOpen={setModalIsOpen}>
|
||||||
{!isInUpdate
|
{!isInUpdate ? (
|
||||||
? <AppForm modalHandler={toggleModal} />
|
<AppForm modalHandler={toggleModal} />
|
||||||
: <AppForm modalHandler={toggleModal} app={appInUpdate} />
|
) : (
|
||||||
}
|
<AppForm modalHandler={toggleModal} app={appInUpdate} />
|
||||||
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Headline
|
<Headline
|
||||||
title='All Applications'
|
title="All Applications"
|
||||||
subtitle={(<Link to='/'>Go back</Link>)}
|
subtitle={<Link to="/">Go back</Link>}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className={classes.ActionsContainer}>
|
<div className={classes.ActionsContainer}>
|
||||||
<ActionButton
|
<ActionButton name="Add" icon="mdiPlusBox" handler={toggleModal} />
|
||||||
name='Add'
|
<ActionButton name="Edit" icon="mdiPencil" handler={toggleEdit} />
|
||||||
icon='mdiPlusBox'
|
|
||||||
handler={toggleModal}
|
|
||||||
/>
|
|
||||||
<ActionButton
|
|
||||||
name='Edit'
|
|
||||||
icon='mdiPencil'
|
|
||||||
handler={toggleEdit}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={classes.Apps}>
|
<div className={classes.Apps}>
|
||||||
{loading
|
{loading ? (
|
||||||
? <Spinner />
|
<Spinner />
|
||||||
: (!isInEdit
|
) : !isInEdit ? (
|
||||||
? <AppGrid apps={apps} />
|
<AppGrid apps={apps} searching />
|
||||||
: <AppTable updateAppHandler={toggleUpdate} />)
|
) : (
|
||||||
}
|
<AppTable updateAppHandler={toggleUpdate} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state: GlobalState) => {
|
const mapStateToProps = (state: GlobalState) => {
|
||||||
return {
|
return {
|
||||||
apps: state.app.apps,
|
apps: state.app.apps,
|
||||||
loading: state.app.loading
|
loading: state.app.loading,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps, { getApps })(Apps);
|
export default connect(mapStateToProps, { getApps })(Apps);
|
|
@ -9,30 +9,49 @@ import BookmarkCard from '../BookmarkCard/BookmarkCard';
|
||||||
interface ComponentProps {
|
interface ComponentProps {
|
||||||
categories: Category[];
|
categories: Category[];
|
||||||
totalCategories?: number;
|
totalCategories?: number;
|
||||||
|
searching: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BookmarkGrid = (props: ComponentProps): JSX.Element => {
|
const BookmarkGrid = (props: ComponentProps): JSX.Element => {
|
||||||
let bookmarks: JSX.Element;
|
let bookmarks: JSX.Element;
|
||||||
|
|
||||||
if (props.categories.length > 0) {
|
if (props.categories.length > 0) {
|
||||||
|
if (props.searching && props.categories[0].bookmarks.length === 0) {
|
||||||
|
bookmarks = (
|
||||||
|
<p className={classes.BookmarksMessage}>
|
||||||
|
No bookmarks match your search criteria
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
bookmarks = (
|
bookmarks = (
|
||||||
<div className={classes.BookmarkGrid}>
|
<div className={classes.BookmarkGrid}>
|
||||||
{props.categories.map((category: Category): JSX.Element => <BookmarkCard category={category} key={category.id} />)}
|
{props.categories.map(
|
||||||
|
(category: Category): JSX.Element => (
|
||||||
|
<BookmarkCard category={category} key={category.id} />
|
||||||
|
)
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (props.totalCategories) {
|
if (props.totalCategories) {
|
||||||
bookmarks = (
|
bookmarks = (
|
||||||
<p className={classes.BookmarksMessage}>There are no pinned categories. You can pin them from the <Link to='/bookmarks'>/bookmarks</Link> menu</p>
|
<p className={classes.BookmarksMessage}>
|
||||||
|
There are no pinned categories. You can pin them from the{' '}
|
||||||
|
<Link to="/bookmarks">/bookmarks</Link> menu
|
||||||
|
</p>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
bookmarks = (
|
bookmarks = (
|
||||||
<p className={classes.BookmarksMessage}>You don't have any bookmarks. You can add a new one from <Link to='/bookmarks'>/bookmarks</Link> menu</p>
|
<p className={classes.BookmarksMessage}>
|
||||||
|
You don't have any bookmarks. You can add a new one from{' '}
|
||||||
|
<Link to="/bookmarks">/bookmarks</Link> menu
|
||||||
|
</p>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bookmarks;
|
return bookmarks;
|
||||||
}
|
};
|
||||||
|
|
||||||
export default BookmarkGrid;
|
export default BookmarkGrid;
|
|
@ -20,24 +20,23 @@ interface ComponentProps {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
categories: Category[];
|
categories: Category[];
|
||||||
getCategories: () => void;
|
getCategories: () => void;
|
||||||
|
searching: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ContentType {
|
export enum ContentType {
|
||||||
category,
|
category,
|
||||||
bookmark
|
bookmark,
|
||||||
}
|
}
|
||||||
|
|
||||||
const Bookmarks = (props: ComponentProps): JSX.Element => {
|
const Bookmarks = (props: ComponentProps): JSX.Element => {
|
||||||
const {
|
const { getCategories, categories, loading, searching = false } = props;
|
||||||
getCategories,
|
|
||||||
categories,
|
|
||||||
loading
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const [modalIsOpen, setModalIsOpen] = useState(false);
|
const [modalIsOpen, setModalIsOpen] = useState(false);
|
||||||
const [formContentType, setFormContentType] = useState(ContentType.category);
|
const [formContentType, setFormContentType] = useState(ContentType.category);
|
||||||
const [isInEdit, setIsInEdit] = useState(false);
|
const [isInEdit, setIsInEdit] = useState(false);
|
||||||
const [tableContentType, setTableContentType] = useState(ContentType.category);
|
const [tableContentType, setTableContentType] = useState(
|
||||||
|
ContentType.category
|
||||||
|
);
|
||||||
const [isInUpdate, setIsInUpdate] = useState(false);
|
const [isInUpdate, setIsInUpdate] = useState(false);
|
||||||
const [categoryInUpdate, setCategoryInUpdate] = useState<Category>({
|
const [categoryInUpdate, setCategoryInUpdate] = useState<Category>({
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -46,8 +45,8 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
|
||||||
orderId: 0,
|
orderId: 0,
|
||||||
bookmarks: [],
|
bookmarks: [],
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date()
|
updatedAt: new Date(),
|
||||||
})
|
});
|
||||||
const [bookmarkInUpdate, setBookmarkInUpdate] = useState<Bookmark>({
|
const [bookmarkInUpdate, setBookmarkInUpdate] = useState<Bookmark>({
|
||||||
name: '',
|
name: '',
|
||||||
url: '',
|
url: '',
|
||||||
|
@ -55,24 +54,24 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
|
||||||
icon: '',
|
icon: '',
|
||||||
id: -1,
|
id: -1,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date()
|
updatedAt: new Date(),
|
||||||
})
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (categories.length === 0) {
|
if (categories.length === 0) {
|
||||||
getCategories();
|
getCategories();
|
||||||
}
|
}
|
||||||
}, [getCategories])
|
}, [getCategories]);
|
||||||
|
|
||||||
const toggleModal = (): void => {
|
const toggleModal = (): void => {
|
||||||
setModalIsOpen(!modalIsOpen);
|
setModalIsOpen(!modalIsOpen);
|
||||||
}
|
};
|
||||||
|
|
||||||
const addActionHandler = (contentType: ContentType) => {
|
const addActionHandler = (contentType: ContentType) => {
|
||||||
setFormContentType(contentType);
|
setFormContentType(contentType);
|
||||||
setIsInUpdate(false);
|
setIsInUpdate(false);
|
||||||
toggleModal();
|
toggleModal();
|
||||||
}
|
};
|
||||||
|
|
||||||
const editActionHandler = (contentType: ContentType) => {
|
const editActionHandler = (contentType: ContentType) => {
|
||||||
// We're in the edit mode and the same button was clicked - go back to list
|
// We're in the edit mode and the same button was clicked - go back to list
|
||||||
|
@ -82,11 +81,11 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
|
||||||
setIsInEdit(true);
|
setIsInEdit(true);
|
||||||
setTableContentType(contentType);
|
setTableContentType(contentType);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const instanceOfCategory = (object: any): object is Category => {
|
const instanceOfCategory = (object: any): object is Category => {
|
||||||
return 'bookmarks' in object;
|
return 'bookmarks' in object;
|
||||||
}
|
};
|
||||||
|
|
||||||
const goToUpdateMode = (data: Category | Bookmark): void => {
|
const goToUpdateMode = (data: Category | Bookmark): void => {
|
||||||
setIsInUpdate(true);
|
setIsInUpdate(true);
|
||||||
|
@ -98,67 +97,76 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
|
||||||
setBookmarkInUpdate(data);
|
setBookmarkInUpdate(data);
|
||||||
}
|
}
|
||||||
toggleModal();
|
toggleModal();
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Modal isOpen={modalIsOpen} setIsOpen={toggleModal}>
|
<Modal isOpen={modalIsOpen} setIsOpen={toggleModal}>
|
||||||
{!isInUpdate
|
{!isInUpdate ? (
|
||||||
? <BookmarkForm modalHandler={toggleModal} contentType={formContentType} />
|
<BookmarkForm
|
||||||
: formContentType === ContentType.category
|
modalHandler={toggleModal}
|
||||||
? <BookmarkForm modalHandler={toggleModal} contentType={formContentType} category={categoryInUpdate} />
|
contentType={formContentType}
|
||||||
: <BookmarkForm modalHandler={toggleModal} contentType={formContentType} bookmark={bookmarkInUpdate} />
|
/>
|
||||||
}
|
) : formContentType === ContentType.category ? (
|
||||||
|
<BookmarkForm
|
||||||
|
modalHandler={toggleModal}
|
||||||
|
contentType={formContentType}
|
||||||
|
category={categoryInUpdate}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<BookmarkForm
|
||||||
|
modalHandler={toggleModal}
|
||||||
|
contentType={formContentType}
|
||||||
|
bookmark={bookmarkInUpdate}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Headline
|
<Headline title="All Bookmarks" subtitle={<Link to="/">Go back</Link>} />
|
||||||
title='All Bookmarks'
|
|
||||||
subtitle={(<Link to='/'>Go back</Link>)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className={classes.ActionsContainer}>
|
<div className={classes.ActionsContainer}>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
name='Add Category'
|
name="Add Category"
|
||||||
icon='mdiPlusBox'
|
icon="mdiPlusBox"
|
||||||
handler={() => addActionHandler(ContentType.category)}
|
handler={() => addActionHandler(ContentType.category)}
|
||||||
/>
|
/>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
name='Add Bookmark'
|
name="Add Bookmark"
|
||||||
icon='mdiPlusBox'
|
icon="mdiPlusBox"
|
||||||
handler={() => addActionHandler(ContentType.bookmark)}
|
handler={() => addActionHandler(ContentType.bookmark)}
|
||||||
/>
|
/>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
name='Edit Categories'
|
name="Edit Categories"
|
||||||
icon='mdiPencil'
|
icon="mdiPencil"
|
||||||
handler={() => editActionHandler(ContentType.category)}
|
handler={() => editActionHandler(ContentType.category)}
|
||||||
/>
|
/>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
name='Edit Bookmarks'
|
name="Edit Bookmarks"
|
||||||
icon='mdiPencil'
|
icon="mdiPencil"
|
||||||
handler={() => editActionHandler(ContentType.bookmark)}
|
handler={() => editActionHandler(ContentType.bookmark)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loading
|
{loading ? (
|
||||||
? <Spinner />
|
<Spinner />
|
||||||
: (!isInEdit
|
) : !isInEdit ? (
|
||||||
? <BookmarkGrid categories={categories} />
|
<BookmarkGrid categories={categories} searching />
|
||||||
: <BookmarkTable
|
) : (
|
||||||
|
<BookmarkTable
|
||||||
contentType={tableContentType}
|
contentType={tableContentType}
|
||||||
categories={categories}
|
categories={categories}
|
||||||
updateHandler={goToUpdateMode}
|
updateHandler={goToUpdateMode}
|
||||||
/>
|
/>
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
</Container>
|
</Container>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state: GlobalState) => {
|
const mapStateToProps = (state: GlobalState) => {
|
||||||
return {
|
return {
|
||||||
loading: state.bookmark.loading,
|
loading: state.bookmark.loading,
|
||||||
categories: state.bookmark.categories
|
categories: state.bookmark.categories,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps, { getCategories })(Bookmarks);
|
export default connect(mapStateToProps, { getCategories })(Bookmarks);
|
|
@ -47,13 +47,16 @@ const Home = (props: ComponentProps): JSX.Element => {
|
||||||
appsLoading,
|
appsLoading,
|
||||||
getCategories,
|
getCategories,
|
||||||
categories,
|
categories,
|
||||||
categoriesLoading
|
categoriesLoading,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [header, setHeader] = useState({
|
const [header, setHeader] = useState({
|
||||||
dateTime: dateTime(),
|
dateTime: dateTime(),
|
||||||
greeting: greeter()
|
greeting: greeter(),
|
||||||
})
|
});
|
||||||
|
|
||||||
|
// Local search query
|
||||||
|
const [localSearch, setLocalSearch] = useState<null | string>(null);
|
||||||
|
|
||||||
// Load applications
|
// Load applications
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -78,78 +81,108 @@ const Home = (props: ComponentProps): JSX.Element => {
|
||||||
interval = setInterval(() => {
|
interval = setInterval(() => {
|
||||||
setHeader({
|
setHeader({
|
||||||
dateTime: dateTime(),
|
dateTime: dateTime(),
|
||||||
greeting: greeter()
|
greeting: greeter(),
|
||||||
})
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
|
// Search bookmarks
|
||||||
|
const searchBookmarks = (query: string): Category[] => {
|
||||||
|
const category = { ...categories[0] };
|
||||||
|
category.name = 'Search Results';
|
||||||
|
category.bookmarks = categories
|
||||||
|
.map(({ bookmarks }) => bookmarks)
|
||||||
|
.flat()
|
||||||
|
.filter(({ name }) => new RegExp(query, 'i').test(name));
|
||||||
|
|
||||||
|
return [category];
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
{searchConfig('hideSearch', 0) !== 1
|
{searchConfig('hideSearch', 0) !== 1 ? (
|
||||||
? <SearchBar />
|
<SearchBar setLocalSearch={setLocalSearch} />
|
||||||
: <div></div>
|
) : (
|
||||||
}
|
<div></div>
|
||||||
|
)}
|
||||||
|
|
||||||
{searchConfig('hideHeader', 0) !== 1
|
{searchConfig('hideHeader', 0) !== 1 ? (
|
||||||
? (
|
|
||||||
<header className={classes.Header}>
|
<header className={classes.Header}>
|
||||||
<p>{header.dateTime}</p>
|
<p>{header.dateTime}</p>
|
||||||
<Link to='/settings' className={classes.SettingsLink}>Go to Settings</Link>
|
<Link to="/settings" className={classes.SettingsLink}>
|
||||||
|
Go to Settings
|
||||||
|
</Link>
|
||||||
<span className={classes.HeaderMain}>
|
<span className={classes.HeaderMain}>
|
||||||
<h1>{header.greeting}</h1>
|
<h1>{header.greeting}</h1>
|
||||||
<WeatherWidget />
|
<WeatherWidget />
|
||||||
</span>
|
</span>
|
||||||
</header>
|
</header>
|
||||||
|
) : (
|
||||||
|
<div></div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{searchConfig('hideApps', 0) !== 1 ? (
|
||||||
|
<Fragment>
|
||||||
|
<SectionHeadline title="Applications" link="/applications" />
|
||||||
|
{appsLoading ? (
|
||||||
|
<Spinner />
|
||||||
|
) : (
|
||||||
|
<AppGrid
|
||||||
|
apps={
|
||||||
|
!localSearch
|
||||||
|
? apps.filter(({ isPinned }) => isPinned)
|
||||||
|
: apps.filter(({ name }) =>
|
||||||
|
new RegExp(localSearch, 'i').test(name)
|
||||||
)
|
)
|
||||||
: <div></div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{searchConfig('hideApps', 0) !== 1
|
|
||||||
? (<Fragment>
|
|
||||||
<SectionHeadline title='Applications' link='/applications' />
|
|
||||||
{appsLoading
|
|
||||||
? <Spinner />
|
|
||||||
: <AppGrid
|
|
||||||
apps={apps.filter((app: App) => app.isPinned)}
|
|
||||||
totalApps={apps.length}
|
totalApps={apps.length}
|
||||||
|
searching={!!localSearch}
|
||||||
/>
|
/>
|
||||||
}
|
)}
|
||||||
<div className={classes.HomeSpace}></div>
|
<div className={classes.HomeSpace}></div>
|
||||||
</Fragment>)
|
</Fragment>
|
||||||
: <div></div>
|
) : (
|
||||||
}
|
<div></div>
|
||||||
|
)}
|
||||||
|
|
||||||
{searchConfig('hideCategories', 0) !== 1
|
{searchConfig('hideCategories', 0) !== 1 ? (
|
||||||
? (<Fragment>
|
<Fragment>
|
||||||
<SectionHeadline title='Bookmarks' link='/bookmarks' />
|
<SectionHeadline title="Bookmarks" link="/bookmarks" />
|
||||||
{categoriesLoading
|
{categoriesLoading ? (
|
||||||
? <Spinner />
|
<Spinner />
|
||||||
: <BookmarkGrid
|
) : (
|
||||||
categories={categories.filter((category: Category) => category.isPinned)}
|
<BookmarkGrid
|
||||||
|
categories={
|
||||||
|
!localSearch
|
||||||
|
? categories.filter(({ isPinned }) => isPinned)
|
||||||
|
: searchBookmarks(localSearch)
|
||||||
|
}
|
||||||
totalCategories={categories.length}
|
totalCategories={categories.length}
|
||||||
|
searching={!!localSearch}
|
||||||
/>
|
/>
|
||||||
}
|
)}
|
||||||
</Fragment>)
|
</Fragment>
|
||||||
: <div></div>
|
) : (
|
||||||
}
|
<div></div>
|
||||||
|
)}
|
||||||
|
|
||||||
<Link to='/settings' className={classes.SettingsButton}>
|
<Link to="/settings" className={classes.SettingsButton}>
|
||||||
<Icon icon='mdiCog' color='var(--color-background)' />
|
<Icon icon="mdiCog" color="var(--color-background)" />
|
||||||
</Link>
|
</Link>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state: GlobalState) => {
|
const mapStateToProps = (state: GlobalState) => {
|
||||||
return {
|
return {
|
||||||
appsLoading: state.app.loading,
|
appsLoading: state.app.loading,
|
||||||
apps: state.app.apps,
|
apps: state.app.apps,
|
||||||
categoriesLoading: state.bookmark.loading,
|
categoriesLoading: state.bookmark.loading,
|
||||||
categories: state.bookmark.categories
|
categories: state.bookmark.categories,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps, { getApps, getCategories })(Home);
|
export default connect(mapStateToProps, { getApps, getCategories })(Home);
|
|
@ -15,36 +15,53 @@ import { searchParser } from '../../utility';
|
||||||
|
|
||||||
interface ComponentProps {
|
interface ComponentProps {
|
||||||
createNotification: (notification: NewNotification) => void;
|
createNotification: (notification: NewNotification) => void;
|
||||||
|
setLocalSearch: (query: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SearchBar = (props: ComponentProps): JSX.Element => {
|
const SearchBar = (props: ComponentProps): JSX.Element => {
|
||||||
|
const { setLocalSearch, createNotification } = props;
|
||||||
|
|
||||||
const inputRef = useRef<HTMLInputElement>(document.createElement('input'));
|
const inputRef = useRef<HTMLInputElement>(document.createElement('input'));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
inputRef.current.focus();
|
inputRef.current.focus();
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
const searchHandler = (e: KeyboardEvent<HTMLInputElement>) => {
|
const searchHandler = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||||
if (e.code === 'Enter') {
|
const searchResult = searchParser(inputRef.current.value);
|
||||||
const prefixFound = searchParser(inputRef.current.value);
|
|
||||||
|
|
||||||
if (!prefixFound) {
|
if (searchResult.isLocal) {
|
||||||
props.createNotification({
|
setLocalSearch(searchResult.search);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.code === 'Enter') {
|
||||||
|
if (!searchResult.query.prefix) {
|
||||||
|
createNotification({
|
||||||
title: 'Error',
|
title: 'Error',
|
||||||
message: 'Prefix not found'
|
message: 'Prefix not found',
|
||||||
})
|
});
|
||||||
|
} else if (searchResult.isLocal) {
|
||||||
|
setLocalSearch(searchResult.search);
|
||||||
|
} else {
|
||||||
|
if (searchResult.sameTab) {
|
||||||
|
document.location.replace(
|
||||||
|
`${searchResult.query.template}${searchResult.search}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
window.open(`${searchResult.query.template}${searchResult.search}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
type='text'
|
type="text"
|
||||||
className={classes.SearchBar}
|
className={classes.SearchBar}
|
||||||
onKeyDown={(e) => searchHandler(e)}
|
onKeyUp={(e) => searchHandler(e)}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default connect(null, { createNotification })(SearchBar);
|
export default connect(null, { createNotification })(SearchBar);
|
|
@ -6,7 +6,7 @@ import {
|
||||||
createNotification,
|
createNotification,
|
||||||
updateConfig,
|
updateConfig,
|
||||||
sortApps,
|
sortApps,
|
||||||
sortCategories
|
sortCategories,
|
||||||
} from '../../../store/actions';
|
} from '../../../store/actions';
|
||||||
|
|
||||||
// Typescript
|
// Typescript
|
||||||
|
@ -14,7 +14,7 @@ import {
|
||||||
GlobalState,
|
GlobalState,
|
||||||
NewNotification,
|
NewNotification,
|
||||||
Query,
|
Query,
|
||||||
SettingsForm
|
SettingsForm,
|
||||||
} from '../../../interfaces';
|
} from '../../../interfaces';
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
|
@ -53,7 +53,7 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
|
||||||
searchSameTab: 0,
|
searchSameTab: 0,
|
||||||
dockerApps: 1,
|
dockerApps: 1,
|
||||||
kubernetesApps: 1,
|
kubernetesApps: 1,
|
||||||
unpinStoppedApps: 1
|
unpinStoppedApps: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get config
|
// Get config
|
||||||
|
@ -73,7 +73,7 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
|
||||||
searchSameTab: searchConfig('searchSameTab', 0),
|
searchSameTab: searchConfig('searchSameTab', 0),
|
||||||
dockerApps: searchConfig('dockerApps', 0),
|
dockerApps: searchConfig('dockerApps', 0),
|
||||||
kubernetesApps: searchConfig('kubernetesApps', 0),
|
kubernetesApps: searchConfig('kubernetesApps', 0),
|
||||||
unpinStoppedApps: searchConfig('unpinStoppedApps', 0)
|
unpinStoppedApps: searchConfig('unpinStoppedApps', 0),
|
||||||
});
|
});
|
||||||
}, [props.loading]);
|
}, [props.loading]);
|
||||||
|
|
||||||
|
@ -105,115 +105,117 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
|
||||||
|
|
||||||
setFormData({
|
setFormData({
|
||||||
...formData,
|
...formData,
|
||||||
[e.target.name]: value
|
[e.target.name]: value,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={e => formSubmitHandler(e)}>
|
<form onSubmit={(e) => formSubmitHandler(e)}>
|
||||||
{/* OTHER OPTIONS */}
|
{/* OTHER OPTIONS */}
|
||||||
<h2 className={classes.SettingsSection}>Miscellaneous</h2>
|
<h2 className={classes.SettingsSection}>Miscellaneous</h2>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='customTitle'>Custom page title</label>
|
<label htmlFor="customTitle">Custom page title</label>
|
||||||
<input
|
<input
|
||||||
type='text'
|
type="text"
|
||||||
id='customTitle'
|
id="customTitle"
|
||||||
name='customTitle'
|
name="customTitle"
|
||||||
placeholder='Flame'
|
placeholder="Flame"
|
||||||
value={formData.customTitle}
|
value={formData.customTitle}
|
||||||
onChange={e => inputChangeHandler(e)}
|
onChange={(e) => inputChangeHandler(e)}
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
|
||||||
{/* BEAHVIOR OPTIONS */}
|
{/* BEAHVIOR OPTIONS */}
|
||||||
<h2 className={classes.SettingsSection}>App Behavior</h2>
|
<h2 className={classes.SettingsSection}>App Behavior</h2>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='pinAppsByDefault'>
|
<label htmlFor="pinAppsByDefault">
|
||||||
Pin new applications by default
|
Pin new applications by default
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
id='pinAppsByDefault'
|
id="pinAppsByDefault"
|
||||||
name='pinAppsByDefault'
|
name="pinAppsByDefault"
|
||||||
value={formData.pinAppsByDefault}
|
value={formData.pinAppsByDefault}
|
||||||
onChange={e => inputChangeHandler(e, true)}
|
onChange={(e) => inputChangeHandler(e, true)}
|
||||||
>
|
>
|
||||||
<option value={1}>True</option>
|
<option value={1}>True</option>
|
||||||
<option value={0}>False</option>
|
<option value={0}>False</option>
|
||||||
</select>
|
</select>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='pinCategoriesByDefault'>
|
<label htmlFor="pinCategoriesByDefault">
|
||||||
Pin new categories by default
|
Pin new categories by default
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
id='pinCategoriesByDefault'
|
id="pinCategoriesByDefault"
|
||||||
name='pinCategoriesByDefault'
|
name="pinCategoriesByDefault"
|
||||||
value={formData.pinCategoriesByDefault}
|
value={formData.pinCategoriesByDefault}
|
||||||
onChange={e => inputChangeHandler(e, true)}
|
onChange={(e) => inputChangeHandler(e, true)}
|
||||||
>
|
>
|
||||||
<option value={1}>True</option>
|
<option value={1}>True</option>
|
||||||
<option value={0}>False</option>
|
<option value={0}>False</option>
|
||||||
</select>
|
</select>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='useOrdering'>Sorting type</label>
|
<label htmlFor="useOrdering">Sorting type</label>
|
||||||
<select
|
<select
|
||||||
id='useOrdering'
|
id="useOrdering"
|
||||||
name='useOrdering'
|
name="useOrdering"
|
||||||
value={formData.useOrdering}
|
value={formData.useOrdering}
|
||||||
onChange={e => inputChangeHandler(e)}
|
onChange={(e) => inputChangeHandler(e)}
|
||||||
>
|
>
|
||||||
<option value='createdAt'>By creation date</option>
|
<option value="createdAt">By creation date</option>
|
||||||
<option value='name'>Alphabetical order</option>
|
<option value="name">Alphabetical order</option>
|
||||||
<option value='orderId'>Custom order</option>
|
<option value="orderId">Custom order</option>
|
||||||
</select>
|
</select>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='defaultSearchProvider'>Default Search Provider</label>
|
<label htmlFor="defaultSearchProvider">Default Search Provider</label>
|
||||||
<select
|
<select
|
||||||
id='defaultSearchProvider'
|
id="defaultSearchProvider"
|
||||||
name='defaultSearchProvider'
|
name="defaultSearchProvider"
|
||||||
value={formData.defaultSearchProvider}
|
value={formData.defaultSearchProvider}
|
||||||
onChange={e => inputChangeHandler(e)}
|
onChange={(e) => inputChangeHandler(e)}
|
||||||
>
|
>
|
||||||
{queries.map((query: Query) => (
|
{queries.map((query: Query, idx) => (
|
||||||
<option value={query.prefix}>{query.name}</option>
|
<option key={idx} value={query.prefix}>
|
||||||
|
{query.name}
|
||||||
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='searchSameTab'>
|
<label htmlFor="searchSameTab">
|
||||||
Open search results in the same tab
|
Open search results in the same tab
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
id='searchSameTab'
|
id="searchSameTab"
|
||||||
name='searchSameTab'
|
name="searchSameTab"
|
||||||
value={formData.searchSameTab}
|
value={formData.searchSameTab}
|
||||||
onChange={e => inputChangeHandler(e, true)}
|
onChange={(e) => inputChangeHandler(e, true)}
|
||||||
>
|
>
|
||||||
<option value={1}>True</option>
|
<option value={1}>True</option>
|
||||||
<option value={0}>False</option>
|
<option value={0}>False</option>
|
||||||
</select>
|
</select>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='appsSameTab'>Open applications in the same tab</label>
|
<label htmlFor="appsSameTab">Open applications in the same tab</label>
|
||||||
<select
|
<select
|
||||||
id='appsSameTab'
|
id="appsSameTab"
|
||||||
name='appsSameTab'
|
name="appsSameTab"
|
||||||
value={formData.appsSameTab}
|
value={formData.appsSameTab}
|
||||||
onChange={e => inputChangeHandler(e, true)}
|
onChange={(e) => inputChangeHandler(e, true)}
|
||||||
>
|
>
|
||||||
<option value={1}>True</option>
|
<option value={1}>True</option>
|
||||||
<option value={0}>False</option>
|
<option value={0}>False</option>
|
||||||
</select>
|
</select>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='bookmarksSameTab'>Open bookmarks in the same tab</label>
|
<label htmlFor="bookmarksSameTab">Open bookmarks in the same tab</label>
|
||||||
<select
|
<select
|
||||||
id='bookmarksSameTab'
|
id="bookmarksSameTab"
|
||||||
name='bookmarksSameTab'
|
name="bookmarksSameTab"
|
||||||
value={formData.bookmarksSameTab}
|
value={formData.bookmarksSameTab}
|
||||||
onChange={e => inputChangeHandler(e, true)}
|
onChange={(e) => inputChangeHandler(e, true)}
|
||||||
>
|
>
|
||||||
<option value={1}>True</option>
|
<option value={1}>True</option>
|
||||||
<option value={0}>False</option>
|
<option value={0}>False</option>
|
||||||
|
@ -223,48 +225,48 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
|
||||||
{/* MODULES OPTIONS */}
|
{/* MODULES OPTIONS */}
|
||||||
<h2 className={classes.SettingsSection}>Modules</h2>
|
<h2 className={classes.SettingsSection}>Modules</h2>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='hideSearch'>Hide search bar</label>
|
<label htmlFor="hideSearch">Hide search bar</label>
|
||||||
<select
|
<select
|
||||||
id='hideSearch'
|
id="hideSearch"
|
||||||
name='hideSearch'
|
name="hideSearch"
|
||||||
value={formData.hideSearch}
|
value={formData.hideSearch}
|
||||||
onChange={e => inputChangeHandler(e, true)}
|
onChange={(e) => inputChangeHandler(e, true)}
|
||||||
>
|
>
|
||||||
<option value={1}>True</option>
|
<option value={1}>True</option>
|
||||||
<option value={0}>False</option>
|
<option value={0}>False</option>
|
||||||
</select>
|
</select>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='hideHeader'>Hide greeting and date</label>
|
<label htmlFor="hideHeader">Hide greeting and date</label>
|
||||||
<select
|
<select
|
||||||
id='hideHeader'
|
id="hideHeader"
|
||||||
name='hideHeader'
|
name="hideHeader"
|
||||||
value={formData.hideHeader}
|
value={formData.hideHeader}
|
||||||
onChange={e => inputChangeHandler(e, true)}
|
onChange={(e) => inputChangeHandler(e, true)}
|
||||||
>
|
>
|
||||||
<option value={1}>True</option>
|
<option value={1}>True</option>
|
||||||
<option value={0}>False</option>
|
<option value={0}>False</option>
|
||||||
</select>
|
</select>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='hideApps'>Hide applications</label>
|
<label htmlFor="hideApps">Hide applications</label>
|
||||||
<select
|
<select
|
||||||
id='hideApps'
|
id="hideApps"
|
||||||
name='hideApps'
|
name="hideApps"
|
||||||
value={formData.hideApps}
|
value={formData.hideApps}
|
||||||
onChange={e => inputChangeHandler(e, true)}
|
onChange={(e) => inputChangeHandler(e, true)}
|
||||||
>
|
>
|
||||||
<option value={1}>True</option>
|
<option value={1}>True</option>
|
||||||
<option value={0}>False</option>
|
<option value={0}>False</option>
|
||||||
</select>
|
</select>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='hideCategories'>Hide categories</label>
|
<label htmlFor="hideCategories">Hide categories</label>
|
||||||
<select
|
<select
|
||||||
id='hideCategories'
|
id="hideCategories"
|
||||||
name='hideCategories'
|
name="hideCategories"
|
||||||
value={formData.hideCategories}
|
value={formData.hideCategories}
|
||||||
onChange={e => inputChangeHandler(e, true)}
|
onChange={(e) => inputChangeHandler(e, true)}
|
||||||
>
|
>
|
||||||
<option value={1}>True</option>
|
<option value={1}>True</option>
|
||||||
<option value={0}>False</option>
|
<option value={0}>False</option>
|
||||||
|
@ -274,26 +276,26 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
|
||||||
{/* DOCKER SETTINGS */}
|
{/* DOCKER SETTINGS */}
|
||||||
<h2 className={classes.SettingsSection}>Docker</h2>
|
<h2 className={classes.SettingsSection}>Docker</h2>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='dockerApps'>Use Docker API</label>
|
<label htmlFor="dockerApps">Use Docker API</label>
|
||||||
<select
|
<select
|
||||||
id='dockerApps'
|
id="dockerApps"
|
||||||
name='dockerApps'
|
name="dockerApps"
|
||||||
value={formData.dockerApps}
|
value={formData.dockerApps}
|
||||||
onChange={e => inputChangeHandler(e, true)}
|
onChange={(e) => inputChangeHandler(e, true)}
|
||||||
>
|
>
|
||||||
<option value={1}>True</option>
|
<option value={1}>True</option>
|
||||||
<option value={0}>False</option>
|
<option value={0}>False</option>
|
||||||
</select>
|
</select>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='unpinStoppedApps'>
|
<label htmlFor="unpinStoppedApps">
|
||||||
Unpin stopped containers / other apps
|
Unpin stopped containers / other apps
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
id='unpinStoppedApps'
|
id="unpinStoppedApps"
|
||||||
name='unpinStoppedApps'
|
name="unpinStoppedApps"
|
||||||
value={formData.unpinStoppedApps}
|
value={formData.unpinStoppedApps}
|
||||||
onChange={e => inputChangeHandler(e, true)}
|
onChange={(e) => inputChangeHandler(e, true)}
|
||||||
>
|
>
|
||||||
<option value={1}>True</option>
|
<option value={1}>True</option>
|
||||||
<option value={0}>False</option>
|
<option value={0}>False</option>
|
||||||
|
@ -303,12 +305,12 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
|
||||||
{/* KUBERNETES SETTINGS */}
|
{/* KUBERNETES SETTINGS */}
|
||||||
<h2 className={classes.SettingsSection}>Kubernetes</h2>
|
<h2 className={classes.SettingsSection}>Kubernetes</h2>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='kubernetesApps'>Use Kubernetes Ingress API</label>
|
<label htmlFor="kubernetesApps">Use Kubernetes Ingress API</label>
|
||||||
<select
|
<select
|
||||||
id='kubernetesApps'
|
id="kubernetesApps"
|
||||||
name='kubernetesApps'
|
name="kubernetesApps"
|
||||||
value={formData.kubernetesApps}
|
value={formData.kubernetesApps}
|
||||||
onChange={e => inputChangeHandler(e, true)}
|
onChange={(e) => inputChangeHandler(e, true)}
|
||||||
>
|
>
|
||||||
<option value={1}>True</option>
|
<option value={1}>True</option>
|
||||||
<option value={0}>False</option>
|
<option value={0}>False</option>
|
||||||
|
@ -321,7 +323,7 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
|
||||||
|
|
||||||
const mapStateToProps = (state: GlobalState) => {
|
const mapStateToProps = (state: GlobalState) => {
|
||||||
return {
|
return {
|
||||||
loading: state.config.loading
|
loading: state.config.loading,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -329,7 +331,7 @@ const actions = {
|
||||||
createNotification,
|
createNotification,
|
||||||
updateConfig,
|
updateConfig,
|
||||||
sortApps,
|
sortApps,
|
||||||
sortCategories
|
sortCategories,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps, actions)(OtherSettings);
|
export default connect(mapStateToProps, actions)(OtherSettings);
|
||||||
|
|
8
client/src/interfaces/SearchResult.ts
Normal file
8
client/src/interfaces/SearchResult.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { Query } from './Query';
|
||||||
|
|
||||||
|
export interface SearchResult {
|
||||||
|
isLocal: boolean;
|
||||||
|
sameTab: boolean;
|
||||||
|
search: string;
|
||||||
|
query: Query;
|
||||||
|
}
|
|
@ -9,3 +9,4 @@ export * from './Notification';
|
||||||
export * from './Config';
|
export * from './Config';
|
||||||
export * from './Forms';
|
export * from './Forms';
|
||||||
export * from './Query';
|
export * from './Query';
|
||||||
|
export * from './SearchResult';
|
||||||
|
|
|
@ -1,26 +1,44 @@
|
||||||
import { queries } from './searchQueries.json';
|
import { queries } from './searchQueries.json';
|
||||||
import { Query } from '../interfaces';
|
import { Query, SearchResult } from '../interfaces';
|
||||||
|
|
||||||
import { searchConfig } from '.';
|
import { searchConfig } from '.';
|
||||||
|
|
||||||
export const searchParser = (searchQuery: string): boolean => {
|
export const searchParser = (searchQuery: string): SearchResult => {
|
||||||
|
const result: SearchResult = {
|
||||||
|
isLocal: false,
|
||||||
|
sameTab: false,
|
||||||
|
search: '',
|
||||||
|
query: {
|
||||||
|
name: '',
|
||||||
|
prefix: '',
|
||||||
|
template: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const splitQuery = searchQuery.match(/^\/([a-z]+)[ ](.+)$/i);
|
const splitQuery = searchQuery.match(/^\/([a-z]+)[ ](.+)$/i);
|
||||||
const prefix = splitQuery ? splitQuery[1] : searchConfig('defaultSearchProvider', 'd');
|
|
||||||
const search = splitQuery ? encodeURIComponent(splitQuery[2]) : encodeURIComponent(searchQuery);
|
const prefix = splitQuery
|
||||||
|
? splitQuery[1]
|
||||||
|
: searchConfig('defaultSearchProvider', 'l');
|
||||||
|
|
||||||
|
const search = splitQuery
|
||||||
|
? encodeURIComponent(splitQuery[2])
|
||||||
|
: encodeURIComponent(searchQuery);
|
||||||
|
|
||||||
const query = queries.find((q: Query) => q.prefix === prefix);
|
const query = queries.find((q: Query) => q.prefix === prefix);
|
||||||
|
|
||||||
if (query) {
|
if (query) {
|
||||||
const sameTab = searchConfig('searchSameTab', false);
|
result.query = query;
|
||||||
|
result.search = search;
|
||||||
|
|
||||||
if (sameTab) {
|
if (prefix === 'l') {
|
||||||
document.location.replace(`${query.template}${search}`);
|
result.isLocal = true;
|
||||||
} else {
|
} else {
|
||||||
window.open(`${query.template}${search}`);
|
result.sameTab = searchConfig('searchSameTab', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return result;
|
||||||
}
|
};
|
||||||
|
|
|
@ -25,6 +25,11 @@
|
||||||
"prefix": "im",
|
"prefix": "im",
|
||||||
"template": "https://www.imdb.com/find?q="
|
"template": "https://www.imdb.com/find?q="
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Local search",
|
||||||
|
"prefix": "l",
|
||||||
|
"template": "#"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Reddit",
|
"name": "Reddit",
|
||||||
"prefix": "r",
|
"prefix": "r",
|
||||||
|
|
4
db.js
4
db.js
|
@ -5,7 +5,7 @@ const logger = new Logger();
|
||||||
const sequelize = new Sequelize({
|
const sequelize = new Sequelize({
|
||||||
dialect: 'sqlite',
|
dialect: 'sqlite',
|
||||||
storage: './data/db.sqlite',
|
storage: './data/db.sqlite',
|
||||||
logging: false
|
logging: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const connectDB = async () => {
|
const connectDB = async () => {
|
||||||
|
@ -28,5 +28,5 @@ const connectDB = async () => {
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
connectDB,
|
connectDB,
|
||||||
sequelize
|
sequelize,
|
||||||
};
|
};
|
||||||
|
|
4536
package-lock.json
generated
4536
package-lock.json
generated
File diff suppressed because it is too large
Load diff
14
package.json
14
package.json
|
@ -5,13 +5,13 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node server.js",
|
"start": "node server.js",
|
||||||
"init-server": "echo Instaling server dependencies && npm install && mkdir public && touch public/flame.css",
|
"init:server": "echo Instaling server dependencies && npm install && mkdir public && touch public/flame.css",
|
||||||
"init-client": "cd client && echo Instaling client dependencies && npm install",
|
"init:client": "cd client && echo Instaling client dependencies && npm install",
|
||||||
"dev-init": "npm run init-server && npm run init-client",
|
"dev:init": "npm run init:server && npm run init:client",
|
||||||
"dev-server": "nodemon server.js",
|
"dev:server": "nodemon server.js",
|
||||||
"dev-client": "npm start --prefix client",
|
"dev:client": "npm start --prefix client",
|
||||||
"dev": "concurrently \"npm run dev-server\" \"npm run dev-client\"",
|
"dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
|
||||||
"skaffold": "concurrently \"npm run init-client\" \"npm run dev-server\""
|
"skaffold": "concurrently \"npm run init:client\" \"npm run dev:server\""
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
|
|
@ -9,27 +9,27 @@ const initConfig = async () => {
|
||||||
const configPairs = await Config.findAll({
|
const configPairs = await Config.findAll({
|
||||||
where: {
|
where: {
|
||||||
key: {
|
key: {
|
||||||
[Op.or]: config.map(pair => pair.key)
|
[Op.or]: config.map((pair) => pair.key),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
// Get key from each pair
|
// Get key from each pair
|
||||||
const configKeys = configPairs.map((pair) => pair.key);
|
const configKeys = configPairs.map((pair) => pair.key);
|
||||||
|
|
||||||
// Create missing pairs
|
// Create missing pairs
|
||||||
config.forEach(async ({ key, value}) => {
|
config.forEach(async ({ key, value }) => {
|
||||||
if (!configKeys.includes(key)) {
|
if (!configKeys.includes(key)) {
|
||||||
await Config.create({
|
await Config.create({
|
||||||
key,
|
key,
|
||||||
value,
|
value,
|
||||||
valueType: typeof value
|
valueType: typeof value,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
logger.log('Initial config created');
|
logger.log('Initial config created');
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports = initConfig;
|
module.exports = initConfig;
|
|
@ -62,7 +62,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "defaultSearchProvider",
|
"key": "defaultSearchProvider",
|
||||||
"value": "d"
|
"value": "l"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "dockerApps",
|
"key": "dockerApps",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue