diff --git a/package.json b/package.json
index 68f8bf6..bfbb6fb 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",
@@ -49,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": {
diff --git a/src/components/bucket/bucket.controller.js b/src/components/bucket/bucket.controller.js
index 766be3c..c3fb2c7 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, $breadcrumb) {
Object.assign(this, {
- $scope, $bucket,
+ $scope, $bucket, $state,
});
this.$scope.$watch(
@@ -10,10 +10,15 @@ export default class BucketController {
newVal => Object.assign(this, newVal)
, true);
+ $breadcrumb.initPaths();
this.$bucket.getBuckets();
}
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
|
-
-
+ |
|
diff --git a/src/components/bucket/bucket.js b/src/components/bucket/bucket.js
index 1c31f17..1721a52 100644
--- a/src/components/bucket/bucket.js
+++ b/src/components/bucket/bucket.js
@@ -14,9 +14,6 @@ const route = $stateProvider => {
controllerAs: 'bucket',
template: BucketTemplate,
onEnter: $nav => $nav.setTypeToBucket(),
- ncyBreadcrumb: {
- label: 'All Buckets ( {{ bucket.data.length }} )',
- },
});
};
diff --git a/src/components/bucket/bucket.service.js b/src/components/bucket/bucket.service.js
index 6fee2d9..2720db6 100644
--- a/src/components/bucket/bucket.service.js
+++ b/src/components/bucket/bucket.service.js
@@ -1,13 +1,13 @@
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';
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();
@@ -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,13 +85,14 @@ 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;
})
.finally(() => {
this.state.lists.requesting = false;
+ this.$breadcrumb.updateBucketPath(this.state.lists.data.length);
});
}
@@ -140,7 +127,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/components/file/file.controller.js b/src/components/file/file.controller.js
new file mode 100644
index 0000000..ac164d4
--- /dev/null
+++ b/src/components/file/file.controller.js
@@ -0,0 +1,34 @@
+export default class FileController {
+ /** @ngInject */
+ constructor($scope, $stateParams, $file, $bucket, $breadcrumb, $upload) {
+ Object.assign(this, {
+ $file, $upload, $bucket, $breadcrumb,
+ });
+
+ $scope.$watch(
+ () => $file.state.lists,
+ newVal => Object.assign(this, newVal)
+ , true);
+
+ const paths = $stateParams.path.split('/');
+ const [bucket, ...folders] = paths;
+
+ this.$file.setPaths(bucket, folders);
+ this.$breadcrumb.updateFilePath(paths);
+
+ 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.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..86eb9cd
--- /dev/null
+++ b/src/components/file/file.html
@@ -0,0 +1,99 @@
+
+
+
+
+
+ |
+ Name |
+ Storage Class |
+ Size |
+ Last Modified |
+
+
+
+
+
+
+ insert_drive_file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
This bucket is empty
+
You can do the following actions
+
+
+
+ file_upload
+ Upload File
+
+
+ or
+
+
+ create_new_folder
+ Create Folder
+
+
+
+
+
+
Oops, your connection seems off...
+
Don't worry. You can refresh to try again.
+
+
+ refresh
+
+
+
diff --git a/src/components/file/file.js b/src/components/file/file.js
new file mode 100644
index 0000000..469bb64
--- /dev/null
+++ b/src/components/file/file.js
@@ -0,0 +1,29 @@
+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';
+import UploadService from './upload/upload.servce';
+import './file.css';
+
+/** @ngInject */
+const route = $stateProvider => {
+ $stateProvider.state('file', {
+ url: '/bucket/*path',
+ parent: 'root',
+ controller: FileController,
+ controllerAs: 'file',
+ template: FileTemplate,
+ onEnter: $nav => $nav.setTypeToFile(),
+ });
+};
+
+const File = module('file', [
+ router,
+])
+.service('$file', FileService)
+.service('$upload', UploadService)
+.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..54c721e
--- /dev/null
+++ b/src/components/file/file.service.js
@@ -0,0 +1,49 @@
+export default class FileService {
+ /** @ngInject */
+ constructor($mdDialog, $fetch, $bucket) {
+ Object.assign(this, {
+ $mdDialog, $fetch, $bucket,
+ });
+
+ this.initState();
+ }
+
+ initState() {
+ this.state = {
+ paths: {
+ bucket: '',
+ folders: [],
+ },
+ lists: {
+ data: [],
+ requesting: false,
+ error: false,
+ },
+ };
+ }
+
+ 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(endpoint)
+ .then(({ data }) => {
+ this.state.lists.error = false;
+ this.state.lists.data = data.files || [];
+ })
+ .catch(() => {
+ this.state.lists.error = true;
+ })
+ .finally(() => {
+ this.state.lists.requesting = false;
+ });
+ }
+}
diff --git a/src/components/file/file.spec.js b/src/components/file/file.spec.js
new file mode 100644
index 0000000..0d0d51f
--- /dev/null
+++ b/src/components/file/file.spec.js
@@ -0,0 +1,213 @@
+import app from './../../index.js';
+import fileCtrl from './file.controller';
+import fileService from './file.service';
+import bucketService from '../bucket/bucket.service';
+import uploadService from './upload/upload.servce';
+
+describe('File Unit Test', function() {
+ let $rootScope;
+ let makeService;
+ let BucService;
+ let makeUpService;
+ let makeDeferred;
+ let makeController;
+ let $fetch;
+ let $toast;
+ let $mdDialog;
+ let $breadcrumb;
+ let Upload;
+ let Config = { API_URL: '0.0.0.0:0000' };
+ let $stateParams = {
+ path: 'BucketName/FolderA/FolderB'
+ };
+
+ beforeEach(angular.mock.module('app'));
+
+ beforeEach(inject(($q, _Upload_, _$rootScope_, _$mdDialog_, _$toast_, _$breadcrumb_, _$fetch_) => {
+ $rootScope = _$rootScope_;
+
+ $toast = _$toast_;
+
+ $fetch = _$fetch_;
+
+ $mdDialog = _$mdDialog_;
+
+ Upload = _Upload_;
+
+ $breadcrumb = _$breadcrumb_;
+
+ makeService = () => {
+ return new fileService($mdDialog, $fetch, BucService);
+ };
+
+ makeUpService = (service) => {
+ return new uploadService(Config, Upload, $mdDialog, service, $toast);
+ };
+
+ makeDeferred = () => {
+ return $q.defer();
+ };
+
+ BucService = new bucketService($fetch, $toast, $mdDialog, $breadcrumb);
+
+
+ makeController = (service, upService) => {
+ return new fileCtrl($rootScope, $stateParams, service, BucService, $breadcrumb, upService);
+ };
+ }));
+ describe('when init service', function() {
+ let service;
+
+ beforeEach(function() {
+ service = makeService();
+ });
+
+ it('should declare paths', function() {
+ expect(service.state.paths.bucket).to.eq('');
+ expect(service.state.paths.folders).to.be.empty;
+ });
+ it('should declare lists', function() {
+ expect(service.state.lists.data).to.be.empty;
+ expect(service.state.lists.requesting).to.eq(false);
+ expect(service.state.lists.error).to.eq(false);
+ });
+ });
+
+ describe('when setPaths in service', function() {
+ let service;
+
+ beforeEach(function() {
+ service = makeService();
+ service.setPaths('BucketName', ["FolderA", "FolderB"]);
+ });
+ it('should put value to service.paths', function() {
+ expect(service.paths).to.have.property('bucket', 'BucketName');
+ expect(service.paths.folders).to.have.property([0], 'FolderA');
+ expect(service.paths.folders).to.have.property([1], 'FolderB');
+ });
+ });
+
+ describe('when getFiles in service and response success', function() {
+ let service;
+ let testMock;
+ let fetchMock;
+ let deferred;
+ let res;
+ beforeEach(function() {
+ service = makeService();
+ deferred = makeDeferred();
+ fetchMock = sinon.mock(service.$fetch);
+ fetchMock.expects('get').returns(deferred.promise);
+ res = {
+ "files": [
+ {
+ "name": "fileName",
+ "Size": "323844"
+ }
+ ]
+ }
+ service.paths = { bucket: 'BucketName', folders: ['FolderA', 'FolderB']};
+ deferred.resolve({ data:res });
+ service.getFiles();
+ $rootScope.$digest();
+ });
+ it('should requesting to be false', function() {
+ expect(service.state.lists.requesting).to.eq(false);
+ });
+ it('should let data have files information', function() {
+ expect(service.state.lists.data).to.have.deep.property('[0].name', "fileName");
+ expect(service.state.lists.data).to.have.deep.property('[0].Size', "323844")
+ });
+ it('should let error to be false', function() {
+ expect(service.state.lists.error).to.eq(false);
+ })
+ });
+
+ describe('when getFiles in service and response fail', function() {
+ let service;
+ let testMock;
+ let fetchMock;
+ let deferred;
+ beforeEach(function() {
+ service = makeService();
+ deferred = makeDeferred();
+ fetchMock = sinon.mock(service.$fetch);
+ fetchMock.expects('get').returns(deferred.promise);
+ service.paths = { bucket: 'BucketName', folders: ['FolderA', 'FolderB']};
+ deferred.reject();
+ service.getFiles();
+ $rootScope.$digest();
+ });
+ it('should requesting to be false', function() {
+ expect(service.state.lists.requesting).to.eq(false);
+ });
+ it('should let error to be true', function() {
+ expect(service.state.lists.error).to.eq(true);
+ })
+ });
+
+ describe('when init controller', function() {
+ let service;
+ let controller;
+ let mockSetPaths;
+ let mockGetFiles;
+ let mockUpdateFP;
+ let mockGetBucket;
+ beforeEach(function() {
+ service = makeService();
+ service.getFiles = () => {};
+ mockSetPaths = sinon.spy(service, 'setPaths');
+ mockUpdateFP = sinon.spy($breadcrumb, 'updateFilePath');
+ mockGetBucket = sinon.spy(BucService, 'getBuckets')
+ mockGetFiles = sinon.spy(service, 'getFiles');
+ controller = makeController(service);
+ });
+ it('should invoke setPaths in fileService and call by PATH', function() {
+ expect(mockSetPaths).to.have.been.calledWith('BucketName', ["FolderA", "FolderB"]);
+ });
+ it('should invoke $breadcrumb.updateFilePath by folders', function() {
+ expect(mockUpdateFP).to.have.been.calledWith($stateParams.path.split('/'));
+ });
+ it('should invoke getbuckets in bucketService', function() {
+ expect(mockGetBucket.called).to.eq(true);
+ });
+ it('should invoke getFiles in fileService', function() {
+ expect(mockGetFiles.called).to.eq(true)
+ });
+ });
+ describe('when refresh in controller', function() {
+ let service;
+ let controller;
+ let mockGetFiles;
+ let upService;
+ beforeEach(function() {
+ service = makeService();
+ service.getFiles = () => {};
+ upService = makeUpService(service);
+ controller = makeController(service);
+ mockGetFiles = sinon.spy(service, 'getFiles');
+ });
+ it('should invoke getFiles in service', function() {
+ expect(mockGetFiles.called).to.eq(false);
+ controller.refresh();
+ expect(mockGetFiles.called).to.eq(true);
+ });
+ });
+ describe('when upload in controller', function() {
+ let service;
+ let controller;
+ let mockCreateDialog;
+ let upService;
+ beforeEach(function() {
+ service = makeService();
+ upService = makeUpService(service);
+ controller = makeController(service, upService);
+ mockCreateDialog = sinon.spy(upService, 'createDialog');
+ });
+ it('should invoke createDialog in service', function() {
+ expect(mockCreateDialog.called).to.eq(false);
+ controller.upload();
+ expect(mockCreateDialog.called).to.eq(true);
+ });
+ });
+});
\ No newline at end of file
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
new file mode 100644
index 0000000..5b60208
--- /dev/null
+++ b/src/components/file/upload/upload.html
@@ -0,0 +1,85 @@
+
+
+
diff --git a/src/components/file/upload/upload.servce.js b/src/components/file/upload/upload.servce.js
new file mode 100644
index 0000000..f4e6ace
--- /dev/null
+++ b/src/components/file/upload/upload.servce.js
@@ -0,0 +1,92 @@
+import { element } from 'angular';
+import totalSize from '../../../utils/totalSize';
+import FileUploadController from './upload.controller';
+import FileUploadTemplate from './upload.html';
+
+export default class FileUploadService {
+ /** @ngInject */
+ constructor(Config, Upload, $mdDialog, $file, $transfer) {
+ Object.assign(this, {
+ Config, Upload, $mdDialog, $file, $transfer,
+ });
+
+ this.initState();
+ }
+
+ initState() {
+ this.state = {
+ files: [],
+ size: 0,
+ };
+ }
+
+ select(selectedFiles) {
+ const additionalFiles = selectedFiles.filter(selectedFile =>
+ this.state.files.every(({ detail }) => detail.name !== selectedFile.name)
+ ).map(detail => ({
+ id: Symbol('unique id'), detail,
+ }));
+
+ const files = [...this.state.files, ...additionalFiles];
+ const size = totalSize(files);
+
+ this.state = { files, size };
+ }
+
+ delete(id) {
+ const files = this.state.files.filter(file => file.id !== id);
+ const size = totalSize(files);
+
+ this.state = { files, size };
+ }
+
+ 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;
+ 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();
+ }
+
+ uploadFile(id, data, url) {
+ const upload = this.Upload.upload({ url, data });
+
+ upload.then(
+ res => this.$transfer.handleSuccess(id, res),
+ err => this.$transfer.handleFailure(id, err),
+ evt => this.$transfer.handleEvent(id, evt)
+ );
+
+ return upload;
+ }
+
+ createDialog($event) {
+ this.$mdDialog.show({
+ controller: FileUploadController,
+ controllerAs: 'upload',
+ template: FileUploadTemplate,
+ parent: element(document.body),
+ targetEvent: $event,
+ clickOutsideToClose: true,
+ });
+ }
+
+ closeDialog() {
+ this.$mdDialog.cancel();
+ this.initState();
+ }
+}
diff --git a/src/components/file/upload/upload.spec.js b/src/components/file/upload/upload.spec.js
new file mode 100644
index 0000000..c89e3ff
--- /dev/null
+++ b/src/components/file/upload/upload.spec.js
@@ -0,0 +1,346 @@
+import app from '../../../index.js';
+import upCtrl from './upload.controller';
+import fileService from '../file.service';
+import uploadService from './upload.servce';
+import transService from '../../layout/transfer/transfer.service';
+
+describe('Upload Unit Test', function() {
+ let $rootScope;
+ let makeFileService;
+ let BucService;
+ let makeUpService;
+ let makeTransService;
+ let makeDeferred;
+ let makeController;
+ let $fetch;
+ let $toast;
+ let $mdDialog;
+ let $breadcrumb;
+ let Upload;
+ let Config = { API_URL: '0.0.0.0:0000' };
+ let $stateParams = {
+ path: 'BucketName/FolderA/FolderB'
+ };
+
+ beforeEach(angular.mock.module('app'));
+
+ beforeEach(inject(($q, _Upload_, _$rootScope_, _$mdDialog_, _$toast_, _$breadcrumb_, _$fetch_) => {
+ $rootScope = _$rootScope_;
+
+ $fetch = _$fetch_;
+
+ $toast = _$toast_;
+
+ $mdDialog = _$mdDialog_;
+
+ Upload = _Upload_;
+
+ BucService = {};
+
+ makeFileService = () => {
+ return new fileService($mdDialog, $fetch, BucService);
+ };
+
+ makeUpService = (fileService, transService) => {
+ return new uploadService(Config, Upload, $mdDialog, fileService, transService);
+ };
+
+ makeDeferred = () => {
+ return $q.defer();
+ };
+
+ makeController = (fileService, upService) => {
+ return new upCtrl(fileService, upService, $rootScope);
+ };
+
+ makeTransService = (fileService) => {
+ return new transService($toast, fileService);
+ };
+ }));
+ describe('when upload in controller', function() {
+ let service;
+ let controller;
+ let mockUpload;
+ beforeEach(function() {
+ service = makeUpService(makeFileService());
+ controller = makeController(makeFileService(), service);
+ service.upload = () => {};
+ mockUpload = sinon.spy(service, 'upload');
+ });
+
+ it('should invoke upload in service', function() {
+ expect(mockUpload.called).to.eq(false);
+ controller.upload();
+ expect(mockUpload.called).to.eq(true);
+ });
+ });
+ describe('when select in controller', function() {
+ let service;
+ let controller;
+ let mockSelect;
+ beforeEach(function() {
+ service = makeUpService(makeFileService());
+ controller = makeController(makeFileService(), service);
+ service.select = () => {};
+ mockSelect = sinon.spy(service, 'select');
+ });
+
+ it('should invoke upload in service', function() {
+ expect(mockSelect.called).to.eq(false);
+ controller.select(123);
+ expect(mockSelect).to.have.been.calledWith(123);
+ });
+ });
+ describe('when delete in controller', function() {
+ let service;
+ let controller;
+ let mockDelete;
+ beforeEach(function() {
+ service = makeUpService(makeFileService());
+ controller = makeController(makeFileService(), service);
+ service.delete = () => {};
+ mockDelete = sinon.spy(service, 'delete');
+ });
+
+ it('should invoke upload in service', function() {
+ expect(mockDelete.called).to.eq(false);
+ controller.delete(321);
+ expect(mockDelete).to.have.been.calledWith(321);
+ });
+ });
+ describe('when cancel in controller', function() {
+ let service;
+ let controller;
+ let mockCloseDialog;
+ beforeEach(function() {
+ service = makeUpService(makeFileService());
+ controller = makeController(makeFileService(), service);
+ service.closeDialog = () => {};
+ mockCloseDialog = sinon.spy(service, 'closeDialog');
+ });
+
+ it('should invoke upload in service', function() {
+ expect(mockCloseDialog.called).to.eq(false);
+ controller.cancel();
+ expect(mockCloseDialog.called).to.eq(true);
+ });
+ });
+ describe('when init service', function() {
+ let service;
+ beforeEach(function() {
+ service = makeUpService(makeFileService());
+ });
+ it('should declare service.state.files', function() {
+ expect(service.state.files).to.be.empty;
+ });
+ it('should declare service.state.size', function() {
+ expect(service.state.size).to.eq(0);
+ });
+ });
+ describe('when select non-repeat files in service', function() {
+ let service;
+ let mockTotal;
+ let file = [
+ {
+ 'name': 'fileName',
+ 'size': 555
+ },
+ {
+ 'name': 'fileName2',
+ 'size': 5858
+ }
+ ]
+ beforeEach(function() {
+ service = makeUpService(makeFileService());
+ service.select(file);
+ $rootScope.$digest();
+ });
+ it('should declare service.state.files', function() {
+ expect(service.state.files[0].id).to.be.a('symbol');
+ expect(service.state.files[0].detail).to.have.property('name', 'fileName');
+ expect(service.state.files[0].detail).to.have.property('size', 555);
+ expect(service.state.files[1].id).to.be.a('symbol');
+ expect(service.state.files[1].detail).to.have.property('name', 'fileName2');
+ expect(service.state.files[1].detail).to.have.property('size', 5858);
+ });
+ it('should total size', function() {
+ expect(service.state.size).to.eq(file.reduce((p, c) => p+c.size,0));
+ });
+ });
+ describe('when select same name files', function() {
+ let service;
+ let mockTotal;
+ let file = [
+ {
+ 'name': 'fileName',
+ 'size': 555
+ },
+ {
+ 'name': 'fileName2',
+ 'size': 5858
+ }
+ ]
+ beforeEach(function() {
+ service = makeUpService(makeFileService());
+ service.state.files = [ {
+ id: Symbol('unique id'),
+ detail: {
+ name: 'fileName2',
+ size: 808
+ }
+ }];
+ service.select(file);
+ $rootScope.$digest();
+ });
+ it('should not insert repeat files', function() {
+ expect(service.state.files[0].id).to.be.a('symbol');
+ expect(service.state.files[0].detail).to.have.property('name', 'fileName2');
+ expect(service.state.files[0].detail).to.have.property('size', 808);
+ expect(service.state.files[1].id).to.be.a('symbol');
+ expect(service.state.files[1].detail).to.have.property('name', 'fileName');
+ expect(service.state.files[1].detail).to.have.property('size', 555);
+ });
+ it('should total size', function() {
+ expect(service.state.size).to.eq(service.state.files.reduce((p, c) => p+c.detail.size,0));
+ });
+ });
+ describe('when delete in service', function() {
+ let service;
+ let mockTotal;
+ let file = [
+ {
+ id: Symbol('unique id'),
+ detail: {
+ name: 'fileName1',
+ size: 8585
+ }
+ },
+ {
+ id: Symbol('unique id'),
+ detail: {
+ name: 'fileName2',
+ size: 808
+ }
+ }
+ ];
+ beforeEach(function() {
+ service = makeUpService(makeFileService());
+ service.state.files = file;
+ });
+ it('should delete files', function() {
+ expect(service.state.files[0].id).to.be.a('symbol');
+ expect(service.state.files[0].detail).to.have.property('name', 'fileName1');
+ expect(service.state.files[0].detail).to.have.property('size', 8585);
+ expect(service.state.files[1].id).to.be.a('symbol');
+ expect(service.state.files[1].detail).to.have.property('name', 'fileName2');
+ expect(service.state.files[1].detail).to.have.property('size', 808);
+ service.delete(service.state.files[0].id);
+ expect(service.state.files[0].id).to.be.a('symbol');
+ expect(service.state.files[0].detail).to.have.property('name', 'fileName2');
+ expect(service.state.files[0].detail).to.have.property('size', 808);
+ });
+ it('should total size', function() {
+ service.delete(service.state.files[0].id);
+ expect(service.state.size).to.eq(service.state.files.reduce((p, c) => p+c.detail.size,0));
+ });
+ });
+ describe('when upload in service', function() {
+ let service;
+ let fileService;
+ let transService;
+ let mockPut;
+ let mockCloseDialog;
+ let mockUploadFile;
+ beforeEach(function() {
+ fileService = makeFileService();
+ transService = makeTransService(fileService);
+ fileService.paths = { bucket: 'BucketName', folders: ['FolderA', 'FolderB']};
+ service = makeUpService(fileService, transService);
+ service.state.files = [
+ {
+ id: Symbol('unique id'),
+ detail: {
+ name: 'FileName',
+ size: 888
+ }
+ }
+ ];
+ service.uploadFile = () => { return 'aPromise' };
+ service.closeDialog = () => {};
+ mockCloseDialog = sinon.spy(service, 'closeDialog');
+ mockUploadFile = sinon.spy(service, 'uploadFile');
+ mockPut = sinon.spy(transService, 'put');
+ service.upload();
+ $rootScope.$digest();
+ });
+ it('should let uploading to be true', function() {
+ expect(service.state.uploading).to.eq(true);
+ });
+ it('should invoke $transfer.put and call by right way', function() {
+ const { bucket, folders } = fileService.paths;
+ const called = [{
+ id: service.state.files[0].id,
+ bucket: bucket,
+ name: service.state.files[0].detail.name,
+ type: 'UPLOAD',
+ status: 'UPLOADING',
+ upload: 'aPromise'
+ }];
+ expect(mockPut).to.have.been.calledWith(called);
+ });
+ it('should invoke uploadFile and call by id, data and url', function() {
+ const { bucket, folders } = fileService.paths;
+ const prefix = folders.length ? '' : `${folders.join('/')}/`;
+ const called = {
+ id: service.state.files[0].id,
+ data: {
+ bucket: bucket,
+ prefix: prefix,
+ file: service.state.files[0].detail
+ },
+ url:`${Config.API_URL}/v1/file/create`
+ }
+ expect(mockUploadFile).to.have.been.calledWith(called.id, called.data, called.url);
+ });
+ it('should invoke closeDialog', function() {
+ expect(mockCloseDialog.called).to.eq(true);
+ });
+ });
+ describe('when createDialog in service', function() {
+ let service;
+ let fileService;
+ let transService;
+ let mockCreateDialog;
+ beforeEach(function() {
+ fileService = makeFileService();
+ transService = makeTransService(fileService);
+ service = makeUpService(fileService, transService);
+ mockCreateDialog = sinon.spy($mdDialog, 'show');
+ service.createDialog();
+ });
+ it('should invoke mdDialog.show', function() {
+ expect(mockCreateDialog.called).to.eq(true);
+ });
+ });
+ describe('when closeDialog in service', function() {
+ let service;
+ let fileService;
+ let transService;
+ let mockCancelDialog;
+ let mockInitState;
+ beforeEach(function() {
+ fileService = makeFileService();
+ transService = makeTransService(fileService);
+ service = makeUpService(fileService, transService);
+ mockInitState = sinon.spy(service, 'initState');
+ mockCancelDialog = sinon.spy($mdDialog, 'cancel');
+ service.closeDialog();
+ });
+ it('should invoke mdDialog.cancel', function() {
+ expect(mockCancelDialog.called).to.eq(true);
+ });
+ it('should invoke initState in service', function() {
+ expect(mockInitState.called).to.eq(true);
+ });
+ });
+});
\ No newline at end of file
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;
diff --git a/src/components/layout/action-navbar/action-navbar.controller.js b/src/components/layout/action-navbar/action-navbar.controller.js
index 34da8c9..a2e41c1 100644
--- a/src/components/layout/action-navbar/action-navbar.controller.js
+++ b/src/components/layout/action-navbar/action-navbar.controller.js
@@ -1,14 +1,19 @@
export default class ActionNavbarController {
/** @ngInject */
- constructor($scope, $bucket, $nav) {
+ constructor($scope, $bucket, $nav, $file, $upload, $layout) {
Object.assign(this, {
- $scope, $bucket,
+ $scope, $bucket, $file, $upload, $layout,
});
this.$scope.$watch(
() => $nav.type,
newVal => (this.type = newVal)
);
+
+ this.$scope.$watch(
+ () => $layout.state,
+ newVal => Object.assign(this, newVal)
+ );
}
/**
@@ -28,24 +33,20 @@ export default class ActionNavbarController {
//
}
- upload() {
- //
- }
-
delete() {
//
}
- none() {
- //
+ closeSidePanels() {
+ this.$layout.closeSidePanels();
}
- properties() {
- //
+ openProperties() {
+ this.$layout.openProperties();
}
- transfers() {
- //
+ openTransfers() {
+ this.$layout.openTransfers();
}
/**
@@ -56,12 +57,16 @@ export default class ActionNavbarController {
*/
create($event) {
if (this.isFile()) {
- // create file dialog
+ this.$upload.createDialog($event);
} else {
this.$bucket.createDialog($event);
}
}
+ createFolder($event) {
+ // handle the create folder event
+ }
+
/**
* Refresh the list by `this.type`
*
@@ -69,7 +74,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 8cbd1cb..7455063 100644
--- a/src/components/layout/action-navbar/action-navbar.html
+++ b/src/components/layout/action-navbar/action-navbar.html
@@ -2,22 +2,21 @@