diff --git a/README.md b/README.md
index e278e00..b34d06a 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
- [WORKING IN PROGRESS!!!] Quickshare
+ Quickshare
Simple file sharing server built with Go/Golang, Typescript, Gin, React, Boltdb, etc.
@@ -24,50 +24,21 @@ Choose Language: English | [简体中文](./docs/README_zh-cn.md)
Visit [Release Page](https://github.com/ihexxa/quickshare/releases) to get Linux | Mac | Windows distribution(s).
-## Features
+## Main Features
-- Upload and download in browser, no client
-- Share files among desktop and mobile devices
-- Portable software
-- Add files from local
-- Add download limit for resource
-- Download from interrupted point
+- Uploading and downloading in browser without client
+- Be compatible with Linux, Mac and Windows
+- Sharing files among different devices (desktop & mobile)
+- Stopping and resuming uploading/downloading
## Installation
-2 steps are needed to start a quickshare: unzip it and start it.
-
-The first step, unzip and start quickshare
-
-### Linux
-
-- Unzip the package: `unzip [package].` (`[package]` could be `quickshare_0.0.8_linux_x86_6 4.zip`)
-- Start quickshare `./quickshare`
-
-### Mac
-
-- Unzip the package: `unzip [package].` (`[package]` could be `quickshare_0.0.8_macos_x86_64.zip`)
-- Start quickshare `./quickshare`
-
-### Windows
-
-- Unzip the package
-- Go into folder and click `quickshare.exe`
-
-Last step, meet quickshare in browser
-
-- Quickshare will start and show `quickshare starts @ [URL]` in terminal (e.g. `URL` could be `192.168.0.1:8888`)
-- Open `URL` in browser and login with `admin` and `quicksh@re`
-- Enjoy (But don't forget to change password according to [FAQ document](./docs/FAQ_en-us.md))
+Coming soon.
### FAQ
Please refer [FAQ document](./docs/FAQ_en-us.md)
-### Configuration
-
-Please refer [Configuration document](./docs/CONFIG_en-us.md)
-
### Contribution
-Will add it soon...
+TODO
diff --git a/src/client/web/src/components/__test__/auth_pane.test.tsx b/src/client/web/src/components/__test__/pane_login.test.tsx
similarity index 97%
rename from src/client/web/src/components/__test__/auth_pane.test.tsx
rename to src/client/web/src/components/__test__/pane_login.test.tsx
index 26c5008..6315565 100644
--- a/src/client/web/src/components/__test__/auth_pane.test.tsx
+++ b/src/client/web/src/components/__test__/pane_login.test.tsx
@@ -1,7 +1,7 @@
import { mock, instance } from "ts-mockito";
import { initWithWorker } from "../core_state";
-import { Updater } from "../auth_pane";
+import { Updater } from "../pane_login";
import { MockUsersClient } from "../../client/users_mock";
import { Response } from "../../client";
import { MockWorker } from "../../worker/interface";
diff --git a/src/client/web/src/components/browser.tsx b/src/client/web/src/components/browser.tsx
index dbc3767..94a441d 100644
--- a/src/client/web/src/components/browser.tsx
+++ b/src/client/web/src/components/browser.tsx
@@ -3,6 +3,7 @@ import * as ReactDOM from "react-dom";
import { List, Map } from "immutable";
import FileSize from "filesize";
+import { Layouter } from "./layouter";
import { ICoreState } from "./core_state";
import {
IUsersClient,
@@ -14,7 +15,6 @@ import { FilesClient } from "../client/files";
import { UsersClient } from "../client/users";
import { UploadMgr } from "../worker/upload_mgr";
import { UploadEntry } from "../worker/interface";
-// import { FileUploader } from "../worker/uploader";
export const uploadCheckCycle = 1000;
@@ -34,6 +34,8 @@ export interface Props {
uploadFiles: List;
uploadValue: string;
+ isVertical: boolean;
+
update?: (updater: (prevState: ICoreState) => ICoreState) => void;
}
@@ -156,11 +158,6 @@ export class Updater {
return Updater.setItems(List(dstDir.split("/")));
};
- static setPwd = async (oldPwd: string, newPwd: string): Promise => {
- const resp = await Updater.usersClient.setPwd(oldPwd, newPwd);
- return resp.status === 200;
- };
-
static addUploadFiles = (fileList: FileList, len: number) => {
for (let i = 0; i < len; i++) {
// do not wait for the promise
@@ -179,11 +176,6 @@ export interface State {
inputValue: string;
selectedSrc: string;
selectedItems: Map;
-
- show: boolean;
- oldPwd: string;
- newPwd1: string;
- newPwd2: string;
}
export class Browser extends React.Component {
@@ -201,10 +193,6 @@ export class Browser extends React.Component {
inputValue: "",
selectedSrc: "",
selectedItems: Map(),
- show: false,
- oldPwd: "",
- newPwd1: "",
- newPwd2: "",
};
this.uploadInput = undefined;
@@ -227,18 +215,9 @@ export class Browser extends React.Component {
});
}
- showPane = () => {
- this.setState({ show: !this.state.show });
- };
- changeOldPwd = (ev: React.ChangeEvent) => {
- this.setState({ oldPwd: ev.target.value });
- };
- changeNewPwd1 = (ev: React.ChangeEvent) => {
- this.setState({ newPwd1: ev.target.value });
- };
- changeNewPwd2 = (ev: React.ChangeEvent) => {
- this.setState({ newPwd2: ev.target.value });
- };
+ // showPane = () => {
+ // this.setState({ show: !this.state.show });
+ // };
onInputChange = (ev: React.ChangeEvent) => {
this.setState({ inputValue: ev.target.value });
};
@@ -352,31 +331,6 @@ export class Browser extends React.Component {
});
};
- setPwd = () => {
- if (this.state.newPwd1 !== this.state.newPwd2) {
- alert("new passwords are not same");
- } else if (this.state.newPwd1 == "") {
- alert("new passwords can not be empty");
- } else if (this.state.oldPwd == this.state.newPwd1) {
- alert("old and new passwords are same");
- } else {
- Updater.setPwd(this.state.oldPwd, this.state.newPwd1).then(
- (ok: boolean) => {
- if (ok) {
- alert("Password is updated");
- } else {
- alert("fail to update password");
- }
- this.setState({
- oldPwd: "",
- newPwd1: "",
- newPwd2: "",
- });
- }
- );
- }
- };
-
render() {
const breadcrumb = this.props.dirPath.map(
(pathPart: string, key: number) => {
@@ -395,96 +349,56 @@ export class Browser extends React.Component {
}
);
+ const nameCellClass = `item-name item-name-${
+ this.props.isVertical ? "vertical" : "horizontal"
+ } pointer`;
+ const sizeCellClass = this.props.isVertical ? `hidden margin-s` : ``;
+ const modTimeCellClass = this.props.isVertical ? `hidden margin-s` : ``;
+
+ const layoutChildren = [
+ ,
+ ,
+
+
+
+ ,
+
+
+
+ ,
+ ];
+
const ops = (
-
+
);
const itemList = this.props.items.map((item: MetadataResp) => {
@@ -497,59 +411,67 @@ export class Browser extends React.Component {
return item.isDir ? (
-
+ |
|
this.gotoChild(item.name)}
>
{item.name}
|
- -- |
- {item.modTime.slice(0, item.modTime.indexOf("T"))} |
-
+ -- |
+
+ {item.modTime.slice(0, item.modTime.indexOf("T"))}
+ |
-
+
+
+
|
) : (
-
+ |
|
{item.name}
|
- {FileSize(item.size, { round: 0 })} |
- {item.modTime.slice(0, item.modTime.indexOf("T"))} |
-
+ {FileSize(item.size, { round: 0 })} |
+
+ {item.modTime.slice(0, item.modTime.indexOf("T"))}
+ |
-
+
+
+
|
);
@@ -561,28 +483,28 @@ export class Browser extends React.Component {
return (
-
+ |
|
- {fileName}
+ {fileName}
+
+
+
+
|
{FileSize(uploading.uploaded, { round: 0 })} |
{FileSize(uploading.size, { round: 0 })} |
-
-
-
- |
);
});
@@ -593,56 +515,60 @@ export class Browser extends React.Component {
{ops}
-
+
{breadcrumb}
-
-
-
-
-
- |
- Name |
- Uploaded |
- Size |
- Action |
-
-
- {uploadingList}
-
-
- |
- |
- |
- |
- |
-
-
-
+ {this.props.uploadings.size === 0 ? null : (
+
+
+
+
+
+
+ |
+ Name |
+ Uploaded |
+ Size |
+
+
+ {uploadingList}
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+ )}
-
-
-
-
-
- |
- Name |
- File Size |
- Mod Time |
- Edit |
-
-
- {itemList}
-
-
- |
- |
- |
- |
- |
-
-
-
+
+
+
+
+
+
+ |
+ Name |
+ File Size |
+ Mod Time |
+ Edit |
+
+
+ {itemList}
+
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+
);
diff --git a/src/client/web/src/components/core_state.ts b/src/client/web/src/components/core_state.ts
index 22fbc30..58a65fc 100644
--- a/src/client/web/src/components/core_state.ts
+++ b/src/client/web/src/components/core_state.ts
@@ -1,4 +1,4 @@
-import { List } from "immutable";
+import { List, Set } from "immutable";
import BgWorker from "../worker/upload.bg.worker";
import { FgWorker } from "../worker/upload.fgworker";
@@ -15,6 +15,7 @@ export interface IContext {
export interface ICoreState {
ctx: IContext;
panel: PanelProps;
+ isVertical: boolean;
}
export function initWithWorker(worker: IWorker): ICoreState {
@@ -33,21 +34,34 @@ export function init(): ICoreState {
return initState();
}
+export function isVertical(): boolean {
+ return window.innerWidth <= window.innerHeight;
+}
+
export function initState(): ICoreState {
return {
ctx: null,
+ isVertical: isVertical(),
panel: {
displaying: "browser",
authPane: {
authed: false,
},
browser: {
+ isVertical: isVertical(),
dirPath: List(["."]),
items: List- ([]),
uploadings: List([]),
uploadValue: "",
uploadFiles: List([]),
},
+ panes: {
+ displaying: "",
+ paneNames: Set(["settings", "login"]),
+ login: {
+ authed: false,
+ },
+ },
},
};
}
diff --git a/src/client/web/src/components/layouter.tsx b/src/client/web/src/components/layouter.tsx
new file mode 100644
index 0000000..5001a2d
--- /dev/null
+++ b/src/client/web/src/components/layouter.tsx
@@ -0,0 +1,38 @@
+import * as React from "react";
+
+export interface Props {
+ isHorizontal: boolean;
+ elements: Array;
+}
+
+export interface State {}
+export class Layouter extends React.Component {
+ constructor(p: Props) {
+ super(p);
+ }
+
+ horizontalLayout = (children: Array): Array => {
+ return children.map((child: JSX.Element, idx: number) => {
+ // if (idx === 0) {
+ // return {child};
+ // }
+ return (
+
+ {child}
+
+
+ );
+ });
+ };
+
+ verticalLayout = (children: Array): Array => {
+ return this.horizontalLayout(children);
+ };
+
+ render() {
+ const elements = this.props.isHorizontal
+ ? this.horizontalLayout(this.props.elements)
+ : this.verticalLayout(this.props.elements);
+ return
{elements}
;
+ }
+}
diff --git a/src/client/web/src/components/auth_pane.tsx b/src/client/web/src/components/pane_login.tsx
similarity index 62%
rename from src/client/web/src/components/auth_pane.tsx
rename to src/client/web/src/components/pane_login.tsx
index a8f12c4..c8ab867 100644
--- a/src/client/web/src/components/auth_pane.tsx
+++ b/src/client/web/src/components/pane_login.tsx
@@ -1,8 +1,12 @@
import * as React from "react";
+import { List } from "immutable";
import { ICoreState } from "./core_state";
import { IUsersClient } from "../client";
import { UsersClient } from "../client/users";
+import { Updater as PanesUpdater } from "./panes";
+import { Updater as BrowserUpdater } from "./browser";
+import { Layouter } from "./layouter";
export interface Props {
authed: boolean;
@@ -90,15 +94,30 @@ export class AuthPane extends React.Component {
};
login = () => {
- Updater.login(this.state.user, this.state.pwd).then((ok: boolean) => {
- if (ok) {
- this.update(Updater.setAuthPane);
- this.setState({ user: "", pwd: "" });
- } else {
- this.setState({ user: "", pwd: "" });
- alert("Failed to login.");
- }
- });
+ Updater.login(this.state.user, this.state.pwd)
+ .then((ok: boolean) => {
+ if (ok) {
+ this.update(Updater.setAuthPane);
+ this.setState({ user: "", pwd: "" });
+ // close all the panes
+ PanesUpdater.displayPane("");
+ this.update(PanesUpdater.updateState);
+
+ // refresh
+ return BrowserUpdater.setItems(
+ List(["."])
+ );
+ } else {
+ this.setState({ user: "", pwd: "" });
+ alert("Failed to login.");
+ }
+ })
+ .then(() => {
+ return BrowserUpdater.refreshUploadings();
+ })
+ .then((_: boolean) => {
+ this.update(BrowserUpdater.setBrowser);
+ });
};
logout = () => {
@@ -112,30 +131,38 @@ export class AuthPane extends React.Component {
};
render() {
+ const elements: Array = [
+ ,
+ ,
+ ,
+ ];
+
return (
-
-
-
+ Login
+