Skip to content

Conversation

@takaokouji
Copy link

Summary

Implements the ability to load Scratch 3.0 projects (.sb3) directly from
Google Drive, addressing issue #426.

Key Features

  • Dynamic Script Loading: Google API scripts are loaded only when the
    user first clicks "Load from Google Drive", improving initial page load
    performance
  • OAuth 2.0 Authentication: Secure authentication using Google Identity
    Services
  • Google Picker API: Native Google Drive file selection UI
  • Seamless Integration: Works with existing project loading mechanism

Implementation Details

New Files

  1. src/lib/google-script-loader.js

    • Dynamically loads Google API Client Library and Google Identity Services
    • Prevents unnecessary script loading on initial page load
    • Implements promise-based loading with error handling
  2. src/lib/google-drive-api.js

    • Manages Google Drive authentication and file operations
    • Implements OAuth 2.0 flow using Google Identity Services
    • Provides Picker API integration for file selection
    • Handles file download from Google Drive
  3. src/containers/google-drive-loader.jsx

    • Higher Order Component (HOC) for Google Drive functionality
    • Integrates with existing project loading state management
    • Provides error handling and user feedback
  4. docs/google-drive-setup.md

    • Comprehensive setup guide for Google Cloud Platform configuration
    • Step-by-step instructions for OAuth 2.0 client creation
    • Troubleshooting section for common issues
  5. .env.example

    • Template for environment variable configuration
    • Documents required credentials
  6. cspell.json

    • Custom dictionary for Google API-specific terms

Modified Files

  1. src/components/menu-bar/menu-bar.jsx

    • Added "Load from Google Drive" menu item in File menu
    • Integrated GoogleDriveLoaderHOC into component composition
  2. .gitignore

    • Added .env to prevent accidental commits of sensitive credentials

Configuration

Environment Variables

Two environment variables are required:

  • GOOGLE_CLIENT_ID: OAuth 2.0 Client ID from Google Cloud Console
  • GOOGLE_API_KEY: API Key for Google Picker API

Setup Instructions

See docs/google-drive-setup.md for detailed setup instructions including:

  1. Creating a Google Cloud Platform project
  2. Enabling required APIs (Google Drive API, Google Picker API)
  3. Configuring OAuth 2.0 credentials
  4. Setting up environment variables

Technical Approach

Why Dynamic Script Loading?

Instead of including Google API scripts in index.html, we use dynamic
loading because:

  1. Performance: Most users won't use Google Drive loading, so we avoid
    loading unnecessary scripts
  2. Privacy: Google scripts are only loaded when explicitly requested
    by the user
  3. Maintainability: Scripts are loaded only when needed, reducing
    initial bundle size

Error Handling

  • Configuration validation on first use
  • User-friendly error messages for common issues
  • Graceful handling of authentication failures
  • Proper cleanup on picker cancellation

Testing

Manual Testing Steps

  1. Set up Google Cloud Platform credentials (see docs)
  2. Configure environment variables
  3. Build the project: npm run build
  4. Test the following scenarios:
    • Click "Load from Google Drive" without configuration (should show error)
    • Configure credentials and try again (should show Google Picker)
    • Select a .sb3 file (should load project)
    • Cancel picker (should close without error)
    • Try to load invalid file type (should show error)

Lint and Build

  • ✅ All lint checks pass: npm run test:lint
  • ✅ Build succeeds: npm run build

Breaking Changes

None. This is a new feature that doesn't affect existing functionality.

Future Enhancements

Possible improvements for future PRs:

  • Support for saving projects to Google Drive
  • Integration with Google Drive file picker in "Save As" dialog
  • Automatic sync with Google Drive
  • Support for loading from shared Google Drive links

Related Issues

Closes #426


🤖 Generated with Claude Code

Co-Authored-By: Claude [email protected]

takaokouji and others added 11 commits November 24, 2025 00:02
Implements the ability to load Scratch 3.0 projects (.sb3) directly
from Google Drive.

Key features:
- Dynamic loading of Google API scripts (improves initial page load)
- OAuth 2.0 authentication with Google Identity Services
- Google Picker API integration for file selection
- Seamless project loading from Google Drive

