Skip to content

Commit

Permalink
fix: improve google integration ux by saving last folder + fix creati…
Browse files Browse the repository at this point in the history
…ng worlds
  • Loading branch information
zardoy committed Mar 23, 2024
1 parent 85c0eb8 commit 2ee21d5
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 77 deletions.
30 changes: 15 additions & 15 deletions src/googledrive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,22 @@ export const useGoogleLogIn = () => {
const login = useGoogleLogin({
onSuccess (tokenResponse) {
localStorage.hasEverLoggedIn = true
googleProviderData.accessToken = tokenResponse.access_token
googleProviderData.expiresIn = ref(new Date(Date.now() + tokenResponse.expires_in * 1000))
googleProviderData.hasEverLoggedIn = true
googleProviderState.accessToken = tokenResponse.access_token
googleProviderState.expiresIn = ref(new Date(Date.now() + tokenResponse.expires_in * 1000))
googleProviderState.hasEverLoggedIn = true
},
// prompt: hasEverLoggedIn ? 'none' : 'consent',
scope: SCOPES,
flow: 'implicit',
onError (error) {
const accessDenied = error.error === 'access_denied' || error.error === 'invalid_scope' || (error as any).error_subtype === 'access_denied'
if (accessDenied) {
googleProviderData.hasEverLoggedIn = false
googleProviderState.hasEverLoggedIn = false
}
}
})
return () => login({
prompt: googleProviderData.hasEverLoggedIn ? 'none' : 'consent'
prompt: googleProviderState.hasEverLoggedIn ? 'none' : 'consent'
})
}

Expand All @@ -61,26 +61,26 @@ export const possiblyHandleStateVariable = async () => {
async callback (response) {
if (response.error) {
setLoadingScreenStatus('Error: ' + response.error, true)
googleProviderData.hasEverLoggedIn = false
googleProviderState.hasEverLoggedIn = false
return
}
setLoadingScreenStatus('Opening world in read only mode...')
googleProviderData.accessToken = response.access_token
googleProviderState.accessToken = response.access_token
await mountGoogleDriveFolder(true, parsed.ids[0])
await loadInMemorySave('/google')
}
})
const choice = await showOptionsModal('Select an action...', ['Login'])
if (choice === 'Login') {
tokenClient.requestAccessToken({
prompt: googleProviderData.hasEverLoggedIn ? '' : 'consent',
prompt: googleProviderState.hasEverLoggedIn ? '' : 'consent',
})
} else {
window.close()
}
}

