From d3a5153920dab1738c1bfd6b7f6f90953a94d13a Mon Sep 17 00:00:00 2001 From: Mickael Kerjean Date: Sat, 1 Sep 2018 00:55:17 +1000 Subject: [PATCH] feature (share): frontend of the share feature --- client/assets/css/mixin.scss | 6 +- client/assets/css/reset.scss | 1 + client/assets/img/arrow_bottom.svg | 61 +++++++++ client/assets/img/arrow_top.svg | 61 +++++++++ client/assets/img/close.svg | 16 +-- client/assets/img/delete.svg | 124 +++++++++++++---- client/assets/img/share.svg | 83 ++++++++++++ client/components/icon.js | 9 ++ client/components/ngif.js | 3 +- client/components/notification.scss | 2 +- client/helpers/index.js | 2 +- client/helpers/random.js | 14 ++ client/model/index.js | 1 + client/model/share.js | 22 +++ client/pages/filespage/share.js | 164 +++++++++++++++++++++++ client/pages/filespage/share.scss | 98 ++++++++++++++ client/pages/filespage/thing-existing.js | 23 +++- server/router/index.go | 5 + 18 files changed, 653 insertions(+), 42 deletions(-) create mode 100644 client/assets/img/arrow_bottom.svg create mode 100644 client/assets/img/arrow_top.svg create mode 100644 client/assets/img/share.svg create mode 100644 client/model/share.js create mode 100644 client/pages/filespage/share.js create mode 100644 client/pages/filespage/share.scss diff --git a/client/assets/css/mixin.scss b/client/assets/css/mixin.scss index e4238cf0..5231698a 100644 --- a/client/assets/css/mixin.scss +++ b/client/assets/css/mixin.scss @@ -1,8 +1,8 @@ -@mixin ripple($color) { +@mixin ripple($color, $bg:"#ffffff00") { background-position: center; - transition: background 0.4s; &:hover{ - background: #ffffff00 radial-gradient(circle, transparent 1%, $color 1%) center/15000%; + transition: background 0.4s; + background: unquote($bg) radial-gradient(circle, transparent 1%, $color 1%) center/15000%; } &:active{ background-color: #00000033; diff --git a/client/assets/css/reset.scss b/client/assets/css/reset.scss index ba72b4e1..34131be9 100644 --- a/client/assets/css/reset.scss +++ b/client/assets/css/reset.scss @@ -131,3 +131,4 @@ select:-moz-focusring { scrollbar-shadow-color:#2d2c4d; scrollbar-track-color:rgba(0,0,0,.1); } +.pointer{cursor: pointer;} diff --git a/client/assets/img/arrow_bottom.svg b/client/assets/img/arrow_bottom.svg new file mode 100644 index 00000000..684c2d81 --- /dev/null +++ b/client/assets/img/arrow_bottom.svg @@ -0,0 +1,61 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/client/assets/img/arrow_top.svg b/client/assets/img/arrow_top.svg new file mode 100644 index 00000000..932c387b --- /dev/null +++ b/client/assets/img/arrow_top.svg @@ -0,0 +1,61 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/client/assets/img/close.svg b/client/assets/img/close.svg index 7fc48d3e..f7e61851 100644 --- a/client/assets/img/close.svg +++ b/client/assets/img/close.svg @@ -22,7 +22,7 @@ inkscape:version="0.92.2 2405546, 2018-03-11">image/svg+xml + - - - - - - - - + +image/svg+xml + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/client/assets/img/share.svg b/client/assets/img/share.svg new file mode 100644 index 00000000..abc89881 --- /dev/null +++ b/client/assets/img/share.svg @@ -0,0 +1,83 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/client/components/icon.js b/client/components/icon.js index 2098acd6..e5efebea 100644 --- a/client/components/icon.js +++ b/client/components/icon.js @@ -8,6 +8,7 @@ import img_save from "../assets/img/save.svg"; import img_power from "../assets/img/power.svg"; import img_edit from "../assets/img/edit.svg"; import img_delete from "../assets/img/delete.svg"; +import img_share from "../assets/img/share.svg"; import img_bucket from "../assets/img/bucket.svg"; import img_link from "../assets/img/link.svg"; import img_loading from "../assets/img/loader.svg"; @@ -23,6 +24,8 @@ import img_alarm from '../assets/img/alarm.svg'; import img_arrow_right from '../assets/img/arrow_right.svg'; import img_arrow_right_white from '../assets/img/arrow_right_white.svg'; import img_arrow_left_white from '../assets/img/arrow_left_white.svg'; +import img_arrow_top from '../assets/img/arrow_top.svg'; +import img_arrow_bottom from '../assets/img/arrow_bottom.svg'; import img_more from '../assets/img/more.svg'; import img_close from '../assets/img/close.svg'; import img_close_dark from '../assets/img/close_dark.svg'; @@ -59,6 +62,8 @@ export const Icon = (props) => { img = img_edit; }else if(props.name === 'delete'){ img = img_delete; + }else if(props.name === 'share'){ + img = img_share; }else if(props.name === 'bucket'){ img = img_bucket; }else if(props.name === 'link'){ @@ -83,6 +88,10 @@ export const Icon = (props) => { img = img_alarm; }else if(props.name === 'todo_white'){ img = img_todo_white; + }else if(props.name === 'arrow_bottom'){ + img = img_arrow_bottom; + }else if(props.name === 'arrow_top'){ + img = img_arrow_top; }else if(props.name === 'arrow_right'){ img = img_arrow_right; }else if(props.name === 'arrow_right_white'){ diff --git a/client/components/ngif.js b/client/components/ngif.js index f8d4c544..acc0d9c2 100644 --- a/client/components/ngif.js +++ b/client/components/ngif.js @@ -24,5 +24,6 @@ export class NgIf extends React.Component { } NgIf.propTypes = { - cond: PropTypes.bool.isRequired + cond: PropTypes.bool.isRequired, + type: PropTypes.string }; diff --git a/client/components/notification.scss b/client/components/notification.scss index 5a0b0cd6..22c3b1b7 100644 --- a/client/components/notification.scss +++ b/client/components/notification.scss @@ -4,7 +4,7 @@ left: 20px; right: 0; font-size: 0.95em; - z-index: 10; + z-index: 1001; .component_notification--container{ overflow: hidden; diff --git a/client/helpers/index.js b/client/helpers/index.js index 92c02a74..e8bf2f98 100644 --- a/client/helpers/index.js +++ b/client/helpers/index.js @@ -10,7 +10,7 @@ export { prepare } from './navigate'; export { invalidate, http_get, http_post, http_delete } from './ajax'; export { prompt, alert, confirm } from './popup'; export { notify } from './notify'; -export { gid } from './random'; +export { gid, randomString } from './random'; export { leftPad } from './common'; export { getMimeType } from './mimetype'; export { settings_get, settings_put } from './settings'; diff --git a/client/helpers/random.js b/client/helpers/random.js index 6e17b8f9..ed6edd1e 100644 --- a/client/helpers/random.js +++ b/client/helpers/random.js @@ -4,3 +4,17 @@ export function gid(prefix){ id += parseInt(Math.random()*Math.pow(10,16)).toString(32); return id; } + +export function randomString(size = 16){ + const alphabet = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p", + "q","r","s","t","u","v","x","y","z","A","B","C","D","E","F","G", + "H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W", + "X","Y","Z","0","1","2","3","4","5","6","7","8","9"]; + const alphabet_size = alphabet.length; + + let str = ""; + for(let i=0; i { + notify.send(err, "error"); + this.setState({ + existings: this.state.existings.slice(0, this.state.existings.length) + }); + }); + } + + onLoad(link){ + let st = Object.assign({}, link); + st.show_advanced = false; + st.link_id = st.id; + st.role = (st.role || "").toLowerCase(); + this.setState(st); + } + + onDelete(link_id){ + return Share.remove(link_id) + .then(() => { + console.log("HERE"); + }) + .catch((err) => notify.send(err, "error")); + } + + render(){ + return ( +
+

Create a New Link

+ +
+
+ Uploader +
+
+ Viewer +
+
+ Editor +
+
+ + 0}> +

Existing Links

+
5 ? '90px' : 'inherit'}}> + { + this.state.existings && this.state.existings.map((link, i) => { + return ( +
+ {link.role} + {link.path} + + +
+ ); + }) + } +
+
+ + +

