Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 2 additions & 3 deletions Assets/Resources/Card/CardTextController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,9 @@ public void UpdateCardText(string jsonData)

private IEnumerator LoadImageFromUrl(string url)
{
string proxyUrl = $"https://cors-anywhere.herokuapp.com/{url}";
Debug.Log($"Requesting image from: {proxyUrl}");
Debug.Log($"Requesting image from: {url}");

using (UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(proxyUrl))
using (UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url))
{
// Add the required headers
uwr.SetRequestHeader("Origin", "http://localhost");
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoding the Origin header to http://localhost will fail CORS checks when deployed to any other host and, in WebGL, browsers ignore attempts to override the Origin header. Remove this header override and rely on proper CORS on the image host (Firebase Storage download URLs already return appropriate CORS headers).

Suggested change
uwr.SetRequestHeader("Origin", "http://localhost");

Copilot uses AI. Check for mistakes.
Expand Down
70 changes: 68 additions & 2 deletions Assets/WebGLTemplates/XRCardTemplate/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,23 @@
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">

<!-- Firebase SDKs -->
<script src="https://www.gstatic.com/firebasejs/9.22.2/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.22.2/firebase-storage-compat.js"></script>
<script>
const firebaseConfig = {
apiKey: "AIzaSyBIuTnTru0iILMqOyKRzu2mZtIBnxEhdug",
authDomain: "fir-test-d319a.firebaseapp.com",
projectId: "fir-test-d319a",
storageBucket: "fir-test-d319a.firebasestorage.app",
messagingSenderId: "913686234554",
appId: "1:913686234554:android:0b20d5d7e441a710"
};
firebase.initializeApp(firebaseConfig);
const storage = firebase.storage();
</script>
Comment on lines +16 to +26
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Firebase config is not valid for the web SDK: storageBucket should be the bucket name (e.g., "fir-test-d319a.appspot.com"), not the download domain, and appId should be the Web App ID (ends with ":web:..."), not an Android app ID. Use the exact Web App config from the Firebase Console; otherwise firebase.storage() operations will fail at runtime.

Copilot uses AI. Check for mistakes.


<style>
/* Override Bootstrap primary color to match navbar color */
:root {
Expand Down Expand Up @@ -79,10 +96,18 @@ <h6 class="fw-bold">AR Card</h6>
<option value="custom">Custom</option>
</select>
</div>

<div class="mb-3" id="customImageUrlInput" style="display: none;">
<label for="customImageUrl" class="form-label">Custom Image URL</label>
<input type="text" class="form-control" id="customImageUrl" placeholder="Enter image URL">
<label style="display: none;" for="customImageUrl" class="form-label">URL</label>
<input style="display: none;" type="text" class="form-control" id="customImageUrl" placeholder="Enter image URL">

<label for="customImageFile" class="form-label">Image</label>
<div style="display: flex; align-items: center;">
<input type="file" class="form-control" id="customImageFile" accept="image/*" onchange="uploadImage()">
<i id="loadingIcon" class="fas fa-spinner fa-spin" style="display: none; margin-left: 10px;"></i>
</div>
</div>

<div class="mb-3">
<label for="cardTopInput" class="form-label">Top Text</label>
<input type="text" class="form-control" id="cardTopInput" placeholder="Enter top text">
Expand Down Expand Up @@ -140,6 +165,47 @@ <h5 class="modal-title">Your Custom Card Link</h5>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"></script>
<script>
function uploadImage() {
const fileInput = document.getElementById('customImageFile');
const file = fileInput.files[0];
const generateCardButton = document.querySelector('.btn-primary');
const loadingIcon = document.getElementById('loadingIcon');
if (!file) {
Comment on lines +171 to +173
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Selecting the action button via the generic .btn-primary selector is brittle and may target the wrong element if there are multiple primary buttons. Assign a dedicated id (e.g., "generateCardButton") to the intended button and select it with getElementById for reliability.

Copilot uses AI. Check for mistakes.
alert('Please select an image file to upload.');
return;
}
Comment on lines +168 to +176
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The upload runs unauthenticated; with default Firebase Storage rules this will fail, and loosening rules to allow public writes would expose the bucket to abuse. Add Firebase Auth (e.g., signInAnonymously) before uploads and enforce restrictive Storage rules (path scoping, file type/size limits) to protect the bucket.

Copilot uses AI. Check for mistakes.

generateCardButton.disabled = true; // Disable the button
loadingIcon.style.display = 'inline-block'; // Show loading icon

const storageRef = storage.ref('images/' + file.name);
const uploadTask = storageRef.put(file);
Comment on lines +181 to +182
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the raw client file name as the Storage object key risks collisions and exposes user-provided filenames. Generate a unique object name (for example, prefix with a UUID/timestamp) and normalize the filename before upload.

Copilot uses AI. Check for mistakes.

uploadTask.on('state_changed',
(snapshot) => {
// Observe state change events such as progress, pause, and resume
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log('Upload is ' + progress + '% done');
},
(error) => {
// Handle unsuccessful uploads
console.error('Upload failed:', error);
alert('Failed to upload image.');
generateCardButton.disabled = false; // Re-enable the button on failure
loadingIcon.style.display = 'none'; // Hide loading icon
},
() => {
// Handle successful uploads on complete
uploadTask.snapshot.ref.getDownloadURL().then((downloadURL) => {
console.log('File available at', downloadURL);
document.getElementById('customImageUrl').value = downloadURL;
generateCardButton.disabled = false; // Re-enable the button on success
loadingIcon.style.display = 'none'; // Hide loading icon
});
}
);
}

function toggleCustomImageInput() {
const cardImageInput = document.getElementById('cardImageInput');
const customImageUrlInput = document.getElementById('customImageUrlInput');
Expand Down
3 changes: 2 additions & 1 deletion Assets/WebGLTemplates/XRCardTemplate/viewCard.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ <h1 id="landingPageHeader">Happy Birthday!</h1>
<div class="decorative-element"></div>
<div class="decorative-element"></div>

<button id="viewCardButton" class="mt-4">View Card</button>
<button id="viewCardButton" class="mt-4" disabled>View Card</button>

<div class="modal fade" id="instructionsModal" tabindex="-1" aria-labelledby="instructionsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
Expand Down Expand Up @@ -110,6 +110,7 @@ <h5 class="modal-title" id="instructionsModalLabel">How to Use the AR Card</h5>
unityInstance = instance;
loadingBar.style.display = "none";
sendCardTextToUnity();
viewCardButton.disabled = false;
if (fullscreenButton) {
fullscreenButton.onclick = () => {
unityInstance.SetFullscreen(1);
Expand Down
2 changes: 1 addition & 1 deletion ProjectSettings/ProjectSettings.asset
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ PlayerSettings:
webGLDebugSymbols: 0
webGLEmscriptenArgs:
webGLModulesDirectory:
webGLTemplate: PROJECT:XRCardTemplate
webGLTemplate: APPLICATION:Minimal
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the WebGL template to APPLICATION:Minimal means the build will use the built-in Minimal template and ignore your custom Assets/WebGLTemplates/XRCardTemplate/index.html and viewCard.html (including the Firebase scripts and UI changes). Switch this back to the project template so your updated HTML is applied at runtime.

Suggested change
webGLTemplate: APPLICATION:Minimal
webGLTemplate: PROJECT:XRCardTemplate

Copilot uses AI. Check for mistakes.
webGLAnalyzeBuildSize: 0
webGLUseEmbeddedResources: 0
webGLCompressionFormat: 1
Expand Down
16 changes: 16 additions & 0 deletions ProjectSettings/TimelineSettings.asset
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &1
MonoBehaviour:
m_ObjectHideFlags: 61
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a287be6c49135cd4f9b2b8666c39d999, type: 3}
m_Name:
m_EditorClassIdentifier:
assetDefaultFramerate: 60
m_DefaultFrameRate: 60
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,28 @@ After customizing your AR greeting card, you can generate a shareable link. Clic
- WebGL
- HTML/CSS/JavaScript

## Development

### Running the Unity WebGL Build Locally

To serve the Unity WebGL build locally without CORS issues:

1. Navigate to your Unity build location:

```powershell
cd <your-unity-build-folder>
```

2. Run the http-server with CORS enabled:

```powershell
npx http-server -p 8000 --cors
```

3. Access at: `http://localhost:8000`

**Note:** The `--cors` flag ensures proper CORS headers are sent, which is essential for Unity WebGL builds to work correctly when loaded from different origins.

## Features

- Customizable landing page and greeting card themes
Expand All @@ -30,4 +52,4 @@ After customizing your AR greeting card, you can generate a shareable link. Clic

## Contributing

Contributions are welcome! Please feel free to submit a pull request or open an issue with suggestions for improvements or new features.
Contributions are welcome! Please feel free to submit a pull request or open an issue with suggestions for improvements or new features.