Implementation details:
- Created dynamic script loader (src/lib/google-script-loader.js)
  to load Google APIs on demand
- Implemented Google Drive API module (src/lib/google-drive-api.js)
  for authentication and file operations
- Added Google Drive loader HOC container
  (src/containers/google-drive-loader.jsx)
- Added "Load from Google Drive" menu item to File menu
- Created comprehensive setup documentation
  (docs/google-drive-setup.md)
- Added environment variable configuration (.env.example)
- Updated .gitignore to exclude .env files
- Added cspell.json for custom dictionary words

Configuration:
- Requires GOOGLE_CLIENT_ID and GOOGLE_API_KEY environment variables
- See docs/google-drive-setup.md for detailed GCP setup instructions

Closes #426

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Fix environment variable handling in webpack for Google API credentials
- Remove MIME type filter to show all files in Google Picker
- Fix LoadingStates import and usage in google-drive-loader
- Wrap picker callback in try-catch to prevent error propagation
- Add comprehensive debug logging throughout download process
- Update Japanese translations for Google Drive features
- Fix COOP/COEP headers configuration in webpack
- Update documentation for Docker environment setup

This commit resolves the issues preventing Google Drive file loading
from working properly. The main fixes include:
1. Correcting the requestProjectUpload call to pass loadingState
2. Using fetch API for reliable binary file downloads
3. Proper error handling in callback invocations

Fixes #426

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Fixed React warning about openUrlLoaderModal prop on DOM elements
- Added openUrlLoaderModal to prop filtering in GUIComponent (gui.jsx)
- Added openUrlLoaderModal to prop filtering in GoogleDriveLoaderComponent
- Removed timing measurement logs from google-drive-loader.jsx

Changes:
- src/components/gui/gui.jsx: Filter out openUrlLoaderModal using underscore prefix
- src/containers/google-drive-loader.jsx: Filter out openUrlLoaderModal, remove timing logs

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- File menu now closes immediately when user selects "Load from Google Drive"
- Improves UX by clearing the menu before showing Google Picker dialog
- Previously, menu stayed open until file loading completed

Changes:
- Import closeFileMenu action from reducers/menus
- Call closeFileMenu() in handleStartSelectingGoogleDrive method
- Add closeFileMenu to mapDispatchToProps and PropTypes
- Filter closeFileMenu prop in render to prevent passing to DOM

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Google Picker now displays in the same language as the application
- Added locale parameter to showPicker method in google-drive-api.js
- Set PickerBuilder locale using .setLocale() method
- Get current locale from Redux state (state.locales.locale)
- Pass locale from google-drive-loader to showPicker

Additional fixes:
- Removed unused mimeType variable (lint error)
- Fixed max-len error in console.error statement

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
…name to HOC

Loading modal timing improvements:
- Show loading modal immediately before vm.loadProject() (matching sb-file-uploader-hoc.jsx)
- Changed onLoadingStarted to dispatch openLoadingProject() instead of requestProjectUpload()
- Removed early onShowLoadingProject() call that caused delayed modal display
- Removed unused requestProjectUpload import

File naming consistency:
- Renamed google-drive-loader.jsx to google-drive-loader-hoc.jsx to match exported name
- Updated import in menu-bar.jsx to use new file name

This ensures Google Drive loader behaves identically to the existing file upload flow.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Improved UX by showing loading screen before file download starts:

- Modified handlePickerResponse to call callback twice:
  1. Immediately when file is selected (selected: true, fileName)
  2. After download completes (success: true, fileName, fileData)

- Updated handlePickerCallback to handle both stages:
  1. On file selected: Set project title and show loading modal
  2. On download complete: Convert to Uint8Array and load project

This eliminates the delay between file selection and loading screen display,
providing immediate visual feedback during the download process.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Changed OAuth 2.0 scope from drive.readonly to drive.file to enable upload functionality:

Changes to src/lib/google-drive-api.js:
- Updated SCOPES constant from 'drive.readonly' to 'drive.file'
- Added comments explaining the scope capabilities:
  - Reading files selected via Picker
  - Uploading new files to Google Drive

