Skip to content

Commit

Permalink
Fix cloud tile fetching logic (#2456)
Browse files Browse the repository at this point in the history
Signed-off-by: Giuseppe Macri <gmacri@foursquare.com>
  • Loading branch information
macrigiuseppe committed Nov 24, 2023
1 parent 5eb62a9 commit 155a582
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 62 deletions.
9 changes: 5 additions & 4 deletions examples/demo-app/src/actions.js
Expand Up @@ -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));
}
};
}
Expand Down
10 changes: 9 additions & 1 deletion examples/demo-app/src/cloud-providers/carto/carto-provider.js
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
27 changes: 8 additions & 19 deletions examples/demo-app/src/cloud-providers/dropbox/dropbox-provider.js
Expand Up @@ -199,39 +199,31 @@ 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};
}

/**
* download the map content
* @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 = {
map: json,
format: KEPLER_FORMAT
};

this._loadParam = loadParams;
return Promise.resolve(response);
}

getUserName() {
// 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;
}
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -475,6 +463,7 @@ export default class DropboxProvider extends Provider {
id,
updatedAt: new Date(client_modified).getTime(),
loadParams: {
id,
path: path_lower
}
};
Expand Down
Expand Up @@ -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';

Expand All @@ -18,15 +18,16 @@ 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,
thumbnail: model.previewReadPath,
updatedAt: model.updatedAt,
description: model.description,
loadParams: {
mapId: model.id
id: model.id,
path: `${baseApi}/${model.id}`
}
};
}
Expand All @@ -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'
Expand Down Expand Up @@ -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
});
Expand All @@ -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;
}
Expand Down
7 changes: 0 additions & 7 deletions examples/demo-app/src/cloud-providers/index.js
Expand Up @@ -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';
Expand Down
2 changes: 1 addition & 1 deletion src/cloud-providers/src/index.ts
Expand Up @@ -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';
12 changes: 8 additions & 4 deletions src/cloud-providers/src/provider.ts
Expand Up @@ -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;
Expand Down Expand Up @@ -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 '';
}

Expand All @@ -138,7 +142,7 @@ export default class Provider {
* @public
* @returns {Promise<string>} return the access token if a user already logged in
*/
getAccessToken(): Promise<string> {
async getAccessToken(): Promise<string | null> {
return Promise.reject('You must implement getAccessToken');
}

Expand All @@ -155,7 +159,7 @@ export default class Provider {
/**
* return a Promise with the user object
*/
getUser(): Promise<CloudUser> {
async getUser(): Promise<CloudUser | null> {
return Promise.reject('You must implement getUser');
}

Expand Down
46 changes: 30 additions & 16 deletions src/components/src/modals/cloud-tile.tsx
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -116,16 +119,20 @@ const CloudTile: React.FC<CloudTileProps> = ({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 () => {
Expand All @@ -145,10 +152,17 @@ const CloudTile: React.FC<CloudTileProps> = ({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 () => {
Expand Down Expand Up @@ -179,8 +193,8 @@ const CloudTile: React.FC<CloudTileProps> = ({provider, actionName}) => {
)}
{isSelected ? <CheckMark /> : null}
</StyledTileWrapper>
{user ? <LogoutButton onClick={onLogout} /> : <LoginButton onClick={onLogin} />}
{error ? <div>{error.message}</div> : null}
{user || error ? <LogoutButton onClick={onLogout} /> : <LoginButton onClick={onLogin} />}
{error ? <StyledWarning>{error.message}</StyledWarning> : null}
</StyledBox>
);
};
Expand Down

0 comments on commit 155a582

Please sign in to comment.