Implemented Dropbox login, file browse and file download

This commit is contained in:
Igor Zinken
2020-12-16 00:16:45 +01:00
parent 0bd9c9fb4b
commit f942e45b1d
15 changed files with 541 additions and 48 deletions

View File

@@ -6,6 +6,10 @@ No, I'm building a tool that does the bare minimum what I require and what I don
find in other open source tools. That doesn't mean of course that contributions
related to Photoshop-esque features aren't welcomed.
## Dropbox integration
Requires you to [register a client id or access token](https://www.dropbox.com/developers/apps).
## Project setup
```
npm install
@@ -52,4 +56,3 @@ npm run lint
* Implement clone brush
* Implement document crop
* Implement change history
* Dropbox integration?

65
package-lock.json generated
View File

@@ -9,9 +9,11 @@
"dependencies": {
"canvas": "^2.6.1",
"core-js": "^3.6.5",
"dropbox": "^8.2.0",
"register-service-worker": "^1.7.1",
"semantic-ui-css": "^2.4.1",
"vue": "^2.6.11",
"vue-slider-component": "^3.2.11",
"vuex": "^3.4.0"
},
"devDependencies": {
@@ -6437,6 +6439,17 @@
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
"dev": true
},
"node_modules/dropbox": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/dropbox/-/dropbox-8.2.0.tgz",
"integrity": "sha512-lA/Bb7ZWPaeDK+S4AIeAAlPc49nwbrTlgMSLO5Nghg6FfhqhAKdICtpENviwb0YT1Zgn1NmYk1LCJfiyrJ7lVQ==",
"dependencies": {
"node-fetch": "^2.6.1"
},
"engines": {
"node": ">=0.10.3"
}
},
"node_modules/duplexer": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
@@ -11809,6 +11822,14 @@
"node": ">=0.8"
}
},
"node_modules/node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
"engines": {
"node": "4.x || >=6.0.0"
}
},
"node_modules/node-forge": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
@@ -16942,6 +16963,14 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz",
"integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg=="
},
"node_modules/vue-class-component": {
"version": "7.2.6",
"resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-7.2.6.tgz",
"integrity": "sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w==",
"peerDependencies": {
"vue": "^2.0.0"
}
},
"node_modules/vue-eslint-parser": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.2.0.tgz",
@@ -17022,12 +17051,32 @@
"integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=",
"dev": true
},
"node_modules/vue-property-decorator": {
"version": "8.5.1",
"resolved": "https://registry.npmjs.org/vue-property-decorator/-/vue-property-decorator-8.5.1.tgz",
"integrity": "sha512-O6OUN2OMsYTGPvgFtXeBU3jPnX5ffQ9V4I1WfxFQ6dqz6cOUbR3Usou7kgFpfiXDvV7dJQSFcJ5yUPgOtPPm1Q==",
"dependencies": {
"vue-class-component": "^7.1.0"
},
"peerDependencies": {
"vue": "*"
}
},
"node_modules/vue-search-select": {
"version": "2.9.3",
"resolved": "https://registry.npmjs.org/vue-search-select/-/vue-search-select-2.9.3.tgz",
"integrity": "sha512-JQBTSKKX3nAoeeEaSqg2iQDtR1YBs0qZ08os/QDOqFocGPSvAffNb4VajSnlFWJlKQDJcev3iQuBLcWM2pxr0Q==",
"dev": true
},
"node_modules/vue-slider-component": {
"version": "3.2.11",
"resolved": "https://registry.npmjs.org/vue-slider-component/-/vue-slider-component-3.2.11.tgz",
"integrity": "sha512-2YyJW6TFnYk5FUvqQLvZcCJ+hthBXB819qNHtwnEUyDbOcTXV0n3Ou1ZphOi5FX9phlQIiC2NvjLuRAVmNq+Zw==",
"dependencies": {
"core-js": "^3.6.5",
"vue-property-decorator": "^8.0.0"
}
},
"node_modules/vue-style-loader": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz",
@@ -24056,6 +24105,14 @@
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
"dev": true
},
"dropbox": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/dropbox/-/dropbox-8.2.0.tgz",
"integrity": "sha512-lA/Bb7ZWPaeDK+S4AIeAAlPc49nwbrTlgMSLO5Nghg6FfhqhAKdICtpENviwb0YT1Zgn1NmYk1LCJfiyrJ7lVQ==",
"requires": {
"node-fetch": "^2.6.1"
}
},
"duplexer": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
@@ -28510,6 +28567,11 @@
}
}
},
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"node-forge": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
@@ -32872,7 +32934,8 @@
"vue-class-component": {
"version": "7.2.6",
"resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-7.2.6.tgz",
"integrity": "sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w=="
"integrity": "sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w==",
"requires": {}
},
"vue-eslint-parser": {
"version": "7.2.0",

View File

@@ -11,6 +11,7 @@
"dependencies": {
"canvas": "^2.6.1",
"core-js": "^3.6.5",
"dropbox": "^8.2.0",
"register-service-worker": "^1.7.1",
"semantic-ui-css": "^2.4.1",
"vue": "^2.6.11",

27
public/login.html Normal file
View File

@@ -0,0 +1,27 @@
<html>
<body>
<h1>oAuth result for PhotoMound</h1>
<p>
You should return to PhotoMound shortly. If not, an authentication
error has occurred with the external API.
</p>
</body>
<script type="text/javascript">
(function() {
var params = window.location.hash.split( "&" );
var accessToken = null;
for ( var i = 0; i < params.length; i++ ) {
var param = params[ i ];
if ( param.indexOf( "access_token" ) > -1 ) {
accessToken = param.split( "access_token=" )[ 1 ];
break;
}
}
if ( accessToken ) {
window.opener.postMessage({
"accessToken": accessToken,
}, window.opener );
}
})();
</script>
</html>

View File

@@ -72,11 +72,11 @@ export default {
'closeDialog',
]),
handleConfirm() {
this?.confirmHandler();
this.confirmHandler?.();
this.close();
},
handleCancel() {
this?.cancelHandler();
this.cancelHandler?.();
this.close();
},
close() {

View File

@@ -0,0 +1,165 @@
/**
* The MIT License (MIT)
*
* Igor Zinken 2020 - https://www.igorski.nl
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
<template>
<div class="dropbox-file-modal">
<h2 v-t="'files'"></h2>
<div v-if="loading" v-t="'loading'"></div>
<div v-else
class="content"
>
<div v-if="leaf">
{{ leaf.name }}
<button v-for="node in leaf.children"
:key="node.path"
type="button"
@click="handleNodeClick( node )"
>{{ node.name }}</button>
</div>
</div>
</div>
</template>
<script>
import { mapMutations } from "vuex";
import { loader } from "zcanvas";
import ImageToDocumentManager from "@/mixins/image-to-document-manager";
import { listFolder, downloadFileAsBlob } from "@/services/dropbox-service";
import messages from "./messages.json";
function mapEntry( entry, children = [] ) {
return {
type: entry[ ".tag" ], // folder/file
name: entry.name,
id: entry.id,
path: entry.path_lower,
children,
}
}
function findLeafByPath( tree, path ) {
let node = tree;
while ( node ) {
if ( node.path === path ) {
return node;
}
const found = recurseChildren( node, path );
if ( found ) {
return found;
}
}
return null;
}
function recurseChildren( node, path ) {
const { children } = node;
if ( !Array.isArray( children )) {
return null;
}
for ( let i = 0, l = children.length; i < l; ++i ) {
const child = children[ i ];
if ( child.path === path ) {
return child;
} else {
const found = recurseChildren( child, path );
if ( found ) {
return found;
}
}
}
return null;
}
export default {
i18n: { messages },
mixins: [ ImageToDocumentManager ],
data: () => ({
loading: false,
tree: {
type: "folder",
name: "",
path: "",
children: [],
},
leaf: null,
}),
created() {
this.retrieveFiles( this.tree.path );
},
methods: {
...mapMutations([
"openDialog",
"closeModal",
]),
async retrieveFiles( path ) {
this.loading = true;
try {
const { result } = await listFolder( path );
const leaf = findLeafByPath( this.tree, path );
// populate leaf with fetched children
leaf.children = result?.entries?.map( mapEntry ) ?? [];
this.leaf = leaf;
} catch {
this.openDialog({ type: "error", message: this.$t( "couldNotRetrieveFilesForPath", { path } ) });
}
this.loading = false;
},
async handleNodeClick( node ) {
switch ( node.type ) {
case "folder":
await this.retrieveFiles( node.path );
break;
case "file":
// TODO: loader, error handling and background load (for bulk selection)
const url = await downloadFileAsBlob( node.path );
const { image, size } = await loader.loadImage( url );
this.addLoadedFile({ name: node.name }, { image, size });
this.closeModal();
break;
}
},
},
};
</script>
<style lang="scss" scoped>
@import "@/styles/component";
.dropbox-file-modal {
@include overlay();
@include component();
h2 {
color: #FFF;
}
@include large() {
$width: 480px;
$height: 300px;
width: $width;
height: $height;
left: calc(50% - #{$width / 2});
top: calc(50% - #{$height / 2});
}
}
</style>

View File

@@ -0,0 +1,7 @@
{
"en-US": {
"files": "Files",
"loading": "Loading...",
"couldNotRetrieveFilesForPath": "Could not retrieve files for \"{path}\""
}
}

View File

@@ -0,0 +1,98 @@
/**
* The MIT License (MIT)
*
* Igor Zinken 2020 - https://www.igorski.nl
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
<template>
<div>
<template v-if="!authenticated">
<h3 v-t="loading ? 'connectingToDropbox' : 'loginToDropbox'"></h3>
<button
v-if="authUrl"
v-t="'login'"
type="button"
@click="login"
></button>
</template>
<template v-else>
<h3 v-t="'connectedToDropbox'"></h3>
<button
v-t="'selectFiles'"
type="button"
@click="openFileBrowser"
></button>
</template>
</div>
</template>
<script>
import { mapMutations } from "vuex";
import { DROPBOX_FILE_BROWSER } from "@/definitions/modal-windows";
import {
isAuthenticated, requestLogin, registerAccessToken
} from "@/services/dropbox-service";
import messages from "./messages.json";
const REDIRECT_URI = `${window.location.href}login.html`;
let loginWindow, boundHandler;
export default {
i18n: { messages },
data: () => ({
authenticated: false,
loading: false,
authUrl: "",
}),
async created() {
this.loading = true;
this.authenticated = await isAuthenticated();
if ( !this.authenticated ) {
this.authUrl = requestLogin(
window.dropboxClientId || localStorage?.getItem( "dropboxClientId" ),
REDIRECT_URI
);
}
this.loading = false;
},
methods: {
...mapMutations([
"openModal",
]),
login() {
loginWindow = window.open( this.authUrl );
boundHandler = this.messageHandler.bind( this );
window.addEventListener( "message", boundHandler );
},
messageHandler({ data }) {
if ( data?.accessToken ) {
registerAccessToken( data.accessToken );
window.removeEventListener( "message", boundHandler );
loginWindow.close();
loginWindow = null;
this.authenticated = true;
}
},
openFileBrowser() {
this.openModal( DROPBOX_FILE_BROWSER );
},
},
};
</script>

View File

@@ -0,0 +1,9 @@
{
"en-US": {
"connectingToDropbox": "Connecting to Dropbox",
"loginToDropbox": "Log into Dropbox",
"connectedToDropbox": "Connected to Dropbox",
"login": "Login",
"selectFiles": "Select files"
}
}

View File

@@ -42,9 +42,9 @@
</template>
<script>
import { mapGetters, mapMutations, mapActions } from "vuex";
import { loadImageFiles } from "@/services/file-loader-queue";
import { mapSelectOptions } from "@/utils/search-select-util"
import ImageToDocumentManager from "@/mixins/image-to-document-manager";
import SelectBox from '@/components/ui/select-box/select-box';
import messages from "./messages.json";
@@ -55,30 +55,17 @@ export default {
components: {
SelectBox,
},
mixins: [ ImageToDocumentManager ],
data: () => ({
acceptedImageTypes: ACCEPTED_IMAGE_TYPES,
fileTarget: "document",
}),
computed: {
...mapGetters([
"activeDocument",
"documents",
"layers",
]),
fileTargetOptions() {
return mapSelectOptions(["layer", "document"]);
},
},
methods: {
...mapMutations([
"addLayer",
"addGraphicToLayer",
"addNewDocument",
"setActiveDocumentSize",
]),
...mapActions([
"addImage",
]),
async handleFileSelect({ target }) {
const files = target?.files;
if ( !files || files.length === 0 ) {
@@ -89,33 +76,6 @@ export default {
const elapsed = Date.now() - start;
console.warn( "Total time for load: " + ( elapsed / 1000 ) + " seconds" );
},
async addLoadedFile( file, { image, size }) {
const { source } = await this.addImage({ file, image, size });
image.src = source;
const currentDocumentIsEmpty = this.layers?.length === 1 && !this.layers[ 0 ].graphics.length;
switch ( this.fileTarget) {
default:
case "layer":
// if this is the first content of an existing document, scale document to image size
if ( currentDocumentIsEmpty ) {
this.setActiveDocumentSize( size );
} else if ( !this.activeDocument ) {
this.addNewDocument( file.name );
}
this.addLayer();
break;
case "document":
if ( !currentDocumentIsEmpty ) {
this.addNewDocument( file.name );
}
this.setActiveDocumentSize( size );
break;
}
this.addGraphicToLayer({ index: this.layers.length - 1, bitmap: image, size });
}
}
};
</script>

View File

@@ -33,6 +33,11 @@
class="content"
>
<file-selector />
<input
v-model="dropbox"
type="checkbox"
/>
<component :is="importType" />
<component :is="activeToolOptions" />
</div>
</div>
@@ -48,6 +53,9 @@ export default {
components: {
FileSelector,
},
data: () => ({
dropbox: false,
}),
computed: {
...mapState([
"optionsPanelOpened",
@@ -63,6 +71,14 @@ export default {
this.setOptionsPanelOpened( !value );
}
},
importType() {
switch ( this.dropbox ) {
default:
return null;
case true:
return () => import( "./components/dropbox-file-selector/dropbox-file-selector" );
}
},
activeToolOptions() {
switch ( this.activeTool ) {
default:

View File

@@ -20,4 +20,5 @@
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
export const RESIZE_DOCUMENT = 1;
export const RESIZE_DOCUMENT = 1;
export const DROPBOX_FILE_BROWSER = 2;

View File

@@ -0,0 +1,70 @@
/**
* The MIT License (MIT)
*
* Igor Zinken 2020 - https://www.igorski.nl
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import{ mapGetters, mapMutations, mapActions } from "vuex";
export default {
computed: {
...mapGetters([
"activeDocument",
"layers",
])
},
methods: {
...mapMutations([
"setActiveDocumentSize",
"addNewDocument",
"addLayer",
"addGraphicToLayer",
]),
...mapActions([
"addImage",
]),
async addLoadedFile( file, { image, size }) {
const { source } = await this.addImage({ file, image, size });
image.src = source;
const currentDocumentIsEmpty = this.layers?.length === 1 && !this.layers[ 0 ].graphics.length;
switch ( this.fileTarget) {
default:
case "layer":
// if this is the first content of an existing document, scale document to image size
if ( currentDocumentIsEmpty ) {
this.setActiveDocumentSize( size );
} else if ( !this.activeDocument ) {
this.addNewDocument( file.name );
}
this.addLayer();
break;
case "document":
if ( !currentDocumentIsEmpty ) {
this.addNewDocument( file.name );
}
this.setActiveDocumentSize( size );
break;
}
this.addGraphicToLayer({ index: this.layers.length - 1, bitmap: image, size });
},
}
};

View File

@@ -68,10 +68,12 @@ import DocumentCanvas from "@/components/document-canvas/document-canvas";
import OptionsPanel from "@/components/options-panel/options-panel";
import Toolbox from "@/components/toolbox/toolbox";
import DialogWindow from "@/components/dialog-window/dialog-window";
import { RESIZE_DOCUMENT } from "@/definitions/modal-windows";
import { isMobile } from "@/utils/environment-util";
import store from "./store";
import messages from "./messages.json";
import {
RESIZE_DOCUMENT, DROPBOX_FILE_BROWSER
} from "@/definitions/modal-windows";
Vue.use( Vuex );
Vue.use( VueI18n );
@@ -109,6 +111,8 @@ export default {
return null;
case RESIZE_DOCUMENT:
return () => import( "@/components/edit-menu/resize-document/resize-document" );
case DROPBOX_FILE_BROWSER:
return () => import( "@/components/dropbox-file-browser/dropbox-file-browser" );
}
},
},

View File

@@ -0,0 +1,69 @@
/**
* The MIT License (MIT)
*
* Igor Zinken 2020 - https://www.igorski.nl
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { Dropbox } from "dropbox";
let accessToken;
let dbx;
/**
* Authentication step 1: for interacting with Dropbox : request access token
* by opening an authentication page
*/
export const requestLogin = ( clientId, loginUrl ) => {
dbx = new Dropbox({ clientId });
return dbx.auth.getAuthenticationUrl( loginUrl );
}
/**
* Authentication step 2: user has received access token, register it in the
* service and in Session storage so we can instantly authenticate on reload
*/
export const registerAccessToken = token => {
accessToken = token;
sessionStorage?.setItem( "dropboxToken", token );
dbx = new Dropbox({ accessToken });
};
export const isAuthenticated = async () => {
dbx = new Dropbox({ accessToken: accessToken ?? sessionStorage?.getItem( "dropboxToken" ) });
try {
// this is a bit daft but does the trick, can we use a different method though?
await listFolder();
return true;
} catch ( error ) {
return false;
}
}
export const listFolder = ( path = "" ) => {
return dbx.filesListFolder({ path });
}
export const downloadFileAsBlob = async path => {
try {
const { result } = await dbx.filesDownload({ path });
return URL.createObjectURL( result.fileBlob );
} catch {
return null;
}
};