Added search bar
This commit is contained in:
parent
550e1e155b
commit
8026533a06
18 changed files with 166 additions and 9 deletions
|
@ -10,7 +10,7 @@ RUN npm install --production
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN mkdir -p ./public ./data ./data/uploads \
|
RUN mkdir -p ./public ./data \
|
||||||
&& cd ./client \
|
&& cd ./client \
|
||||||
&& npm install --production \
|
&& npm install --production \
|
||||||
&& npm run build \
|
&& npm run build \
|
||||||
|
|
|
@ -11,7 +11,7 @@ RUN apk --no-cache --virtual build-dependencies add python make g++ \
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN mkdir -p ./public ./data ./data/uploads \
|
RUN mkdir -p ./public ./data \
|
||||||
&& cd ./client \
|
&& cd ./client \
|
||||||
&& npm install --production \
|
&& npm install --production \
|
||||||
&& npm run build \
|
&& npm run build \
|
||||||
|
|
17
README.md
17
README.md
|
@ -81,6 +81,23 @@ Follow instructions from wiki: [Installation without Docker](https://github.com/
|
||||||
![Homescreen screenshot](./github/_themes.png)
|
![Homescreen screenshot](./github/_themes.png)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
### Search bar
|
||||||
|
> While opening links, module will follow `Open all links in the same tab` setting
|
||||||
|
#### Supported search engines
|
||||||
|
| Name | Prefix | Search URL |
|
||||||
|
|------------|--------|-------------------------------------|
|
||||||
|
| Disroot | /ds | http://search.disroot.org/search?q= |
|
||||||
|
| DuckDuckGo | /d | https://duckduckgo.com/?q= |
|
||||||
|
| Google | /g | https://www.google.com/search?q= |
|
||||||
|
|
||||||
|
#### Supported services
|
||||||
|
| Name | Prefix | Search URL |
|
||||||
|
|--------------------|--------|-----------------------------------------------|
|
||||||
|
| IMDb | /im | https://www.imdb.com/find?q= |
|
||||||
|
| Reddit | /r | -https://www.reddit.com/search?q= |
|
||||||
|
| The Movie Database | /mv | https://www.themoviedb.org/search?query= |
|
||||||
|
| Youtube | /yt | https://www.youtube.com/results?search_query= |
|
||||||
|
|
||||||
### Setting up weather module
|
### Setting up weather module
|
||||||
1. Obtain API Key from [Weather API](https://www.weatherapi.com/pricing.aspx).
|
1. Obtain API Key from [Weather API](https://www.weatherapi.com/pricing.aspx).
|
||||||
> Free plan allows for 1M calls per month. Flame is making less then 3K API calls per month.
|
> Free plan allows for 1M calls per month. Flame is making less then 3K API calls per month.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
REACT_APP_VERSION=1.4.4
|
REACT_APP_VERSION=1.5.0
|
|
@ -22,6 +22,7 @@ import classes from './Home.module.css';
|
||||||
import AppGrid from '../Apps/AppGrid/AppGrid';
|
import AppGrid from '../Apps/AppGrid/AppGrid';
|
||||||
import BookmarkGrid from '../Bookmarks/BookmarkGrid/BookmarkGrid';
|
import BookmarkGrid from '../Bookmarks/BookmarkGrid/BookmarkGrid';
|
||||||
import WeatherWidget from '../Widgets/WeatherWidget/WeatherWidget';
|
import WeatherWidget from '../Widgets/WeatherWidget/WeatherWidget';
|
||||||
|
import SearchBox from '../SearchBox/SearchBox';
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
import { greeter } from './functions/greeter';
|
import { greeter } from './functions/greeter';
|
||||||
|
@ -87,6 +88,11 @@ const Home = (props: ComponentProps): JSX.Element => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
{searchConfig('hideSearch', 0) !== 1
|
||||||
|
? <SearchBox />
|
||||||
|
: <div></div>
|
||||||
|
}
|
||||||
|
|
||||||
{searchConfig('hideHeader', 0) !== 1
|
{searchConfig('hideHeader', 0) !== 1
|
||||||
? (
|
? (
|
||||||
<header className={classes.Header}>
|
<header className={classes.Header}>
|
||||||
|
|
17
client/src/components/SearchBox/SearchBox.module.css
Normal file
17
client/src/components/SearchBox/SearchBox.module.css
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
.SearchBox {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 0;
|
||||||
|
color: var(--color-primary);
|
||||||
|
/* font-size: 20px; */
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 2px solid var(--color-accent);
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SearchBox:focus {
|
||||||
|
opacity: 1;
|
||||||
|
outline: none;
|
||||||
|
}
|
29
client/src/components/SearchBox/SearchBox.tsx
Normal file
29
client/src/components/SearchBox/SearchBox.tsx
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { useRef, useEffect, KeyboardEvent } from 'react';
|
||||||
|
|
||||||
|
import classes from './SearchBox.module.css';
|
||||||
|
import { searchParser } from '../../utility';
|
||||||
|
|
||||||
|
const SearchBox = (): JSX.Element => {
|
||||||
|
const inputRef = useRef<HTMLInputElement>(document.createElement('input'));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
inputRef.current.focus();
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const searchHandler = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.code === 'Enter') {
|
||||||
|
searchParser(inputRef.current.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type='text'
|
||||||
|
className={classes.SearchBox}
|
||||||
|
onKeyDown={(e) => searchHandler(e)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SearchBox;
|
|
@ -34,6 +34,7 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
|
||||||
hideHeader: 0,
|
hideHeader: 0,
|
||||||
hideApps: 0,
|
hideApps: 0,
|
||||||
hideCategories: 0,
|
hideCategories: 0,
|
||||||
|
hideSearch: 0,
|
||||||
useOrdering: 'createdAt',
|
useOrdering: 'createdAt',
|
||||||
openSameTab: 0
|
openSameTab: 0
|
||||||
})
|
})
|
||||||
|
@ -47,6 +48,7 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
|
||||||
hideHeader: searchConfig('hideHeader', 0),
|
hideHeader: searchConfig('hideHeader', 0),
|
||||||
hideApps: searchConfig('hideApps', 0),
|
hideApps: searchConfig('hideApps', 0),
|
||||||
hideCategories: searchConfig('hideCategories', 0),
|
hideCategories: searchConfig('hideCategories', 0),
|
||||||
|
hideSearch: searchConfig('hideSearch', 0),
|
||||||
useOrdering: searchConfig('useOrdering', 'createdAt'),
|
useOrdering: searchConfig('useOrdering', 'createdAt'),
|
||||||
openSameTab: searchConfig('openSameTab', 0)
|
openSameTab: searchConfig('openSameTab', 0)
|
||||||
})
|
})
|
||||||
|
@ -151,6 +153,18 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
|
||||||
|
|
||||||
{/* MODULES OPTIONS */}
|
{/* MODULES OPTIONS */}
|
||||||
<h2 className={classes.SettingsSection}>Modules</h2>
|
<h2 className={classes.SettingsSection}>Modules</h2>
|
||||||
|
<InputGroup>
|
||||||
|
<label htmlFor='hideSearch'>Hide search bar</label>
|
||||||
|
<select
|
||||||
|
id='hideSearch'
|
||||||
|
name='hideSearch'
|
||||||
|
value={formData.hideSearch}
|
||||||
|
onChange={(e) => inputChangeHandler(e, true)}
|
||||||
|
>
|
||||||
|
<option value={1}>True</option>
|
||||||
|
<option value={0}>False</option>
|
||||||
|
</select>
|
||||||
|
</InputGroup>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor='hideHeader'>Hide greeting and date</label>
|
<label htmlFor='hideHeader'>Hide greeting and date</label>
|
||||||
<select
|
<select
|
||||||
|
|
|
@ -12,6 +12,7 @@ export interface SettingsForm {
|
||||||
hideHeader: number;
|
hideHeader: number;
|
||||||
hideApps: number;
|
hideApps: number;
|
||||||
hideCategories: number;
|
hideCategories: number;
|
||||||
|
hideSearch: number;
|
||||||
useOrdering: string;
|
useOrdering: string;
|
||||||
openSameTab: number;
|
openSameTab: number;
|
||||||
}
|
}
|
5
client/src/interfaces/Query.ts
Normal file
5
client/src/interfaces/Query.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export interface Query {
|
||||||
|
name: string;
|
||||||
|
prefix: string;
|
||||||
|
template: string;
|
||||||
|
}
|
|
@ -7,4 +7,5 @@ export * from './Bookmark';
|
||||||
export * from './Category';
|
export * from './Category';
|
||||||
export * from './Notification';
|
export * from './Notification';
|
||||||
export * from './Config';
|
export * from './Config';
|
||||||
export * from './Forms';
|
export * from './Forms';
|
||||||
|
export * from './Query';
|
|
@ -2,4 +2,5 @@ export * from './iconParser';
|
||||||
export * from './urlParser';
|
export * from './urlParser';
|
||||||
export * from './searchConfig';
|
export * from './searchConfig';
|
||||||
export * from './checkVersion';
|
export * from './checkVersion';
|
||||||
export * from './sortData';
|
export * from './sortData';
|
||||||
|
export * from './searchParser';
|
22
client/src/utility/searchParser.ts
Normal file
22
client/src/utility/searchParser.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { queries } from './searchQueries.json';
|
||||||
|
import { Query } from '../interfaces';
|
||||||
|
|
||||||
|
import { searchConfig } from '.';
|
||||||
|
|
||||||
|
export const searchParser = (searchQuery: string): void => {
|
||||||
|
const space = searchQuery.indexOf(' ');
|
||||||
|
const prefix = searchQuery.slice(1, space);
|
||||||
|
const search = encodeURIComponent(searchQuery.slice(space + 1));
|
||||||
|
|
||||||
|
const query = queries.find((q: Query) => q.prefix === prefix);
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
const sameTab = searchConfig('openSameTab', false);
|
||||||
|
|
||||||
|
if (sameTab) {
|
||||||
|
document.location.replace(`${query.template}${search}`);
|
||||||
|
} else {
|
||||||
|
window.open(`${query.template}${search}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
client/src/utility/searchQueries.json
Normal file
39
client/src/utility/searchQueries.json
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"queries": [
|
||||||
|
{
|
||||||
|
"name": "Google",
|
||||||
|
"prefix": "g",
|
||||||
|
"template": "https://www.google.com/search?q="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DuckDuckGo",
|
||||||
|
"prefix": "d",
|
||||||
|
"template": "https://duckduckgo.com/?q="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Disroot",
|
||||||
|
"prefix": "ds",
|
||||||
|
"template": "http://search.disroot.org/search?q="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "YouTube",
|
||||||
|
"prefix": "yt",
|
||||||
|
"template": "https://www.youtube.com/results?search_query="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Reddit",
|
||||||
|
"prefix": "r",
|
||||||
|
"template": "https://www.reddit.com/search?q="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "IMDb",
|
||||||
|
"prefix": "im",
|
||||||
|
"template": "https://www.imdb.com/find?q="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "The Movie Database",
|
||||||
|
"prefix": "mv",
|
||||||
|
"template": "https://www.themoviedb.org/search?query="
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
2
db.js
2
db.js
|
@ -13,7 +13,7 @@ const connectDB = async () => {
|
||||||
await sequelize.authenticate();
|
await sequelize.authenticate();
|
||||||
logger.log('Connected to database');
|
logger.log('Connected to database');
|
||||||
|
|
||||||
const syncModels = false;
|
const syncModels = true;
|
||||||
|
|
||||||
if (syncModels) {
|
if (syncModels) {
|
||||||
logger.log('Starting model synchronization');
|
logger.log('Starting model synchronization');
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { join } = require('path');
|
|
||||||
const multer = require('multer');
|
const multer = require('multer');
|
||||||
const uuid = require('uuid');
|
|
||||||
|
if (!fs.existsSync('data/uploads')) {
|
||||||
|
fs.mkdirSync('data/uploads');
|
||||||
|
}
|
||||||
|
|
||||||
const storage = multer.diskStorage({
|
const storage = multer.diskStorage({
|
||||||
destination: (req, file, cb) => {
|
destination: (req, file, cb) => {
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
"node-schedule": "^2.0.0",
|
"node-schedule": "^2.0.0",
|
||||||
"sequelize": "^6.6.2",
|
"sequelize": "^6.6.2",
|
||||||
"sqlite3": "^5.0.2",
|
"sqlite3": "^5.0.2",
|
||||||
"uuid": "^8.3.2",
|
|
||||||
"ws": "^7.4.6"
|
"ws": "^7.4.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -47,6 +47,10 @@
|
||||||
{
|
{
|
||||||
"key": "hideCategories",
|
"key": "hideCategories",
|
||||||
"value": false
|
"value": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "hideSearch",
|
||||||
|
"value": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
Loading…
Reference in a new issue