export const googleProviderData = proxy({
export const googleProviderState = proxy({
accessToken: (localStorage.saveAccessToken ? localStorage.accessToken : null) as string | null,
hasEverLoggedIn: !!(localStorage.hasEverLoggedIn),
isReady: false,
Expand All @@ -92,18 +92,18 @@ export const googleProviderData = proxy({
} | null
})

subscribe(googleProviderData, () => {
localStorage.googleReadonlyMode = googleProviderData.readonlyMode
localStorage.lastSelectedFolder = googleProviderData.lastSelectedFolder ? JSON.stringify(googleProviderData.lastSelectedFolder) : null
if (googleProviderData.hasEverLoggedIn) {
subscribe(googleProviderState, () => {
localStorage.googleReadonlyMode = googleProviderState.readonlyMode
localStorage.lastSelectedFolder = googleProviderState.lastSelectedFolder ? JSON.stringify(googleProviderState.lastSelectedFolder) : null
if (googleProviderState.hasEverLoggedIn) {
localStorage.hasEverLoggedIn = true
} else {
delete localStorage.hasEverLoggedIn
}

if (localStorage.saveAccessToken && googleProviderData) {
if (localStorage.saveAccessToken && googleProviderState) {
// For testing only
localStorage.accessToken = googleProviderData.accessToken || null
localStorage.accessToken = googleProviderState.accessToken || null
} else {
delete localStorage.accessToken
}
Expand Down
132 changes: 70 additions & 62 deletions src/react/SingleplayerProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { googleDriveGetFileIdFromPath, mountExportFolder, mountGoogleDriveFolder
import { hideCurrentModal, showModal } from '../globalState'
import { haveDirectoryPicker, setLoadingScreenStatus } from '../utils'
import { exportWorld } from '../builtinCommands'
import { googleProviderData, useGoogleLogIn, GoogleDriveProvider, isGoogleDriveAvailable, APP_ID } from '../googledrive'
import { googleProviderState, useGoogleLogIn, GoogleDriveProvider, isGoogleDriveAvailable, APP_ID } from '../googledrive'
import Singleplayer, { WorldProps } from './Singleplayer'
import { useIsModalActive } from './utils'
import { showOptionsModal } from './SelectOption'
Expand All @@ -18,6 +18,7 @@ const worldsProxy = proxy({
value: null as null | WorldProps[],
brokenWorlds: [] as string[],
selectedProvider: 'local' as 'local' | 'google',
selectedGoogleId: '',
error: '',
})

Expand Down Expand Up @@ -48,7 +49,7 @@ export const readWorlds = (abortController: AbortController) => {
(async () => {
const brokenWorlds = [] as string[]
try {
const loggedIn = !!googleProviderData.accessToken
const loggedIn = !!googleProviderState.accessToken
worldsProxy.value = null
if (worldsProxy.selectedProvider === 'google' && !loggedIn) {
worldsProxy.value = []
Expand Down Expand Up @@ -135,7 +136,7 @@ export const loadGoogleDriveApi = async () => {
gapi.load('client', () => {
gapi.load('client:picker', () => {
void gapi.client.load('https://www.googleapis.com/discovery/v1/apis/drive/v3/rest').then(() => {
googleProviderData.isReady = true
googleProviderState.isReady = true
resolve()
})
})
Expand All @@ -145,24 +146,17 @@ export const loadGoogleDriveApi = async () => {

const Inner = () => {
const worlds = useSnapshot(worldsProxy).value as WorldProps[] | null
const { selectedProvider, error, brokenWorlds } = useSnapshot(worldsProxy)
const { selectedProvider, error, brokenWorlds, selectedGoogleId } = useSnapshot(worldsProxy)
const readWorldsAbortController = useRef(new AbortController())

useEffect(() => {
return () => {
worldsProxy.selectedProvider = 'local'
}
}, [])

// 3rd party providers
useEffect(() => {
if (selectedProvider !== 'google') return
void loadGoogleDriveApi()
}, [selectedProvider])

const [selectedGoogleId, setSelectedGoogleId] = useState('')
const loggedIn = !!useSnapshot(googleProviderData).accessToken
const googleDriveReadonly = useSnapshot(googleProviderData).readonlyMode
const loggedIn = !!useSnapshot(googleProviderState).accessToken
const googleDriveReadonly = useSnapshot(googleProviderState).readonlyMode

useEffect(() => {
(async () => {
Expand All @@ -171,7 +165,7 @@ const Inner = () => {
worldsProxy.value = []
return
}
await mountGoogleDriveFolder(googleProviderData.readonlyMode, selectedGoogleId)
await mountGoogleDriveFolder(googleProviderState.readonlyMode, selectedGoogleId)
const exists = async (path) => {
try {
await fs.promises.stat(path)
Expand Down Expand Up @@ -199,21 +193,74 @@ const Inner = () => {

const googleLogIn = useGoogleLogIn()

const isGoogleProviderReady = useSnapshot(googleProviderData).isReady
const providerActions = selectedProvider === 'google' ? isGoogleProviderReady ? loggedIn ? {
const googlePicker = useRef/* <google.picker.Picker | null> */(null as any)

useEffect(() => {
return () => {
googlePicker.current?.dispose()
}
})

const selectGoogleFolder = async () => {
if (googleProviderState.lastSelectedFolder) {
// ask to use saved previous fodler
const choice = await showOptionsModal(`Use previously selected folder "${googleProviderState.lastSelectedFolder.name}"?`, ['Yes', 'No'])
if (!choice) return
if (choice === 'Yes') {
worldsProxy.selectedGoogleId = googleProviderState.lastSelectedFolder.id
return
}
}

const { google } = window

const view = new google.picker.DocsView(google.picker.ViewId.FOLDERS)
.setIncludeFolders(true)
.setMimeTypes('application/vnd.google-apps.folder')
.setSelectFolderEnabled(true)
.setParent('root')


googlePicker.current = new google.picker.PickerBuilder()
.enableFeature(google.picker.Feature.NAV_HIDDEN)
.enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
.setDeveloperKey('AIzaSyBTiHpEqaLL7mEcrsnSS4M-z8cpRH5UwY0')
.setAppId(APP_ID)
.setOAuthToken(googleProviderState.accessToken)
.addView(view)
.addView(new google.picker.DocsUploadView())
.setTitle('Select a folder with your worlds')
.setCallback((data) => {
if (data.action === google.picker.Action.PICKED) {
googleProviderState.lastSelectedFolder = {
id: data.docs[0].id,
name: data.docs[0].name,
}
worldsProxy.selectedGoogleId = data.docs[0].id
}
})
.build()
googlePicker.current.setVisible(true)
}

const isGoogleProviderReady = useSnapshot(googleProviderState).isReady
const providerActions = loggedIn && selectedProvider === 'google' && isGoogleProviderReady && !selectedGoogleId ? {
'Select Folder': selectGoogleFolder
} : selectedProvider === 'google' ? isGoogleProviderReady ? loggedIn ? {
'Log Out' () {
googleProviderData.hasEverLoggedIn = false
googleProviderData.accessToken = null
googleProviderData.lastSelectedFolder = null
window.google.accounts.oauth2.revoke(googleProviderData.accessToken)
googleProviderState.hasEverLoggedIn = false
googleProviderState.accessToken = null
googleProviderState.lastSelectedFolder = null
window.google.accounts.oauth2.revoke(googleProviderState.accessToken)
},
async [`Read Only: ${googleDriveReadonly ? 'ON' : 'OFF'}`] () {
if (googleProviderData.readonlyMode) {
if (googleProviderState.readonlyMode) {
const choice = await showOptionsModal('[Unstable Feature] Enabling world save might corrupt your worlds, eg remove entities (note: you can always restore previous version of files in Drive)', ['Continue'])
if (choice !== 'Continue') return
}
googleProviderData.readonlyMode = !googleProviderData.readonlyMode
googleProviderState.readonlyMode = !googleProviderState.readonlyMode
},
'Select Folder': selectGoogleFolder,
// 'Worlds Path': <Input rootStyles={{ width: 100 }} placeholder='Worlds path' defaultValue={worldsPath} onBlur={(e) => {
// googleProviderData.worldsPath = e.target.value
// }} />
Expand All @@ -224,48 +271,9 @@ const Inner = () => {
} : undefined
// end

useEffect(() => {
let picker
if (loggedIn && selectedProvider === 'google' && isGoogleProviderReady) {
const { google } = window

const view = new google.picker.DocsView(google.picker.ViewId.FOLDERS)
.setIncludeFolders(true)
.setMimeTypes('application/vnd.google-apps.folder')
.setSelectFolderEnabled(true)
.setParent('root')


picker = new google.picker.PickerBuilder()
.enableFeature(google.picker.Feature.NAV_HIDDEN)
.enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
.setDeveloperKey('AIzaSyBTiHpEqaLL7mEcrsnSS4M-z8cpRH5UwY0')
.setAppId(APP_ID)
.setOAuthToken(googleProviderData.accessToken)
.addView(view)
.addView(new google.picker.DocsUploadView())
.setTitle('Select a folder with your worlds')
.setCallback((data) => {
if (data.action === google.picker.Action.PICKED) {
googleProviderData.lastSelectedFolder = {
id: data.docs[0].id,
name: data.docs[0].name,
}
setSelectedGoogleId(data.docs[0].id)
}
})
.build()
picker.setVisible(true)
}

return () => {
if (picker) picker.dispose()
}
}, [selectedProvider, loggedIn])

return <Singleplayer
error={error}
isReadonly={selectedProvider === 'google' && (googleDriveReadonly || !isGoogleProviderReady)}
isReadonly={selectedProvider === 'google' && (googleDriveReadonly || !isGoogleProviderReady || !selectedGoogleId)}
providers={{
local: 'Local',
google: 'Google Drive',
Expand Down

0 comments on commit 2ee21d5

Please sign in to comment.