Merge pull request 'v0.3: Extension' (#3) from develop into master
Reviewed-on: #3
This commit is contained in:
		
						commit
						d0092657d7
					
				
							
								
								
									
										10
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								.drone.yml
									
									
									
									
									
								
							@ -7,8 +7,12 @@ steps:
 | 
			
		||||
  commands:
 | 
			
		||||
  - npm install
 | 
			
		||||
  - npm run buildProd
 | 
			
		||||
  - mkdir build
 | 
			
		||||
  - tar -czvf build/bundle.tar.gz dist
 | 
			
		||||
  - npm run ext:sign
 | 
			
		||||
  environment:
 | 
			
		||||
    WEB_EXT_API_KEY:
 | 
			
		||||
      from_secret: WEB_EXT_API_KEY
 | 
			
		||||
    WEB_EXT_API_SECRET:
 | 
			
		||||
      from_secret: WEB_EXT_API_SECRET
 | 
			
		||||
  
 | 
			
		||||
- name: gitea_release
 | 
			
		||||
  image: plugins/gitea-release
 | 
			
		||||
@ -17,7 +21,7 @@ steps:
 | 
			
		||||
      from_secret: GITEA_API_KEY
 | 
			
		||||
    base_url: https://git.kapelle.org
 | 
			
		||||
    files:
 | 
			
		||||
    - build/*
 | 
			
		||||
    - web-ext-artifacts/*
 | 
			
		||||
    checksum:
 | 
			
		||||
    - md5
 | 
			
		||||
    - sha1
 | 
			
		||||
 | 
			
		||||
@ -18,5 +18,8 @@
 | 
			
		||||
        "ecmaFeatures": {
 | 
			
		||||
            "jsx": true
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
	},
 | 
			
		||||
	"globals": {
 | 
			
		||||
		"VERSION": true 
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										32
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							@ -15,6 +15,20 @@
 | 
			
		||||
			"group": "none",
 | 
			
		||||
			"problemMatcher": []
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"label": "watch",
 | 
			
		||||
			"command": "npm",
 | 
			
		||||
			"type": "shell",
 | 
			
		||||
			"args": [
 | 
			
		||||
				"run",
 | 
			
		||||
				"watch"
 | 
			
		||||
			],
 | 
			
		||||
			"presentation": {
 | 
			
		||||
				"reveal": "always"
 | 
			
		||||
			},
 | 
			
		||||
			"group": "none",
 | 
			
		||||
			"problemMatcher": []
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"label": "Build prod",
 | 
			
		||||
			"command": "npm",
 | 
			
		||||
@ -26,7 +40,7 @@
 | 
			
		||||
			"presentation": {
 | 
			
		||||
				"reveal": "always"
 | 
			
		||||
			},
 | 
			
		||||
			"group": "none",
 | 
			
		||||
			"group": "build",
 | 
			
		||||
			"problemMatcher": []
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
@ -40,7 +54,21 @@
 | 
			
		||||
			"presentation": {
 | 
			
		||||
				"reveal": "always"
 | 
			
		||||
			},
 | 
			
		||||
			"group": "none",
 | 
			
		||||
			"group": "build",
 | 
			
		||||
			"problemMatcher": []
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"label": "ext:run",
 | 
			
		||||
			"command": "npm",
 | 
			
		||||
			"type": "shell",
 | 
			
		||||
			"args": [
 | 
			
		||||
				"run",
 | 
			
		||||
				"ext:run"
 | 
			
		||||
			],
 | 
			
		||||
			"presentation": {
 | 
			
		||||
				"reveal": "always"
 | 
			
		||||
			},
 | 
			
		||||
			"group": "build",
 | 
			
		||||
			"problemMatcher": []
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										42
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								README.md
									
									
									
									
									
								
							@ -1,33 +1,19 @@
 | 
			
		||||
[](https://drone.srv.kapelle.org/niklas/startpage)
 | 
			
		||||
# How to install
 | 
			
		||||
 | 
			
		||||
# How to set the new Tab
 | 
			
		||||
## Firefox
 | 
			
		||||
1. Download the extension
 | 
			
		||||
2. Goto `about:addons`
 | 
			
		||||
3. Click on the top right button
 | 
			
		||||
4. Click `Install Add-on From File`
 | 
			
		||||
5. Select the Add-on
 | 
			
		||||
 | 
			
		||||
[Reddit thread](https://www.reddit.com/r/startpages/comments/g3qndt/psa_how_to_set_a_custom_new_tab_page_in_firefox/)
 | 
			
		||||
# How to build
 | 
			
		||||
 | 
			
		||||
Create 2 files inside of the firefox install directory. 
 | 
			
		||||
1. Build the bundle with `npm run buildProd`
 | 
			
		||||
2. Provide the secrets required for signing the addon
 | 
			
		||||
3. Sign the Add-on by Mozilla with `npm run ext:sign`
 | 
			
		||||
 | 
			
		||||
`${installDir}/defaults/pref/autoconfig.js`
 | 
			
		||||
```js
 | 
			
		||||
pref("general.config.filename", "autoconfig.cfg");
 | 
			
		||||
pref("general.config.obscure_value", 0);
 | 
			
		||||
pref("general.config.sandbox_enabled", false);
 | 
			
		||||
```
 | 
			
		||||
# Usefull links
 | 
			
		||||
 | 
			
		||||
`${installDir}/autoconfig.cfg`
 | 
			
		||||
```
 | 
			
		||||
// first line is a comment
 | 
			
		||||
var {classes:Cc,interfaces:Ci,utils:Cu} = Components;
 | 
			
		||||
var newTabURL = "http://localhost:8080";
 | 
			
		||||
aboutNewTabService = Cc["@mozilla.org/browser/aboutnewtab-service;1"].getService(Ci.nsIAboutNewTabService);
 | 
			
		||||
aboutNewTabService.newTabURL = newTabURL;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Replace the "localhost" line in `autoconfig.cfg` with your URL. 
 | 
			
		||||
Also you can set the homepage via your normal settings.
 | 
			
		||||
 | 
			
		||||
Alternatively you can use a extentions called [New Tab override](https://addons.mozilla.org/en-US/firefox/addon/new-tab-override/).
 | 
			
		||||
 | 
			
		||||
## Chrome
 | 
			
		||||
 | 
			
		||||
Maybe [this](https://chrome.google.com/webstore/detail/custom-new-tab-url/mmjbdbjnoablegbkcklggeknkfcjkjia) extention. Not tested yet.
 | 
			
		||||
- [Mozilla Developer hub](https://addons.mozilla.org/en-US/developers/)
 | 
			
		||||
- [Mozilla Extension documentation](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions)
 | 
			
		||||
- [Web ext documentation](https://extensionworkshop.com/documentation/develop/web-ext-command-reference/)
 | 
			
		||||
							
								
								
									
										4128
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4128
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -6,7 +6,11 @@
 | 
			
		||||
	"scripts": {
 | 
			
		||||
		"devServer": "webpack-dev-server  --mode development --open",
 | 
			
		||||
		"build": "webpack --mode development",
 | 
			
		||||
		"buildProd": "webpack --mode production"
 | 
			
		||||
		"buildProd": "webpack --mode production",
 | 
			
		||||
		"watch": "webpack --watch --mode development",
 | 
			
		||||
		"ext:build": "web-ext build --source-dir dist --overwrite-dest",
 | 
			
		||||
		"ext:sign": "web-ext sign --source-dir dist --channel unlisted",
 | 
			
		||||
		"ext:run": "web-ext run --source-dir dist --no-reload"
 | 
			
		||||
	},
 | 
			
		||||
	"author": "",
 | 
			
		||||
	"license": "ISC",
 | 
			
		||||
@ -16,6 +20,7 @@
 | 
			
		||||
		"@types/react-dom": "^16.9.6",
 | 
			
		||||
		"@typescript-eslint/eslint-plugin": "^2.28.0",
 | 
			
		||||
		"@typescript-eslint/parser": "^2.28.0",
 | 
			
		||||
		"copy-webpack-plugin": "^5.1.1",
 | 
			
		||||
		"css-loader": "^3.5.2",
 | 
			
		||||
		"eslint": "^6.8.0",
 | 
			
		||||
		"eslint-plugin-react": "^7.19.0",
 | 
			
		||||
@ -26,6 +31,8 @@
 | 
			
		||||
		"sass-loader": "^8.0.2",
 | 
			
		||||
		"ts-loader": "^7.0.0",
 | 
			
		||||
		"typescript": "^3.8.3",
 | 
			
		||||
		"web-ext": "^4.2.0",
 | 
			
		||||
		"web-ext-types": "^3.2.1",
 | 
			
		||||
		"webpack": "^4.42.1",
 | 
			
		||||
		"webpack-cli": "^3.3.11",
 | 
			
		||||
		"webpack-dev-server": "^3.10.3"
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,6 @@
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
 | 
			
		||||
import Quick from "./Quick";
 | 
			
		||||
import QuickItem from "./QuickItem";
 | 
			
		||||
import QuickCategory from "./QuickCategory";
 | 
			
		||||
import Clock from "./Clock";
 | 
			
		||||
import Search from "./Search";
 | 
			
		||||
 | 
			
		||||
@ -14,25 +12,9 @@ class App extends React.Component {
 | 
			
		||||
	render(): JSX.Element {
 | 
			
		||||
		return <div>
 | 
			
		||||
			<div className="center-wrapper">
 | 
			
		||||
				<Clock/>
 | 
			
		||||
				<Search/>
 | 
			
		||||
				<Quick>
 | 
			
		||||
					<QuickCategory name="Productivity">
 | 
			
		||||
						<QuickItem name="Nextcloud" url="https://nc.kapelle.org"/>
 | 
			
		||||
						<QuickItem name="Git" url="https://git.kapelle.org"/>
 | 
			
		||||
						<QuickItem name="GitHub" url="https://github.com/"/>
 | 
			
		||||
					</QuickCategory>
 | 
			
		||||
 | 
			
		||||
					<QuickCategory name="Personal">
 | 
			
		||||
						<QuickItem name="Youtube" url="https://youtube.com/"/>
 | 
			
		||||
						<QuickItem name="Reddit" url="https://reddit.com/"/>
 | 
			
		||||
						<QuickItem name="Netflix" url="https://netflix.com/"/>
 | 
			
		||||
					</QuickCategory>
 | 
			
		||||
 | 
			
		||||
					<QuickCategory name="Other">
 | 
			
		||||
						<QuickItem name="Bitwarden" url="https://vault.kapelle.org"/>
 | 
			
		||||
					</QuickCategory>
 | 
			
		||||
				</Quick>
 | 
			
		||||
				<Clock />
 | 
			
		||||
				<Search />
 | 
			
		||||
				<Quick />
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -45,12 +45,12 @@ class Clock extends React.Component<{}, State> {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	startTimer() {
 | 
			
		||||
		this.interval = setInterval(()=>{
 | 
			
		||||
		this.interval = setInterval(() => {
 | 
			
		||||
			this.updateTime();
 | 
			
		||||
 | 
			
		||||
			// This will change only if the tab stays open after midnight. But still, lets cover that
 | 
			
		||||
			this.updateDate();
 | 
			
		||||
		},1000);
 | 
			
		||||
		}, 1000);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	componentDidMount() {
 | 
			
		||||
@ -59,7 +59,7 @@ class Clock extends React.Component<{}, State> {
 | 
			
		||||
		this.startTimer();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	componentWillUnmount(){
 | 
			
		||||
	componentWillUnmount() {
 | 
			
		||||
		clearInterval(this.interval);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,21 +1,43 @@
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
 | 
			
		||||
import "../style/quick.scss";
 | 
			
		||||
import { Bookmarks } from "../types/Bookmarks";
 | 
			
		||||
import QuickCategory from "./QuickCategory";
 | 
			
		||||
import getBookmarks from "../functions/getBookmarks";
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
	children: React.ReactNode;
 | 
			
		||||
interface State {
 | 
			
		||||
	bookmarks: Bookmarks;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Quick extends React.Component<Props> {
 | 
			
		||||
	
 | 
			
		||||
	constructor(props){
 | 
			
		||||
class Quick extends React.Component<{}, State> {
 | 
			
		||||
 | 
			
		||||
	constructor(props) {
 | 
			
		||||
		super(props);
 | 
			
		||||
		this.state = {
 | 
			
		||||
			bookmarks: {
 | 
			
		||||
				folder: [],
 | 
			
		||||
				orphans: []
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	async getBookmarks(): Promise<void> {
 | 
			
		||||
		const bookmarks = await getBookmarks();
 | 
			
		||||
		this.setState({
 | 
			
		||||
			bookmarks: bookmarks
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	componentDidMount() {
 | 
			
		||||
		this.getBookmarks();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	render(): JSX.Element {
 | 
			
		||||
		return <div className="quick-component">
 | 
			
		||||
			<div className="container">
 | 
			
		||||
				{this.props.children}
 | 
			
		||||
				{this.state.bookmarks.folder.map((e, i) => {
 | 
			
		||||
					return <QuickCategory folder={e} key={i} />;
 | 
			
		||||
				})}
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,25 +1,28 @@
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
 | 
			
		||||
import "../style/quickCategory.scss";
 | 
			
		||||
import { BookmarkFolder } from "../types/Bookmarks";
 | 
			
		||||
import QuickItem from "./QuickItem";
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
	children: React.ReactNode;
 | 
			
		||||
	name: string;
 | 
			
		||||
	folder: BookmarkFolder;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class QuickCategory extends React.Component<Props> {
 | 
			
		||||
	
 | 
			
		||||
	constructor(props){
 | 
			
		||||
 | 
			
		||||
	constructor(props) {
 | 
			
		||||
		super(props);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	render(): JSX.Element {
 | 
			
		||||
		return <div className="quickCategory-component">
 | 
			
		||||
			<div className="title">
 | 
			
		||||
				{this.props.name}
 | 
			
		||||
				{this.props.folder.name}
 | 
			
		||||
			</div>
 | 
			
		||||
			<div className="children">
 | 
			
		||||
				{this.props.children}
 | 
			
		||||
				{this.props.folder.bookmarks.map((e, i) => {
 | 
			
		||||
					return <QuickItem bookmark={e} key={i} />;
 | 
			
		||||
				})}
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,10 @@
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
 | 
			
		||||
import "../style/quickItem.scss";
 | 
			
		||||
import { Bookmark } from "../types/Bookmarks";
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
	name: string;
 | 
			
		||||
	url: string;
 | 
			
		||||
	bookmark: Bookmark;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class QuickItem extends React.Component<Props> {
 | 
			
		||||
@ -14,12 +14,12 @@ class QuickItem extends React.Component<Props> {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	onClick() {
 | 
			
		||||
		window.location.href = this.props.url;
 | 
			
		||||
		window.location.href = this.props.bookmark.url;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	render(): JSX.Element {
 | 
			
		||||
		return <div className="quickItem-component" onClick={() => this.onClick()}>
 | 
			
		||||
			{this.props.name}
 | 
			
		||||
			{this.props.bookmark.name}
 | 
			
		||||
		</div>;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,40 +1,102 @@
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
 | 
			
		||||
import "../style/search.scss";
 | 
			
		||||
import Sugestion from "./Sugestion";
 | 
			
		||||
import { Suggestion, SuggestionType } from "../types/suggestion";
 | 
			
		||||
import getGoogleSuggestions from "../functions/getGoogleSuggestions";
 | 
			
		||||
 | 
			
		||||
class Search extends React.Component {
 | 
			
		||||
interface State {
 | 
			
		||||
	sugestions: Suggestion[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Search extends React.Component<{}, State> {
 | 
			
		||||
 | 
			
		||||
	private searchInput = React.createRef<HTMLInputElement>()
 | 
			
		||||
 | 
			
		||||
	constructor(props) {
 | 
			
		||||
		super(props);
 | 
			
		||||
 | 
			
		||||
		this.state = {
 | 
			
		||||
			sugestions: []
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	handleQuery(query: string) {
 | 
			
		||||
		this.search(query);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	search(query: string){
 | 
			
		||||
	search(query: string) {
 | 
			
		||||
		const url = new URL("https://duckduckgo.com/");
 | 
			
		||||
		const param = new URLSearchParams();
 | 
			
		||||
		param.append("q",query);
 | 
			
		||||
		param.append("q", query);
 | 
			
		||||
		url.search = param.toString();
 | 
			
		||||
		window.location.href = url.toString();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	onKeyDown(event: React.KeyboardEvent) {
 | 
			
		||||
	async sugest(input: string) {
 | 
			
		||||
		if (input === "") {
 | 
			
		||||
			this.setState({
 | 
			
		||||
				sugestions: []
 | 
			
		||||
			});
 | 
			
		||||
		} else {
 | 
			
		||||
			const results = await getGoogleSuggestions(input);
 | 
			
		||||
 | 
			
		||||
			const newSuggestionState: Suggestion[] = results.suggestions.map((e) => {
 | 
			
		||||
				return {
 | 
			
		||||
					display: e,
 | 
			
		||||
					type: SuggestionType.QUERY
 | 
			
		||||
				};
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			this.setState({
 | 
			
		||||
				sugestions: newSuggestionState
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	onKeyDown(event: React.KeyboardEvent<HTMLInputElement>) {
 | 
			
		||||
		if (event.key === 'Enter') {
 | 
			
		||||
			this.handleQuery(this.searchInput.current.value);
 | 
			
		||||
		} else if (event.key === "ArrowDown") {
 | 
			
		||||
			console.log("down");
 | 
			
		||||
		} else if (event.key === "ArrowUp") {
 | 
			
		||||
			console.log("up");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	onChange() {
 | 
			
		||||
		this.sugest(this.searchInput.current.value);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	onSugestionClick(e: Suggestion) {
 | 
			
		||||
		if (e.type === SuggestionType.QUERY) {
 | 
			
		||||
			this.search(e.display);
 | 
			
		||||
		} else {
 | 
			
		||||
			window.location.href = e.url;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	render(): JSX.Element {
 | 
			
		||||
		return <div className="search-component" >
 | 
			
		||||
			<input type="text" 
 | 
			
		||||
			<input type="text"
 | 
			
		||||
				autoFocus
 | 
			
		||||
				onKeyDown={(e)=>this.onKeyDown(e)}
 | 
			
		||||
				onKeyDown={(e) => this.onKeyDown(e)}
 | 
			
		||||
				onChange={() => this.onChange()}
 | 
			
		||||
				ref={this.searchInput}
 | 
			
		||||
				/>
 | 
			
		||||
				autoComplete="off"
 | 
			
		||||
			/>
 | 
			
		||||
 | 
			
		||||
			<div className="suggestion-area">
 | 
			
		||||
				{this.state.sugestions.map((e) => {
 | 
			
		||||
					return <Sugestion
 | 
			
		||||
						key={e.display}
 | 
			
		||||
						suggestion={e}
 | 
			
		||||
						onClick={() => { this.onSugestionClick(e); }}
 | 
			
		||||
					/>;
 | 
			
		||||
				})}
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								src/components/Sugestion.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/components/Sugestion.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
 | 
			
		||||
import "../style/suggestion.scss";
 | 
			
		||||
import { Suggestion } from "../types/suggestion";
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
	suggestion: Suggestion;
 | 
			
		||||
	onClick: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Sugestion = (props: Props) => {
 | 
			
		||||
	return <div className="suggestion-component" onClick={props.onClick}>
 | 
			
		||||
		{props.suggestion.display}
 | 
			
		||||
	</div>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Sugestion;
 | 
			
		||||
							
								
								
									
										43
									
								
								src/functions/getBookmarks.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/functions/getBookmarks.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
			
		||||
import { Bookmarks, Bookmark } from "../types/Bookmarks";
 | 
			
		||||
 | 
			
		||||
function createBookmark(from: browser.bookmarks.BookmarkTreeNode): Bookmark {
 | 
			
		||||
	return {
 | 
			
		||||
		name: from.title,
 | 
			
		||||
		url: from.url
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default async (): Promise<Bookmarks> => {
 | 
			
		||||
	const treeNode = await browser.bookmarks.getTree();
 | 
			
		||||
 | 
			
		||||
	const root: Bookmarks = {
 | 
			
		||||
		folder: [],
 | 
			
		||||
		orphans: []
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const rootNode = treeNode[0].children.find((e)=>e.id === "menu________");
 | 
			
		||||
 | 
			
		||||
	// Set all top level bookmarks (without folder)
 | 
			
		||||
	root.orphans = rootNode.children
 | 
			
		||||
		.filter((e)=> e.type === "bookmark" )
 | 
			
		||||
		.sort((a,b)=> a.index - b.index )
 | 
			
		||||
		.map(createBookmark);
 | 
			
		||||
 | 
			
		||||
	// Get all top level folders 
 | 
			
		||||
	root.folder = rootNode.children
 | 
			
		||||
		.filter((e)=> e.type === "folder")
 | 
			
		||||
		.filter((e)=> e.children.length > 0)
 | 
			
		||||
		.sort((a,b)=> a.index - b.index )
 | 
			
		||||
		.map((e)=>{
 | 
			
		||||
			const children: Bookmark[] = e.children
 | 
			
		||||
				.filter((e)=> e.type === "bookmark")
 | 
			
		||||
				.sort((a,b)=> a.index - b.index )
 | 
			
		||||
				.map(createBookmark);
 | 
			
		||||
			return {
 | 
			
		||||
				name: e.title,
 | 
			
		||||
				bookmarks: children
 | 
			
		||||
			};
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
	return root;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										17
									
								
								src/functions/getGoogleSuggestions.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/functions/getGoogleSuggestions.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
import GoogleSuggestions from "../types/GoogleSuggestions";
 | 
			
		||||
 | 
			
		||||
export default async (query: string): Promise<GoogleSuggestions> => {
 | 
			
		||||
	const url = new URL(" http://suggestqueries.google.com/complete/search");
 | 
			
		||||
	const param = new URLSearchParams();
 | 
			
		||||
	param.append("q", query);
 | 
			
		||||
	param.append("client", "firefox"); // TODO check if the result are different on different clients
 | 
			
		||||
	url.search = param.toString();
 | 
			
		||||
 | 
			
		||||
	const response = await fetch(url.toString());
 | 
			
		||||
	const results = await response.json();
 | 
			
		||||
 | 
			
		||||
	return {
 | 
			
		||||
		query: results[0],
 | 
			
		||||
		suggestions: results[1]
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										25
									
								
								src/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/manifest.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
			
		||||
{
 | 
			
		||||
	"manifest_version": 2,
 | 
			
		||||
	"name": "Startpage",
 | 
			
		||||
	"version": "$VERSION",
 | 
			
		||||
	"description": "Startpage",
 | 
			
		||||
	"developer": {
 | 
			
		||||
		"name": "Djeeberjr"
 | 
			
		||||
	},
 | 
			
		||||
	"chrome_url_overrides": {
 | 
			
		||||
		"newtab": "startpage/index.html"
 | 
			
		||||
	},
 | 
			
		||||
	"chrome_settings_overrides": {
 | 
			
		||||
		"homepage": "startpage/index.html"
 | 
			
		||||
	},
 | 
			
		||||
	"permissions": [
 | 
			
		||||
		"<all_urls>",
 | 
			
		||||
		"bookmarks"
 | 
			
		||||
	],
 | 
			
		||||
	"applications": {
 | 
			
		||||
		"gecko": {
 | 
			
		||||
			"update_url": "https://cocainum.srv.kapelle.org/api/service/staticRedirect/staticRedirect/startpageExtUpdate",
 | 
			
		||||
			"id": "{9c6ffd7a-0e0b-4276-8208-aa72030477ce}"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,12 +1,19 @@
 | 
			
		||||
.search-component{
 | 
			
		||||
 | 
			
		||||
	text-align: center;
 | 
			
		||||
	margin-top: 2em;
 | 
			
		||||
	margin-bottom: 2em;
 | 
			
		||||
 | 
			
		||||
	margin: 2em auto 2em auto;
 | 
			
		||||
	width: 50%;
 | 
			
		||||
	position: relative;
 | 
			
		||||
	
 | 
			
		||||
	input[type=text]{
 | 
			
		||||
		width: 40%;
 | 
			
		||||
		width: 100%;
 | 
			
		||||
		border: none;
 | 
			
		||||
		padding: 0.5em 1.5em;
 | 
			
		||||
		padding: 0.5em 1em;
 | 
			
		||||
		box-sizing: border-box;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	.suggestion-area{
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		right: 0;
 | 
			
		||||
		left: 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								src/style/suggestion.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/style/suggestion.scss
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
.suggestion-component{
 | 
			
		||||
	padding: 0.2em 1em;
 | 
			
		||||
	background-color: white;
 | 
			
		||||
	color: black;
 | 
			
		||||
	border: 1px solid #d4d4d4;
 | 
			
		||||
	border-top: none;
 | 
			
		||||
	cursor: pointer;
 | 
			
		||||
 | 
			
		||||
	&:hover {
 | 
			
		||||
		background-color: #d4d4d4;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								src/types/Bookmarks.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/types/Bookmarks.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
interface Bookmarks {
 | 
			
		||||
	folder: BookmarkFolder[];
 | 
			
		||||
	orphans: Bookmark[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface BookmarkFolder {
 | 
			
		||||
	name: string;
 | 
			
		||||
	bookmarks: Bookmark[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Bookmark {
 | 
			
		||||
	name: string;
 | 
			
		||||
	url: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { Bookmarks, Bookmark, BookmarkFolder };
 | 
			
		||||
							
								
								
									
										31
									
								
								src/types/GoogleSuggestions.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/types/GoogleSuggestions.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
			
		||||
/*
 | 
			
		||||
	Example if client == chrome:
 | 
			
		||||
	[
 | 
			
		||||
		"hello w",
 | 
			
		||||
		["hello world","hello world anime","hello world java","hello world c","hello world python","hello world html","hello world mhw","hello welcome home"],
 | 
			
		||||
		["","","","","","","",""],
 | 
			
		||||
		[],
 | 
			
		||||
		{
 | 
			
		||||
			"google:clientdata":{"bpc":false,"tlw":false},
 | 
			
		||||
			"google:suggestrelevance":[1250,1050,850,700,601,600,551,550],
 | 
			
		||||
			"google:suggesttype":["QUERY","QUERY","QUERY","QUERY","QUERY","QUERY","QUERY","QUERY"],
 | 
			
		||||
			"google:verbatimrelevance":851
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
 | 
			
		||||
	Example if client == firefox:
 | 
			
		||||
	[
 | 
			
		||||
		"hello wo",
 | 
			
		||||
		[
 | 
			
		||||
			"hello world","hello world anime","hello world java","hello world c","hello world python","hello world html",
 | 
			
		||||
			"hello world mhw","hello world anime stream","hello world programm","hello world anime ger sub"
 | 
			
		||||
		]
 | 
			
		||||
	]
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
interface GoogleSuggestions {
 | 
			
		||||
	query: string;
 | 
			
		||||
	suggestions: string[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default GoogleSuggestions;
 | 
			
		||||
							
								
								
									
										12
									
								
								src/types/Suggestion.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/types/Suggestion.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
enum SuggestionType {
 | 
			
		||||
	QUICK,
 | 
			
		||||
	QUERY
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Suggestion {
 | 
			
		||||
	display: string;
 | 
			
		||||
	type: SuggestionType;
 | 
			
		||||
	url?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { SuggestionType, Suggestion };
 | 
			
		||||
@ -6,6 +6,7 @@
 | 
			
		||||
		"sourceMap": true,
 | 
			
		||||
		"noLib": false,
 | 
			
		||||
		"jsx": "react",
 | 
			
		||||
		"typeRoots": ["node_modules/@types", "node_modules/web-ext-types"],
 | 
			
		||||
	},
 | 
			
		||||
	"exclude": [
 | 
			
		||||
		"node_modules"
 | 
			
		||||
 | 
			
		||||
@ -3,10 +3,19 @@
 | 
			
		||||
 | 
			
		||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
 | 
			
		||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
 | 
			
		||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
 | 
			
		||||
const Webpack = require("webpack");
 | 
			
		||||
 | 
			
		||||
const path = require("path");
 | 
			
		||||
 | 
			
		||||
const DEVELOPMENT = process.env.NODE_ENV === "development";
 | 
			
		||||
 | 
			
		||||
const version = require('child_process')
 | 
			
		||||
	.execSync("git describe --abbrev=0")
 | 
			
		||||
	.toString()
 | 
			
		||||
	.replace("v", "")
 | 
			
		||||
	.trim();
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
	context: path.join(__dirname, "src"),
 | 
			
		||||
	resolve: {
 | 
			
		||||
@ -14,7 +23,7 @@ module.exports = {
 | 
			
		||||
	},
 | 
			
		||||
	entry: ["./index.tsx"],
 | 
			
		||||
	output: {
 | 
			
		||||
		path: path.join(__dirname, "dist"),
 | 
			
		||||
		path: path.join(__dirname, "dist/startpage"),
 | 
			
		||||
		filename: "bundle.js"
 | 
			
		||||
	},
 | 
			
		||||
	devtool: DEVELOPMENT ? "source-map" : false,
 | 
			
		||||
@ -27,6 +36,9 @@ module.exports = {
 | 
			
		||||
		liveReload: true,
 | 
			
		||||
		watchContentBase: true
 | 
			
		||||
	},
 | 
			
		||||
	watchOptions: {
 | 
			
		||||
		ignored: ['dist/**', 'node_modules/**']
 | 
			
		||||
	},
 | 
			
		||||
	module: {
 | 
			
		||||
		rules: [
 | 
			
		||||
			{
 | 
			
		||||
@ -58,6 +70,18 @@ module.exports = {
 | 
			
		||||
			hash: true,
 | 
			
		||||
			minify: !DEVELOPMENT
 | 
			
		||||
		}),
 | 
			
		||||
		new MiniCssExtractPlugin()
 | 
			
		||||
		new MiniCssExtractPlugin(),
 | 
			
		||||
		new CopyWebpackPlugin([
 | 
			
		||||
			{
 | 
			
		||||
				from: "manifest.json",
 | 
			
		||||
				to: "..",
 | 
			
		||||
				transform(content) {
 | 
			
		||||
					return content.toString().replace("$VERSION", version);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		]),
 | 
			
		||||
		new Webpack.DefinePlugin({
 | 
			
		||||
			VERSION: JSON.stringify(version),
 | 
			
		||||
		})
 | 
			
		||||
	]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user