Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3ce71da
Add component to show invite link (WIP)
Bettelstab Jun 23, 2025
505ec10
Show invite link with copy functionality and QR-Code, add tests
Bettelstab Jun 24, 2025
b0a5f2f
Query secrets
Bettelstab Jun 25, 2025
d5e7301
Update directus collections
Bettelstab Jun 25, 2025
b3c8b7f
Add config and invite api
Bettelstab Jun 25, 2025
de89cd0
Let vite resolve paths using tsconfig
Bettelstab Jun 25, 2025
f1d1943
Redeem invite link when logged in or after logging in
Bettelstab Jun 25, 2025
2b3b3d5
Redirect to inviting profile when redeeming
Bettelstab Jun 25, 2025
c0f4715
Fix some logic with login and redeeming
Bettelstab Jun 25, 2025
7388a02
Use correct redeem flow
Bettelstab Jun 25, 2025
814229e
Hide missing form error
Bettelstab Jun 25, 2025
5dba5e0
Add basic relations view
Bettelstab Jun 27, 2025
4209bf2
Pass profile to redeem Api and adapt to changed redeem flow
Bettelstab Jun 27, 2025
fe06536
Merge main
Bettelstab Jul 5, 2025
7eeb7f3
Remove unnecessary aliases in vite config
Bettelstab Jul 5, 2025
250b843
Remove dead import
Bettelstab Jul 5, 2025
509c89f
gitignore mac specific file
Bettelstab Jul 5, 2025
17bd81d
Remove lazy loading
Bettelstab Jul 5, 2025
70e3f7f
Fix linting
Bettelstab Jul 5, 2025
1433786
add InviteApi import
antontranelis Jul 8, 2025
08cf110
Change case of file name (tbd)
Bettelstab Jul 9, 2025
438d590
Don't toast error if user profile was not loaded yet
Bettelstab Jul 9, 2025
ed4cdce
Fix casing
Bettelstab Jul 10, 2025
81e2d53
Refactor layers and items into one common state (WIP)
Bettelstab Jul 10, 2025
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: 4 additions & 1 deletion app/.env
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
VITE_OPEN_COLLECTIVE_API_KEY=your_key
VITE_OPEN_COLLECTIVE_API_KEY=your_key
VITE_API_URL=https://api.utopia-lab.org
VITE_VALIDATE_INVITE_FLOW_ID=01d61db0-25aa-4bfa-bc24-c6a8f208a455
VITE_REDEEM_INVITE_FLOW_ID=cc80ec73-ecf5-4789-bee5-1127fb1a6ed4
2 changes: 1 addition & 1 deletion app/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ module.exports = {
'import/no-relative-parent-imports': [
'error',
{
ignore: ['#[src,types,root,components,utils,assets]/*'],
ignore: ['#[src,types,root,components,utils,assets]/*', '@/config/*'],
},
],
'import/no-self-import': 'error',
Expand Down
1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules/
dist/
.DS_Store
50 changes: 48 additions & 2 deletions app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"react-dom": "^18.2.0",
"react-rnd": "^10.4.1",
"react-router-dom": "^6.23.0",
"vite-tsconfig-paths": "^5.1.4",
"utopia-ui": "^3.0.111"
},
"devDependencies": {
Expand Down
17 changes: 12 additions & 5 deletions app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
Content,
AuthProvider,
Modal,
InvitePage,
LoginPage,
SignupPage,
Quests,
Expand All @@ -33,8 +34,8 @@ import {
MarketView,
SVG,
LoadingMapOverlay,
ProfileView,
ProfileForm,
ProfileView,
UserSettings,
} from 'utopia-ui'

Expand All @@ -48,11 +49,16 @@ import { itemsApi } from './api/itemsApi'
import { layersApi } from './api/layersApi'
import { mapApi } from './api/mapApi'
import { permissionsApi } from './api/permissionsApi'
import { userApi } from './api/userApi'
import { UserApi } from './api/userApi'
import { ModalContent } from './ModalContent'
import { Landingpage } from './pages/Landingpage'
import MapContainer from './pages/MapContainer'
import { getBottomRoutes, routes } from './routes/sidebar'
import { config } from '@/config'
import { InviteApi } from './api/inviteApi'

const userApi = new UserApi()
const inviteApi = new InviteApi(userApi)

function App() {
const [permissionsApiInstance, setPermissionsApiInstance] = useState<permissionsApi>()
Expand Down Expand Up @@ -140,12 +146,12 @@ function App() {
if (map && layers)
return (
<div className='App tw:overflow-x-hidden'>
<AuthProvider userApi={new userApi()}>
<AuthProvider userApi={userApi} inviteApi={inviteApi}>
<AppShell
assetsApi={new assetsApi('https://api.utopia-lab.org/assets/')}
appName={map.name}
embedded={embedded}
openCollectiveApiKey={import.meta.env.VITE_OPEN_COLLECTIVE_API_KEY}
openCollectiveApiKey={config.openCollectiveApiKey}
>
<Permissions
api={permissionsApiInstance}
Expand All @@ -160,7 +166,8 @@ function App() {
<Quests />
<Routes>
<Route path='/*' element={<MapContainer map={map} layers={layers} />}>
<Route path='login' element={<LoginPage />} />
<Route path='invite/:id' element={<InvitePage inviteApi={inviteApi} />} />
<Route path='login' element={<LoginPage inviteApi={inviteApi} />} />
<Route path='signup' element={<SignupPage />} />
<Route
path='reset-password'
Expand Down
6 changes: 6 additions & 0 deletions app/src/api/directus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,19 @@ interface CustomUserFields {
position: Point
}

interface ItemSecret {
secret: string
item: string
}

export interface MyCollections {
places: Place[]
events: Event[]
updates: Update[]
tags: Tag[]
projects: Project[]
directus_users: CustomUserFields[]
item_secrets: ItemSecret[]
items: Item[]
team: any[]
features: any[]
Expand Down
79 changes: 79 additions & 0 deletions app/src/api/inviteApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* @eslint-disable-next-line import/no-relative-parent-imports */
import { config } from '@/config'

import type { UserApi } from 'utopia-ui'

type InvitingProfileResponse = [
{
item: string
},
]

export class InviteApi {
userApi: UserApi

constructor(userApi: UserApi) {
this.userApi = userApi
}

async validateInvite(inviteId: string): Promise<string | null> {
try {
const response = await fetch(
`${config.apiUrl}/flows/trigger/${config.validateInviteFlowId}?secret=${inviteId}`,
{
method: 'GET',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
},
},
)

if (!response.ok) return null

const data = (await response.json()) as InvitingProfileResponse

return data[0].item
} catch (error: unknown) {
// eslint-disable-next-line no-console
console.error('Error fetching inviting profile:', error)
if (error instanceof Error && error.message) {
throw new Error(error.message)
} else {
throw new Error('An unknown error occurred while fetching the inviting profile.')
}
}
}

async redeemInvite(inviteId: string, itemId: string): Promise<string | null> {
try {
const token = await this.userApi.getToken()

if (!token) {
throw new Error('User is not authenticated. Cannot redeem invite.')
}

const response = await fetch(`${config.apiUrl}/flows/trigger/${config.redeemInviteFlowId}`, {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ secret: inviteId, item: itemId }),
})

if (!response.ok) return null

return (await response.json()) as string
} catch (error: unknown) {
// eslint-disable-next-line no-console
console.error('Error fetching inviting profile:', error)
if (error instanceof Error && error.message) {
throw new Error(error.message)
} else {
throw new Error('An unknown error occurred while fetching the inviting profile.')
}
}
}
}
1 change: 1 addition & 0 deletions app/src/api/itemsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export class itemsApi<T> implements ItemsApi<T> {
readItems(this.collectionName as never, {
fields: [
'*',
'secrets.*',
'to.*',
'relations.*',
'user_created.*',
Expand Down
4 changes: 2 additions & 2 deletions app/src/api/userApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { createUser, passwordRequest, passwordReset, readMe, updateMe } from '@d

import { directusClient } from './directus'

import type { UserApi, UserItem } from 'utopia-ui'
import type { UserItem } from 'utopia-ui'

interface DirectusError {
errors: {
Expand All @@ -17,7 +17,7 @@ interface DirectusError {
}[]
}

export class userApi implements UserApi {
export class UserApi {
async register(email: string, password: string, userName: string): Promise<any> {
try {
return await directusClient.request(createUser({ email, password, first_name: userName }))
Expand Down
12 changes: 12 additions & 0 deletions app/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const config = {
apiUrl: String(import.meta.env.VITE_API_URL ?? 'https://api.utopia-lab.org'),
validateInviteFlowId: String(
import.meta.env.VITE_VALIDATE_INVITE_FLOW_ID ?? '01d61db0-25aa-4bfa-bc24-c6a8f208a455',
),
redeemInviteFlowId: String(
import.meta.env.VITE_REDEEM_INVITE_FLOW_ID ?? 'cc80ec73-ecf5-4789-bee5-1127fb1a6ed4',
),
openCollectiveApiKey: String(import.meta.env.VITE_OPEN_COLLECTIVE_API_KEY ?? ''),
}

export type Config = typeof config
42 changes: 33 additions & 9 deletions app/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,40 @@
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"utopia-ui": ["../lib/src"],
"#components/*": ["../lib/src/Components/*"],
"#utils/*": ["../lib/src/Utils/*"],
"#types/*": ["../lib/src/types/*"],
"#assets/*": ["../lib/src/assets/*"],
"#src/*": ["../lib/src/*"],
"#root/*": ["../lib/*"]
"@/*": [
"src/*"
],
"utopia-ui": [
"../lib/src"
],
"#components/*": [
"../lib/src/Components/*"
],
"#utils/*": [
"../lib/src/Utils/*"
],
"#types/*": [
"../lib/src/types/*"
],
"#assets/*": [
"../lib/src/assets/*"
],
"#src/*": [
"../lib/src/*"
],
"#root/*": [
"../lib/*"
]
}
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
"include": [
"src"
],
"references": [
{
"path": "./tsconfig.node.json"
}
]
}
Loading
Loading