Changes to docs/google-drive-setup.md:
- Updated recommended scope to 'drive.file'
- Added detailed explanation of scope capabilities
- Added note that 'drive.readonly' prevents upload functionality

Required GCP configuration:
- Users must update OAuth consent screen to add 'drive.file' scope
- Existing auth tokens will be invalidated after scope change
- Users will need to re-authenticate with new permissions

This resolves the "Server rejected" error when uploading files via Google Picker.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Removed all development/debugging console.log statements while keeping
essential error logging (console.error) for production diagnostics.

Changes to src/containers/google-drive-loader-hoc.jsx:
- Removed debug logs from handlePickerCallback:
  - Picker callback details (cancelled, hasError, hasSuccess, etc.)
  - User cancelled picker notification
  - File selected notification
  - Project title setting notification
  - Loading modal display notification
  - File download details (fileName, fileDataType, constructor, byteLength)
  - Uint8Array conversion logs (2 occurrences)
- Kept error logging for picker errors

Changes to src/lib/google-drive-api.js:
- Removed debug logs from downloadFile method:
  - Download start notification
  - gapi.client.drive.files.get call notification
  - Response details (status, bodyType, bodyLength, etc.)
  - Binary string to ArrayBuffer conversion notification
  - Download success notification
- Removed debug log from handlePickerResponse (file download success)
- Kept error logging for download failures and callback errors

All functionality remains unchanged - only logging statements were removed.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Use .setQuery('.sb3') to filter files in Google Picker
- Add internationalization for picker dialog title
- Add Japanese translation for picker title: "Google ドライブから Scratch 3.0 プロジェクト (.sb3) を選択"
- Pass localized title from HOC to Google Drive API

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@takaokouji takaokouji merged commit 2f435b4 into develop Dec 14, 2025
2 checks passed
@takaokouji takaokouji deleted the feature/google-drive-loader branch December 14, 2025 01:05
github-actions bot pushed a commit that referenced this pull request Dec 14, 2025
…e-drive-loader

Add Google Drive file loading functionality
takaokouji added a commit that referenced this pull request Dec 14, 2025
This commit implements the ability to save Scratch 3.0 projects (.sb3)
to Google Drive with a custom dialog for specifying the filename and
save location, complementing the existing "Load from Google Drive"
functionality.

## New Features

- **Menu Integration**: Added "Save to Google Drive" menu item in File menu
- **Save Dialog**: Custom dialog with filename input and save location selection
- **Folder Selection**: Users can save to My Drive root or select a specific folder
- **File Upload**: Multipart upload using Google Drive Files API v3

## Implementation

### New Components

1. **google-drive-saver-hoc.jsx**: HOC for Google Drive save functionality
   - Manages save dialog state and upload process
   - Converts Ruby code to blocks before saving
   - Provides error handling and user feedback

2. **google-drive-save-dialog.jsx**: Custom save dialog component
   - Filename input with .sb3 extension validation
   - Save location dropdown (My Drive or folder selection)
   - Cancel, Reset, and Save buttons

3. **google-drive-save-dialog.css**: Dialog styling

### Modified Files

1. **google-drive-api.js**: Added upload functionality
   - uploadFile(): Multipart upload to Google Drive
   - showFolderPicker(): Folder selection via Google Picker
   - handleFolderPickerResponse(): Handle folder picker callback

2. **menu-bar.jsx**: Integrated save functionality
   - Added "Save to Google Drive" menu item
   - Integrated GoogleDriveSaverHOC
   - Added GoogleDriveSaveDialog component

3. **ja.js**: Added Japanese translations for all new UI elements

## Technical Details

- Uses existing OAuth 2.0 authentication from GoogleDriveLoaderHOC
- Leverages drive.file scope (already configured)
- Multipart upload with metadata and file content
- Base64 encoding for binary file data
- Folder picker using Google Picker API

## Testing

- ✅ Lint checks pass: npm run test:lint
- Dialog UI follows app.diagrams.net design patterns
- Supports both My Drive root and folder selection

## Related

- Implements feature request from Issue #428
- Built on top of PR #427 (Google Drive load functionality)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Google Drive file loading functionality

2 participants