Skip to content
This repository has been archived by the owner on Apr 25, 2023. It is now read-only.

feat: import google sheet dataset #14

Merged
merged 29 commits into from
Jul 8, 2021
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
eecb272
Import google sheet icon
Basel-Issmail Jun 10, 2021
081df60
Add import from google drive option
Basel-Issmail Jun 10, 2021
8b9f259
Add import from google drive second view
Basel-Issmail Jun 10, 2021
1b4e1d8
import google script
Basel-Issmail Jun 10, 2021
e974789
Add sheet file icon
Basel-Issmail Jun 12, 2021
e9320c3
Add additional/optional parameters to assets card
Basel-Issmail Jun 12, 2021
a7a98e6
Add importing google sheet graphQL changes
Basel-Issmail Jun 12, 2021
4edac85
Add google sheet import
Basel-Issmail Jun 12, 2021
91807b9
Fix params
Basel-Issmail Jun 21, 2021
15efcda
save chages
Basel-Issmail Jun 21, 2021
fa1a841
save changes
Basel-Issmail Jun 21, 2021
546240f
Fix scopes
Basel-Issmail Jun 21, 2021
0615f4d
fix picker function
Basel-Issmail Jun 21, 2021
12d3a16
Merge branch 'main' of github.com:reearth/reearth-web into feat/impor…
Basel-Issmail Jun 21, 2021
5fe639b
Fix permissions
Basel-Issmail Jun 22, 2021
311e1fb
re-sync dataset data
Basel-Issmail Jun 23, 2021
971f205
close modal after import
Basel-Issmail Jun 23, 2021
156f7a6
Change default icon color
Basel-Issmail Jun 23, 2021
325b111
Fix typo
Basel-Issmail Jun 23, 2021
bf1751a
abstract styles
Basel-Issmail Jun 23, 2021
ce38e76
Fix message
Basel-Issmail Jun 23, 2021
224698f
No need for initial values
Basel-Issmail Jul 2, 2021
0b9eb98
Merge branch 'main' of github.com:reearth/reearth-web into feat/impor…
Basel-Issmail Jul 2, 2021
210921e
Fix conflict
Basel-Issmail Jul 2, 2021
f3026a5
No need for isImage
Basel-Issmail Jul 2, 2021
a365eb5
Add types
Basel-Issmail Jul 5, 2021
671516f
re-export type
Basel-Issmail Jul 5, 2021
003851a
Remove isImage property
Basel-Issmail Jul 8, 2021
00bccf4
Merge branch 'main' of github.com:reearth/reearth-web into feat/impor…
Basel-Issmail Jul 8, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@
"@reduxjs/toolkit": "^1.5.0",
"@rot1024/use-transition": "^1.0.0",
"@sentry/browser": "^6.7.1",
"@types/gapi.auth2": "^0.0.54",
"@types/gapi.client": "^1.0.4",
"@types/gapi.client.sheets": "^4.0.20201029",
"@types/google.picker": "^0.0.37",
"@types/tinycolor2": "^1.4.2",
"apollo-link-sentry": "^3.0.2",
"apollo-upload-client": "^14.1.3",
Expand Down
8 changes: 8 additions & 0 deletions src/components/atoms/Icon/Icons/sheet-file.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/components/atoms/Icon/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import Dataset from "./Icons/datasetIcon.svg";
import DatasetAdd from "./Icons/datasetAdd.svg";
import File from "./Icons/fileIcon.svg";
import PcIcon from "./Icons/pcIcon.svg";
import GoogleDriveIcon from "./Icons/googleDriveIcon.svg";
import SheetFileIcon from "./Icons/sheet-file.svg";

