mirror of
https://github.com/igorski/bitmappery.git
synced 2026-06-17 03:34:56 +02:00
Implemented Dropbox login, file browse and file download
This commit is contained in:
@@ -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
65
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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
27
public/login.html
Normal 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>
|
||||
@@ -72,11 +72,11 @@ export default {
|
||||
'closeDialog',
|
||||
]),
|
||||
handleConfirm() {
|
||||
this?.confirmHandler();
|
||||
this.confirmHandler?.();
|
||||
this.close();
|
||||
},
|
||||
handleCancel() {
|
||||
this?.cancelHandler();
|
||||
this.cancelHandler?.();
|
||||
this.close();
|
||||
},
|
||||
close() {
|
||||
|
||||
165
src/components/dropbox-file-browser/dropbox-file-browser.vue
Normal file
165
src/components/dropbox-file-browser/dropbox-file-browser.vue
Normal 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>
|
||||
7
src/components/dropbox-file-browser/messages.json
Normal file
7
src/components/dropbox-file-browser/messages.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"en-US": {
|
||||
"files": "Files",
|
||||
"loading": "Loading...",
|
||||
"couldNotRetrieveFilesForPath": "Could not retrieve files for \"{path}\""
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"en-US": {
|
||||
"connectingToDropbox": "Connecting to Dropbox",
|
||||
"loginToDropbox": "Log into Dropbox",
|
||||
"connectedToDropbox": "Connected to Dropbox",
|
||||
"login": "Login",
|
||||
"selectFiles": "Select files"
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
70
src/mixins/image-to-document-manager.js
Normal file
70
src/mixins/image-to-document-manager.js
Normal 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 });
|
||||
},
|
||||
}
|
||||
};
|
||||
@@ -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" );
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
69
src/services/dropbox-service.js
Normal file
69
src/services/dropbox-service.js
Normal 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;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user