feature (share): frontend of the share feature

This commit is contained in:
Mickael Kerjean
2018-09-01 00:55:17 +10:00
committed by Mickael KERJEAN
parent 1077165e3d
commit d3a5153920
18 changed files with 653 additions and 42 deletions

View File

@ -1,8 +1,8 @@
@mixin ripple($color) { @mixin ripple($color, $bg:"#ffffff00") {
background-position: center; background-position: center;
transition: background 0.4s;
&:hover{ &: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{ &:active{
background-color: #00000033; background-color: #00000033;

View File

@ -131,3 +131,4 @@ select:-moz-focusring {
scrollbar-shadow-color:#2d2c4d; scrollbar-shadow-color:#2d2c4d;
scrollbar-track-color:rgba(0,0,0,.1); scrollbar-track-color:rgba(0,0,0,.1);
} }
.pointer{cursor: pointer;}

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
fill="#000000"
height="24"
viewBox="0 0 24 24"
width="24"
version="1.1"
id="svg855"
sodipodi:docname="arrow_bottom.svg"
inkscape:version="0.92.2 2405546, 2018-03-11">
<metadata
id="metadata861">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs859" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="940"
inkscape:window-height="1027"
id="namedview857"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="11.491525"
inkscape:cy="12"
inkscape:window-x="964"
inkscape:window-y="37"
inkscape:window-maximized="0"
inkscape:current-layer="svg855" />
<path
d="m 7.705,8.045 4.59,4.58 4.59,-4.58 1.41,1.41 -6,6 -6,-6 z"
id="path851"
style="fill:#000000;fill-opacity:0.53333321"
inkscape:connector-curvature="0" />
<path
d="M0-.25h24v24H0z"
fill="none"
id="path853" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
fill="#000000"
height="24"
viewBox="0 0 24 24"
width="24"
version="1.1"
id="svg855"
sodipodi:docname="arrow_top.svg"
inkscape:version="0.92.2 2405546, 2018-03-11">
<metadata
id="metadata861">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs859" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="940"
inkscape:window-height="1027"
id="namedview857"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="11.491525"
inkscape:cy="12"
inkscape:window-x="964"
inkscape:window-y="37"
inkscape:window-maximized="0"
inkscape:current-layer="svg855" />
<path
d="m 16.885,15.455 -4.59,-4.58 -4.59,4.58 -1.41,-1.41 6,-6 6,6 z"
id="path851"
style="fill:#000000;fill-opacity:0.53333309"
inkscape:connector-curvature="0" />
<path
d="M0-.25h24v24H0z"
fill="none"
id="path853" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -22,7 +22,7 @@
inkscape:version="0.92.2 2405546, 2018-03-11"><metadata inkscape:version="0.92.2 2405546, 2018-03-11"><metadata
id="metadata1544"><rdf:RDF><cc:Work id="metadata1544"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs1542" /><sodipodi:namedview id="defs1542" /><sodipodi:namedview
pagecolor="#ffffff" pagecolor="#ffffff"
bordercolor="#666666" bordercolor="#666666"
@ -32,24 +32,24 @@
guidetolerance="10" guidetolerance="10"
inkscape:pageopacity="0" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:window-width="1894" inkscape:window-width="940"
inkscape:window-height="1027" inkscape:window-height="1027"
id="namedview1540" id="namedview1540"
showgrid="false" showgrid="false"
inkscape:zoom="1.84375" inkscape:zoom="0.65186406"
inkscape:cx="211.15042" inkscape:cx="254.37191"
inkscape:cy="276.97144" inkscape:cy="87.860425"
inkscape:window-x="10" inkscape:window-x="964"
inkscape:window-y="37" inkscape:window-y="37"
inkscape:window-maximized="0" inkscape:window-maximized="0"
inkscape:current-layer="g1507" /> inkscape:current-layer="g1507" />
<g <g
id="g1507"> id="g1507">
<path <path
d="m 36.241,36.241 c -0.781,0.781 -2.047,0.781 -2.828,0 l -7.425,-7.425 -7.778,7.778 c -0.781,0.781 -2.047,0.781 -2.828,0 -0.781,-0.781 -0.781,-2.047 0,-2.828 l 7.778,-7.778 -7.425,-7.425 c -0.781,-0.781 -0.781,-2.048 0,-2.828 0.781,-0.781 2.047,-0.781 2.828,0 l 7.425,7.425 7.071,-7.071 c 0.781,-0.781 2.047,-0.781 2.828,0 0.781,0.781 0.781,2.047 0,2.828 l -7.071,7.071 7.425,7.425 c 0.781,0.781 0.781,2.047 0,2.828 z" d="m 41.00531,40.844062 c -1.137768,1.137765 -2.982088,1.137765 -4.119861,0 L 26.068628,30.027234 14.737551,41.35831 c -1.137771,1.137771 -2.982093,1.137771 -4.119861,0 -1.1377722,-1.137768 -1.1377722,-2.982088 0,-4.119861 L 21.948766,25.907372 11.131938,15.090551 c -1.1377647,-1.137771 -1.1377647,-2.983553 0,-4.119861 1.137774,-1.1377721 2.982098,-1.1377721 4.119865,0 L 26.068628,21.787512 36.369739,11.486399 c 1.137768,-1.137768 2.982093,-1.137768 4.119862,0 1.137767,1.137769 1.137767,2.982094 0,4.119862 L 30.188489,25.907372 41.00531,36.724197 c 1.137771,1.137767 1.137771,2.982091 0,4.119865 z"
id="path1505" id="path1505"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#000000;fill-opacity:0.53333336" style="fill:#000000;fill-opacity:0.53333285;stroke-width:1.45681119"
sodipodi:nodetypes="sscssscscscssscss" /> sodipodi:nodetypes="sscssscscscssscss" />
</g> </g>
<g <g

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -1,43 +1,117 @@
<?xml version="1.0" encoding="iso-8859-1"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" width="512px" height="512px" viewBox="0 0 482.428 482.429" style="enable-background:new 0 0 482.428 482.429;" xml:space="preserve"> <svg
<g> xmlns:dc="http://purl.org/dc/elements/1.1/"
<g> xmlns:cc="http://creativecommons.org/ns#"
<path d="M381.163,57.799h-75.094C302.323,25.316,274.686,0,241.214,0c-33.471,0-61.104,25.315-64.85,57.799h-75.098 c-30.39,0-55.111,24.728-55.111,55.117v2.828c0,23.223,14.46,43.1,34.83,51.199v260.369c0,30.39,24.724,55.117,55.112,55.117 h210.236c30.389,0,55.111-24.729,55.111-55.117V166.944c20.369-8.1,34.83-27.977,34.83-51.199v-2.828 C436.274,82.527,411.551,57.799,381.163,57.799z M241.214,26.139c19.037,0,34.927,13.645,38.443,31.66h-76.879 C206.293,39.783,222.184,26.139,241.214,26.139z M375.305,427.312c0,15.978-13,28.979-28.973,28.979H136.096 c-15.973,0-28.973-13.002-28.973-28.979V170.861h268.182V427.312z M410.135,115.744c0,15.978-13,28.979-28.973,28.979H101.266 c-15.973,0-28.973-13.001-28.973-28.979v-2.828c0-15.978,13-28.979,28.973-28.979h279.897c15.973,0,28.973,13.001,28.973,28.979 V115.744z" fill="#6F6F6F"/> xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
<path d="M171.144,422.863c7.218,0,13.069-5.853,13.069-13.068V262.641c0-7.216-5.852-13.07-13.069-13.07 c-7.217,0-13.069,5.854-13.069,13.07v147.154C158.074,417.012,163.926,422.863,171.144,422.863z" fill="#6F6F6F"/> xmlns:svg="http://www.w3.org/2000/svg"
<path d="M241.214,422.863c7.218,0,13.07-5.853,13.07-13.068V262.641c0-7.216-5.854-13.07-13.07-13.07 c-7.217,0-13.069,5.854-13.069,13.07v147.154C228.145,417.012,233.996,422.863,241.214,422.863z" fill="#6F6F6F"/> xmlns="http://www.w3.org/2000/svg"
<path d="M311.284,422.863c7.217,0,13.068-5.853,13.068-13.068V262.641c0-7.216-5.852-13.07-13.068-13.07 c-7.219,0-13.07,5.854-13.07,13.07v147.154C298.213,417.012,304.067,422.863,311.284,422.863z" fill="#6F6F6F"/> xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
width="512px"
height="512px"
viewBox="0 0 482.428 482.429"
style="enable-background:new 0 0 482.428 482.429;"
xml:space="preserve"
sodipodi:docname="delete.svg"
inkscape:version="0.92.2 2405546, 2018-03-11"><metadata
id="metadata12072"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs12070" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="940"
inkscape:window-height="1027"
id="namedview12068"
showgrid="false"
inkscape:zoom="0.4609375"
inkscape:cx="345.71234"
inkscape:cy="256"
inkscape:window-x="964"
inkscape:window-y="37"
inkscape:window-maximized="0"
inkscape:current-layer="g12033" />
<g
id="g12035">
<g
id="g12033">
<path
style="fill:#6f6f6f;stroke-width:0.94720054"
d="m 239.71038,10.858567 c -29.28633,0.148959 -56.22821,23.158167 -61.13883,51.996129 -0.0359,5.52244 -8.11936,1.523852 -11.81411,2.734301 -21.6593,0.35718 -43.3802,-0.676875 -65.00719,0.438452 -25.743961,2.814896 -47.041084,26.38176 -47.173172,52.292131 -1.722118,22.32277 11.67841,44.77809 32.328768,53.53532 1.502767,7.1355 0.21419,16.11228 0.6438,23.95568 0.110145,75.28311 -0.218433,150.57737 0.16095,225.85367 1.489805,25.85192 23.952414,48.2976 49.805724,49.76687 68.99532,0.27998 138.01654,0.22966 207.01317,0.0247 26.01852,-1.27691 48.7205,-23.83443 50.19249,-49.83767 0.36528,-83.15398 0.0497,-166.32508 0.15538,-249.48558 20.84859,-8.52199 34.59567,-30.97499 32.97936,-53.41131 0.0752,-26.071611 -21.32469,-49.900442 -47.22991,-52.699206 -24.66109,-1.093651 -49.41288,-0.08487 -74.1055,-0.4329 -3.94837,0.612911 -2.30787,-5.374859 -3.87914,-7.978923 -7.2617,-27.587042 -34.37102,-47.8548238 -62.93179,-46.751643 z m 1.50404,28.530717 c 15.47006,-0.305619 30.26667,11.084948 34.03447,26.199713 -22.6834,-0.0059 -45.39692,0.01233 -68.06154,-0.0092 3.80702,-15.070204 18.34117,-26.419823 34.02707,-26.190463 z M 108.67851,94.136363 c 89.45257,0.139166 178.92883,-0.277651 268.36669,0.2072 13.74131,1.418578 23.92664,15.176077 22.26662,28.708317 0.0325,14.55054 -14.07514,26.56026 -28.40492,24.87142 -88.5066,-0.14037 -177.03691,0.28006 -265.5288,-0.20906 C 91.12559,146.24223 80.989285,131.70163 83.181794,117.70168 83.970887,104.7413 95.577131,93.920596 108.67851,94.136363 Z M 366.33,176.47066 c -0.14077,81.42848 0.28062,162.8807 -0.20905,244.29447 -1.43862,13.91151 -15.47685,24.10588 -29.15232,22.27588 -66.19678,-0.14162 -132.41743,0.28175 -198.59945,-0.20906 -13.89169,-1.4488 -24.11909,-15.47575 -22.27216,-29.15786 0,-79.06781 0,-158.13561 0,-237.20343 83.41098,0 166.82198,0 250.23298,0 z"
id="path12025"
inkscape:connector-curvature="0" />
<path
style="fill:#6f6f6f;stroke-width:0.982081"
d="m 171.68644,247.47379 c -9.34676,0.15644 -15.74032,9.88805 -14.08673,18.71133 0.12351,47.62701 -0.24401,95.27903 0.17839,142.89087 1.20764,10.97136 15.91803,16.52794 24.07249,9.08425 8.41759,-6.81887 4.47469,-18.88392 5.34774,-28.08138 -0.12439,-43.37127 0.2452,-86.76784 -0.17839,-130.12381 -1.03795,-7.31439 -7.95054,-12.95705 -15.3335,-12.48126 z"
id="path12027"
inkscape:connector-curvature="0" />
<path
style="fill:#6f6f6f;stroke-width:0.982081"
d="m 240.50116,247.47379 c -9.34649,0.15616 -15.74067,9.88817 -14.08673,18.71133 0.12352,47.62701 -0.24401,95.27903 0.17839,142.89087 1.80504,17.56489 30.37412,15.34227 29.42023,-2.30176 -0.12333,-48.93642 0.24377,-97.8982 -0.17838,-146.81918 -1.03735,-7.3141 -7.95101,-12.95694 -15.33351,-12.48126 z"
id="path12029"
inkscape:connector-curvature="0" />
<path
style="fill:#6f6f6f;stroke-width:0.982081"
d="m 309.31588,247.47379 c -9.34711,0.1553 -15.74314,9.88713 -14.08672,18.71133 0.12354,47.62499 -0.24406,95.27581 0.17838,142.88511 1.19856,10.97321 15.91664,16.53606 24.07058,9.09001 8.41933,-6.81787 4.47366,-18.8842 5.34774,-28.08138 -0.12442,-43.37124 0.24524,-86.76804 -0.17839,-130.12381 -1.03674,-7.31321 -7.95012,-12.95658 -15.33159,-12.48126 z"
id="path12031"
inkscape:connector-curvature="0" />
</g> </g>
</g> </g>
<g> <g
id="g12037">
</g> </g>
<g> <g
id="g12039">
</g> </g>
<g> <g
id="g12041">
</g> </g>
<g> <g
id="g12043">
</g> </g>
<g> <g
id="g12045">
</g> </g>
<g> <g
id="g12047">
</g> </g>
<g> <g
id="g12049">
</g> </g>
<g> <g
id="g12051">
</g> </g>
<g> <g
id="g12053">
</g> </g>
<g> <g
id="g12055">
</g> </g>
<g> <g
id="g12057">
</g> </g>
<g> <g
id="g12059">
</g> </g>
<g> <g
id="g12061">
</g> </g>
<g> <g
id="g12063">
</g> </g>
<g> <g
id="g12065">
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg2121"
sodipodi:docname="share.svg"
inkscape:version="0.92.2 2405546, 2018-03-11">
<defs
id="defs2115" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="349.03513"
inkscape:cy="449.48307"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1894"
inkscape:window-height="1027"
inkscape:window-x="10"
inkscape:window-y="37"
inkscape:window-maximized="0" />
<metadata
id="metadata2118">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<circle
style="fill:#6f6f6f;fill-opacity:0.06640625;stroke:#6f6f6f;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path6378"
cx="171.96956"
cy="70.839935"
r="28.324646" />
<circle
style="fill:#6f6f6f;fill-opacity:0.06640625;stroke:#6f6f6f;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path6378-3"
cx="171.96956"
cy="225.38493"
r="28.324646" />
<circle
style="fill:#6f6f6f;fill-opacity:0.06640625;stroke:#6f6f6f;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path6378-6"
cx="38.030434"
cy="148.11235"
r="28.324646" />
<path
style="fill:none;stroke:#6f6f6f;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 65.35794,134.60213 149.47465,83.197409"
id="path7970"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#6f6f6f;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.94117647"
d="m 61.46365,160.30446 86.45329,50.62581"
id="path7972"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -8,6 +8,7 @@ import img_save from "../assets/img/save.svg";
import img_power from "../assets/img/power.svg"; import img_power from "../assets/img/power.svg";
import img_edit from "../assets/img/edit.svg"; import img_edit from "../assets/img/edit.svg";
import img_delete from "../assets/img/delete.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_bucket from "../assets/img/bucket.svg";
import img_link from "../assets/img/link.svg"; import img_link from "../assets/img/link.svg";
import img_loading from "../assets/img/loader.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 from '../assets/img/arrow_right.svg';
import img_arrow_right_white from '../assets/img/arrow_right_white.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_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_more from '../assets/img/more.svg';
import img_close from '../assets/img/close.svg'; import img_close from '../assets/img/close.svg';
import img_close_dark from '../assets/img/close_dark.svg'; import img_close_dark from '../assets/img/close_dark.svg';
@ -59,6 +62,8 @@ export const Icon = (props) => {
img = img_edit; img = img_edit;
}else if(props.name === 'delete'){ }else if(props.name === 'delete'){
img = img_delete; img = img_delete;
}else if(props.name === 'share'){
img = img_share;
}else if(props.name === 'bucket'){ }else if(props.name === 'bucket'){
img = img_bucket; img = img_bucket;
}else if(props.name === 'link'){ }else if(props.name === 'link'){
@ -83,6 +88,10 @@ export const Icon = (props) => {
img = img_alarm; img = img_alarm;
}else if(props.name === 'todo_white'){ }else if(props.name === 'todo_white'){
img = img_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'){ }else if(props.name === 'arrow_right'){
img = img_arrow_right; img = img_arrow_right;
}else if(props.name === 'arrow_right_white'){ }else if(props.name === 'arrow_right_white'){

View File

@ -24,5 +24,6 @@ export class NgIf extends React.Component {
} }
NgIf.propTypes = { NgIf.propTypes = {
cond: PropTypes.bool.isRequired cond: PropTypes.bool.isRequired,
type: PropTypes.string
}; };

View File

@ -4,7 +4,7 @@
left: 20px; left: 20px;
right: 0; right: 0;
font-size: 0.95em; font-size: 0.95em;
z-index: 10; z-index: 1001;
.component_notification--container{ .component_notification--container{
overflow: hidden; overflow: hidden;

View File

@ -10,7 +10,7 @@ export { prepare } from './navigate';
export { invalidate, http_get, http_post, http_delete } from './ajax'; export { invalidate, http_get, http_post, http_delete } from './ajax';
export { prompt, alert, confirm } from './popup'; export { prompt, alert, confirm } from './popup';
export { notify } from './notify'; export { notify } from './notify';
export { gid } from './random'; export { gid, randomString } from './random';
export { leftPad } from './common'; export { leftPad } from './common';
export { getMimeType } from './mimetype'; export { getMimeType } from './mimetype';
export { settings_get, settings_put } from './settings'; export { settings_get, settings_put } from './settings';

View File

@ -4,3 +4,17 @@ export function gid(prefix){
id += parseInt(Math.random()*Math.pow(10,16)).toString(32); id += parseInt(Math.random()*Math.pow(10,16)).toString(32);
return id; 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<size; i++){
str += alphabet[Math.floor(Math.random()*alphabet_size)]
}
return str;
}

View File

@ -1,2 +1,3 @@
export { Files } from './files'; export { Files } from './files';
export { Session } from './session'; export { Session } from './session';
export { Share } from './share';

22
client/model/share.js Normal file
View File

@ -0,0 +1,22 @@
import { http_get, http_post, http_delete } from '../helpers/';
class ShareModel {
constructor(){}
all(path = "/"){
const url = `api/share?path=${path}`;
return http_get(url);
}
upsert(obj){
const url = `/api/share/${obj.id}`
return http_post(url, obj);
}
remove(id){
const url = `/api/share/${id}`;
return http_delete(url);
}
}
export const Share = new ShareModel()

View File

@ -0,0 +1,164 @@
import React from 'react';
import PropTypes from 'prop-types';
import { NgIf, Icon } from '../../components/';
import { Share } from '../../model/';
import { randomString, notify } from '../../helpers/';
import './share.scss';
export class ShareComponent extends React.Component {
constructor(props){
super(props);
this.state = {
show_advanced: false,
role: null,
id: randomString(7),
existings: [
{id: "dflkjse", role: "UPLOADER", path: "./test/test"},
{id: "dflkjse", role: "VIEWER", path: "./test/test", password: "xxxx"},
{id: "dflkjse", role: "EDITOR", path: "./test/test"},
{id: "dflkjse", role: "VIEWER", path: "./test/test", password: "xxxx"},
{id: "dflkjse", role: "EDITOR", path: "./test/test"},
{id: "dflkjse", role: "UPLOADER", path: "./test/test"},
]
};
}
updateState(key, value){
if(this.state[key] === value){
this.setState({[key]: null});
}else{
this.setState({[key]: value});
}
if(key === "role" && value && window.innerHeight < 500){
window.dispatchEvent(new Event('resize'));
}
}
registerLink(e){
e.target.setSelectionRange(0, e.target.value.length);
let st = Object.assign({}, this.state);
delete st.existings;
delete st.show_advanced;
this.setState({existing: [st].concat(this.state.existings)});
return Share.upsert(st)
.catch((err) => {
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 (
<div className="component_share">
<h2>Create a New Link</h2>
<div className="share--content link-type no-select">
<div onClick={this.updateState.bind(this, 'role', 'uploader')} className={this.state.role === "uploader" ? "active" : ""}>
Uploader
</div>
<div onClick={this.updateState.bind(this, 'role', 'viewer')} className={this.state.role === "viewer" ? "active" : ""}>
Viewer
</div>
<div onClick={this.updateState.bind(this, 'role', 'editor')} className={this.state.role === "editor" ? "active" : ""}>
Editor
</div>
</div>
<NgIf cond={this.state.role === null && !!this.state.existings && this.state.existings.length > 0}>
<h2>Existing Links</h2>
<div className="share--content existing-links" style={{"maxHeight": this.state.existings && this.state.existings.length > 5 ? '90px' : 'inherit'}}>
{
this.state.existings && this.state.existings.map((link, i) => {
return (
<div className="link-details" key={i}>
<span className="role">{link.role}</span>
<span>{link.path}</span>
<Icon onClick={this.onDelete.bind(this, link.id)} name="delete"/>
<Icon onClick={this.onLoad.bind(this, link)} name="edit"/>
</div>
);
})
}
</div>
</NgIf>
<NgIf cond={this.state.role !== null}>
<h2>Restrictions</h2>
<div className="share--content advanced-settings no-select">
<SuperCheckbox value={this.state.users} label="Only for users" placeholder="list of users who can access the link" onChange={this.updateState.bind(this, 'users')} inputType="text"/>
<SuperCheckbox value={this.state.password} label="Password" placeholder="protect access with a password" onChange={this.updateState.bind(this, 'password')} inputType="password"/>
</div>
<h2 className="no-select pointer" onClick={this.updateState.bind(this, 'show_advanced', !this.state.show_advanced)}>
Advanced
<NgIf type="inline" cond={!!this.state.show_advanced}><Icon name="arrow_top"/></NgIf>
<NgIf type="inline" cond={!this.state.show_advanced}><Icon name="arrow_bottom"/></NgIf>
</h2>
<div className="share--content advanced-settings no-select">
<NgIf cond={this.state.show_advanced === true}>
<SuperCheckbox value={this.state.can_manage_own} label="Can Manage Own" onChange={this.updateState.bind(this, 'can_manage_own')}/>
<SuperCheckbox value={this.state.can_share} label="Can Share" onChange={this.updateState.bind(this, 'can_share')}/>
<SuperCheckbox value={this.state.expiration} label="Expiration" placeholder="The link won't be valid after" onChange={this.updateState.bind(this, 'expiration')} inputType="date"/>
<SuperCheckbox value={this.state.url} label="Custom Link url" placeholder="beautiful_url" onChange={this.updateState.bind(this, 'url')} inputType="text"/>
</NgIf>
</div>
<div className="shared-link">
<input onClick={this.registerLink.bind(this)} type="text" value={window.location.origin+"/s/"+(this.state.url || this.state.id)} onChange={() => {}}/>
</div>
</NgIf>
</div>
);
}
}
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 (
<div className="component_supercheckbox">
<label>
<input type="checkbox" checked={_is_expended} onChange={onCheckboxTick}/>
{props.label}
</label>
<NgIf cond={_is_expended && props.inputType !== undefined}>
<input type={props.inputType} placeholder={props.placeholder} value={props.value || ""} onChange={onValueChange}/>
</NgIf>
</div>
);
};
SuperCheckbox.PropTypes = {
label: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
inputType: PropTypes.string,
placeholder: PropTypes.string,
value: PropTypes.string
};

View File

@ -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);
}
}

View File

@ -5,9 +5,10 @@ import { Link } from 'react-router-dom';
import { DragSource, DropTarget } from 'react-dnd'; import { DragSource, DropTarget } from 'react-dnd';
import './thing.scss'; import './thing.scss';
import { Card, NgIf, Icon, EventEmitter } from '../../components/'; import { Card, NgIf, Icon, EventEmitter, Button } from '../../components/';
import { pathBuilder, prompt, leftPad, getMimeType, debounce, memory } from '../../helpers/'; import { pathBuilder, prompt, alert, leftPad, getMimeType, debounce, memory } from '../../helpers/';
import { Files } from '../../model/'; import { Files } from '../../model/';
import { ShareComponent } from './share';
import img_placeholder from '../../assets/img/placeholder.png'; import img_placeholder from '../../assets/img/placeholder.png';
const fileSource = { const fileSource = {
@ -181,6 +182,14 @@ export class ExistingThing extends React.Component {
onDeleteCancel(){ onDeleteCancel(){
this.setState({delete_request: false}); this.setState({delete_request: false});
} }
onShareRequest(filename){
alert.now(
<ShareComponent/>,
(ok) => {}
);
}
_confirm_delete_text(){ _confirm_delete_text(){
return this.props.file.name.length > 16? this.props.file.name.substring(0, 10).toLowerCase() : this.props.file.name; 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 {
<Filename filename={this.props.file.name} filesize={this.props.file.size} filetype={this.props.file.type} onRename={this.onRename.bind(this)} is_renaming={this.state.is_renaming} onRenameCancel={this.onRenameRequest.bind(this, false)}/> <Filename filename={this.props.file.name} filesize={this.props.file.size} filetype={this.props.file.type} onRename={this.onRename.bind(this)} is_renaming={this.state.is_renaming} onRenameCancel={this.onRenameRequest.bind(this, false)}/>
<Message message={this.state.message} /> <Message message={this.state.message} />
<DateTime show={this.state.icon !== 'loading'} timestamp={this.props.file.time} /> <DateTime show={this.state.icon !== 'loading'} timestamp={this.props.file.time} />
<ActionButton onClickRename={this.onRenameRequest.bind(this)} onClickDelete={this.onDeleteRequest.bind(this)} is_renaming={this.state.is_renaming} can_rename={this.props.metadata.can_rename !== false} can_delete={this.props.metadata.can_delete !== false} /> <ActionButton onClickRename={this.onRenameRequest.bind(this)} onClickDelete={this.onDeleteRequest.bind(this)} onClickShare={this.onShareRequest.bind(this)} is_renaming={this.state.is_renaming} can_rename={this.props.metadata.can_rename !== false} can_delete={this.props.metadata.can_delete !== false} />
</Card> </Card>
</Link> </Link>
</div> </div>
@ -291,6 +300,11 @@ const ActionButton = (props) => {
props.onClickDelete(); props.onClickDelete();
}; };
const onShare = (e) => {
e.preventDefault();
props.onClickShare();
};
return ( return (
<div className="component_action"> <div className="component_action">
<NgIf cond={props.can_rename !== false && props.is_renaming === false} type="inline"> <NgIf cond={props.can_rename !== false && props.is_renaming === false} type="inline">
@ -299,6 +313,9 @@ const ActionButton = (props) => {
<NgIf cond={props.can_delete !== false} type="inline"> <NgIf cond={props.can_delete !== false} type="inline">
<Icon name="delete" onClick={onDelete} className="component_updater--icon"/> <Icon name="delete" onClick={onDelete} className="component_updater--icon"/>
</NgIf> </NgIf>
<NgIf cond={props.can_share !== false} type="inline">
<Icon name="share" onClick={onShare} className="component_updater--icon"/>
</NgIf>
</div> </div>
); );
} }

View File

@ -26,6 +26,11 @@ func Init(a *App) *http.Server {
files.HandleFunc("/mkdir", APIHandler(LoggedInOnly(FileMkdir), *a)).Methods("GET") files.HandleFunc("/mkdir", APIHandler(LoggedInOnly(FileMkdir), *a)).Methods("GET")
files.HandleFunc("/touch", APIHandler(LoggedInOnly(FileTouch), *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.HandleFunc("/api/config", CtxInjector(ConfigHandler, *a))
r.PathPrefix("/assets").Handler(StaticHandler("./data/public/", *a)) r.PathPrefix("/assets").Handler(StaticHandler("./data/public/", *a))