From f946b0beab0ff15e99ff99c34d38df271fb46e1e Mon Sep 17 00:00:00 2001 From: Jamie Date: Mon, 18 Apr 2016 16:27:00 +0800 Subject: [PATCH 01/65] Add style to allow word to be break and wrap --- src/styles/base.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/styles/base.css b/src/styles/base.css index 8caa00f..26b1d20 100644 --- a/src/styles/base.css +++ b/src/styles/base.css @@ -41,3 +41,7 @@ .text-center { text-align: center; } + +.break-word { + word-break: break-word; +} From 6230d9c143c4b6558d9ab0a2cbd2df0b1c4a57f2 Mon Sep 17 00:00:00 2001 From: Jamie Date: Mon, 18 Apr 2016 16:32:41 +0800 Subject: [PATCH 02/65] Update the style to place in the middle of the table --- src/styles/table.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/table.css b/src/styles/table.css index 7f43265..5e84570 100644 --- a/src/styles/table.css +++ b/src/styles/table.css @@ -27,7 +27,7 @@ th { .table > tfoot > tr > td { padding: 8px; line-height: 1.42857143; - vertical-align: top; + vertical-align: middle; border-top: 1px solid #dddddd; } From 3b1c6e888a5978ab9e235a495957234c01c0f83e Mon Sep 17 00:00:00 2001 From: jigsawye Date: Thu, 21 Apr 2016 15:37:15 +0800 Subject: [PATCH 03/65] Remove angular-breadcrumb & implement breadcrumb component Remove `angular-breadcrumb` because it doesn't support nested path. And I re-implement a new breadcrumb component. * Remove angular-breadcrumb from dependencies * Add layout/breadcrumb component * Add a method call to $bucket for update the bucket length --- package.json | 1 - src/components/bucket/bucket.js | 3 --- src/components/bucket/bucket.service.js | 5 +++-- .../breadcrumb/breadcrumb.controller.js | 13 +++++++++++++ .../layout/breadcrumb/breadcrumb.html | 8 ++++++++ .../layout/breadcrumb/breadcrumb.service.js | 19 +++++++++++++++++++ src/components/layout/layout.html | 2 +- src/components/layout/layout.js | 9 +++++++++ 8 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 src/components/layout/breadcrumb/breadcrumb.controller.js create mode 100644 src/components/layout/breadcrumb/breadcrumb.html create mode 100644 src/components/layout/breadcrumb/breadcrumb.service.js diff --git a/package.json b/package.json index 68f8bf6..9af6ecf 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "angular": "^1.5.0", "angular-animate": "^1.5.0", "angular-aria": "^1.5.0", - "angular-breadcrumb": "^0.4.1", "angular-material": "^1.0.5", "angular-messages": "^1.5.0", "angular-translate": "^2.9.2", diff --git a/src/components/bucket/bucket.js b/src/components/bucket/bucket.js index 050007a..7037565 100644 --- a/src/components/bucket/bucket.js +++ b/src/components/bucket/bucket.js @@ -25,9 +25,6 @@ const route = $stateProvider => { controllerAs: 'nav', }, }, - ncyBreadcrumb: { - label: 'All Buckets ( {{ bucket.data.length }} )', - }, }); }; diff --git a/src/components/bucket/bucket.service.js b/src/components/bucket/bucket.service.js index 6f053f1..58c77eb 100644 --- a/src/components/bucket/bucket.service.js +++ b/src/components/bucket/bucket.service.js @@ -5,9 +5,9 @@ import BucketCreateTemplate from './create/create.html'; export default class BucketService { /** @ngInject */ - constructor($fetch, $toast, $mdDialog) { + constructor($fetch, $toast, $mdDialog, $breadcrumb) { Object.assign(this, { - $fetch, $toast, $mdDialog, + $fetch, $toast, $mdDialog, $breadcrumb, }); this.initState(); @@ -105,6 +105,7 @@ export default class BucketService { }) .finally(() => { this.state.lists.requesting = false; + this.$breadcrumb.updateBucketPath(this.state.lists.data.length); }); } diff --git a/src/components/layout/breadcrumb/breadcrumb.controller.js b/src/components/layout/breadcrumb/breadcrumb.controller.js new file mode 100644 index 0000000..9eb320a --- /dev/null +++ b/src/components/layout/breadcrumb/breadcrumb.controller.js @@ -0,0 +1,13 @@ +export default class BreadcrumbController { + /** @ngInject */ + constructor($scope, $bucket, $breadcrumb) { + Object.assign(this, { + $scope, $bucket, $breadcrumb, + }); + + this.$scope.$watch( + () => $breadcrumb.paths, + newVal => (this.paths = newVal) + , true); + } +} diff --git a/src/components/layout/breadcrumb/breadcrumb.html b/src/components/layout/breadcrumb/breadcrumb.html new file mode 100644 index 0000000..8f44a1f --- /dev/null +++ b/src/components/layout/breadcrumb/breadcrumb.html @@ -0,0 +1,8 @@ + diff --git a/src/components/layout/breadcrumb/breadcrumb.service.js b/src/components/layout/breadcrumb/breadcrumb.service.js new file mode 100644 index 0000000..a4b517d --- /dev/null +++ b/src/components/layout/breadcrumb/breadcrumb.service.js @@ -0,0 +1,19 @@ +export default class BreadcrumbService { + /** @ngInject */ + constructor() { + this.initPaths(); + } + + initPaths() { + this.paths = [{ + link: '/bucket', + text: 'All Bucket', + isBucket: true, + len: 0, + }]; + } + + updateBucketPath(len) { + this.paths[0].len = len; + } +} diff --git a/src/components/layout/layout.html b/src/components/layout/layout.html index 363d63c..5000fb0 100644 --- a/src/components/layout/layout.html +++ b/src/components/layout/layout.html @@ -3,7 +3,7 @@
- +
diff --git a/src/components/layout/layout.js b/src/components/layout/layout.js index 907f2b5..289313a 100644 --- a/src/components/layout/layout.js +++ b/src/components/layout/layout.js @@ -4,6 +4,9 @@ import router from 'angular-ui-router'; import LayoutTemplate from './layout.html'; import TopNavbarController from './top-navbar/top-navbar.controller'; import TopNavbarTemplate from './top-navbar/top-navbar.html'; +import BreadcrumbController from './breadcrumb/breadcrumb.controller'; +import BreadcrumbTemplate from './breadcrumb/breadcrumb.html'; +import BreadcrumbService from './breadcrumb/breadcrumb.service'; import './layout.css'; @@ -21,6 +24,11 @@ const route = $stateProvider => { controller: TopNavbarController, controllerAs: 'nav', }, + 'breadcrumb@root': { + template: BreadcrumbTemplate, + controller: BreadcrumbController, + controllerAs: 'bc', + }, }, }); }; @@ -28,6 +36,7 @@ const route = $stateProvider => { const Layout = module('layout', [ router, ]) +.service('$breadcrumb', BreadcrumbService) .config(route); export default Layout.name; From 2f1e1dcbd37b583fdfcf66f53ac1f96a8b5dc565 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 21 Apr 2016 15:46:12 +0800 Subject: [PATCH 04/65] Add base style for display property --- src/styles/base.css | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/styles/base.css b/src/styles/base.css index 26b1d20..abdf654 100644 --- a/src/styles/base.css +++ b/src/styles/base.css @@ -45,3 +45,17 @@ .break-word { word-break: break-word; } + +/* display */ + +.inline{ + display: inline; +} + +.block{ + display: block; +} + +.inline-block{ + display: inline-block; +} From d0227f25360434076fee31576c72ad6f6c67a407 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 21 Apr 2016 15:50:21 +0800 Subject: [PATCH 05/65] Fix flexbox type layout issues in IE10 --- src/styles/s3.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/styles/s3.css b/src/styles/s3.css index 1351233..2a033f3 100644 --- a/src/styles/s3.css +++ b/src/styles/s3.css @@ -127,3 +127,8 @@ md-input-container md-progress-circular { .load-fail-state { margin-top: 10%; } + +/* fix flexbox type layout issues in IE10 */ +span.flex { + display: block; +} From ce606a8cdea4da778948a01eb9fb04a16e1e3eef Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 21 Apr 2016 16:18:50 +0800 Subject: [PATCH 06/65] Add list item style for file list --- src/styles/s3.css | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/styles/s3.css b/src/styles/s3.css index 2a033f3..d5e7de4 100644 --- a/src/styles/s3.css +++ b/src/styles/s3.css @@ -128,6 +128,31 @@ md-input-container md-progress-circular { margin-top: 10%; } +/* list item --------------------------------------------- */ +md-list-item.checked { + background: #E8EAF6; +} + +md-list-item > .md-list-item-inner > p { + padding: 0 8px; +} + +md-list-item > p.flex-none, +md-list-item > .md-list-item-inner > p.flex-none, +md-list-item .md-list-item-inner > p.flex-none, +md-list-item .md-list-item-inner > .md-list-item-inner > p.flex-none { + flex: 0 0 auto; + -ms-flex: 0 0 auto; +} + +md-list-item > p.flex-grow, +md-list-item > .md-list-item-inner > p.flex-grow, +md-list-item .md-list-item-inner > p.flex-grow, +md-list-item .md-list-item-inner > .md-list-item-inner > p.flex-grow { + flex: 1 1 100%; + -ms-flex: 1 1 100%; +} + /* fix flexbox type layout issues in IE10 */ span.flex { display: block; From ef3ccc90e825c3ab0ce2ffbece5b1a29502d4291 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 21 Apr 2016 16:42:30 +0800 Subject: [PATCH 07/65] Design file list * Add file.css * Add file.html --- src/components/file/file.css | 24 +++++++++ src/components/file/file.html | 98 +++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/components/file/file.css create mode 100644 src/components/file/file.html diff --git a/src/components/file/file.css b/src/components/file/file.css new file mode 100644 index 0000000..551d8f5 --- /dev/null +++ b/src/components/file/file.css @@ -0,0 +1,24 @@ + +/** + * @author Jamie jamie.h@inwinstack.com + */ + +.checkbox-icon-width { + width: 80px; +} + +.storage-class-width { + width: 140px; +} + +.size-width { + width: 84px; +} + +.time-width { + width: 270px; +} + +.time-title-width { + width: 286px; +} \ No newline at end of file diff --git a/src/components/file/file.html b/src/components/file/file.html new file mode 100644 index 0000000..f6dd562 --- /dev/null +++ b/src/components/file/file.html @@ -0,0 +1,98 @@ + +
+ + + + + + + + + + +
NameStorage ClassSizeLast Modified
+ + + + insert_drive_file +

+ +

+

+ +

+

+ +

+

+ +

+
+
+
+ +
+ +
Loading...
+
+ +
+
This bucket is empty
+
You can do the following actions
+ +
+ + add + Upload File + + + or + + + Create Folder + +
+
+ +
+
Oops, your connection seems off...
+
Don't worry. You can refresh to try again.
+ + + refresh + +
+
\ No newline at end of file From e7ad6708d5e0eb8fe3b6d58272f26ef093bf492d Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 21 Apr 2016 16:47:10 +0800 Subject: [PATCH 08/65] Change the accent theme grey to indigo --- src/config/material.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/material.config.js b/src/config/material.config.js index 8916f5e..81663d3 100644 --- a/src/config/material.config.js +++ b/src/config/material.config.js @@ -4,5 +4,5 @@ export default ($mdThemingProvider) => { .theme('default') .primaryPalette('blue') .warnPalette('orange') - .accentPalette('grey'); + .accentPalette('indigo'); }; From b17959dbbfaced74cf3eb72145c779418f385b74 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Thu, 21 Apr 2016 17:10:48 +0800 Subject: [PATCH 09/65] Add File component --- src/components/file/file.controller.js | 7 +++++++ src/components/file/file.html | 1 + src/components/file/file.js | 25 +++++++++++++++++++++++++ src/components/file/file.service.js | 3 +++ src/components/index.js | 2 ++ 5 files changed, 38 insertions(+) create mode 100644 src/components/file/file.controller.js create mode 100644 src/components/file/file.html create mode 100644 src/components/file/file.js create mode 100644 src/components/file/file.service.js diff --git a/src/components/file/file.controller.js b/src/components/file/file.controller.js new file mode 100644 index 0000000..8ad9b6a --- /dev/null +++ b/src/components/file/file.controller.js @@ -0,0 +1,7 @@ +export default class FileController { + /** @ngInject */ + constructor($stateParams) { + this.path = $stateParams.path; + console.log(this.path.split('/')); // ['bucketName', 'folderA', 'folderB']; + } +} diff --git a/src/components/file/file.html b/src/components/file/file.html new file mode 100644 index 0000000..ce13f14 --- /dev/null +++ b/src/components/file/file.html @@ -0,0 +1 @@ +

File List

diff --git a/src/components/file/file.js b/src/components/file/file.js new file mode 100644 index 0000000..35def55 --- /dev/null +++ b/src/components/file/file.js @@ -0,0 +1,25 @@ +import { module } from 'angular'; +import router from 'angular-ui-router'; + +import FileController from './file.controller'; +import FileService from './file.service'; +import FileTemplate from './file.html'; + +/** @ngInject */ +const route = $stateProvider => { + $stateProvider.state('file', { + url: '/bucket/*path', + parent: 'root', + controller: FileController, + controllerAs: 'file', + template: FileTemplate, + }); +}; + +const File = module('file', [ + router, +]) +.service('$file', FileService) +.config(route); + +export default File.name; diff --git a/src/components/file/file.service.js b/src/components/file/file.service.js new file mode 100644 index 0000000..2a93618 --- /dev/null +++ b/src/components/file/file.service.js @@ -0,0 +1,3 @@ +export default class FileService { + +} diff --git a/src/components/index.js b/src/components/index.js index 8c1c994..d209813 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -3,12 +3,14 @@ import Layout from './layout/layout'; import NotFound from './not-found/not-found'; import Auth from './auth/auth'; import Bucket from './bucket/bucket'; +import File from './file/file'; const Components = module('app.components', [ Layout, NotFound, Auth, Bucket, + File, ]); export default Components.name; From 0a18a116ce09d46ac394b74a5e93785e86bd94b1 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Thu, 21 Apr 2016 17:25:07 +0800 Subject: [PATCH 10/65] Remove angular-breadcrumb from source code --- src/config/breadcrumb.config.js | 4 ---- src/config/breadcrumb.html | 5 ----- src/config/index.js | 2 -- 3 files changed, 11 deletions(-) delete mode 100644 src/config/breadcrumb.config.js delete mode 100644 src/config/breadcrumb.html diff --git a/src/config/breadcrumb.config.js b/src/config/breadcrumb.config.js deleted file mode 100644 index c4c08e7..0000000 --- a/src/config/breadcrumb.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import template from './breadcrumb.html'; - -/** @ngInject */ -export default $breadcrumbProvider => $breadcrumbProvider.setOptions({ template }); diff --git a/src/config/breadcrumb.html b/src/config/breadcrumb.html deleted file mode 100644 index fc71562..0000000 --- a/src/config/breadcrumb.html +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/src/config/index.js b/src/config/index.js index 87b1861..3e4cd5f 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -5,7 +5,6 @@ import satellizer from './satellizer.config'; import material from './material.config'; import authenticateGuard from './AuthenticateGuard'; import http from './http.config'; -import breadcrumb from './breadcrumb.config'; const Config = module('app.config', []) .config(router) @@ -13,7 +12,6 @@ const Config = module('app.config', []) .config(satellizer) .config(material) .config(http) - .config(breadcrumb) .constant('Config', { API_URL: `${process.env.SERVER_HOST}/api`, }) From 9ea4a82a170339c837275799088c659423ffd7e4 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Fri, 22 Apr 2016 17:27:23 +0800 Subject: [PATCH 11/65] Add click event to bucket component When user clicked the bucket on bucket list, we will change the state whcih is the user selected. * Add `selectBucket` method to BucketController * Update the view of bucket component --- src/components/bucket/bucket.controller.js | 8 ++++++-- src/components/bucket/bucket.html | 5 ++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/bucket/bucket.controller.js b/src/components/bucket/bucket.controller.js index 766be3c..c7dafe5 100644 --- a/src/components/bucket/bucket.controller.js +++ b/src/components/bucket/bucket.controller.js @@ -1,8 +1,8 @@ export default class BucketController { /** @ngInject */ - constructor($scope, $bucket) { + constructor($scope, $bucket, $state) { Object.assign(this, { - $scope, $bucket, + $scope, $bucket, $state, }); this.$scope.$watch( @@ -16,4 +16,8 @@ export default class BucketController { createBucket($event) { this.$bucket.createDialog($event); } + + selectBucket(bucket) { + this.$state.go('file', { path: bucket }); + } } diff --git a/src/components/bucket/bucket.html b/src/components/bucket/bucket.html index fd50d39..0044eeb 100644 --- a/src/components/bucket/bucket.html +++ b/src/components/bucket/bucket.html @@ -12,14 +12,13 @@ - + info_outline - - + From dfb26b311fe9fee7c9259869c80d7e9a71ab8cc5 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Fri, 22 Apr 2016 17:29:33 +0800 Subject: [PATCH 12/65] Add sample view & service to file component When the user enter into `file` state, the FileController will set the path to $file service and get file list. For testing the UI flow, I add a fake API call to $file service. * Add `getFiles` method to $file service for fetch file list * Add `setPaths` method to $file service for set the API prefix * Add basic view for view the state of FileController --- src/components/file/file.controller.js | 15 ++++++-- src/components/file/file.html | 3 ++ src/components/file/file.service.js | 50 ++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/components/file/file.controller.js b/src/components/file/file.controller.js index 8ad9b6a..c687fe1 100644 --- a/src/components/file/file.controller.js +++ b/src/components/file/file.controller.js @@ -1,7 +1,16 @@ export default class FileController { /** @ngInject */ - constructor($stateParams) { - this.path = $stateParams.path; - console.log(this.path.split('/')); // ['bucketName', 'folderA', 'folderB']; + constructor($scope, $stateParams, $file) { + Object.assign(this, { + $scope, $file, + }); + + this.$scope.$watch( + () => $file.state.lists, + newVal => Object.assign(this, newVal) + , true); + + this.$file.setPaths($stateParams.path); + this.$file.getFiles(); } } diff --git a/src/components/file/file.html b/src/components/file/file.html index ce13f14..b8a612c 100644 --- a/src/components/file/file.html +++ b/src/components/file/file.html @@ -1 +1,4 @@

File List

+

Requesting: {{ file.requesting }}

+

Error: {{ file.error }}

+

{{ f }}

diff --git a/src/components/file/file.service.js b/src/components/file/file.service.js index 2a93618..329d568 100644 --- a/src/components/file/file.service.js +++ b/src/components/file/file.service.js @@ -1,3 +1,53 @@ export default class FileService { + /** @ngInject */ + constructor($q, $fetch, $bucket) { + Object.assign(this, { + $q, $fetch, $bucket, + }); + this.initState(); + } + + initState() { + this.state = { + paths: { + bucket: '', + folders: '', + }, + lists: { + data: [], + requesting: false, + error: false, + }, + create: { + checking: false, + checked: false, + duplicated: false, + }, + }; + } + + setPaths(path) { + const [bucket, ...folders] = path.split('/'); + this.paths = { bucket, folders }; + } + + getFiles() { + this.state.lists.requesting = true; + this.state.lists.data = []; + + const fakeList = ['folder1', 'folder2', 'folder3', 'folder4']; + + this.$q(resolve => setTimeout(() => resolve(fakeList), 2000)) + .then(list => { + this.state.lists.error = false; + this.state.lists.data = list; + }) + .catch(() => { + this.state.lists.error = true; + }) + .finally(() => { + this.state.lists.requesting = false; + }); + } } From 3146d6eb4e80e3b268f9b610cf97bd2b14b04155 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Mon, 25 Apr 2016 13:30:54 +0800 Subject: [PATCH 13/65] Rename utils to services --- src/index.js | 4 ++-- src/{utils => services}/fetch/fetch.js | 0 src/{utils => services}/fetch/fetch.service.js | 0 src/{utils => services}/index.js | 4 ++-- src/{utils => services}/toast/toast.js | 0 src/{utils => services}/toast/toast.service.js | 0 6 files changed, 4 insertions(+), 4 deletions(-) rename src/{utils => services}/fetch/fetch.js (100%) rename src/{utils => services}/fetch/fetch.service.js (100%) rename src/{utils => services}/index.js (64%) rename src/{utils => services}/toast/toast.js (100%) rename src/{utils => services}/toast/toast.service.js (100%) diff --git a/src/index.js b/src/index.js index ccbe9dd..3b4eb52 100644 --- a/src/index.js +++ b/src/index.js @@ -4,14 +4,14 @@ import './index.css'; import './templates'; import Vendor from './vendor'; import Config from './config'; -import Utils from './utils'; +import Services from './services'; import Directives from './directives'; import Components from './components'; module('app', [ Vendor, Config, - Utils, + Services, Directives, Components, ]); diff --git a/src/utils/fetch/fetch.js b/src/services/fetch/fetch.js similarity index 100% rename from src/utils/fetch/fetch.js rename to src/services/fetch/fetch.js diff --git a/src/utils/fetch/fetch.service.js b/src/services/fetch/fetch.service.js similarity index 100% rename from src/utils/fetch/fetch.service.js rename to src/services/fetch/fetch.service.js diff --git a/src/utils/index.js b/src/services/index.js similarity index 64% rename from src/utils/index.js rename to src/services/index.js index 623084f..7297e7b 100644 --- a/src/utils/index.js +++ b/src/services/index.js @@ -2,9 +2,9 @@ import { module } from 'angular'; import Toast from './toast/toast'; import Fetch from './fetch/fetch'; -const Utils = module('app.utils', [ +const Services = module('app.services', [ Toast, Fetch, ]); -export default Utils.name; +export default Services.name; diff --git a/src/utils/toast/toast.js b/src/services/toast/toast.js similarity index 100% rename from src/utils/toast/toast.js rename to src/services/toast/toast.js diff --git a/src/utils/toast/toast.service.js b/src/services/toast/toast.service.js similarity index 100% rename from src/utils/toast/toast.service.js rename to src/services/toast/toast.service.js From e782e353843de56dfa2c46ea49318eada81858ea Mon Sep 17 00:00:00 2001 From: jigsawye Date: Mon, 25 Apr 2016 13:39:52 +0800 Subject: [PATCH 14/65] Move natural sotr function to utils for reusable --- src/components/bucket/bucket.service.js | 20 +++----------------- src/utils/sort.js | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 17 deletions(-) create mode 100644 src/utils/sort.js diff --git a/src/components/bucket/bucket.service.js b/src/components/bucket/bucket.service.js index 6fee2d9..d651b20 100644 --- a/src/components/bucket/bucket.service.js +++ b/src/components/bucket/bucket.service.js @@ -1,5 +1,5 @@ import { element } from 'angular'; -import natural from 'javascript-natural-sort'; +import { sortByName } from '../../utils/sort'; import BucketCreateController from './create/create.controller'; import BucketCreateTemplate from './create/create.html'; @@ -73,20 +73,6 @@ export default class BucketService { this.resetCheckBucketState(); } - /** - * Natural sort for the specified object key. - * - * @param {Object} a - * @param {Object} b - * @return {Integer} - */ - sortByName(a, b) { - const x = a.Name; - const y = b.Name; - - return natural(x, y); - } - /** * Call the bucket list API and modify the state of service. * @@ -99,7 +85,7 @@ export default class BucketService { this.$fetch.post('/v1/bucket/list') .then(({ data }) => { this.state.lists.error = false; - this.state.lists.data = data.Buckets.sort(this.sortByName); + this.state.lists.data = data.Buckets.sort(sortByName); }) .catch(() => { this.state.lists.error = true; @@ -140,7 +126,7 @@ export default class BucketService { createBucket(bucket) { this.$fetch.post('/v1/bucket/create', { bucket }) .then(({ data }) => { - this.state.lists.data = data.Buckets.sort(this.sortByName); + this.state.lists.data = data.Buckets.sort(sortByName); this.$toast.show(`Bucket ${bucket} has created!`); }) .catch(() => { diff --git a/src/utils/sort.js b/src/utils/sort.js new file mode 100644 index 0000000..20a07d5 --- /dev/null +++ b/src/utils/sort.js @@ -0,0 +1,18 @@ +import natural from 'javascript-natural-sort'; + +/** + * Return a function that will sort by given key. + * + * @param {String} x + * @param {String} y + * + * @return {Function} + */ +const sortKey = key => (x, y) => natural(x[key], y[key]); + +/** + * Natural sort by Name. + * + * @return {Function} + */ +export const sortByName = sortKey('Name'); From 55f211c7fc2062b53fe561455075c9d68a724f8b Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 26 Apr 2016 15:37:04 +0800 Subject: [PATCH 15/65] Replace upload and create folder icon --- src/components/file/file.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/file/file.html b/src/components/file/file.html index f6dd562..2ad71a5 100644 --- a/src/components/file/file.html +++ b/src/components/file/file.html @@ -65,7 +65,7 @@ class="md-raised md-primary" aria-label="Upload File" ng-click="@A.6" > - add + file_upload Upload File @@ -75,6 +75,7 @@ class="md-raised" aria-label="Create Folder" ng-click="@A.7" > + create_new_folder Create Folder From dd78031e4945c03e752f5fc8367a8d35b47bd013 Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 27 Apr 2016 15:42:57 +0800 Subject: [PATCH 16/65] Add dialog-footer and list-dialog style --- src/styles/dialog.css | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/styles/dialog.css b/src/styles/dialog.css index 1ae5712..dfdca9c 100644 --- a/src/styles/dialog.css +++ b/src/styles/dialog.css @@ -20,3 +20,18 @@ md-dialog.input-dialog { .dialog-description { margin-bottom: 30px; } + +.dialog-footer { + margin-left: 20px; + margin-top: 25px; + text-align: center; + width: 100%; +} + +.dialog-footer span { + padding-right: 10px; +} + +.list-dialog md-list-item p { + margin-right: 40px; +} \ No newline at end of file From 57bc03a884d1c5f4b867e9f1cedb96a0f1060f04 Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 27 Apr 2016 15:44:41 +0800 Subject: [PATCH 17/65] Add the ui of upload file --- src/components/file/upload/upload.html | 78 ++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/components/file/upload/upload.html diff --git a/src/components/file/upload/upload.html b/src/components/file/upload/upload.html new file mode 100644 index 0000000..ae18adc --- /dev/null +++ b/src/components/file/upload/upload.html @@ -0,0 +1,78 @@ + +
+ +
+

Upload Files

+ + + + + clear + +
+
+ +
+
+

+ To upload files to S3 Portal, click Add Files. To remove files already selected, click the ✖ to the far right of the file name. +

+
+ + + +

Add Files

+ add +
+ + + + + photo + insert_drive_file + +

+

+ + clear +
+ + + +
+
+
+ + + + Cancel + + + + Upload + + + +
+
\ No newline at end of file From 7f836dd404c8b978ec22528d900b81558a598707 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Fri, 6 May 2016 16:12:59 +0800 Subject: [PATCH 18/65] Real api call --- src/components/file/file.service.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/file/file.service.js b/src/components/file/file.service.js index 329d568..5880739 100644 --- a/src/components/file/file.service.js +++ b/src/components/file/file.service.js @@ -36,12 +36,10 @@ export default class FileService { this.state.lists.requesting = true; this.state.lists.data = []; - const fakeList = ['folder1', 'folder2', 'folder3', 'folder4']; - - this.$q(resolve => setTimeout(() => resolve(fakeList), 2000)) - .then(list => { + this.$fetch.get(`/v1/file/list/${this.paths.bucket}`) + .then(({ data }) => { this.state.lists.error = false; - this.state.lists.data = list; + this.state.lists.data = data.files || []; }) .catch(() => { this.state.lists.error = true; From ad02be379bcac43e7a944c42163a8bd638d68a71 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Fri, 6 May 2016 16:25:27 +0800 Subject: [PATCH 19/65] Bind controller properties to view Add file controller properties to file.html for UI display. * import file styles in file module * bind properties --- src/components/file/file.html | 18 +++++++++--------- src/components/file/file.js | 1 + 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/components/file/file.html b/src/components/file/file.html index a5f67c7..d6207ad 100644 --- a/src/components/file/file.html +++ b/src/components/file/file.html @@ -1,7 +1,7 @@
@@ -15,28 +15,28 @@
- + insert_drive_file

- +

@@ -46,7 +46,7 @@
Loading...
@@ -55,7 +55,7 @@
This bucket is empty
You can do the following actions
@@ -84,7 +84,7 @@
Oops, your connection seems off...
Don't worry. You can refresh to try again.
diff --git a/src/components/file/file.js b/src/components/file/file.js index 35def55..688374b 100644 --- a/src/components/file/file.js +++ b/src/components/file/file.js @@ -4,6 +4,7 @@ import router from 'angular-ui-router'; import FileController from './file.controller'; import FileService from './file.service'; import FileTemplate from './file.html'; +import './file.css'; /** @ngInject */ const route = $stateProvider => { From 6fc82ff78c8f17d214a6008c129348c5927e8ded Mon Sep 17 00:00:00 2001 From: jigsawye Date: Fri, 6 May 2016 16:36:48 +0800 Subject: [PATCH 20/65] Update $file service * paths.folders is Array * add `prefix` parameters to api call --- src/components/file/file.service.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/file/file.service.js b/src/components/file/file.service.js index 5880739..c87a1fc 100644 --- a/src/components/file/file.service.js +++ b/src/components/file/file.service.js @@ -1,8 +1,8 @@ export default class FileService { /** @ngInject */ - constructor($q, $fetch, $bucket) { + constructor($fetch, $bucket) { Object.assign(this, { - $q, $fetch, $bucket, + $fetch, $bucket, }); this.initState(); @@ -12,7 +12,7 @@ export default class FileService { this.state = { paths: { bucket: '', - folders: '', + folders: [], }, lists: { data: [], @@ -36,7 +36,8 @@ export default class FileService { this.state.lists.requesting = true; this.state.lists.data = []; - this.$fetch.get(`/v1/file/list/${this.paths.bucket}`) + this.$fetch + .get(`/v1/file/list/${this.paths.bucket}?prefix=${this.paths.folders.join('/')}`) .then(({ data }) => { this.state.lists.error = false; this.state.lists.data = data.files || []; From ce3cdb1d8bc7fe88c4c435ce92eb7df0477d38fb Mon Sep 17 00:00:00 2001 From: jigsawye Date: Mon, 16 May 2016 12:27:47 +0800 Subject: [PATCH 21/65] fix bug in action-navbar.html --- src/components/layout/action-navbar/action-navbar.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/layout/action-navbar/action-navbar.html b/src/components/layout/action-navbar/action-navbar.html index 8cbd1cb..5a094fb 100644 --- a/src/components/layout/action-navbar/action-navbar.html +++ b/src/components/layout/action-navbar/action-navbar.html @@ -11,7 +11,7 @@ add From 6679b8378cb3ff4e93fd65b9912c4350894a7348 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Mon, 16 May 2016 13:58:06 +0800 Subject: [PATCH 22/65] Update file module Add method to breadcrumb for update the paths and update file module. This commit can display bucket and files, but not yet support display folder. * add `updateFilePath` to $breadcrumb * add necessary method call to file controller --- src/components/file/file.controller.js | 11 +++++--- src/components/file/file.js | 1 + src/components/file/file.service.js | 8 +++--- .../layout/breadcrumb/breadcrumb.service.js | 26 +++++++++++++++++++ 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/components/file/file.controller.js b/src/components/file/file.controller.js index c687fe1..6d1ba78 100644 --- a/src/components/file/file.controller.js +++ b/src/components/file/file.controller.js @@ -1,8 +1,8 @@ export default class FileController { /** @ngInject */ - constructor($scope, $stateParams, $file) { + constructor($scope, $stateParams, $file, $bucket, $breadcrumb) { Object.assign(this, { - $scope, $file, + $scope, $file, $bucket, $breadcrumb, }); this.$scope.$watch( @@ -10,7 +10,12 @@ export default class FileController { newVal => Object.assign(this, newVal) , true); - this.$file.setPaths($stateParams.path); + const [bucket, ...folders] = $stateParams.path.split('/'); + + this.$file.setPaths(bucket, folders); + this.$breadcrumb.updateFilePath(folders); + + this.$bucket.getBuckets(); this.$file.getFiles(); } } diff --git a/src/components/file/file.js b/src/components/file/file.js index 688374b..2b1b585 100644 --- a/src/components/file/file.js +++ b/src/components/file/file.js @@ -14,6 +14,7 @@ const route = $stateProvider => { controller: FileController, controllerAs: 'file', template: FileTemplate, + onEnter: $nav => $nav.setTypeToFile(), }); }; diff --git a/src/components/file/file.service.js b/src/components/file/file.service.js index c87a1fc..83d14bd 100644 --- a/src/components/file/file.service.js +++ b/src/components/file/file.service.js @@ -27,17 +27,19 @@ export default class FileService { }; } - setPaths(path) { - const [bucket, ...folders] = path.split('/'); + setPaths(bucket, folders) { this.paths = { bucket, folders }; } getFiles() { + const { bucket, folders } = this.paths; + const endpoint = `/v1/file/list/${bucket}?prefix=${folders.join('/')}`; + this.state.lists.requesting = true; this.state.lists.data = []; this.$fetch - .get(`/v1/file/list/${this.paths.bucket}?prefix=${this.paths.folders.join('/')}`) + .get(endpoint) .then(({ data }) => { this.state.lists.error = false; this.state.lists.data = data.files || []; diff --git a/src/components/layout/breadcrumb/breadcrumb.service.js b/src/components/layout/breadcrumb/breadcrumb.service.js index a4b517d..9e7fdea 100644 --- a/src/components/layout/breadcrumb/breadcrumb.service.js +++ b/src/components/layout/breadcrumb/breadcrumb.service.js @@ -4,6 +4,11 @@ export default class BreadcrumbService { this.initPaths(); } + /** + * Initial the paths state. + * + * @return {void} + */ initPaths() { this.paths = [{ link: '/bucket', @@ -13,7 +18,28 @@ export default class BreadcrumbService { }]; } + /** + * Update the files length of bucket. + * + * @param {integer} len + * + * @return {void} + */ updateBucketPath(len) { this.paths[0].len = len; } + + /** + * Update paths in breadcrumb bar. + * + * @param {Array} paths + * + * @return {void} + */ + updateFilePath(paths) { + paths.forEach(path => { + const pathLink = this.paths.map(({ link }) => link).join(''); + this.paths.push({ link: pathLink, text: path }); + }); + } } From 0a58ba8e9a832b9479132b87553ad30470e5c769 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Mon, 16 May 2016 14:50:45 +0800 Subject: [PATCH 23/65] Add ng-file-upload to dependencies --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 9af6ecf..bfbb6fb 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "javascript-natural-sort": "^0.7.1", "lodash": "^4.5.1", "material-design-icons": "^2.2.0", + "ng-file-upload": "^12.0.4", "satellizer": "^0.14.0" }, "devDependencies": { From d11309aacdb248f87dcde3b9ef5c3d499c9bac8a Mon Sep 17 00:00:00 2001 From: jigsawye Date: Tue, 17 May 2016 14:16:34 +0800 Subject: [PATCH 24/65] Add fileUpload to vendor and remove angular-breadcrumb --- src/vendor/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vendor/index.js b/src/vendor/index.js index 7c92515..eb26892 100644 --- a/src/vendor/index.js +++ b/src/vendor/index.js @@ -3,8 +3,8 @@ import router from 'angular-ui-router'; import material from 'angular-material'; import translate from 'angular-translate'; import validationMatch from 'angular-validation-match'; +import fileUpload from 'ng-file-upload'; import satellizer from 'satellizer'; -import 'angular-breadcrumb'; const Vendor = module('app.vendor', [ router, @@ -12,7 +12,7 @@ const Vendor = module('app.vendor', [ translate, validationMatch, satellizer, - 'ncy-angular-breadcrumb', + fileUpload, ]); export default Vendor.name; From 6b3d4afc66ad4a570ed4d5e1a31c72f25f1e10ef Mon Sep 17 00:00:00 2001 From: jigsawye Date: Tue, 17 May 2016 14:57:12 +0800 Subject: [PATCH 25/65] Add filesize filter for size format The source file size is 'bytes', so we need a filter to format size for display * Add `src/filters/filesize.js` --- src/filters/filesize.js | 28 ++++++++++++++++++++++++++++ src/filters/index.js | 7 +++++++ src/index.js | 2 ++ 3 files changed, 37 insertions(+) create mode 100644 src/filters/filesize.js create mode 100644 src/filters/index.js diff --git a/src/filters/filesize.js b/src/filters/filesize.js new file mode 100644 index 0000000..b95cf6c --- /dev/null +++ b/src/filters/filesize.js @@ -0,0 +1,28 @@ +const units = [ + 'bytes', + 'KB', + 'MB', + 'GB', + 'TB', + 'PB', +]; + +/** + * Format file size. + * + * @return {String} + */ +export default () => bytes => { + if (isNaN(parseFloat(bytes)) || ! isFinite(bytes)) { + return '?'; + } + + let unit = 0; + + while (bytes >= 1024) { + bytes /= 1024; + unit ++; + } + + return `${bytes.toFixed(2)} ${units[unit]}`; +}; diff --git a/src/filters/index.js b/src/filters/index.js new file mode 100644 index 0000000..5e45983 --- /dev/null +++ b/src/filters/index.js @@ -0,0 +1,7 @@ +import { module } from 'angular'; +import filesize from './filesize'; + +const Filters = module('app.Filters', []) + .filter('filesize', filesize); + +export default Filters.name; diff --git a/src/index.js b/src/index.js index 3b4eb52..14364b5 100644 --- a/src/index.js +++ b/src/index.js @@ -6,6 +6,7 @@ import Vendor from './vendor'; import Config from './config'; import Services from './services'; import Directives from './directives'; +import Filters from './filters'; import Components from './components'; module('app', [ @@ -13,5 +14,6 @@ module('app', [ Config, Services, Directives, + Filters, Components, ]); From f71b02c1b1c74b686c7daa45cd9554a0c0f05279 Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 17 May 2016 17:47:57 +0800 Subject: [PATCH 26/65] Add align style for set the vertical alignment --- src/styles/base.css | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/styles/base.css b/src/styles/base.css index 8caa00f..ae5f323 100644 --- a/src/styles/base.css +++ b/src/styles/base.css @@ -41,3 +41,35 @@ .text-center { text-align: center; } + +.break-word { + word-break: break-word; +} + +/* display */ + +.inline{ + display: inline; +} + +.block{ + display: block; +} + +.inline-block{ + display: inline-block; +} + +/* align */ + +.valign-bottom{ + vertical-align: bottom; +} + +.valign-middle{ + vertical-align: middle; +} + +.valign-top{ + vertical-align: top; +} From 4fc9ec78a15a9f5f529b4b7a675d03e7fdbe6f0e Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 17 May 2016 17:50:22 +0800 Subject: [PATCH 27/65] Add style for highlight the messages Add style for highlight the messages that are important to user --- src/styles/s3.css | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/styles/s3.css b/src/styles/s3.css index 1351233..15dfb34 100644 --- a/src/styles/s3.css +++ b/src/styles/s3.css @@ -127,3 +127,40 @@ md-input-container md-progress-circular { .load-fail-state { margin-top: 10%; } + +/* list item --------------------------------------------- */ +md-list-item.checked { + background: #E8EAF6; +} + +md-list-item > .md-list-item-inner > p { + padding: 0 8px; +} + +md-list-item > p.flex-none, +md-list-item > .md-list-item-inner > p.flex-none, +md-list-item .md-list-item-inner > p.flex-none, +md-list-item .md-list-item-inner > .md-list-item-inner > p.flex-none { + flex: 0 0 auto; + -ms-flex: 0 0 auto; +} + +md-list-item > p.flex-grow, +md-list-item > .md-list-item-inner > p.flex-grow, +md-list-item .md-list-item-inner > p.flex-grow, +md-list-item .md-list-item-inner > .md-list-item-inner > p.flex-grow { + flex: 1 1 100%; + -ms-flex: 1 1 100%; +} + +md-list.md-default-theme md-list-item.md-2-line .md-list-item-text p.text-warn, +md-list md-list-item.md-2-line .md-list-item-text p.text-warn, +md-list.md-default-theme md-list-item.md-3-line .md-list-item-text p.text-warn, +md-list md-list-item.md-3-line .md-list-item-text p.text-warn { + color: #FF6D00; +} + +/* fix flexbox type layout issues in IE10 */ +span.flex { + display: block; +} \ No newline at end of file From 37cc662b8279595859c85bcede6e19a59529b98e Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 17 May 2016 17:54:00 +0800 Subject: [PATCH 28/65] Design transfer view * Add transfer.css * Add transfer.html --- src/components/transfer/transfer.css | 20 ++++++ src/components/transfer/transfer.html | 96 +++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 src/components/transfer/transfer.css create mode 100644 src/components/transfer/transfer.html diff --git a/src/components/transfer/transfer.css b/src/components/transfer/transfer.css new file mode 100644 index 0000000..70da214 --- /dev/null +++ b/src/components/transfer/transfer.css @@ -0,0 +1,20 @@ + +/** + * @author Jamie jamie.h@inwinstack.com + */ + +.transfer-list md-list-item { + border-bottom: 1px solid #eee; +} + +.transfer-list md-list-item:last-child { + border-bottom: none; +} + +.transfer-loaded { + padding-right: 20px; +} + +.transfer-rate { + margin: 10px 20px 10px 0; +} \ No newline at end of file diff --git a/src/components/transfer/transfer.html b/src/components/transfer/transfer.html new file mode 100644 index 0000000..7af9c37 --- /dev/null +++ b/src/components/transfer/transfer.html @@ -0,0 +1,96 @@ + +
+ +
+

+ Transfers +

+ + + + + clear + +
+
+ + + + + +

Automatically clear finished transfers

+
+ + + file_upload + delete + +
+

+ +
+ +
+ +

+ + + + + 1.87MB + / + 2GB + + + + + % + +

+
+ + check_circle + + +
+ +
+
+
+
+
+
\ No newline at end of file From 5bbc8afdf27dc8607cfdf20c60cd3acf3cc240d3 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 18 May 2016 13:55:54 +0800 Subject: [PATCH 29/65] Update file.html The file size should be formated. * Add filesize filter to file size * Add refresh method to click event --- src/components/file/file.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/file/file.html b/src/components/file/file.html index d6207ad..86eb9cd 100644 --- a/src/components/file/file.html +++ b/src/components/file/file.html @@ -30,13 +30,13 @@

@@ -63,7 +63,7 @@
file_upload Upload File @@ -73,7 +73,7 @@ create_new_folder Create Folder @@ -91,7 +91,7 @@ refresh From 3d1c5c6daedd5e17bf516432d71bf99fc358bbcd Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 18 May 2016 13:58:10 +0800 Subject: [PATCH 30/65] Remove unnecessary state and dependence in file.service.js --- src/components/file/file.service.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/components/file/file.service.js b/src/components/file/file.service.js index 83d14bd..54c721e 100644 --- a/src/components/file/file.service.js +++ b/src/components/file/file.service.js @@ -1,8 +1,8 @@ export default class FileService { /** @ngInject */ - constructor($fetch, $bucket) { + constructor($mdDialog, $fetch, $bucket) { Object.assign(this, { - $fetch, $bucket, + $mdDialog, $fetch, $bucket, }); this.initState(); @@ -19,11 +19,6 @@ export default class FileService { requesting: false, error: false, }, - create: { - checking: false, - checked: false, - duplicated: false, - }, }; } From d90dfd1c49dd91cca3b133873d78b6080bac51ea Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 18 May 2016 13:59:36 +0800 Subject: [PATCH 31/65] Add file upload component Add a upload component for file uploading. The component allow user can upload multiple files to s3 protal. * Add file upload service for handle file upload. * Add some method(upload, refresh) to file controller. * Add file upload controller for connect service and dialog. * Apply file upload UI design. --- src/components/file/file.controller.js | 18 ++- src/components/file/file.js | 2 + .../file/upload/upload.controller.js | 29 +++++ src/components/file/upload/upload.html | 59 +++++---- src/components/file/upload/upload.servce.js | 113 ++++++++++++++++++ 5 files changed, 192 insertions(+), 29 deletions(-) create mode 100644 src/components/file/upload/upload.controller.js create mode 100644 src/components/file/upload/upload.servce.js diff --git a/src/components/file/file.controller.js b/src/components/file/file.controller.js index 6d1ba78..7d2fc13 100644 --- a/src/components/file/file.controller.js +++ b/src/components/file/file.controller.js @@ -1,11 +1,11 @@ export default class FileController { /** @ngInject */ - constructor($scope, $stateParams, $file, $bucket, $breadcrumb) { + constructor($scope, $stateParams, $file, $bucket, $breadcrumb, $upload) { Object.assign(this, { - $scope, $file, $bucket, $breadcrumb, + $file, $upload, $bucket, $breadcrumb, }); - this.$scope.$watch( + $scope.$watch( () => $file.state.lists, newVal => Object.assign(this, newVal) , true); @@ -18,4 +18,16 @@ export default class FileController { this.$bucket.getBuckets(); this.$file.getFiles(); } + + createFolder($event) { + this.$file.createFolder($event); + } + + upload($event) { + this.$upload.createDialog($event); + } + + refresh() { + this.$file.getFiles(); + } } diff --git a/src/components/file/file.js b/src/components/file/file.js index 2b1b585..469bb64 100644 --- a/src/components/file/file.js +++ b/src/components/file/file.js @@ -4,6 +4,7 @@ import router from 'angular-ui-router'; import FileController from './file.controller'; import FileService from './file.service'; import FileTemplate from './file.html'; +import UploadService from './upload/upload.servce'; import './file.css'; /** @ngInject */ @@ -22,6 +23,7 @@ const File = module('file', [ router, ]) .service('$file', FileService) +.service('$upload', UploadService) .config(route); export default File.name; diff --git a/src/components/file/upload/upload.controller.js b/src/components/file/upload/upload.controller.js new file mode 100644 index 0000000..a9f246a --- /dev/null +++ b/src/components/file/upload/upload.controller.js @@ -0,0 +1,29 @@ +export default class FileUploadController { + /** @ngInject */ + constructor($file, $upload, $scope) { + Object.assign(this, { + $file, $upload, $scope, + }); + + $scope.$watch( + () => $upload.state, + newVal => Object.assign(this, newVal) + , true); + } + + upload() { + this.$upload.upload(); + } + + select(files) { + this.$upload.select(files); + } + + delete(name) { + this.$upload.delete(name); + } + + cancel() { + this.$upload.closeDialog(); + } +} diff --git a/src/components/file/upload/upload.html b/src/components/file/upload/upload.html index ae18adc..b2b322e 100644 --- a/src/components/file/upload/upload.html +++ b/src/components/file/upload/upload.html @@ -1,14 +1,14 @@ -
+

Upload Files

- + clear @@ -21,58 +21,65 @@

Upload Files

To upload files to S3 Portal, click Add Files. To remove files already selected, click the ✖ to the far right of the file name.

- + -

Add Files

- add +
+

Add Files

+ add +
- + - - - photo - insert_drive_file - -

-

- + + + photo + insert_drive_file + +

+

+ clear
- + -
- + Cancel Upload - \ No newline at end of file + diff --git a/src/components/file/upload/upload.servce.js b/src/components/file/upload/upload.servce.js new file mode 100644 index 0000000..9caf81f --- /dev/null +++ b/src/components/file/upload/upload.servce.js @@ -0,0 +1,113 @@ +import { element } from 'angular'; +import FileUploadController from './upload.controller'; +import FileUploadTemplate from './upload.html'; + +export default class FileUploadService { + /** @ngInject */ + constructor(Config, Upload, $mdDialog, $file, $toast) { + Object.assign(this, { + Config, Upload, $mdDialog, $file, $toast, + }); + + this.initState(); + } + + initState() { + this.state = { + files: [], + size: 0, + uploadingFiles: [], + }; + } + + select(selectFiles) { + const filterFiles = selectFiles.filter(selectFile => + this.state.files.every(file => file.name !== selectFile.name) + ); + const files = this.state.files.concat(filterFiles); + const size = this.size(files); + this.state = { ...this.state, files, size }; + } + + delete(name) { + const files = this.state.files.filter(file => file.name !== name); + const size = this.size(files); + + this.state = { ...this.state, files, size }; + } + + isUploading() { + return !! this.state.uploadingFiles.length; + } + + abort() { + this.state.uploadingFiles.forEach(file => file.abort()); + this.state.uploadingFiles = []; + } + + upload() { + const { bucket, folders } = this.$file.paths; + const prefix = folders.length ? '' : `${folders.join('/')}/`; + const url = `${this.Config.API_URL}/v1/file/create`; + + this.state.files.forEach(file => + this.uploadFile(url, { bucket, prefix, file }) + ); + + this.closeDialog(); + } + + size(files) { + return files.reduce((previous, current) => previous + current.size, 0); + } + + uploadFile = (url, data) => { + const { name } = data.file; + const upload = this.Upload.upload({ url, data }); + this.state.uploadingFiles.push(upload); + + upload.then( + () => this.handleUploadSuccess(name), + () => this.handleUploadFailure(name), + this.handleEvent + ); + } + + handleEvent = evt => { + console.log(evt); + } + + handleUploadSuccess(name) { + this.removeUploadingFile(name); + this.$file.getFiles(); + this.$toast.show(`${name} is uploaded successfully!`); + } + + handleUploadFailure(name) { + this.removeUploadingFile(name); + this.$toast.show(`${name} is uploaded failure!`); + } + + removeUploadingFile(name) { + this.state.uploadingFiles.filter(uploadingFile => + uploadingFile.name !== name + ); + } + + createDialog($event) { + this.$mdDialog.show({ + controller: FileUploadController, + controllerAs: 'upload', + template: FileUploadTemplate, + parent: element(document.body), + targetEvent: $event, + clickOutsideToClose: true, + }); + } + + closeDialog() { + this.$mdDialog.cancel(); + this.state.files = []; + this.state.size = 0; + } +} From 0054bd712ee4678e62da48757356d12d4afeff21 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 18 May 2016 14:03:31 +0800 Subject: [PATCH 32/65] Add sign out handler Add a handler to sign out event, because we need let user know they have files are uploading. If user want to sign out and have some file are uploading, then we will abort all uploading file and log out the user. * Add a condition to signOut and abort file upload when sign out --- .../layout/top-navbar/top-navbar.controller.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/layout/top-navbar/top-navbar.controller.js b/src/components/layout/top-navbar/top-navbar.controller.js index 80be162..742e24a 100644 --- a/src/components/layout/top-navbar/top-navbar.controller.js +++ b/src/components/layout/top-navbar/top-navbar.controller.js @@ -1,8 +1,8 @@ export default class TopNavbarController { /** @ngInject */ - constructor($translate, $auth, $state, $toast, $mdDialog, AuthService) { + constructor($scope, $translate, $auth, $state, $toast, $mdDialog, $upload, AuthService) { Object.assign(this, { - $translate, $auth, $state, $toast, $mdDialog, AuthService, + $scope, $translate, $auth, $state, $toast, $mdDialog, $upload, AuthService, }); this.languages = [ @@ -32,7 +32,11 @@ export default class TopNavbarController { * @return {void} */ signOut($event) { - this.showConfirmMessage($event).then(this.executedSignOut); + if (this.$upload.isUploading()) { + this.showConfirmMessage($event).then(this.executedSignOut); + } else { + this.executedSignOut(); + } } /** @@ -61,6 +65,7 @@ or uploads and leaving now will cancel them.Still leaving?`) */ executedSignOut = () => this.AuthService.signOut() .then(() => { + this.$upload.abort(); this.$auth.logout(); this.$state.go('auth.signin'); this.$toast.show('Sign Out Success!'); From 30283b7de0e9948048e8086a8767d2cd73180ec5 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 18 May 2016 14:09:55 +0800 Subject: [PATCH 33/65] Add method to Upload button in action navbar --- .../layout/action-navbar/action-navbar.controller.js | 12 ++++++------ .../layout/action-navbar/action-navbar.html | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/layout/action-navbar/action-navbar.controller.js b/src/components/layout/action-navbar/action-navbar.controller.js index 34da8c9..62cbdf2 100644 --- a/src/components/layout/action-navbar/action-navbar.controller.js +++ b/src/components/layout/action-navbar/action-navbar.controller.js @@ -1,8 +1,8 @@ export default class ActionNavbarController { /** @ngInject */ - constructor($scope, $bucket, $nav) { + constructor($scope, $bucket, $nav, $file, $upload) { Object.assign(this, { - $scope, $bucket, + $scope, $bucket, $file, $upload, }); this.$scope.$watch( @@ -28,8 +28,8 @@ export default class ActionNavbarController { // } - upload() { - // + upload($event) { + this.$upload.createDialog($event); } delete() { @@ -56,7 +56,7 @@ export default class ActionNavbarController { */ create($event) { if (this.isFile()) { - // create file dialog + // } else { this.$bucket.createDialog($event); } @@ -69,7 +69,7 @@ export default class ActionNavbarController { */ refresh() { if (this.isFile()) { - // get the files + this.$file.getFiles(); } else { this.$bucket.getBuckets(); } diff --git a/src/components/layout/action-navbar/action-navbar.html b/src/components/layout/action-navbar/action-navbar.html index 5a094fb..2e57048 100644 --- a/src/components/layout/action-navbar/action-navbar.html +++ b/src/components/layout/action-navbar/action-navbar.html @@ -2,7 +2,7 @@
file_upload From f024de07dad80c48d131b25cb7dc788934e2a261 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 18 May 2016 14:28:18 +0800 Subject: [PATCH 34/65] Fix breadcrumb bar The current breadcrumb have loop bugs, this commit just fixed it. * Should reset paths when upload file path * Should update path when enter bucket list --- src/components/bucket/bucket.controller.js | 3 ++- src/components/file/file.controller.js | 5 +++-- src/components/layout/breadcrumb/breadcrumb.service.js | 10 ++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/bucket/bucket.controller.js b/src/components/bucket/bucket.controller.js index c7dafe5..c3fb2c7 100644 --- a/src/components/bucket/bucket.controller.js +++ b/src/components/bucket/bucket.controller.js @@ -1,6 +1,6 @@ export default class BucketController { /** @ngInject */ - constructor($scope, $bucket, $state) { + constructor($scope, $bucket, $state, $breadcrumb) { Object.assign(this, { $scope, $bucket, $state, }); @@ -10,6 +10,7 @@ export default class BucketController { newVal => Object.assign(this, newVal) , true); + $breadcrumb.initPaths(); this.$bucket.getBuckets(); } diff --git a/src/components/file/file.controller.js b/src/components/file/file.controller.js index 7d2fc13..ac164d4 100644 --- a/src/components/file/file.controller.js +++ b/src/components/file/file.controller.js @@ -10,10 +10,11 @@ export default class FileController { newVal => Object.assign(this, newVal) , true); - const [bucket, ...folders] = $stateParams.path.split('/'); + const paths = $stateParams.path.split('/'); + const [bucket, ...folders] = paths; this.$file.setPaths(bucket, folders); - this.$breadcrumb.updateFilePath(folders); + this.$breadcrumb.updateFilePath(paths); this.$bucket.getBuckets(); this.$file.getFiles(); diff --git a/src/components/layout/breadcrumb/breadcrumb.service.js b/src/components/layout/breadcrumb/breadcrumb.service.js index 9e7fdea..259fd4a 100644 --- a/src/components/layout/breadcrumb/breadcrumb.service.js +++ b/src/components/layout/breadcrumb/breadcrumb.service.js @@ -37,9 +37,11 @@ export default class BreadcrumbService { * @return {void} */ updateFilePath(paths) { - paths.forEach(path => { - const pathLink = this.paths.map(({ link }) => link).join(''); - this.paths.push({ link: pathLink, text: path }); - }); + this.initPaths(); + paths.reduce((previous, current) => { + const link = `${previous}/${current}`; + this.paths.push({ link, text: current }); + return link; + }, '/bucket'); } } From b8f6cffad92f40a66d072e5ca12b9db8cd7a87bb Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 18 May 2016 15:46:33 +0800 Subject: [PATCH 35/65] Add transfer UI & update layout component Add layout controller & layout service for control transfer display state. * Apply the UI design * Add LayoutController & LayoutService * Add toggleTransfer() to the transfer button click event --- src/components/index.js | 2 ++ .../action-navbar/action-navbar.controller.js | 8 +++--- .../layout/action-navbar/action-navbar.html | 2 +- src/components/layout/layout.controller.js | 12 +++++++++ src/components/layout/layout.html | 6 ++++- src/components/layout/layout.js | 12 +++++++++ src/components/layout/layout.service.js | 15 +++++++++++ .../layout/transfer/transfer.controller.js | 12 +++++++++ .../{ => layout}/transfer/transfer.css | 0 .../{ => layout}/transfer/transfer.html | 26 +++++++++---------- 10 files changed, 76 insertions(+), 19 deletions(-) create mode 100644 src/components/layout/layout.controller.js create mode 100644 src/components/layout/layout.service.js create mode 100644 src/components/layout/transfer/transfer.controller.js rename src/components/{ => layout}/transfer/transfer.css (100%) rename src/components/{ => layout}/transfer/transfer.html (94%) diff --git a/src/components/index.js b/src/components/index.js index d209813..a6d0113 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,6 +1,7 @@ import { module } from 'angular'; import Layout from './layout/layout'; import NotFound from './not-found/not-found'; +import Transfer from './transfer/transfer'; import Auth from './auth/auth'; import Bucket from './bucket/bucket'; import File from './file/file'; @@ -8,6 +9,7 @@ import File from './file/file'; const Components = module('app.components', [ Layout, NotFound, + Transfer, Auth, Bucket, File, diff --git a/src/components/layout/action-navbar/action-navbar.controller.js b/src/components/layout/action-navbar/action-navbar.controller.js index 62cbdf2..4b6940e 100644 --- a/src/components/layout/action-navbar/action-navbar.controller.js +++ b/src/components/layout/action-navbar/action-navbar.controller.js @@ -1,8 +1,8 @@ export default class ActionNavbarController { /** @ngInject */ - constructor($scope, $bucket, $nav, $file, $upload) { + constructor($scope, $bucket, $nav, $file, $upload, $layout) { Object.assign(this, { - $scope, $bucket, $file, $upload, + $scope, $bucket, $file, $upload, $layout, }); this.$scope.$watch( @@ -44,8 +44,8 @@ export default class ActionNavbarController { // } - transfers() { - // + toggleTransfer() { + this.$layout.toggleTransfer(); } /** diff --git a/src/components/layout/action-navbar/action-navbar.html b/src/components/layout/action-navbar/action-navbar.html index 2e57048..0c4bb48 100644 --- a/src/components/layout/action-navbar/action-navbar.html +++ b/src/components/layout/action-navbar/action-navbar.html @@ -122,7 +122,7 @@ Transfers diff --git a/src/components/layout/layout.controller.js b/src/components/layout/layout.controller.js new file mode 100644 index 0000000..854e082 --- /dev/null +++ b/src/components/layout/layout.controller.js @@ -0,0 +1,12 @@ +export default class LayoutController { + /** @ngInject */ + constructor($layout) { + Object.assign(this, { + $layout, + }); + } + + toggleTransfer() { + this.$layout.toggleTransfer(); + } +} diff --git a/src/components/layout/layout.html b/src/components/layout/layout.html index 5000fb0..7e99a0f 100644 --- a/src/components/layout/layout.html +++ b/src/components/layout/layout.html @@ -9,5 +9,9 @@
- +
diff --git a/src/components/layout/layout.js b/src/components/layout/layout.js index d230579..819b2d5 100644 --- a/src/components/layout/layout.js +++ b/src/components/layout/layout.js @@ -1,6 +1,8 @@ import { module } from 'angular'; import router from 'angular-ui-router'; +import LayoutController from './layout.controller'; +import LayoutService from './layout.service'; import LayoutTemplate from './layout.html'; import TopNavbarController from './top-navbar/top-navbar.controller'; import TopNavbarTemplate from './top-navbar/top-navbar.html'; @@ -10,6 +12,8 @@ import BreadcrumbService from './breadcrumb/breadcrumb.service'; import ActionNavbarController from './action-navbar/action-navbar.controller'; import ActionNavbarTemplate from './action-navbar/action-navbar.html'; import ActionNavbarService from './action-navbar/action-navbar.service'; +import TransferController from './transfer/transfer.controller'; +import TransferTemplate from './transfer/transfer.html'; import './layout.css'; @@ -21,6 +25,8 @@ const route = $stateProvider => { views: { '': { template: LayoutTemplate, + controller: LayoutController, + controllerAs: 'layout', }, 'top-navbar@root': { template: TopNavbarTemplate, @@ -37,6 +43,11 @@ const route = $stateProvider => { controller: BreadcrumbController, controllerAs: 'bc', }, + 'transfer@root': { + template: TransferTemplate, + controller: TransferController, + controllerAs: 'transfer', + }, }, }); }; @@ -46,6 +57,7 @@ const Layout = module('layout', [ ]) .service('$breadcrumb', BreadcrumbService) .service('$nav', ActionNavbarService) +.service('$layout', LayoutService) .config(route); export default Layout.name; diff --git a/src/components/layout/layout.service.js b/src/components/layout/layout.service.js new file mode 100644 index 0000000..9115abb --- /dev/null +++ b/src/components/layout/layout.service.js @@ -0,0 +1,15 @@ +export default class LayoutService { + constructor() { + this.initState(); + } + + initState() { + this.state = { + transfer: false, + }; + } + + toggleTransfer() { + this.state.transfer = ! this.state.transfer; + } +} diff --git a/src/components/layout/transfer/transfer.controller.js b/src/components/layout/transfer/transfer.controller.js new file mode 100644 index 0000000..a3691e5 --- /dev/null +++ b/src/components/layout/transfer/transfer.controller.js @@ -0,0 +1,12 @@ +export default class TransferController { + /** @ngInject */ + constructor($layout) { + Object.assign(this, { + $layout, + }); + } + + close() { + this.$layout.toggleTransfer(); + } +} diff --git a/src/components/transfer/transfer.css b/src/components/layout/transfer/transfer.css similarity index 100% rename from src/components/transfer/transfer.css rename to src/components/layout/transfer/transfer.css diff --git a/src/components/transfer/transfer.html b/src/components/layout/transfer/transfer.html similarity index 94% rename from src/components/transfer/transfer.html rename to src/components/layout/transfer/transfer.html index 7af9c37..0cd3d15 100644 --- a/src/components/transfer/transfer.html +++ b/src/components/layout/transfer/transfer.html @@ -5,18 +5,18 @@

Transfers

- + - + clear
- + @@ -25,7 +25,7 @@

>

Automatically clear finished transfers

- + delete - +

- +
- +

- + 1.87MB / 2GB - + %

- + class="material-icons md-warn" ng-if="@A.21" ng-click="@A.24" >refresh--> - +
- \ No newline at end of file + From f46b0b1abdeae9dfb0a0f2d2f03e74e9bc76e7d3 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 18 May 2016 16:02:00 +0800 Subject: [PATCH 36/65] Add transfer service --- src/components/layout/layout.js | 3 +++ src/components/layout/transfer/transfer.service.js | 11 +++++++++++ 2 files changed, 14 insertions(+) create mode 100644 src/components/layout/transfer/transfer.service.js diff --git a/src/components/layout/layout.js b/src/components/layout/layout.js index 819b2d5..a5c8c11 100644 --- a/src/components/layout/layout.js +++ b/src/components/layout/layout.js @@ -14,8 +14,10 @@ import ActionNavbarTemplate from './action-navbar/action-navbar.html'; import ActionNavbarService from './action-navbar/action-navbar.service'; import TransferController from './transfer/transfer.controller'; import TransferTemplate from './transfer/transfer.html'; +import TransferService from './transfer/transfer.service'; import './layout.css'; +import './transfer/transfer.css'; /** @ngInject */ const route = $stateProvider => { @@ -58,6 +60,7 @@ const Layout = module('layout', [ .service('$breadcrumb', BreadcrumbService) .service('$nav', ActionNavbarService) .service('$layout', LayoutService) +.service('$transfer', TransferService) .config(route); export default Layout.name; diff --git a/src/components/layout/transfer/transfer.service.js b/src/components/layout/transfer/transfer.service.js new file mode 100644 index 0000000..496d855 --- /dev/null +++ b/src/components/layout/transfer/transfer.service.js @@ -0,0 +1,11 @@ +export default class TransferService { + constructor() { + this.initState(); + } + + initState() { + this.state = { + transfers: {}, + }; + } +} From cf833f43864a839a870234adac267099f27c2792 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 18 May 2016 16:24:05 +0800 Subject: [PATCH 37/65] Remove transfer import in components/index.js --- src/components/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/index.js b/src/components/index.js index a6d0113..d209813 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,7 +1,6 @@ import { module } from 'angular'; import Layout from './layout/layout'; import NotFound from './not-found/not-found'; -import Transfer from './transfer/transfer'; import Auth from './auth/auth'; import Bucket from './bucket/bucket'; import File from './file/file'; @@ -9,7 +8,6 @@ import File from './file/file'; const Components = module('app.components', [ Layout, NotFound, - Transfer, Auth, Bucket, File, From c931e89ba549239ed1862f511cd5c1f6e93806c5 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 18 May 2016 16:26:54 +0800 Subject: [PATCH 38/65] Use boolean to control upadting status --- src/components/file/upload/upload.servce.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/components/file/upload/upload.servce.js b/src/components/file/upload/upload.servce.js index 9caf81f..2ab3381 100644 --- a/src/components/file/upload/upload.servce.js +++ b/src/components/file/upload/upload.servce.js @@ -16,6 +16,7 @@ export default class FileUploadService { this.state = { files: [], size: 0, + uploading: false, uploadingFiles: [], }; } @@ -37,7 +38,7 @@ export default class FileUploadService { } isUploading() { - return !! this.state.uploadingFiles.length; + return this.state.uploading; } abort() { @@ -61,19 +62,20 @@ export default class FileUploadService { return files.reduce((previous, current) => previous + current.size, 0); } - uploadFile = (url, data) => { + uploadFile(url, data) { const { name } = data.file; const upload = this.Upload.upload({ url, data }); this.state.uploadingFiles.push(upload); + this.state.uploading = true; upload.then( () => this.handleUploadSuccess(name), - () => this.handleUploadFailure(name), - this.handleEvent + err => this.handleUploadFailure(name, err), + evt => this.handleEvent(name, evt) ); } - handleEvent = evt => { + handleEvent(name, evt) { console.log(evt); } @@ -83,15 +85,19 @@ export default class FileUploadService { this.$toast.show(`${name} is uploaded successfully!`); } - handleUploadFailure(name) { + handleUploadFailure(name, err) { this.removeUploadingFile(name); - this.$toast.show(`${name} is uploaded failure!`); + this.$toast.show(`${name} is uploaded failure! Error: ${err}`); } removeUploadingFile(name) { this.state.uploadingFiles.filter(uploadingFile => uploadingFile.name !== name ); + + if (! this.state.uploadingFiles.length) { + this.state.uploading = false; + } } createDialog($event) { From 48fadf8c89d5aedb579ce94bb0b25210bc54e73e Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 18 May 2016 16:30:15 +0800 Subject: [PATCH 39/65] Move toal size method to utils --- src/components/file/upload/upload.servce.js | 9 +++------ src/utils/totalSize.js | 9 +++++++++ 2 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 src/utils/totalSize.js diff --git a/src/components/file/upload/upload.servce.js b/src/components/file/upload/upload.servce.js index 2ab3381..4da84ec 100644 --- a/src/components/file/upload/upload.servce.js +++ b/src/components/file/upload/upload.servce.js @@ -1,4 +1,5 @@ import { element } from 'angular'; +import totalSize from '../../../utils/totalSize'; import FileUploadController from './upload.controller'; import FileUploadTemplate from './upload.html'; @@ -26,13 +27,13 @@ export default class FileUploadService { this.state.files.every(file => file.name !== selectFile.name) ); const files = this.state.files.concat(filterFiles); - const size = this.size(files); + const size = totalSize(files); this.state = { ...this.state, files, size }; } delete(name) { const files = this.state.files.filter(file => file.name !== name); - const size = this.size(files); + const size = totalSize(files); this.state = { ...this.state, files, size }; } @@ -58,10 +59,6 @@ export default class FileUploadService { this.closeDialog(); } - size(files) { - return files.reduce((previous, current) => previous + current.size, 0); - } - uploadFile(url, data) { const { name } = data.file; const upload = this.Upload.upload({ url, data }); diff --git a/src/utils/totalSize.js b/src/utils/totalSize.js new file mode 100644 index 0000000..e88454d --- /dev/null +++ b/src/utils/totalSize.js @@ -0,0 +1,9 @@ +/** + * Calculate the total size of files. + * + * @type {Array} + */ +export default files => files + .reduce((previous, current) => + previous + current.size, 0 + ); From d26b84b86cb89e5034b7571c75db53c4d45aedcd Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 18 May 2016 16:34:09 +0800 Subject: [PATCH 40/65] Update method name --- src/components/file/upload/upload.servce.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/file/upload/upload.servce.js b/src/components/file/upload/upload.servce.js index 4da84ec..e202721 100644 --- a/src/components/file/upload/upload.servce.js +++ b/src/components/file/upload/upload.servce.js @@ -66,8 +66,8 @@ export default class FileUploadService { this.state.uploading = true; upload.then( - () => this.handleUploadSuccess(name), - err => this.handleUploadFailure(name, err), + () => this.handleSuccess(name), + err => this.handleFailure(name, err), evt => this.handleEvent(name, evt) ); } @@ -76,13 +76,13 @@ export default class FileUploadService { console.log(evt); } - handleUploadSuccess(name) { + handleSuccess(name) { this.removeUploadingFile(name); this.$file.getFiles(); this.$toast.show(`${name} is uploaded successfully!`); } - handleUploadFailure(name, err) { + handleFailure(name, err) { this.removeUploadingFile(name); this.$toast.show(`${name} is uploaded failure! Error: ${err}`); } From 89fac79e9aa2168a37da29e9e412ec172dda8371 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 18 May 2016 18:25:56 +0800 Subject: [PATCH 41/65] Refactor upload component Modify the state of upload service, because the privous are not useable in transfer. All files(pending, uploading, success...) in state.files now, and use `status` for control. * Refactor state tree. * Refactor methods. * Update totalSize --- .../file/upload/upload.controller.js | 5 +- src/components/file/upload/upload.html | 10 +- src/components/file/upload/upload.servce.js | 97 ++++++++++++------- src/utils/totalSize.js | 2 +- 4 files changed, 71 insertions(+), 43 deletions(-) diff --git a/src/components/file/upload/upload.controller.js b/src/components/file/upload/upload.controller.js index a9f246a..d20aabe 100644 --- a/src/components/file/upload/upload.controller.js +++ b/src/components/file/upload/upload.controller.js @@ -7,7 +7,10 @@ export default class FileUploadController { $scope.$watch( () => $upload.state, - newVal => Object.assign(this, newVal) + newVal => Object.assign(this, { + ...newVal, + files: newVal.files.filter(file => file.status === 'PENDING'), + }) , true); } diff --git a/src/components/file/upload/upload.html b/src/components/file/upload/upload.html index b2b322e..3ed64e6 100644 --- a/src/components/file/upload/upload.html +++ b/src/components/file/upload/upload.html @@ -41,15 +41,15 @@

Upload Files

- photo - insert_drive_file + photo + insert_drive_file -

-

+

+

clear
diff --git a/src/components/file/upload/upload.servce.js b/src/components/file/upload/upload.servce.js index e202721..65cb307 100644 --- a/src/components/file/upload/upload.servce.js +++ b/src/components/file/upload/upload.servce.js @@ -18,21 +18,29 @@ export default class FileUploadService { files: [], size: 0, uploading: false, - uploadingFiles: [], }; } - select(selectFiles) { - const filterFiles = selectFiles.filter(selectFile => - this.state.files.every(file => file.name !== selectFile.name) - ); - const files = this.state.files.concat(filterFiles); + select(selectedFiles) { + const additionalFiles = selectedFiles.filter(selectedFile => + this.state.files.every(({ detail, status }) => + (detail.name === selectedFile.name && status !== 'PENDING') || + (detail.name !== selectedFile.name && status === 'PENDING') + ) + ).map(detail => ({ + id: Symbol('unique id'), + status: 'PENDING', + detail, + })); + + const files = [...this.state.files, ...additionalFiles]; + const size = totalSize(files); this.state = { ...this.state, files, size }; } - delete(name) { - const files = this.state.files.filter(file => file.name !== name); + delete(id) { + const files = this.state.files.filter(file => file.id !== id); const size = totalSize(files); this.state = { ...this.state, files, size }; @@ -43,8 +51,12 @@ export default class FileUploadService { } abort() { - this.state.uploadingFiles.forEach(file => file.abort()); - this.state.uploadingFiles = []; + this.state.files.forEach(file => file.upload.abort()); + this.state.files = []; + } + + findFileIndex(id) { + return this.state.files.findIndex(file => file.id === id); } upload() { @@ -52,49 +64,63 @@ export default class FileUploadService { const prefix = folders.length ? '' : `${folders.join('/')}/`; const url = `${this.Config.API_URL}/v1/file/create`; - this.state.files.forEach(file => - this.uploadFile(url, { bucket, prefix, file }) - ); + this.state = { + uploading: true, + files: this.state.files.map(file => ({ + ...file, + status: 'UPLOADING', + upload: this.uploadFile(file.id, { + bucket, prefix, file: file.detail, + }, url), + })), + }; this.closeDialog(); } - uploadFile(url, data) { - const { name } = data.file; + uploadFile(id, data, url) { const upload = this.Upload.upload({ url, data }); - this.state.uploadingFiles.push(upload); - this.state.uploading = true; upload.then( - () => this.handleSuccess(name), - err => this.handleFailure(name, err), - evt => this.handleEvent(name, evt) + res => this.handleSuccess(id, res), + err => this.handleFailure(id, err), + evt => this.handleEvent(id, evt) ); + + return upload; } - handleEvent(name, evt) { - console.log(evt); + handleEvent(id, evt) { + const i = this.findFileIndex(id); + this.state.files[i].process = evt; + console.log(this.state.files[i].process); + console.log(this.state); } - handleSuccess(name) { - this.removeUploadingFile(name); + handleSuccess(id, res) { + const i = this.findFileIndex(id); + this.state.files[i].status = 'UPLOADED'; + this.updateUpdateStatus(); this.$file.getFiles(); - this.$toast.show(`${name} is uploaded successfully!`); + console.log(res); } - handleFailure(name, err) { - this.removeUploadingFile(name); - this.$toast.show(`${name} is uploaded failure! Error: ${err}`); + handleFailure(id, err) { + const i = this.findFileIndex(id); + this.state.files[i].status = 'FAILURE'; + this.updateUpdateStatus(); + console.log(err); } - removeUploadingFile(name) { - this.state.uploadingFiles.filter(uploadingFile => - uploadingFile.name !== name - ); + removeUploadFile(id) { + const i = this.findFileIndex(id); + delete this.state.files[i]; + } - if (! this.state.uploadingFiles.length) { - this.state.uploading = false; - } + updateUpdateStatus() { + this.state.uploading = this.state.files.every( + file => file.status === 'UPLOADING' + ); } createDialog($event) { @@ -110,7 +136,6 @@ export default class FileUploadService { closeDialog() { this.$mdDialog.cancel(); - this.state.files = []; this.state.size = 0; } } diff --git a/src/utils/totalSize.js b/src/utils/totalSize.js index e88454d..61d9b6e 100644 --- a/src/utils/totalSize.js +++ b/src/utils/totalSize.js @@ -5,5 +5,5 @@ */ export default files => files .reduce((previous, current) => - previous + current.size, 0 + previous + current.detail.size, 0 ); From c0a0904c28c2271268523fd779888f2a8a38a538 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Thu, 19 May 2016 14:56:17 +0800 Subject: [PATCH 42/65] Update TokenInterceptor for other error handler The authentication will throw a `token_invalid` fails so add new condition for handle it. * Update config/http.config.js --- src/config/http.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/config/http.config.js b/src/config/http.config.js index 7270500..25869aa 100644 --- a/src/config/http.config.js +++ b/src/config/http.config.js @@ -1,7 +1,8 @@ const TokenInterceptor = ($q, $injector) => ({ responseError(rejection) { const { data } = rejection; - if (data.error && data.error === 'token_not_provided') { + if (data.error && data.error === 'token_not_provided' || data.error === 'token_invalid') { + $injector.get('$auth').logout(); $injector.get('$state').go('auth.signin'); $injector.get('$toast').show('Your token has expired, please sign in again!'); } From c98ab5bda72f6aa91ba33550fb2d92eccf7695a1 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Thu, 19 May 2016 15:01:21 +0800 Subject: [PATCH 43/65] Complete transfer component Move all upload object to transfer service, because the transfer should display upload status and delete status, so the both data must be handle in same service. * Move methods to transfer service * Add some translations * Move app's updating status to $transfer and renamed to processing --- .../file/upload/upload.controller.js | 5 +- src/components/file/upload/upload.servce.js | 90 +++++-------------- .../top-navbar/top-navbar.controller.js | 8 +- .../layout/transfer/transfer.controller.js | 40 ++++++++- src/components/layout/transfer/transfer.html | 42 ++++----- .../layout/transfer/transfer.service.js | 83 ++++++++++++++++- src/translations/EN.js | 12 +++ 7 files changed, 179 insertions(+), 101 deletions(-) diff --git a/src/components/file/upload/upload.controller.js b/src/components/file/upload/upload.controller.js index d20aabe..a9f246a 100644 --- a/src/components/file/upload/upload.controller.js +++ b/src/components/file/upload/upload.controller.js @@ -7,10 +7,7 @@ export default class FileUploadController { $scope.$watch( () => $upload.state, - newVal => Object.assign(this, { - ...newVal, - files: newVal.files.filter(file => file.status === 'PENDING'), - }) + newVal => Object.assign(this, newVal) , true); } diff --git a/src/components/file/upload/upload.servce.js b/src/components/file/upload/upload.servce.js index 65cb307..15ef8a6 100644 --- a/src/components/file/upload/upload.servce.js +++ b/src/components/file/upload/upload.servce.js @@ -5,9 +5,9 @@ import FileUploadTemplate from './upload.html'; export default class FileUploadService { /** @ngInject */ - constructor(Config, Upload, $mdDialog, $file, $toast) { + constructor(Config, Upload, $mdDialog, $file, $transfer) { Object.assign(this, { - Config, Upload, $mdDialog, $file, $toast, + Config, Upload, $mdDialog, $file, $transfer, }); this.initState(); @@ -17,20 +17,14 @@ export default class FileUploadService { this.state = { files: [], size: 0, - uploading: false, }; } select(selectedFiles) { const additionalFiles = selectedFiles.filter(selectedFile => - this.state.files.every(({ detail, status }) => - (detail.name === selectedFile.name && status !== 'PENDING') || - (detail.name !== selectedFile.name && status === 'PENDING') - ) + this.state.files.every(({ detail }) => detail.name !== selectedFile.name) ).map(detail => ({ - id: Symbol('unique id'), - status: 'PENDING', - detail, + id: Symbol('unique id'), detail, })); const files = [...this.state.files, ...additionalFiles]; @@ -46,34 +40,24 @@ export default class FileUploadService { this.state = { ...this.state, files, size }; } - isUploading() { - return this.state.uploading; - } - - abort() { - this.state.files.forEach(file => file.upload.abort()); - this.state.files = []; - } - - findFileIndex(id) { - return this.state.files.findIndex(file => file.id === id); - } - upload() { const { bucket, folders } = this.$file.paths; const prefix = folders.length ? '' : `${folders.join('/')}/`; const url = `${this.Config.API_URL}/v1/file/create`; - this.state = { - uploading: true, - files: this.state.files.map(file => ({ - ...file, - status: 'UPLOADING', - upload: this.uploadFile(file.id, { - bucket, prefix, file: file.detail, - }, url), - })), - }; + this.state.uploading = true; + this.$transfer.put(this.state.files.map(({ + id, detail, + }) => ({ + id, + bucket, + name: detail.name, + type: 'UPLOAD', + status: 'UPLOADING', + upload: this.uploadFile(id, { + bucket, prefix, file: detail, + }, url), + }))); this.closeDialog(); } @@ -82,47 +66,14 @@ export default class FileUploadService { const upload = this.Upload.upload({ url, data }); upload.then( - res => this.handleSuccess(id, res), - err => this.handleFailure(id, err), - evt => this.handleEvent(id, evt) + res => this.$transfer.handleSuccess(id, res), + err => this.$transfer.handleFailure(id, err), + evt => this.$transfer.handleEvent(id, evt) ); return upload; } - handleEvent(id, evt) { - const i = this.findFileIndex(id); - this.state.files[i].process = evt; - console.log(this.state.files[i].process); - console.log(this.state); - } - - handleSuccess(id, res) { - const i = this.findFileIndex(id); - this.state.files[i].status = 'UPLOADED'; - this.updateUpdateStatus(); - this.$file.getFiles(); - console.log(res); - } - - handleFailure(id, err) { - const i = this.findFileIndex(id); - this.state.files[i].status = 'FAILURE'; - this.updateUpdateStatus(); - console.log(err); - } - - removeUploadFile(id) { - const i = this.findFileIndex(id); - delete this.state.files[i]; - } - - updateUpdateStatus() { - this.state.uploading = this.state.files.every( - file => file.status === 'UPLOADING' - ); - } - createDialog($event) { this.$mdDialog.show({ controller: FileUploadController, @@ -137,5 +88,6 @@ export default class FileUploadService { closeDialog() { this.$mdDialog.cancel(); this.state.size = 0; + this.state.files = []; } } diff --git a/src/components/layout/top-navbar/top-navbar.controller.js b/src/components/layout/top-navbar/top-navbar.controller.js index 742e24a..dd8e829 100644 --- a/src/components/layout/top-navbar/top-navbar.controller.js +++ b/src/components/layout/top-navbar/top-navbar.controller.js @@ -1,8 +1,8 @@ export default class TopNavbarController { /** @ngInject */ - constructor($scope, $translate, $auth, $state, $toast, $mdDialog, $upload, AuthService) { + constructor($scope, $translate, $auth, $state, $toast, $mdDialog, $transfer, AuthService) { Object.assign(this, { - $scope, $translate, $auth, $state, $toast, $mdDialog, $upload, AuthService, + $scope, $translate, $auth, $state, $toast, $mdDialog, $transfer, AuthService, }); this.languages = [ @@ -32,7 +32,7 @@ export default class TopNavbarController { * @return {void} */ signOut($event) { - if (this.$upload.isUploading()) { + if (this.$transfer.isProcessing()) { this.showConfirmMessage($event).then(this.executedSignOut); } else { this.executedSignOut(); @@ -65,7 +65,7 @@ or uploads and leaving now will cancel them.Still leaving?`) */ executedSignOut = () => this.AuthService.signOut() .then(() => { - this.$upload.abort(); + this.$transfer.abort(); this.$auth.logout(); this.$state.go('auth.signin'); this.$toast.show('Sign Out Success!'); diff --git a/src/components/layout/transfer/transfer.controller.js b/src/components/layout/transfer/transfer.controller.js index a3691e5..12b4a1a 100644 --- a/src/components/layout/transfer/transfer.controller.js +++ b/src/components/layout/transfer/transfer.controller.js @@ -1,12 +1,48 @@ export default class TransferController { /** @ngInject */ - constructor($layout) { + constructor($scope, $layout, $transfer) { Object.assign(this, { - $layout, + $layout, $transfer, }); + + $scope.$watch( + () => $transfer.state, + newVal => Object.assign(this, newVal) + , true); + } + + toggleAutoClear() { + this.$transfer.toggleAutoClear(); } close() { this.$layout.toggleTransfer(); } + + md2line(t) { + const status = ['FAILED', 'DELETED', 'PAUSED', 'COMPLETED']; + return status.indexOf(t.status) >= 0; + } + + md3line(t) { + const status = ['UPLOADING', 'RESUMING']; + return status.indexOf(t.status) >= 0; + } + + isUpload(t) { + return t.type === 'UPLOAD'; + } + + isDelete(t) { + return t.type === 'DELETE'; + } + + isUploading(t) { + return t.status === 'UPLOADING'; + } + + showInfo(t) { + const status = ['FAILED', 'PAUSED']; + return status.indexOf(t.status) < 0; + } } diff --git a/src/components/layout/transfer/transfer.html b/src/components/layout/transfer/transfer.html index 0cd3d15..fcc6b48 100644 --- a/src/components/layout/transfer/transfer.html +++ b/src/components/layout/transfer/transfer.html @@ -21,49 +21,51 @@

Automatically clear finished transfers

file_upload delete

-
+

- - + + - - 1.87MB + + / - 2GB + - - + + %

@@ -71,7 +73,7 @@

check_circle -
+
{ + if (transfer.status === 'UPLOADING') { + transfer.upload.abort(); + } + }); + this.state.transfers = []; + } + + remove(id) { + this.state.transfers = this.state.transfers.filter( + (transfer, index) => index !== id + ); + } + + findTransferIndex(id) { + return this.state.transfers.findIndex(transfer => transfer.id === id); + } + + handleEvent(id, { loaded, total }) { + const i = this.findTransferIndex(id); + const precentage = (loaded / total * 100).toFixed(2); + this.state.transfers[i].process = { + loaded, total, precentage, + }; + } + + handleSuccess(id) { + const i = this.findTransferIndex(id); + this.state.transfers[i].status = 'COMPLETED'; + this.$toast.show(`${name} is uploaded successfully!`); + + if (this.state.autoClear) { + this.remove(i); + } + + this.updateProcessStatus(); + this.$file.getFiles(); + } + + handleFailure(id, { statusText }) { + const i = this.findTransferIndex(id); + this.state.transfers[i] = { + ...this.state.transfers[i], + status: 'FAILED', + message: statusText, + }; + this.$toast.show(`${name} is uploaded failure! Error message: ${statusText}`); + this.updateProcessStatus(); + } + + updateProcessStatus() { + this.state.process = this.state.transfers.every( + transfer => transfer.status !== 'UPLOADING' && transfer.status !== 'RESUMING' + ); + } } diff --git a/src/translations/EN.js b/src/translations/EN.js index e0bf219..836bfda 100644 --- a/src/translations/EN.js +++ b/src/translations/EN.js @@ -2,4 +2,16 @@ export default { SETTINGS: { SIGN_OUT: 'Sign Out', }, + TRANSFER: { + TITLE: { + UPLOAD: 'Upload {{ name }} to {{ bucket }}', + DELETE: 'Delete {{ name }} from {{ bucket }}', + }, + STATUS: { + UPLOADING: 'Uploading', + COMPLETED: 'Completed', + DELETE: 'Deleted', + RESUMING: 'Resuming', + }, + }, }; From f5df9e0e1d41e34607f150530af2626f6ce3243c Mon Sep 17 00:00:00 2001 From: jigsawye Date: Thu, 19 May 2016 15:09:13 +0800 Subject: [PATCH 44/65] Fix bugs of toast message --- src/components/layout/transfer/transfer.service.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/layout/transfer/transfer.service.js b/src/components/layout/transfer/transfer.service.js index 01b6eec..c13a977 100644 --- a/src/components/layout/transfer/transfer.service.js +++ b/src/components/layout/transfer/transfer.service.js @@ -61,7 +61,7 @@ export default class TransferService { handleSuccess(id) { const i = this.findTransferIndex(id); this.state.transfers[i].status = 'COMPLETED'; - this.$toast.show(`${name} is uploaded successfully!`); + this.$toast.show(`${this.state.transfers[i].name} is uploaded successfully!`); if (this.state.autoClear) { this.remove(i); @@ -78,7 +78,9 @@ export default class TransferService { status: 'FAILED', message: statusText, }; - this.$toast.show(`${name} is uploaded failure! Error message: ${statusText}`); + this.$toast.show( + `${this.state.transfers[i].name} is uploaded failure! Error message: ${statusText}` + ); this.updateProcessStatus(); } From e6b383d3dc61e19532fe845dd4bb40e3ae901f04 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Thu, 19 May 2016 15:11:42 +0800 Subject: [PATCH 45/65] Add $file dependence to $transfer --- src/components/layout/transfer/transfer.service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/layout/transfer/transfer.service.js b/src/components/layout/transfer/transfer.service.js index c13a977..a8128ae 100644 --- a/src/components/layout/transfer/transfer.service.js +++ b/src/components/layout/transfer/transfer.service.js @@ -1,8 +1,8 @@ export default class TransferService { /** @ngInject */ - constructor($toast) { + constructor($toast, $file) { Object.assign(this, { - $toast, + $toast, $file, }); this.initState(); } From 891ea2dbc15d0ceb0f2b1dca2d80bf1179297fc8 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Thu, 19 May 2016 16:06:59 +0800 Subject: [PATCH 46/65] Update transfers & properties button of action navbar Implement the `none`, `properties`, `transfers` button. * Add `properties` to layout state * Add `openTransfers`, `openProperties` and `closeSidePanels` methods --- .../action-navbar/action-navbar.controller.js | 17 ++++++++----- .../layout/action-navbar/action-navbar.html | 7 +++--- src/components/layout/layout.html | 2 +- src/components/layout/layout.service.js | 24 ++++++++++++++++--- .../layout/transfer/transfer.controller.js | 2 +- 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/components/layout/action-navbar/action-navbar.controller.js b/src/components/layout/action-navbar/action-navbar.controller.js index 4b6940e..eea25e3 100644 --- a/src/components/layout/action-navbar/action-navbar.controller.js +++ b/src/components/layout/action-navbar/action-navbar.controller.js @@ -9,6 +9,11 @@ export default class ActionNavbarController { () => $nav.type, newVal => (this.type = newVal) ); + + this.$scope.$watch( + () => $layout.state, + newVal => Object.assign(this, newVal) + ); } /** @@ -36,16 +41,16 @@ export default class ActionNavbarController { // } - none() { - // + closeSidePanels() { + this.$layout.closeSidePanels(); } - properties() { - // + openProperties() { + this.$layout.openProperties(); } - toggleTransfer() { - this.$layout.toggleTransfer(); + openTransfers() { + this.$layout.openTransfers(); } /** diff --git a/src/components/layout/action-navbar/action-navbar.html b/src/components/layout/action-navbar/action-navbar.html index 0c4bb48..f1d7898 100644 --- a/src/components/layout/action-navbar/action-navbar.html +++ b/src/components/layout/action-navbar/action-navbar.html @@ -107,14 +107,15 @@ None info_outline Properties @@ -122,7 +123,7 @@ Transfers diff --git a/src/components/layout/layout.html b/src/components/layout/layout.html index 7e99a0f..ca51d50 100644 --- a/src/components/layout/layout.html +++ b/src/components/layout/layout.html @@ -11,7 +11,7 @@

diff --git a/src/components/layout/layout.service.js b/src/components/layout/layout.service.js index 9115abb..7e43a64 100644 --- a/src/components/layout/layout.service.js +++ b/src/components/layout/layout.service.js @@ -5,11 +5,29 @@ export default class LayoutService { initState() { this.state = { - transfer: false, + transfers: false, + properties: false, }; } - toggleTransfer() { - this.state.transfer = ! this.state.transfer; + openProperties() { + this.state = { + transfers: false, + properties: true, + }; + } + + openTransfers() { + this.state = { + transfers: true, + properties: false, + }; + } + + closeSidePanels() { + this.state = { + transfers: false, + properties: false, + }; } } diff --git a/src/components/layout/transfer/transfer.controller.js b/src/components/layout/transfer/transfer.controller.js index 12b4a1a..78b2590 100644 --- a/src/components/layout/transfer/transfer.controller.js +++ b/src/components/layout/transfer/transfer.controller.js @@ -16,7 +16,7 @@ export default class TransferController { } close() { - this.$layout.toggleTransfer(); + this.$layout.closeSidePanels(); } md2line(t) { From 3b7d1e4de963b930096559132e24412ed4b9ca4e Mon Sep 17 00:00:00 2001 From: jigsawye Date: Thu, 19 May 2016 16:20:22 +0800 Subject: [PATCH 47/65] Update properties event in actions button of action navbar --- src/components/layout/action-navbar/action-navbar.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/layout/action-navbar/action-navbar.html b/src/components/layout/action-navbar/action-navbar.html index f1d7898..264008d 100644 --- a/src/components/layout/action-navbar/action-navbar.html +++ b/src/components/layout/action-navbar/action-navbar.html @@ -95,7 +95,7 @@ Properties From c8a9133cacd67149d93a804a0c10f60db9a9ebea Mon Sep 17 00:00:00 2001 From: jigsawye Date: Thu, 19 May 2016 17:24:44 +0800 Subject: [PATCH 48/65] Format the file list for handle download Now the files has additional `checked` property for handle user select files, and that will let us know how mnay file are selected, then control th download button is disable or not. * Format the files list * Add a `downloadETag` let us know which file is selected for download --- src/components/file/file.controller.js | 4 +++ src/components/file/file.html | 4 +-- src/components/file/file.service.js | 31 ++++++++++++++++++- .../action-navbar/action-navbar.controller.js | 11 +++++-- .../layout/action-navbar/action-navbar.html | 2 +- 5 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/components/file/file.controller.js b/src/components/file/file.controller.js index ac164d4..936612a 100644 --- a/src/components/file/file.controller.js +++ b/src/components/file/file.controller.js @@ -24,6 +24,10 @@ export default class FileController { this.$file.createFolder($event); } + selectFile(etag) { + this.$file.selectFile(etag); + } + upload($event) { this.$upload.createDialog($event); } diff --git a/src/components/file/file.html b/src/components/file/file.html index 86eb9cd..d25b248 100644 --- a/src/components/file/file.html +++ b/src/components/file/file.html @@ -15,8 +15,8 @@ - - + + insert_drive_file

diff --git a/src/components/file/file.service.js b/src/components/file/file.service.js index 54c721e..0f66a2a 100644 --- a/src/components/file/file.service.js +++ b/src/components/file/file.service.js @@ -16,6 +16,7 @@ export default class FileService { }, lists: { data: [], + downloadName: null, requesting: false, error: false, }, @@ -37,7 +38,7 @@ export default class FileService { .get(endpoint) .then(({ data }) => { this.state.lists.error = false; - this.state.lists.data = data.files || []; + this.state.lists.data = this.formatFilesData(data); }) .catch(() => { this.state.lists.error = true; @@ -46,4 +47,32 @@ export default class FileService { this.state.lists.requesting = false; }); } + + formatFilesData({ files = [] }) { + return files.map(file => ({ + ...file, + checked: false, + })); + } + + selectFile(etag) { + let count = 0; + let downloadName = null; + + this.state.lists.data = this.state.lists.data.map(file => { + let checked = file.checked; + + if (file.ETag === etag) checked = ! checked; + if (checked) count ++; + + return { ...file, checked }; + }); + + if (count === 1) { + const index = this.state.lists.data.findIndex(file => file.checked); + downloadName = this.state.lists.data[index].Name; + } + + this.state.lists.downloadName = downloadName; + } } diff --git a/src/components/layout/action-navbar/action-navbar.controller.js b/src/components/layout/action-navbar/action-navbar.controller.js index eea25e3..d71ebc3 100644 --- a/src/components/layout/action-navbar/action-navbar.controller.js +++ b/src/components/layout/action-navbar/action-navbar.controller.js @@ -2,18 +2,23 @@ export default class ActionNavbarController { /** @ngInject */ constructor($scope, $bucket, $nav, $file, $upload, $layout) { Object.assign(this, { - $scope, $bucket, $file, $upload, $layout, + $bucket, $file, $upload, $layout, }); - this.$scope.$watch( + $scope.$watch( () => $nav.type, newVal => (this.type = newVal) ); - this.$scope.$watch( + $scope.$watch( () => $layout.state, newVal => Object.assign(this, newVal) ); + + $scope.$watch( + () => $file.state.lists.downloadName, + newVal => (this.downloadButton = newVal === null) + ); } /** diff --git a/src/components/layout/action-navbar/action-navbar.html b/src/components/layout/action-navbar/action-navbar.html index 264008d..b94b370 100644 --- a/src/components/layout/action-navbar/action-navbar.html +++ b/src/components/layout/action-navbar/action-navbar.html @@ -44,7 +44,7 @@ Download From dc366516d86d3ce0f9008783cc607d04ce9126fb Mon Sep 17 00:00:00 2001 From: jigsawye Date: Thu, 19 May 2016 20:26:02 +0800 Subject: [PATCH 49/65] Fix bugs when response files is null --- src/components/file/file.service.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/file/file.service.js b/src/components/file/file.service.js index 0f66a2a..637c924 100644 --- a/src/components/file/file.service.js +++ b/src/components/file/file.service.js @@ -38,7 +38,7 @@ export default class FileService { .get(endpoint) .then(({ data }) => { this.state.lists.error = false; - this.state.lists.data = this.formatFilesData(data); + this.state.lists.data = this.formatFilesData(data.files); }) .catch(() => { this.state.lists.error = true; @@ -48,11 +48,12 @@ export default class FileService { }); } - formatFilesData({ files = [] }) { - return files.map(file => ({ - ...file, - checked: false, - })); + formatFilesData(files) { + return (files === null) ? [] : + files.map(file => ({ + ...file, + checked: false, + })); } selectFile(etag) { From 38814965a17b988f05c33e76c5aea1e51f2b4240 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Thu, 19 May 2016 20:38:04 +0800 Subject: [PATCH 50/65] Keep the previous bucket length of breadcrumb Now the bucket length of breadcrumb will keep last length. In previous version will reset to 0 every time. * Add a condition for handle this problem. --- src/components/layout/breadcrumb/breadcrumb.service.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/layout/breadcrumb/breadcrumb.service.js b/src/components/layout/breadcrumb/breadcrumb.service.js index 259fd4a..bc1f5d1 100644 --- a/src/components/layout/breadcrumb/breadcrumb.service.js +++ b/src/components/layout/breadcrumb/breadcrumb.service.js @@ -10,11 +10,13 @@ export default class BreadcrumbService { * @return {void} */ initPaths() { + const len = (typeof this.paths === 'undefined') ? 0 : this.paths[0].len; + this.paths = [{ link: '/bucket', text: 'All Bucket', isBucket: true, - len: 0, + len, }]; } From 34c93906d6a40ef85a7d5a08ffbc8b38414671e6 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Fri, 20 May 2016 01:15:45 +0800 Subject: [PATCH 51/65] Add file download method Add a download method to file service, that method can let user download selected file. * Rename `folders` to `prefix`, and it should be string * Add `download` method to file service. --- src/components/file/file.controller.js | 2 +- src/components/file/file.service.js | 29 ++++++++++++++----- .../action-navbar/action-navbar.controller.js | 2 +- src/config/index.js | 1 + 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/components/file/file.controller.js b/src/components/file/file.controller.js index 936612a..a692911 100644 --- a/src/components/file/file.controller.js +++ b/src/components/file/file.controller.js @@ -13,7 +13,7 @@ export default class FileController { const paths = $stateParams.path.split('/'); const [bucket, ...folders] = paths; - this.$file.setPaths(bucket, folders); + this.$file.setPaths(bucket, folders.join('/')); this.$breadcrumb.updateFilePath(paths); this.$bucket.getBuckets(); diff --git a/src/components/file/file.service.js b/src/components/file/file.service.js index 637c924..5d4b3b7 100644 --- a/src/components/file/file.service.js +++ b/src/components/file/file.service.js @@ -1,8 +1,8 @@ export default class FileService { /** @ngInject */ - constructor($mdDialog, $fetch, $bucket) { + constructor($mdDialog, $fetch, $bucket, Config) { Object.assign(this, { - $mdDialog, $fetch, $bucket, + $mdDialog, $fetch, $bucket, Config, }); this.initState(); @@ -12,7 +12,7 @@ export default class FileService { this.state = { paths: { bucket: '', - folders: [], + prefix: '', }, lists: { data: [], @@ -23,13 +23,13 @@ export default class FileService { }; } - setPaths(bucket, folders) { - this.paths = { bucket, folders }; + setPaths(bucket, prefix) { + this.state.paths = { bucket, prefix }; } getFiles() { - const { bucket, folders } = this.paths; - const endpoint = `/v1/file/list/${bucket}?prefix=${folders.join('/')}`; + const { bucket, prefix } = this.state.paths; + const endpoint = `/v1/file/list/${bucket}?prefix=${prefix}`; this.state.lists.requesting = true; this.state.lists.data = []; @@ -71,9 +71,22 @@ export default class FileService { if (count === 1) { const index = this.state.lists.data.findIndex(file => file.checked); - downloadName = this.state.lists.data[index].Name; + downloadName = this.state.lists.data[index].Key; } this.state.lists.downloadName = downloadName; } + + download() { + const { bucket, prefix } = this.state.paths; + const { downloadName } = this.state.lists; + const endpoint = `/v1/file/get/${bucket}/${prefix}${downloadName}`; + + this.$fetch.get(endpoint) + .then(({ data }) => { + const { uri } = data; + window.open(`${this.Config.BASE_URL}${uri}`); + }) + .catch(err => console.log(err)); + } } diff --git a/src/components/layout/action-navbar/action-navbar.controller.js b/src/components/layout/action-navbar/action-navbar.controller.js index d71ebc3..1837c77 100644 --- a/src/components/layout/action-navbar/action-navbar.controller.js +++ b/src/components/layout/action-navbar/action-navbar.controller.js @@ -35,7 +35,7 @@ export default class ActionNavbarController { } download() { - // + this.$file.download(); } upload($event) { diff --git a/src/config/index.js b/src/config/index.js index 3e4cd5f..7759af0 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -13,6 +13,7 @@ const Config = module('app.config', []) .config(material) .config(http) .constant('Config', { + BASE_URL: process.env.SERVER_HOST, API_URL: `${process.env.SERVER_HOST}/api`, }) .run(authenticateGuard); From 61f28488c91fa7009c60a1e0f3af4bcb9f5186bb Mon Sep 17 00:00:00 2001 From: jigsawye Date: Fri, 20 May 2016 01:25:49 +0800 Subject: [PATCH 52/65] Fix bugs of paths property in upload service --- src/components/file/upload/upload.servce.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/file/upload/upload.servce.js b/src/components/file/upload/upload.servce.js index 15ef8a6..23628c3 100644 --- a/src/components/file/upload/upload.servce.js +++ b/src/components/file/upload/upload.servce.js @@ -41,8 +41,7 @@ export default class FileUploadService { } upload() { - const { bucket, folders } = this.$file.paths; - const prefix = folders.length ? '' : `${folders.join('/')}/`; + const { bucket, prefix } = this.$file.state.paths; const url = `${this.Config.API_URL}/v1/file/create`; this.state.uploading = true; @@ -55,7 +54,9 @@ export default class FileUploadService { type: 'UPLOAD', status: 'UPLOADING', upload: this.uploadFile(id, { - bucket, prefix, file: detail, + bucket, + file: detail, + prefix: (prefix === '' ? '' : `${prefix}/`), }, url), }))); From a14c4ad650f4b6df01311041de0a77464d8547a8 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Fri, 20 May 2016 11:55:21 +0800 Subject: [PATCH 53/65] Rename folders to prefix and make it as string --- src/components/file/file.controller.js | 3 ++- src/components/file/upload/upload.servce.js | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/file/file.controller.js b/src/components/file/file.controller.js index a692911..fbf66ce 100644 --- a/src/components/file/file.controller.js +++ b/src/components/file/file.controller.js @@ -12,8 +12,9 @@ export default class FileController { const paths = $stateParams.path.split('/'); const [bucket, ...folders] = paths; + const prefix = (folders.length) ? `${folders.join('/')}/` : ''; - this.$file.setPaths(bucket, folders.join('/')); + this.$file.setPaths(bucket, prefix); this.$breadcrumb.updateFilePath(paths); this.$bucket.getBuckets(); diff --git a/src/components/file/upload/upload.servce.js b/src/components/file/upload/upload.servce.js index 23628c3..4ed10bb 100644 --- a/src/components/file/upload/upload.servce.js +++ b/src/components/file/upload/upload.servce.js @@ -54,9 +54,7 @@ export default class FileUploadService { type: 'UPLOAD', status: 'UPLOADING', upload: this.uploadFile(id, { - bucket, - file: detail, - prefix: (prefix === '' ? '' : `${prefix}/`), + bucket, prefix, file: detail, }, url), }))); From b853db76c3b13c7140c2434b2a6433d15eb62a53 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Fri, 20 May 2016 13:25:10 +0800 Subject: [PATCH 54/65] Use html5 download api to download file --- src/components/file/file.service.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/file/file.service.js b/src/components/file/file.service.js index 5d4b3b7..9fe3824 100644 --- a/src/components/file/file.service.js +++ b/src/components/file/file.service.js @@ -85,7 +85,10 @@ export default class FileService { this.$fetch.get(endpoint) .then(({ data }) => { const { uri } = data; - window.open(`${this.Config.BASE_URL}${uri}`); + const a = document.createElement('a'); + a.download = downloadName; + a.href = uri; + a.click(); }) .catch(err => console.log(err)); } From cd3a9b11a79cb6e9cbff86be04824d187f7a34bc Mon Sep 17 00:00:00 2001 From: jigsawye Date: Mon, 23 May 2016 13:04:59 +0800 Subject: [PATCH 55/65] Refactor file service Move the download action to downloadFile method, and fix the file url issue. * Add downloadFile() to file service * Fix issue of download file url --- src/components/file/file.service.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/file/file.service.js b/src/components/file/file.service.js index 9fe3824..da137b5 100644 --- a/src/components/file/file.service.js +++ b/src/components/file/file.service.js @@ -77,6 +77,14 @@ export default class FileService { this.state.lists.downloadName = downloadName; } + downloadFile(uri, fileName) { + const a = document.createElement('a'); + a.download = fileName; + a.href = `${this.Config.BASE_URL}${uri}`; + console.log(a.href) + a.click(); + } + download() { const { bucket, prefix } = this.state.paths; const { downloadName } = this.state.lists; @@ -85,10 +93,7 @@ export default class FileService { this.$fetch.get(endpoint) .then(({ data }) => { const { uri } = data; - const a = document.createElement('a'); - a.download = downloadName; - a.href = uri; - a.click(); + this.downloadFile(uri, downloadName); }) .catch(err => console.log(err)); } From a3a9bdeb2d533304717091bcae2c3eccb83f42e7 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Mon, 23 May 2016 13:18:27 +0800 Subject: [PATCH 56/65] Add error handler when the download file not exist --- src/components/file/file.service.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/file/file.service.js b/src/components/file/file.service.js index da137b5..9684148 100644 --- a/src/components/file/file.service.js +++ b/src/components/file/file.service.js @@ -1,8 +1,8 @@ export default class FileService { /** @ngInject */ - constructor($mdDialog, $fetch, $bucket, Config) { + constructor($mdDialog, $fetch, $bucket, $toast, Config) { Object.assign(this, { - $mdDialog, $fetch, $bucket, Config, + $mdDialog, $fetch, $bucket, $toast, Config, }); this.initState(); @@ -95,6 +95,9 @@ export default class FileService { const { uri } = data; this.downloadFile(uri, downloadName); }) - .catch(err => console.log(err)); + .catch(({ data }) => { + this.$toast.show(`The ${downloadName} doesn't exist, please try again!`); + this.getFiles(); + }); } } From 24dcf80cfd99ac722f64a3b14539a51347ec3494 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Fri, 20 May 2016 12:07:42 +0800 Subject: [PATCH 57/65] Fix bug when file size small than 1024 bytes --- src/filters/filesize.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/filters/filesize.js b/src/filters/filesize.js index b95cf6c..070dfe9 100644 --- a/src/filters/filesize.js +++ b/src/filters/filesize.js @@ -24,5 +24,7 @@ export default () => bytes => { unit ++; } - return `${bytes.toFixed(2)} ${units[unit]}`; + const result = (unit === 0) ? +bytes : bytes.toFixed(2); + + return `${result} ${units[unit]}`; }; From 67bed8a2c296eb01baf6c36e4936e1dcae799ae2 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Fri, 20 May 2016 13:19:49 +0800 Subject: [PATCH 58/65] Fix bug when action navbar switch --- .../action-navbar/action-navbar.controller.js | 10 ++++----- .../layout/action-navbar/action-navbar.html | 21 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/components/layout/action-navbar/action-navbar.controller.js b/src/components/layout/action-navbar/action-navbar.controller.js index 1837c77..194daab 100644 --- a/src/components/layout/action-navbar/action-navbar.controller.js +++ b/src/components/layout/action-navbar/action-navbar.controller.js @@ -38,10 +38,6 @@ export default class ActionNavbarController { this.$file.download(); } - upload($event) { - this.$upload.createDialog($event); - } - delete() { // } @@ -66,12 +62,16 @@ export default class ActionNavbarController { */ create($event) { if (this.isFile()) { - // + this.$upload.createDialog($event); } else { this.$bucket.createDialog($event); } } + createFolder($event) { + // handle the create folder event + } + /** * Refresh the list by `this.type` * diff --git a/src/components/layout/action-navbar/action-navbar.html b/src/components/layout/action-navbar/action-navbar.html index b94b370..e057188 100644 --- a/src/components/layout/action-navbar/action-navbar.html +++ b/src/components/layout/action-navbar/action-navbar.html @@ -2,22 +2,21 @@

- file_upload - Upload + add + file_upload + Create Bucket + Upload - add - create_new_folder - Create Bucket - Create Folder + create_new_folder + Create Folder From 1943d0e9be56c1ba5c3518eb708c6f4aaaf3fb77 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Mon, 23 May 2016 15:30:38 +0800 Subject: [PATCH 59/65] Fix menu items of actions button --- .../layout/action-navbar/action-navbar.html | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/layout/action-navbar/action-navbar.html b/src/components/layout/action-navbar/action-navbar.html index e057188..6544946 100644 --- a/src/components/layout/action-navbar/action-navbar.html +++ b/src/components/layout/action-navbar/action-navbar.html @@ -49,22 +49,22 @@ - + - Upload + Create Bucket + Upload - + - Create Bucket - Create Folder + Create Folder From 6598bb51be3c430ad656e4c442a88a28b0421af5 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Mon, 23 May 2016 17:32:25 +0800 Subject: [PATCH 60/65] Initial the folder component --- src/components/file/file.controller.js | 6 +- src/components/file/file.js | 2 + .../file/folder/folder.controller.js | 14 +++++ src/components/file/folder/folder.html | 60 +++++++++++++++++++ src/components/file/folder/folder.service.js | 45 ++++++++++++++ .../action-navbar/action-navbar.controller.js | 6 +- 6 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 src/components/file/folder/folder.controller.js create mode 100644 src/components/file/folder/folder.html create mode 100644 src/components/file/folder/folder.service.js diff --git a/src/components/file/file.controller.js b/src/components/file/file.controller.js index fbf66ce..fda6d66 100644 --- a/src/components/file/file.controller.js +++ b/src/components/file/file.controller.js @@ -1,8 +1,8 @@ export default class FileController { /** @ngInject */ - constructor($scope, $stateParams, $file, $bucket, $breadcrumb, $upload) { + constructor($scope, $stateParams, $file, $bucket, $breadcrumb, $upload, $folder) { Object.assign(this, { - $file, $upload, $bucket, $breadcrumb, + $file, $upload, $bucket, $breadcrumb, $folder, }); $scope.$watch( @@ -22,7 +22,7 @@ export default class FileController { } createFolder($event) { - this.$file.createFolder($event); + this.$folder.createDialog($event); } selectFile(etag) { diff --git a/src/components/file/file.js b/src/components/file/file.js index 469bb64..edad693 100644 --- a/src/components/file/file.js +++ b/src/components/file/file.js @@ -5,6 +5,7 @@ import FileController from './file.controller'; import FileService from './file.service'; import FileTemplate from './file.html'; import UploadService from './upload/upload.servce'; +import FolderService from './folder/folder.service'; import './file.css'; /** @ngInject */ @@ -24,6 +25,7 @@ const File = module('file', [ ]) .service('$file', FileService) .service('$upload', UploadService) +.service('$folder', FolderService) .config(route); export default File.name; diff --git a/src/components/file/folder/folder.controller.js b/src/components/file/folder/folder.controller.js new file mode 100644 index 0000000..cf9c1d8 --- /dev/null +++ b/src/components/file/folder/folder.controller.js @@ -0,0 +1,14 @@ +export default class FolderCreateController { + /** @ngInject */ + constructor($folder) { + this.$folder = $folder; + } + + create() { + this.$folder.createFolder(this.folder); + } + + cancel() { + this.$folder.closeDialog(); + } +} diff --git a/src/components/file/folder/folder.html b/src/components/file/folder/folder.html new file mode 100644 index 0000000..b35b785 --- /dev/null +++ b/src/components/file/folder/folder.html @@ -0,0 +1,60 @@ + +
+ +
+

Create Folder

+ + + + clear + +
+
+ +
+
+

+ Create a folder. +

+
+ + + + + + That folder already exists! Please select a different name and try again. + + +
+
+
+
+
+
+ + + + Cancel + + + + Create + + + +
+
diff --git a/src/components/file/folder/folder.service.js b/src/components/file/folder/folder.service.js new file mode 100644 index 0000000..c896ce0 --- /dev/null +++ b/src/components/file/folder/folder.service.js @@ -0,0 +1,45 @@ +import { element } from 'angular'; +import FolderCreateController from './folder.controller'; +import FolderCreateTemplate from './folder.html'; + +export default class FolderCreateService { + /** @ngInject */ + constructor($mdDialog, $fetch, $file) { + Object.assign(this, { + $mdDialog, $fetch, $file, + }); + } + + initState() { + this.state = { + duplicated: false, + }; + } + + createFolder(folder) { + const { bucket } = this.$file.state.paths; + const prefix = `${folder}/`; + + this.$fetch.post('/v1/file/create', { + bucket, prefix, + }) + .then(res => console.log(res)) + .catch(err => console.log(err)); + } + + createDialog($event) { + this.$mdDialog.show({ + controller: FolderCreateController, + controllerAs: 'create', + template: FolderCreateTemplate, + parent: element(document.body), + targetEvent: $event, + clickOutsideToClose: true, + }); + } + + closeDialog() { + this.$mdDialog.cancel(); + this.initState(); + } +} diff --git a/src/components/layout/action-navbar/action-navbar.controller.js b/src/components/layout/action-navbar/action-navbar.controller.js index 194daab..cee0aee 100644 --- a/src/components/layout/action-navbar/action-navbar.controller.js +++ b/src/components/layout/action-navbar/action-navbar.controller.js @@ -1,8 +1,8 @@ export default class ActionNavbarController { /** @ngInject */ - constructor($scope, $bucket, $nav, $file, $upload, $layout) { + constructor($scope, $bucket, $nav, $file, $upload, $layout, $folder) { Object.assign(this, { - $bucket, $file, $upload, $layout, + $bucket, $file, $upload, $layout, $folder }); $scope.$watch( @@ -69,7 +69,7 @@ export default class ActionNavbarController { } createFolder($event) { - // handle the create folder event + this.$folder.createDialog($event); } /** From a2aa487b33bf7871f58d53f3211f11f0d9e99051 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Mon, 23 May 2016 21:35:42 +0800 Subject: [PATCH 61/65] Add createFolder to folder service Add API call to folder service. * Add createFolder to create folder * Remove title of folder.html --- src/components/file/folder/folder.html | 6 ------ src/components/file/folder/folder.service.js | 20 ++++++++++++-------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/components/file/folder/folder.html b/src/components/file/folder/folder.html index b35b785..2e951f3 100644 --- a/src/components/file/folder/folder.html +++ b/src/components/file/folder/folder.html @@ -15,12 +15,6 @@

Create Folder

-
-

- Create a folder. -

-
- console.log(res)) - .catch(err => console.log(err)); + .then(res => { + this.$file.getFiles(); + this.$toast.show(`Bucket ${folder} has created!`); + this.closeDialog() + }) + .catch(() => this.state.duplicated = true); } createDialog($event) { From 07849474d6a4c3cc8f8a0e4ba8f9ca7c34b23f60 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Mon, 23 May 2016 21:37:40 +0800 Subject: [PATCH 62/65] Add folder support to file service The privous version file list are not supported folder, this commit just refactor to support it. * Add method to sort.js for supported folder sort * Add icon.js to get icon of file type * Add click event to file list item * Add some method to filter files --- src/components/file/file.controller.js | 12 ++++++-- src/components/file/file.html | 11 ++++++-- src/components/file/file.service.js | 39 +++++++++++++++++++++++--- src/utils/icon.js | 8 ++++++ src/utils/sort.js | 11 ++++++++ 5 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 src/utils/icon.js diff --git a/src/components/file/file.controller.js b/src/components/file/file.controller.js index fda6d66..ed0687f 100644 --- a/src/components/file/file.controller.js +++ b/src/components/file/file.controller.js @@ -1,8 +1,8 @@ export default class FileController { /** @ngInject */ - constructor($scope, $stateParams, $file, $bucket, $breadcrumb, $upload, $folder) { + constructor($scope, $location, $stateParams, $file, $bucket, $breadcrumb, $upload, $folder) { Object.assign(this, { - $file, $upload, $bucket, $breadcrumb, $folder, + $location, $file, $upload, $bucket, $breadcrumb, $folder, }); $scope.$watch( @@ -25,6 +25,14 @@ export default class FileController { this.$folder.createDialog($event); } + clickFile({ isFolder, display }) { + if (isFolder) { + const currentPath = this.$file.getFullPaths(); + const path = `/bucket/${currentPath}${display}`; + this.$location.path(path); + } + } + selectFile(etag) { this.$file.selectFile(etag); } diff --git a/src/components/file/file.html b/src/components/file/file.html index d25b248..20934e5 100644 --- a/src/components/file/file.html +++ b/src/components/file/file.html @@ -15,11 +15,16 @@ - + - insert_drive_file +

- +

{ this.state.lists.error = false; - this.state.lists.data = this.formatFilesData(data.files); + this.state.lists.data = sortFiles(this.formatFilesData(data.files)); }) .catch(() => { this.state.lists.error = true; @@ -48,10 +56,33 @@ export default class FileService { }); } + getIcon(name) { + return getIconString(name); + } + + formatFileType(name) { + const isFolder = (name.endsWith('/')) + const removeSlash = isFolder ? name.slice(0, -1) : name; + const display = removeSlash.replace(this.state.paths.prefix, ''); + return { isFolder, display }; + } + formatFilesData(files) { - return (files === null) ? [] : - files.map(file => ({ + const { prefix } = this.state.paths; + const baseLen = prefix.split('/').length; + + return (! files) ? [] : + files.filter(({ Key }) => { + const { length } = Key.split('/'); + return ( + length === baseLen + || length === baseLen + 1 + && Key.endsWith('/') + ) && Key !== prefix; + }).map(file => ({ ...file, + ...this.formatFileType(file.Key), + icon: this.getIcon(file.Key), checked: false, })); } @@ -71,7 +102,7 @@ export default class FileService { if (count === 1) { const index = this.state.lists.data.findIndex(file => file.checked); - downloadName = this.state.lists.data[index].Key; + downloadName = this.state.lists.data[index].display; } this.state.lists.downloadName = downloadName; diff --git a/src/utils/icon.js b/src/utils/icon.js new file mode 100644 index 0000000..1204e72 --- /dev/null +++ b/src/utils/icon.js @@ -0,0 +1,8 @@ +const icons = [ + ['/', 'folder'], +]; + +export default name => { + const index = icons.findIndex(icon => name.endsWith(icon[0])); + return (index === -1) ? 'insert_drive_file' : icons[index][1]; +}; diff --git a/src/utils/sort.js b/src/utils/sort.js index 20a07d5..5622208 100644 --- a/src/utils/sort.js +++ b/src/utils/sort.js @@ -10,9 +10,20 @@ import natural from 'javascript-natural-sort'; */ const sortKey = key => (x, y) => natural(x[key], y[key]); +const sortByDisplay = sortKey('display'); + /** * Natural sort by Name. * * @return {Function} */ export const sortByName = sortKey('Name'); + +export const sortFiles = xs => { + const folders = xs.filter(x => x.isFolder); + const files = xs.filter(x => ! x.isFolder); + return [ + ...folders.sort(sortByDisplay), + ...files.sort(sortByDisplay), + ]; +}; From 82eff42534d097fa64fe0e4b93351f7cb5268928 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 25 May 2016 14:31:36 +0800 Subject: [PATCH 63/65] Fix issue etag will be same string The folder's etag has same string because the body is empty string when create folder. This commit just use Key instead of ETag when select file/folder. * Use Key instead of ETag. --- src/components/file/file.controller.js | 4 ++-- src/components/file/file.html | 2 +- src/components/file/file.service.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/file/file.controller.js b/src/components/file/file.controller.js index fbf66ce..5e49cd7 100644 --- a/src/components/file/file.controller.js +++ b/src/components/file/file.controller.js @@ -25,8 +25,8 @@ export default class FileController { this.$file.createFolder($event); } - selectFile(etag) { - this.$file.selectFile(etag); + selectFile(name) { + this.$file.selectFile(name); } upload($event) { diff --git a/src/components/file/file.html b/src/components/file/file.html index d25b248..f30d971 100644 --- a/src/components/file/file.html +++ b/src/components/file/file.html @@ -16,7 +16,7 @@ - + insert_drive_file

diff --git a/src/components/file/file.service.js b/src/components/file/file.service.js index 9684148..5d5ec0f 100644 --- a/src/components/file/file.service.js +++ b/src/components/file/file.service.js @@ -56,14 +56,14 @@ export default class FileService { })); } - selectFile(etag) { + selectFile(name) { let count = 0; let downloadName = null; this.state.lists.data = this.state.lists.data.map(file => { let checked = file.checked; - if (file.ETag === etag) checked = ! checked; + if (file.Key === name) checked = ! checked; if (checked) count ++; return { ...file, checked }; From 89fd0a4885ab71d0dbe0eee4e33cb9109495c6ad Mon Sep 17 00:00:00 2001 From: jigsawye Date: Wed, 25 May 2016 14:54:21 +0800 Subject: [PATCH 64/65] The select item should be file that is downloadable --- src/components/file/file.service.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/file/file.service.js b/src/components/file/file.service.js index 565a54c..069a183 100644 --- a/src/components/file/file.service.js +++ b/src/components/file/file.service.js @@ -102,7 +102,10 @@ export default class FileService { if (count === 1) { const index = this.state.lists.data.findIndex(file => file.checked); - downloadName = this.state.lists.data[index].display; + const { isFolder, display } = this.state.lists.data[index]; + if (! isFolder) { + downloadName = display; + } } this.state.lists.downloadName = downloadName; From 0180272d112e94b55cbdf40c0aa3673865476828 Mon Sep 17 00:00:00 2001 From: chaoen Date: Thu, 26 May 2016 10:15:13 +0000 Subject: [PATCH 65/65] Add create folder component unit test Test all function in folder controller. Test all function in folder service. --- src/components/file/folder/folder.spec.js | 178 ++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 src/components/file/folder/folder.spec.js diff --git a/src/components/file/folder/folder.spec.js b/src/components/file/folder/folder.spec.js new file mode 100644 index 0000000..ef2201e --- /dev/null +++ b/src/components/file/folder/folder.spec.js @@ -0,0 +1,178 @@ +import app from '../../../index.js'; +import folderCtrl from './folder.controller.js'; +import folderServ from './folder.service.js'; +import folderHtml from './folder.html'; + + +describe('Create Folder Unit Test', () => { + let $rootScope; + let makeService; + let makeDeferred; + let makeController; + let makeTemplate; + let $mdDialog; + let $fetch; + let $file; + let $toast; + let $compile; + beforeEach(angular.mock.module('app')); + + beforeEach(inject(($q, _$rootScope_, _$mdDialog_, _$compile_, _$fetch_, _$file_, _$toast_) => { + $rootScope = _$rootScope_; + $mdDialog = _$mdDialog_; + $fetch = _$fetch_; + $file = _$file_; + $toast = _$toast_; + $compile = _$compile_; + + makeTemplate = angular.element(folderHtml); + + $compile(makeTemplate)($rootScope); + + makeDeferred = () => { + return $q.defer(); + }; + + makeController = (folderServ) => { + return new folderCtrl(folderServ); + }; + + makeService = () => { + return new folderServ($mdDialog, $fetch, $file, $toast); + }; + })); + describe('when init controller', () => { + let controller; + beforeEach(() => { + controller = makeController(makeService()); + }); + it('should declare controller.$folder', () => { + expect(controller.$folder).not.to.be.null; + }); + }); + describe('when trigger create() in controller', () => { + let mockCreate; + let controller; + let service; + let form; + beforeEach(() => { + service = makeService(); + service.createFolder = () => {}; + controller = makeController(service); + mockCreate = sinon.spy(service, 'createFolder'); + form = $rootScope.create.form; + form.folder.$setViewValue('FolderName'); + controller.create(); + }); + it('should invoke createFolder and call by folder name', () => { + expect(mockCreate.called).to.eq(true); + expect(mockCreate).to.have.been.calledWith(form.folder.$ViewValue); + }); + }); + describe('when trigger cancel() in controller', () => { + let mockClose; + let controller; + let service; + beforeEach(() => { + service = makeService(); + service.closeDialog = () => {}; + controller = makeController(service); + mockClose = sinon.spy(service, 'closeDialog'); + controller.cancel(); + }); + it('should invoke closeDialog in service', () => { + expect(mockClose.called).to.eq(true); + }); + }); + describe('when trigger initState() in service', () => { + let service; + beforeEach(() => { + service = makeService(); + service.initState(); + }); + it('should declare state.duplicated', () => { + expect(service.state.duplicated).to.eq(false); + }); + }); + describe('when trigger createFolder in service and success', () => { + let deferred; + let service; + let mockGetFiles; + let mockToast; + let mockClose; + let mockFetch; + let message; + beforeEach(() => { + $file.getFiles = () => {}; + $toast.show = () => {}; + mockFetch = sinon.mock($fetch); + service = makeService(); + service.closeDialog = () => {}; + deferred = makeDeferred(); + mockFetch.expects('post').returns(deferred.promise); + deferred.resolve(); + mockGetFiles = sinon.spy($file, 'getFiles'); + mockToast = sinon.spy($toast, 'show'); + mockClose = sinon.spy(service, 'closeDialog'); + message = "Bucket FolderName has created!"; + service.createFolder('FolderName'); + $rootScope.$digest(); + }); + it('should invoke getFiles in fileService', () => { + expect(mockGetFiles.called).to.eq(true); + }); + it('should invoke $toast.show and call by message', () => { + expect(mockToast).to.have.been.calledWith(message); + }); + it('should invoke closeDialog()', () => { + expect(mockClose.called).to.eq(true); + }); + }); + describe('when trigger createFolder in service and fail', () => { + let deferred; + let service; + let mockFetch; + beforeEach(() => { + mockFetch = sinon.mock($fetch); + service = makeService(); + service.state = {}; + deferred = makeDeferred(); + mockFetch.expects('post').returns(deferred.promise); + deferred.reject(); + service.createFolder('FolderName'); + $rootScope.$digest(); + }); + it('should invoke getFiles in fileService', () => { + expect(service.state.duplicated).to.eq(true); + }); + }); + describe('when trigger createDialog in service', () => { + let service; + let mockDialog; + beforeEach(() => { + mockDialog = sinon.spy($mdDialog, 'show'); + service = makeService(); + service.createDialog(); + }); + it('should invoke mdDialog.show', () => { + expect(mockDialog.called).to.eq(true); + }); + }); + describe('when trigger closeDialog in service', () => { + let service; + let mockDialog; + let mockInit; + beforeEach(() => { + service = makeService(); + mockDialog = sinon.spy($mdDialog, 'cancel'); + mockInit = sinon.spy(service, 'initState'); + service.closeDialog(); + }); + it('should invoke mdDialog.cancel', () => { + expect(mockDialog.called).to.eq(true); + }); + it('should invoke initState', () => { + expect(mockInit.called).to.eq(true); + }) + }); +}); \ No newline at end of file