diff --git a/examples/demo-app/src/actions.js b/examples/demo-app/src/actions.js index a85b88379a..b6bd7548c6 100644 --- a/examples/demo-app/src/actions.js +++ b/examples/demo-app/src/actions.js @@ -97,11 +97,12 @@ export function onExportFileSuccess({response = {}, provider, options}) { }; } -export function onLoadCloudMapSuccess({response, provider, loadParams}) { +export function onLoadCloudMapSuccess({provider, loadParams}) { return dispatch => { - if (provider.getMapUrl) { - const mapUrl = provider.getMapUrl(false, loadParams); - dispatch(push(mapUrl)); + const mapUrl = provider?.getMapUrl(loadParams); + if (mapUrl) { + const url = `demo/map/${provider.name}?path=${mapUrl}`; + dispatch(push(url)); } }; } diff --git a/examples/demo-app/src/cloud-providers/carto/carto-provider.js b/examples/demo-app/src/cloud-providers/carto/carto-provider.js index 1e44730233..e6eefde280 100644 --- a/examples/demo-app/src/cloud-providers/carto/carto-provider.js +++ b/examples/demo-app/src/cloud-providers/carto/carto-provider.js @@ -164,7 +164,7 @@ export default class CartoProvider extends Provider { * Returns the access token. If it has expired returns null. The toolkit library loads it * from localStorage automatically */ - getAccessToken() { + async getAccessToken() { let accessToken = null; try { accessToken = this._carto.oauth.expired ? null : this._carto.oauth.token; @@ -194,6 +194,14 @@ export default class CartoProvider extends Provider { return; } + async getUser() { + return { + name: this.getUserName(), + abbreviated: '', + email: '' + } + } + async downloadMap(queryParams) { try { const {owner: username, mapId, privateMap} = queryParams; diff --git a/examples/demo-app/src/cloud-providers/dropbox/dropbox-provider.js b/examples/demo-app/src/cloud-providers/dropbox/dropbox-provider.js index 0b90ac1c32..a3199bdbb5 100644 --- a/examples/demo-app/src/cloud-providers/dropbox/dropbox-provider.js +++ b/examples/demo-app/src/cloud-providers/dropbox/dropbox-provider.js @@ -199,10 +199,7 @@ export default class DropboxProvider extends Provider { return await this._shareFile(metadata); } - // save private map save map url - this._loadParam = {path: metadata.path_lower}; - - return this._loadParam; + return {id: metadata.id, path: metadata.path_lower}; } /** @@ -210,11 +207,8 @@ export default class DropboxProvider extends Provider { * @param loadParams */ async downloadMap(loadParams) { - const token = this.getAccessToken(); - if (!token) { - this.login(() => this.downloadMap(loadParams)); - } - const result = await this._dropbox.filesDownload(loadParams); + const {path} = loadParams; + const result = await this._dropbox.filesDownload({path}); const json = await this._readFile(result.fileBlob); const response = { @@ -222,7 +216,6 @@ export default class DropboxProvider extends Provider { format: KEPLER_FORMAT }; - this._loadParam = loadParams; return Promise.resolve(response); } @@ -230,8 +223,7 @@ export default class DropboxProvider extends Provider { // load user from if (window.localStorage) { const jsonString = window.localStorage.getItem('dropbox'); - const user = jsonString && JSON.parse(jsonString).user; - return user; + return jsonString && JSON.parse(jsonString).user; } return null; } @@ -269,14 +261,10 @@ export default class DropboxProvider extends Provider { /** * Get the map url of current map, this url can only be accessed by current logged in user - * @param {boolean} fullUrl */ - getMapUrl(fullURL = true) { - const {path} = this._loadParam; - const mapLink = `demo/map/dropbox?path=${path}`; - return fullURL - ? `${window.location.protocol}//${window.location.host}/${mapLink}` - : `/${mapLink}`; + getMapUrl(loadParams) { + const {path} = loadParams; + return path; } getManagementUrl() { @@ -475,6 +463,7 @@ export default class DropboxProvider extends Provider { id, updatedAt: new Date(client_modified).getTime(), loadParams: { + id, path: path_lower } }; diff --git a/examples/demo-app/src/cloud-providers/foursquare/foursquare-provider.js b/examples/demo-app/src/cloud-providers/foursquare/foursquare-provider.js index 42b01bd4db..1587ed7f50 100644 --- a/examples/demo-app/src/cloud-providers/foursquare/foursquare-provider.js +++ b/examples/demo-app/src/cloud-providers/foursquare/foursquare-provider.js @@ -2,7 +2,7 @@ import FSQIcon from './foursquare-icon'; import {Provider, KEPLER_FORMAT} from '@kepler.gl/cloud-providers'; import {Auth0Client} from '@auth0/auth0-spa-js'; -const NAME = 'Foursquare'; +const NAME = 'foursquare'; const DISPLAY_NAME = 'Foursquare'; const APP_NAME = 'Kepler.gl'; @@ -18,7 +18,7 @@ const FOURSQUARE_KEPLER_GL_IMPORT_SOURCE = 'kepler.gl-raw'; * @param model Foursquare Map * @return {MapItem} Map */ -function convertFSQModelToMapItem(model) { +function convertFSQModelToMapItem(model, baseApi) { return { id: model.id, title: model.name, @@ -26,7 +26,8 @@ function convertFSQModelToMapItem(model) { updatedAt: model.updatedAt, description: model.description, loadParams: { - mapId: model.id + id: model.id, + path: `${baseApi}/${model.id}` } }; } @@ -45,14 +46,12 @@ export default class FoursquareProvider extends Provider { this.appName = APP_NAME; this.apiURL = apiURL; - const redirect_uri = window.location.origin + window.location.pathname; - this._auth0 = new Auth0Client({ domain: authDomain, clientId: clientId, scope: FOURSQUARE_AUTH_SCOPE, authorizationParams: { - redirect_uri, + redirect_uri: window.location.origin, audience: FOURSQUARE_AUTH_AUDIENCE }, cacheLocation: 'localstorage' @@ -133,17 +132,17 @@ export default class FoursquareProvider extends Provider { } ); const data = await response.json(); - return data.items.map(convertFSQModelToMapItem); + return data.items.map(map => convertFSQModelToMapItem(map, `${this.apiURL}/v1/maps`)); } async downloadMap(loadParams) { - const {mapId} = loadParams; - if (!mapId) { + const {id} = loadParams; + if (!id) { return Promise.reject('No Map is was provider as part of loadParams'); } const headers = await this.getHeaders(); - const response = await fetch(`${this.apiURL}/v1/maps/${mapId}`, { + const response = await fetch(`${this.apiURL}/v1/maps/${id}`, { method: 'GET', headers }); @@ -156,6 +155,11 @@ export default class FoursquareProvider extends Provider { }); } + getMapUrl(loadParams) { + const {id} = loadParams; + return `${this.apiURL}/v1/maps/${id}`; + } + getManagementUrl() { return this._folderLink; } diff --git a/examples/demo-app/src/cloud-providers/index.js b/examples/demo-app/src/cloud-providers/index.js index 9b9f4c9123..8449c78526 100644 --- a/examples/demo-app/src/cloud-providers/index.js +++ b/examples/demo-app/src/cloud-providers/index.js @@ -33,13 +33,6 @@ const { FOURSQUARE_USER_MAPS_URL } = CLOUD_PROVIDERS_CONFIGURATION; -console.log('provider index', { - clientId: FOURSQUARE_CLIENT_ID, - authDomain: FOURSQUARE_DOMAIN, - apiURL: FOURSQUARE_API_URL, - userMapsURL: FOURSQUARE_USER_MAPS_URL -}); - const DROPBOX_CLIENT_NAME = 'Kepler.gl Demo App'; export const DEFAULT_CLOUD_PROVIDER = 'dropbox'; diff --git a/src/cloud-providers/src/index.ts b/src/cloud-providers/src/index.ts index 600288bfe4..05b73e264a 100644 --- a/src/cloud-providers/src/index.ts +++ b/src/cloud-providers/src/index.ts @@ -20,5 +20,5 @@ export {default as Provider, FILE_CONFLICT_MSG, KEPLER_FORMAT} from './provider'; // eslint-disable-next-line prettier/prettier -export type {MapListItem, Thumbnail, ProviderProps, IconProps} from './provider'; +export type {MapListItem, Thumbnail, ProviderProps, IconProps, CloudUser} from './provider'; export {default as Upload} from './upload'; diff --git a/src/cloud-providers/src/provider.ts b/src/cloud-providers/src/provider.ts index 895ec55169..aa78765129 100644 --- a/src/cloud-providers/src/provider.ts +++ b/src/cloud-providers/src/provider.ts @@ -22,6 +22,11 @@ import Upload from './upload'; import {MapData, ExportFileOptions, Millisecond, SavedMap} from '@kepler.gl/types'; import {ComponentType} from 'react'; +export type MapItemLoadParams = { + id: string; + path: string; +}; + export type MapListItem = { id: string; title: string; @@ -125,11 +130,10 @@ export default class Provider { /** * This method is called by kepler.gl demo app to pushes a new location to history, becoming the current location. - * @param fullURL - Whether to return the full url with domain, or just the location * @returns mapUrl * @public */ - getMapUrl(fullURL: boolean = true): string { + getMapUrl(loadParams: MapItemLoadParams): string { return ''; } @@ -138,7 +142,7 @@ export default class Provider { * @public * @returns {Promise} return the access token if a user already logged in */ - getAccessToken(): Promise { + async getAccessToken(): Promise { return Promise.reject('You must implement getAccessToken'); } @@ -155,7 +159,7 @@ export default class Provider { /** * return a Promise with the user object */ - getUser(): Promise { + async getUser(): Promise { return Promise.reject('You must implement getUser'); } diff --git a/src/components/src/modals/cloud-tile.tsx b/src/components/src/modals/cloud-tile.tsx index 0a22655d2b..07d890a6ad 100644 --- a/src/components/src/modals/cloud-tile.tsx +++ b/src/components/src/modals/cloud-tile.tsx @@ -2,9 +2,8 @@ import React, {useCallback, useEffect, useState} from 'react'; import styled from 'styled-components'; import {Logout, Login} from '../common/icons'; import {CenterVerticalFlexbox, Button, CheckMark} from '../common/styled-components'; -import {Provider} from '@kepler.gl/cloud-providers'; +import {Provider, CloudUser} from '@kepler.gl/cloud-providers'; import {useCloudListProvider} from '../hooks/use-cloud-list-provider'; -import {CloudUser} from '@kepler.gl/cloud-providers/src/provider'; interface StyledTileWrapperProps { selected?: boolean; @@ -93,7 +92,11 @@ const NewTag = styled.div` z-index: 500; font-size: 11px; line-height: 10px; -; +`; + +export const StyledWarning = styled.span` + color: ${props => props.theme.errorColor}; + font-weight: ${props => props.theme.selectFontWeightBold}; `; interface CloudTileProps { @@ -116,16 +119,20 @@ const CloudTile: React.FC = ({provider, actionName}) => { const isSelected = provider === currentProvider; useEffect(() => { - if (provider) { - setError(null); - setIsLoading(true); - setError(null); - provider - .getUser() - .then(user => setUser(user)) - .catch(setError) - .finally(() => setIsLoading(false)); + if (!provider) { + return; } + setError(null); + setIsLoading(true); + setError(null); + provider + .getUser() + .then(setUser) + .catch(setError) + .finally(() => { + setError(null); + setIsLoading(false); + }); }, [provider]); const onLogin = useCallback(async () => { @@ -145,10 +152,17 @@ const CloudTile: React.FC = ({provider, actionName}) => { if (isLoading) { return; } - if (!user) { + if (user) { + setProvider(provider); + return; + } + try { await onLogin(); + setProvider(provider); + } catch (err) { + setError(err as Error); + setProvider(null); } - setProvider(provider); }, [setProvider, provider, user, isLoading]); const onLogout = useCallback(async () => { @@ -179,8 +193,8 @@ const CloudTile: React.FC = ({provider, actionName}) => { )} {isSelected ? : null} - {user ? : } - {error ?
{error.message}
: null} + {user || error ? : } + {error ? {error.message} : null} ); };