Restrictions

+
+ + +
+ +

+ Advanced + + +

+
+ + + + + + +
+ +
+ {}}/> +
+
+
+ ); + } +} + +const SuperCheckbox = (props) => { + const onCheckboxTick = (e) => { + return props.onChange(e.target.checked ? "" : null); + }; + const onValueChange = (e) => { + props.onChange(e.target.value); + }; + + const _is_expended = function(val){ + return val === null || val === undefined ? false : true; + }(props.value); + return ( +
+ + + + +
+ ); +}; +SuperCheckbox.PropTypes = { + label: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, + inputType: PropTypes.string, + placeholder: PropTypes.string, + value: PropTypes.string +}; diff --git a/client/pages/filespage/share.scss b/client/pages/filespage/share.scss new file mode 100644 index 00000000..d1f346db --- /dev/null +++ b/client/pages/filespage/share.scss @@ -0,0 +1,98 @@ +@import "../../assets/css/mixin.scss"; + +.component_share{ + h2{ + margin: 0 0 5px 0; + font-size: 1.2em; + font-weight: 100; + .component_icon{ + float: right; + } + } + .share--content{ + margin-bottom: 10px; + + &.link-type{ + display: flex; + flex-direction: row; + > div{ + cursor: pointer; + text-align: center; + margin-right: 4px; + padding: 15px 0; + width: 100%; + border-radius: 3px; + background: var(--bg-color); + color: var(--emphasis); + &.active{ + background: var(--primary); + } + @include ripple(var(--emphasis-primary), var(--primary)); + } + } + &.existing-links{ + overflow-y: auto; + -webkit-overflow-scrolling: touch; + + .link-details{ + padding: 3px 5px; + border: 2px solid var(--super-light); + border-left: none; + border-right: none; + margin-top: -2px; + line-height: 20px; + .role{ + margin-left: -5px; + margin-right: 5px; + font-size: 0.8em; + color: var(--light); + display: inline-block; + min-width: 75px; + } + .component_icon{ + width: 20px; + float: right; + cursor: pointer; + } + } + } + &.advanced-settings{ + max-height: 115px; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + } + } + .shared-link{ + input{ + width: 100%; + font-size: 0.9em; + background: var(--emphasis-primary); + color: var(--emphasis); + padding: 3px 5px; + border-radius: 2px; + box-sizing: border-box; + border: 2px solid var(--primary); + } + } +} +.component_supercheckbox{ + > label{ + color: var(--emphasis); + font-size: 0.95em; + font-style: italic; + input{ + margin-right: 5px; + margin-top: 2px; + } + } + > div > input{ + width: calc(100% - 10px); + box-sizing: border-box; + margin-bottom: 5px; + border: 1px solid var(--light); + border-radius: 2px; + font-size: 0.9em; + padding: 2px 5px; + color: var(--emphasis-secondary); + } +} diff --git a/client/pages/filespage/thing-existing.js b/client/pages/filespage/thing-existing.js index 77a06dae..db6fca51 100644 --- a/client/pages/filespage/thing-existing.js +++ b/client/pages/filespage/thing-existing.js @@ -5,9 +5,10 @@ import { Link } from 'react-router-dom'; import { DragSource, DropTarget } from 'react-dnd'; import './thing.scss'; -import { Card, NgIf, Icon, EventEmitter } from '../../components/'; -import { pathBuilder, prompt, leftPad, getMimeType, debounce, memory } from '../../helpers/'; +import { Card, NgIf, Icon, EventEmitter, Button } from '../../components/'; +import { pathBuilder, prompt, alert, leftPad, getMimeType, debounce, memory } from '../../helpers/'; import { Files } from '../../model/'; +import { ShareComponent } from './share'; import img_placeholder from '../../assets/img/placeholder.png'; const fileSource = { @@ -181,6 +182,14 @@ export class ExistingThing extends React.Component { onDeleteCancel(){ this.setState({delete_request: false}); } + + onShareRequest(filename){ + alert.now( + , + (ok) => {} + ); + } + _confirm_delete_text(){ return this.props.file.name.length > 16? this.props.file.name.substring(0, 10).toLowerCase() : this.props.file.name; } @@ -213,7 +222,7 @@ export class ExistingThing extends React.Component { - + @@ -291,6 +300,11 @@ const ActionButton = (props) => { props.onClickDelete(); }; + const onShare = (e) => { + e.preventDefault(); + props.onClickShare(); + }; + return (
@@ -299,6 +313,9 @@ const ActionButton = (props) => { + + +
); } diff --git a/server/router/index.go b/server/router/index.go index a33f4ede..8242ab53 100644 --- a/server/router/index.go +++ b/server/router/index.go @@ -26,6 +26,11 @@ func Init(a *App) *http.Server { files.HandleFunc("/mkdir", APIHandler(LoggedInOnly(FileMkdir), *a)).Methods("GET") files.HandleFunc("/touch", APIHandler(LoggedInOnly(FileTouch), *a)).Methods("GET") + share := r.PathPrefix("/api/share").Subrouter() + share.HandleFunc("", APIHandler(ShareList, *a)).Methods("GET") + share.HandleFunc("/{id}", APIHandler(ShareInsert, *a)).Methods("POST") + share.HandleFunc("/{id}", APIHandler(ShareInsert, *a)).Methods("DELETE") + r.HandleFunc("/api/config", CtxInjector(ConfigHandler, *a)) r.PathPrefix("/assets").Handler(StaticHandler("./data/public/", *a))