123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471 |
- import Utils from '../core/Utils'
- import Plugin from './Plugin'
- import 'whatwg-fetch'
- import yo from 'yo-yo'
- export default class Google extends Plugin {
- constructor (core, opts) {
- super(core, opts)
- this.type = 'acquirer'
- this.id = 'GoogleDrive'
- this.titile = 'Google Drive'
- this.icon = yo`
- <svg class="UppyModalTab-icon" width="28" height="28" viewBox="0 0 16 16">
- <path d="M2.955 14.93l2.667-4.62H16l-2.667 4.62H2.955zm2.378-4.62l-2.666 4.62L0 10.31l5.19-8.99 2.666 4.62-2.523 4.37zm10.523-.25h-5.333l-5.19-8.99h5.334l5.19 8.99z"/>
- </svg>
- `
- this.files = []
- this.renderBrowserItem = this.renderBrowserItem.bind(this)
- this.filterItems = this.filterItems.bind(this)
- this.filterQuery = this.filterQuery.bind(this)
- this.addFile = this.addFile.bind(this)
- this.getFolder = this.getFolder.bind(this)
- this.handleClick = this.handleClick.bind(this)
- this.logout = this.logout.bind(this)
- this.renderBrowser = this.renderBrowser.bind(this)
- this.sortByTitle = this.sortByTitle.bind(this)
- this.sortByDate = this.sortByDate.bind(this)
- this.render = this.render.bind(this)
- // set default options
- const defaultOptions = {}
- // merge default options with the ones set by user
- this.opts = Object.assign({}, defaultOptions, opts)
- }
- install () {
- // Set default state for Google Drive
- this.core.setState({
- googleDrive: {
- authenticated: false,
- files: [],
- folders: [],
- directory: [{
- title: 'My Drive',
- id: 'root'
- }],
- active: {},
- filterInput: ''
- }
- })
- const target = this.opts.target
- const plugin = this
- this.target = this.mount(target, plugin)
- this.checkAuthentication()
- .then((authenticated) => {
- this.updateState({authenticated})
- if (authenticated) {
- return this.getFolder(this.core.getState().googleDrive.directory.id)
- }
- return authenticated
- })
- .then((newState) => {
- this.updateState(newState)
- })
- return
- }
- focus () {
- const firstInput = document.querySelector(`${this.target} .UppyGoogleDrive-focusInput`)
- // only works for the first time if wrapped in setTimeout for some reason
- // firstInput.focus()
- setTimeout(function () {
- firstInput.focus()
- }, 10)
- }
- /**
- * Little shorthand to update the state with my new state
- */
- updateState (newState) {
- const {state} = this.core
- const googleDrive = Object.assign({}, state.googleDrive, newState)
- this.core.setState({googleDrive})
- }
- /**
- * Check to see if the user is authenticated.
- * @return {Promise} authentication status
- */
- checkAuthentication () {
- return fetch(`${this.opts.host}/google/authorize`, {
- method: 'get',
- credentials: 'include',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- }
- })
- .then((res) => {
- if (res.status >= 200 && res.status <= 300) {
- return res.json()
- } else {
- let error = new Error(res.statusText)
- error.response = res
- throw error
- }
- })
- .then((data) => data.isAuthenticated)
- .catch((err) => err)
- }
- /**
- * Based on folder ID, fetch a new folder
- * @param {String} id Folder id
- * @return {Promise} Folders/files in folder
- */
- getFolder (id = 'root') {
- return fetch(`${this.opts.host}/google/list?dir=${id}`, {
- method: 'get',
- credentials: 'include',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- }
- })
- .then((res) => {
- if (res.status >= 200 && res.status <= 300) {
- return res.json().then((data) => {
- // let result = Utils.groupBy(data.items, (item) => item.mimeType)
- let folders = []
- let files = []
- data.items.forEach((item) => {
- if (item.mimeType === 'application/vnd.google-apps.folder') {
- folders.push(item)
- } else {
- files.push(item)
- }
- })
- return {
- folders,
- files
- }
- })
- } else {
- let error = new Error(res.statusText)
- error.response = res
- throw error
- }
- })
- .catch((err) => {
- return err
- })
- }
- /**
- * Fetches new folder and adds to breadcrumb nav
- * @param {String} id Folder id
- * @param {String} title Folder title
- */
- getSubFolder (id, title) {
- this.getFolder(id)
- .then((data) => {
- const state = this.core.getState().googleDrive
- const index = state.directory.findIndex((dir) => id === dir.id)
- let directory
- if (index !== -1) {
- directory = state.directory.slice(0, index + 1)
- } else {
- directory = state.directory.concat([{
- id,
- title
- }])
- }
- this.updateState(Utils.extend(data, {directory}))
- })
- }
- /**
- * Will soon be replaced by actual Uppy file handling.
- * Requests the server download the selected file.
- * @param {String} fileId
- * @return {Promise} Result
- */
- addFile (file) {
- const tagFile = {
- source: this,
- data: file,
- name: file.title,
- type: this.getFileType(file),
- isRemote: true,
- remote: {
- url: `${this.opts.host}/google/get?fileId=${file.id}`,
- body: {
- fileId: file.id
- }
- }
- }
- this.core.emitter.emit('file-add', tagFile)
- }
- handleUploadError (response) {
- this.checkAuthentication()
- .then((authenticated) => {
- this.updateState({authenticated})
- })
- }
- /**
- * Removes session token on client side.
- */
- logout () {
- fetch(`${this.opts.host}/google/logout?redirect=${location.href}`, {
- method: 'get',
- credentials: 'include',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- }
- })
- .then((res) => res.json())
- .then((res) => {
- if (res.ok) {
- console.log('ok')
- const newState = {
- authenticated: false,
- files: [],
- folders: [],
- directory: {
- title: 'My Drive',
- id: 'root'
- }
- }
- this.updateState(newState)
- }
- })
- }
- getFileType (file) {
- const fileTypes = {
- 'application/vnd.google-apps.folder': 'Folder',
- 'application/vnd.google-apps.document': 'Google Docs',
- 'application/vnd.google-apps.spreadsheet': 'Google Sheets',
- 'application/vnd.google-apps.presentation': 'Google Slides',
- 'image/jpeg': 'JPEG Image',
- 'image/png': 'PNG Image'
- }
- return fileTypes[file.mimeType] ? fileTypes[file.mimeType] : file.fileExtension.toUpperCase()
- }
- /**
- * Used to set active file/folder.
- * @param {Object} file Active file/folder
- */
- handleClick (file) {
- const state = this.core.getState().googleDrive
- const newState = Object.assign({}, state, {
- active: file
- })
- this.updateState(newState)
- }
- filterQuery (e) {
- const state = this.core.getState().googleDrive
- this.updateState(Object.assign({}, state, {
- filterInput: e.target.value
- }))
- }
- filterItems (items) {
- const state = this.core.getState().googleDrive
- return items.filter((folder) => {
- return folder.title.toLowerCase().indexOf(state.filterInput.toLowerCase()) !== -1
- })
- }
- sortByTitle () {
- const state = this.core.getState().googleDrive
- const {files, folders, sorting} = state
- let sortedFiles = files.sort((fileA, fileB) => {
- if (sorting === 'titleDescending') {
- return fileB.title.localeCompare(fileA.title)
- }
- return fileA.title.localeCompare(fileB.title)
- })
- let sortedFolders = folders.sort((folderA, folderB) => {
- if (sorting === 'titleDescending') {
- return folderB.title.localeCompare(folderA.title)
- }
- return folderA.title.localeCompare(folderB.title)
- })
- this.updateState(Object.assign({}, state, {
- files: sortedFiles,
- folders: sortedFolders,
- sorting: (sorting === 'titleDescending') ? 'titleAscending' : 'titleDescending'
- }))
- }
- sortByDate () {
- const state = this.core.getState().googleDrive
- const {files, folders, sorting} = state
- let sortedFiles = files.sort((fileA, fileB) => {
- let a = new Date(fileA.modifiedByMeDate)
- let b = new Date(fileB.modifiedByMeDate)
- if (sorting === 'dateDescending') {
- return a > b ? -1 : a < b ? 1 : 0
- }
- return a > b ? 1 : a < b ? -1 : 0
- })
- let sortedFolders = folders.sort((folderA, folderB) => {
- let a = new Date(folderA.modifiedByMeDate)
- let b = new Date(folderB.modifiedByMeDate)
- if (sorting === 'dateDescending') {
- return a > b ? -1 : a < b ? 1 : 0
- }
- return a > b ? 1 : a < b ? -1 : 0
- })
- this.updateState(Object.assign({}, state, {
- files: sortedFiles,
- folders: sortedFolders,
- sorting: (sorting === 'dateDescending') ? 'dateAscending' : 'dateDescending'
- }))
- }
- /**
- * Render user authentication view
- */
- renderAuth () {
- const state = btoa(JSON.stringify({
- redirect: location.href.split('#')[0]
- }))
- const link = `${this.opts.host}/connect/google?state=${state}`
- return yo`
- <div class="UppyGoogleDrive-authenticate">
- <h1>You need to authenticate with Google before selecting files.</h1>
- <a href=${link}>Authenticate</a>
- </div>
- `
- }
- /**
- * Render file browser
- * @param {Object} state Google Drive state
- */
- renderBrowser (state) {
- let folders = state.folders
- let files = state.files
- let previewElem = ''
- const isFileSelected = Object.keys(state.active).length !== 0 && JSON.stringify(state.active) !== JSON.stringify({})
- if (state.filterInput !== '') {
- folders = this.filterItems(state.folders)
- files = this.filterItems(state.files)
- }
- folders = folders.map((folder) => this.renderBrowserItem(folder))
- files = files.map((file) => this.renderBrowserItem(file))
- const breadcrumbs = state.directory.map((dir) => yo`<li><button onclick=${this.getSubFolder.bind(this, dir.id, dir.title)}>${dir.title}</button></li> `)
- if (isFileSelected) {
- previewElem = yo`
- <div>
- <h1><span class="UppyGoogleDrive-fileIcon"><img src=${state.active.iconLink}/></span>${state.active.title}</h1>
- <ul>
- <li>Type: ${this.getFileType(state.active)}</li>
- <li>Modified By Me: ${state.active.modifiedByMeDate}</li>
- </ul>
- ${state.active.thumbnailLink ? yo`<img src=${state.active.thumbnailLink} class="UppyGoogleDrive-fileThumbnail" />` : yo``}
- </div>
- `
- }
- return yo`
- <div>
- <div class="UppyGoogleDrive-header">
- <ul class="UppyGoogleDrive-breadcrumbs">
- ${breadcrumbs}
- </ul>
- </div>
- <div class="container-fluid">
- <div class="row">
- <div class="hidden-md-down col-lg-3 col-xl-3">
- <ul class="UppyGoogleDrive-sidebar">
- <li class="UppyGoogleDrive-filter"><input class="UppyGoogleDrive-focusInput" type='text' onkeyup=${this.filterQuery} placeholder="Search.." value=${state.filterInput}/></li>
- <li><button onclick=${this.getSubFolder.bind(this, 'root', 'My Drive')}><img src="https://ssl.gstatic.com/docs/doclist/images/icon_11_collection_list_3.png"/> My Drive</button></li>
- <li><button><img src="https://ssl.gstatic.com/docs/doclist/images/icon_11_shared_collection_list_1.png"/> Shared with me</button></li>
- <li><button onclick=${this.logout}>Logout</button></li>
- </ul>
- </div>
- <div class="col-md-12 col-lg-9 col-xl-6">
- <div class="UppyGoogleDrive-browserContainer">
- <table class="UppyGoogleDrive-browser">
- <thead>
- <tr>
- <td class="UppyGoogleDrive-sortableHeader" onclick=${this.sortByTitle}>Name</td>
- <td>Owner</td>
- <td class="UppyGoogleDrive-sortableHeader" onclick=${this.sortByDate}>Last Modified</td>
- <td>Filesize</td>
- </tr>
- </thead>
- <tbody>
- ${folders}
- ${files}
- </tbody>
- </table>
- </div>
- </div>
- <div class="hidden-lg-down col-xl-2">
- <div class="UppyGoogleDrive-fileInfo">
- ${previewElem}
- </div>
- </div>
- </div>
- </div>
- </div>
- `
- }
- renderBrowserItem (item) {
- const state = this.core.getState().googleDrive
- const isAFileSelected = Object.keys(state.active).length !== 0 && JSON.stringify(state.active) !== JSON.stringify({})
- const isFolder = item.mimeType === 'application/vnd.google-apps.folder'
- return yo`
- <tr class=${(isAFileSelected && state.active.id === item.id) ? 'is-active' : ''}
- onclick=${this.handleClick.bind(this, item)}
- ondblclick=${isFolder ? this.getSubFolder.bind(this, item.id, item.title) : this.addFile.bind(this, item)}>
- <td><span class="UppyGoogleDrive-folderIcon"><img src=${item.iconLink}/></span> ${item.title}</td>
- <td>Me</td>
- <td>${item.modifiedByMeDate}</td>
- <td>-</td>
- </tr>
- `
- }
- renderError (err) {
- return `Something went wrong. Probably our fault. ${err}`
- }
- render (state) {
- if (state.googleDrive.authenticated) {
- return this.renderBrowser(state.googleDrive)
- } else {
- return this.renderAuth()
- }
- }
- }
|