// Asset
import AssetGrid from "./Icons/assetGrid.svg";
Expand Down Expand Up @@ -130,6 +132,8 @@ export default {
dataset: Dataset,
datasetAdd: DatasetAdd,
file: File,
googleDrive: GoogleDriveIcon,
sheetFile: SheetFileIcon,
computer: PcIcon,
assetGrid: AssetGrid,
assetGridSmall: AssetGridSmall,
Expand Down
10 changes: 7 additions & 3 deletions src/components/molecules/Common/AssetModal/AssetCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ type CardSize = "small" | "medium" | "large";
export type Props = {
className?: string;
name: string;
url: string;
url?: string;
icon?: string;
iconSize?: string;
cardSize?: CardSize;
checked?: boolean;
selected?: boolean;
Expand All @@ -21,6 +23,8 @@ const AssetCard: React.FC<Props> = ({
className,
name,
url,
icon,
iconSize,
cardSize,
checked,
selected,
Expand All @@ -34,10 +38,10 @@ const AssetCard: React.FC<Props> = ({
cardSize={cardSize}
onClick={() => onCheck?.(!check)}>
<ImgWrapper cardSize={cardSize}>
{/\.(jpg|jpeg|png|gif|svg|webp|GIF|JPG|JPEG|PNG|SVG|WEBP)$/.test(url) ? (
{url && /\.(jpg|jpeg|png|gif|svg|webp|GIF|JPG|JPEG|PNG|SVG|WEBP)$/.test(url) ? (
<PreviewImage url={url} />
) : (
<Icon icon="file" />
<Icon icon={icon} size={iconSize} />
)}
</ImgWrapper>
<FileName size={cardSize === "large" ? "m" : "xs"} cardSize={cardSize} customColor>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@ import { metricsSizes } from "@reearth/theme/metrics";

export type Asset = {
id: string;
teamId: string;
teamId?: string;
name: string;
size: number;
url: string;
contentType: string;
size?: number;
url?: string;
contentType?: string;
KaWaite marked this conversation as resolved.
Show resolved Hide resolved
};

export type Props = {
asset: Asset;
selected?: boolean;
checked?: boolean;
isImage?: boolean;
onCheck?: (checked: boolean) => void;
};

Expand All @@ -33,7 +32,8 @@ const AssetListItem: React.FC<Props> = ({ asset, selected, checked, onCheck }) =
icon={
checked
? "checkCircle"
: /\.(jpg|jpeg|png|gif|svg|webp|GIF|JPG|JPEG|PNG|SVG|WEBP)$/.test(asset.url)
: asset.url &&
/\.(jpg|jpeg|png|gif|svg|webp|GIF|JPG|JPEG|PNG|SVG|WEBP)$/.test(asset.url)
? "image"
: "file"
}
Expand All @@ -43,9 +43,11 @@ const AssetListItem: React.FC<Props> = ({ asset, selected, checked, onCheck }) =
<ListItemName size="m" customColor>
{asset.name}
</ListItemName>
<ListItemSize size="m" customColor>
{parseFloat((asset.size / 1000).toFixed(2))} KB
</ListItemSize>
{asset.size && (
<ListItemSize size="m" customColor>
{parseFloat((asset.size / 1000).toFixed(2))} KB
</ListItemSize>
)}
</ListItem>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { useCallback, useEffect, useState } from "react";

export type SheetParameter = {
accessToken: string;
fileId: string;
sheetName: string;
};

export type GoogleSheet = {
properties: {
gridProperties: { rowCount: number; columnCount: number };
index: number;
sheetId: string;
sheetType: string;
title: string;
};
};

export type File = {
id: string;
name: string;
};

export default (onSheetSelect: (sheet: SheetParameter) => void) => {
const [isLoading, setIsLoading] = useState(true);
const [pickerApiLoaded, setPickerApiLoaded] = useState(false);
const [pickedFile, setPickedFile] = useState<File>();
const [pickedFileSheets, setPickedFileSheets] = useState<GoogleSheet[]>([]);
const [selectedSheet, setSelectedSheet] = useState<File>();
const [accessToken, setAccessToken] = useState("");

const pickerCallback = async (data: any) => {
if (data[google.picker.Response.ACTION] === google.picker.Action.PICKED) {
setPickedFile({ name: data.docs[0].name, id: data.docs[0].id });
gapi.client.sheets.spreadsheets
.get({
spreadsheetId: data.docs[0].id,
})
.then(function (response: any) {
setPickedFileSheets(response.result.sheets as GoogleSheet[]);
});
}
};

useEffect(() => {
const googleApiKey = window.REEARTH_CONFIG?.googleApiKey;
if (pickerApiLoaded && accessToken && googleApiKey) {
setIsLoading(false);
const picker = new google.picker.PickerBuilder()
.addView(google.picker.ViewId.SPREADSHEETS)
.setOAuthToken(accessToken)
.setDeveloperKey(googleApiKey)
.setCallback(pickerCallback)
.build();
picker.setVisible(true);
}
}, [accessToken, pickerApiLoaded]);

const handleClientLoad = async () => {
const googleApiKey = window.REEARTH_CONFIG?.googleApiKey;
setIsLoading(true);
const googleClientId = window.REEARTH_CONFIG?.googleClientId;
await gapi.load("client:auth2", () => {
gapi.client
.init({
apiKey: googleApiKey,
clientId: googleClientId,
scope:
"https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/documents.readonly",
discoveryDocs: ["https://sheets.googleapis.com/$discovery/rest?version=v4"],
})
.then(function () {
if (gapi.auth2.getAuthInstance().isSignedIn.get()) {
gapi.auth2.getAuthInstance().signOut();
}
setIsLoading(false);
});
});
await gapi.load("picker", () => {
setPickerApiLoaded(true);
});
};

const updateSigninStatus = (isSignedIn: boolean, accessToken: any) => {
if (isSignedIn) {
setAccessToken(accessToken);
}
};

const handleSheetSelect = useCallback(
(sheet: File) => {
setSelectedSheet({ id: sheet.id, name: sheet.name });
onSheetSelect({
accessToken,
fileId: pickedFile?.id as string,
sheetName: sheet.name as string,
});
},
[onSheetSelect, accessToken, pickedFile?.id],
);

const handleAuthClick = () => {
Promise.resolve(gapi.auth2.getAuthInstance().signIn()).then(() => {
updateSigninStatus(
gapi.auth2.getAuthInstance().isSignedIn.get(),
gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse().access_token,
);
});
};

useEffect(() => {
const gDriveScript = document.createElement("script");
gDriveScript.src = "https://apis.google.com/js/api.js";
gDriveScript.async = true;
gDriveScript.onload = () => {
handleClientLoad();
};
document.body.appendChild(gDriveScript);
return () => {
document.body.removeChild(gDriveScript);
};
}, []);

return {
isLoading,
pickedFile,
pickedFileSheets,
selectedSheet,
handleAuthClick,
handleSheetSelect,
handleClientLoad,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import React from "react";
import { useIntl } from "react-intl";
import { styled, useTheme } from "@reearth/theme";

import Icon from "@reearth/components/atoms/Icon";
import Flex from "@reearth/components/atoms/Flex";
import Loading from "@reearth/components/atoms/Loading";
import Divider from "@reearth/components/atoms/Divider";
import Text from "@reearth/components/atoms/Text";
import { metricsSizes } from "@reearth/theme/metrics";
import AssetCard from "@reearth/components/molecules/Common/AssetModal/AssetCard";
import AssetListItem from "@reearth/components/molecules/Common/AssetModal/AssetListItem";
import Button from "@reearth/components/atoms/Button";
import useHooks, { GoogleSheet, SheetParameter as SheetParam } from "./hooks";

export type SheetParameter = SheetParam;

export type Props = {
onReturn: () => void;
onSheetSelect: (sheet: SheetParam) => void;
syncLoading: boolean;
};

const Gdrive: React.FC<Props> = ({ onReturn, syncLoading, onSheetSelect }) => {
const intl = useIntl();
const theme = useTheme();

const {
isLoading,
pickedFile,
pickedFileSheets,
selectedSheet,
handleAuthClick,
handleSheetSelect,
} = useHooks(onSheetSelect);

return (
<>
<StyledIcon icon={"arrowLongLeft"} size={24} onClick={onReturn} />
{syncLoading && <Loading />}
<Flex justify="center" direction="column" align="center">
<Flex justify="center" align="center">
<GdriveIcon size={32} icon="googleDrive" />
<Text size="m" color={theme.main.strongText} weight="bold">
{intl.formatMessage({ defaultMessage: "Connect with Google Drive" })}
</Text>
</Flex>
<Divider margin="24px" />
{pickedFile?.id && (
<>
<AssetCard
name={pickedFile.name}
cardSize="medium"
icon="sheetFile"
iconSize="50px"
onCheck={() => handleAuthClick()}
/>
<Divider margin="24px" />
<AssetWrapper direction="column" justify="space-between">
<AssetList wrap="nowrap" direction="column" justify="space-between">
{pickedFileSheets?.map((sheetItem: GoogleSheet) => (
<AssetListItem
key={sheetItem.properties.sheetId}
asset={{ id: sheetItem.properties.sheetId, name: sheetItem.properties.title }}
onCheck={() => {
handleSheetSelect({
id: sheetItem.properties.sheetId,
name: sheetItem.properties.title,
});
}}
selected={sheetItem.properties.sheetId === selectedSheet?.id}
/>
))}
</AssetList>
</AssetWrapper>
<Divider margin="24px" />
</>
)}

{!pickedFile?.id && (
<>
<Text
size="m"
color={theme.colors.text.weak}
otherProperties={{ marginBottom: metricsSizes["m"] + "px" }}>
{intl.formatMessage({
defaultMessage: "Re:Earth supports uploading Google Sheets and CSV files.",
})}
</Text>

{isLoading ? (
<Loading />
) : (
<Button
large
text="Connect your google account"
buttonType="primary"
onClick={() => handleAuthClick()}
/>
)}
</>
)}
</Flex>
</>
);
};

const GdriveIcon = styled(Icon)`
margin-right: ${metricsSizes["m"]}px;
`;

const StyledIcon = styled(Icon)`
cursor: pointer;
`;

const AssetList = styled(Flex)`
width: 100%;
max-height: 458px;
overflow-y: scroll;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}

&::after {
content: "";
flex: "0 33%";
}
`;

const AssetWrapper = styled(Flex)`
height: 425px;
width: 100%;
`;

export default Gdrive;
Loading