mirror of
https://github.com/suitenumerique/meet.git
synced 2026-07-27 12:19:10 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 195e701fc4 | |||
| 88a5717022 | |||
| e17d42ebe3 | |||
| ae95a00301 | |||
| faff1c1228 | |||
| e04b8d081f | |||
| efb5ac5834 | |||
| 0cf4960969 | |||
| f11bcea3a2 | |||
| d8c8ac0811 | |||
| 9fd1af2302 | |||
| e6c292ecd7 | |||
| d6b5e9a50c | |||
| 789bce5092 | |||
| f888fc1717 | |||
| 545877febb | |||
| d2dba511e2 | |||
| 84c2986c01 | |||
| 44e5cd6ef3 | |||
| 7510d0fc2b | |||
| f50426b11a | |||
| b604235c35 | |||
| 6e20d5385f | |||
| 1c046abf5f | |||
| 3718851435 | |||
| c390499394 | |||
| 980d3c19d8 |
@@ -0,0 +1,33 @@
|
||||
name: Download Crowdin translations
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
types: [file-fully-translated]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
crowdin:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download Crowdin files
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: false
|
||||
upload_translations: false
|
||||
download_translations: true
|
||||
localization_branch_name: l10n_crowdin_translations
|
||||
create_pull_request: true
|
||||
pull_request_title: "New Crowdin translations"
|
||||
pull_request_body: "New Crowdin pull request with translations"
|
||||
pull_request_base_branch_name: "main"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
CROWDIN_BASE_PATH: ${{ github.workspace }}
|
||||
+14
-12
@@ -208,22 +208,24 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "18.x"
|
||||
cache: "yarn"
|
||||
cache-dependency-path: src/frontend/yarn.lock
|
||||
cache: "npm"
|
||||
cache-dependency-path: src/frontend/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd src/frontend/ && yarn install --frozen-lockfile
|
||||
run: cd src/frontend/ && npm ci
|
||||
|
||||
- name: Extract the frontend translation
|
||||
run: make frontend-i18n-extract
|
||||
|
||||
- name: Upload files to Crowdin
|
||||
run: |
|
||||
docker run \
|
||||
--rm \
|
||||
-e CROWDIN_API_TOKEN=$CROWDIN_API_TOKEN \
|
||||
-e CROWDIN_PROJECT_ID=$CROWDIN_PROJECT_ID \
|
||||
-e CROWDIN_BASE_PATH=$CROWDIN_BASE_PATH \
|
||||
-v "${{ github.workspace }}:/app" \
|
||||
crowdin/cli:3.16.0 \
|
||||
crowdin upload sources -c /app/crowdin/config.yml
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
config: crowdin/config.yml
|
||||
upload_sources: true
|
||||
upload_translations: true
|
||||
download_translations: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
CROWDIN_BASE_PATH: ${{ github.workspace }}
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
|
||||
@@ -217,7 +217,7 @@ env.d/development/kc_postgresql:
|
||||
env.d/development/crowdin:
|
||||
cp -n env.d/development/crowdin.dist env.d/development/crowdin
|
||||
|
||||
crowdin-download: ## Download translated message from crowdin
|
||||
crowdin-download: ## Download translated message from Crowdin
|
||||
@$(COMPOSE_RUN_CROWDIN) download -c crowdin/config.yml
|
||||
.PHONY: crowdin-download
|
||||
|
||||
@@ -225,14 +225,17 @@ crowdin-download-sources: ## Download sources from Crowdin
|
||||
@$(COMPOSE_RUN_CROWDIN) download sources -c crowdin/config.yml
|
||||
.PHONY: crowdin-download-sources
|
||||
|
||||
crowdin-upload: ## Upload source translations to crowdin
|
||||
crowdin-upload: ## Upload source translations to Crowdin
|
||||
@$(COMPOSE_RUN_CROWDIN) upload sources -c crowdin/config.yml
|
||||
.PHONY: crowdin-upload
|
||||
|
||||
crowdin-upload-translations: ## Upload translations to Crowdin
|
||||
@$(COMPOSE_RUN_CROWDIN) upload translations -c crowdin/config.yml
|
||||
.PHONY: crowdin-upload-translations
|
||||
|
||||
i18n-compile: ## compile all translations
|
||||
i18n-compile: \
|
||||
back-i18n-compile \
|
||||
frontend-i18n-compile
|
||||
back-i18n-compile
|
||||
.PHONY: i18n-compile
|
||||
|
||||
i18n-generate: ## create the .pot files and extract frontend messages
|
||||
@@ -295,23 +298,16 @@ help:
|
||||
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(firstword $(MAKEFILE_LIST)) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(GREEN)%-30s$(RESET) %s\n", $$1, $$2}'
|
||||
.PHONY: help
|
||||
|
||||
# FIXME : adapt this command
|
||||
frontend-i18n-extract: ## Extract the frontend translation inside a json to be used for crowdin
|
||||
cd $(PATH_FRONT) && yarn i18n:extract
|
||||
frontend-i18n-extract: ## Check the frontend code and generate missing translations keys in translation files
|
||||
cd $(PATH_FRONT) && npm run i18n:extract
|
||||
.PHONY: frontend-i18n-extract
|
||||
|
||||
# FIXME : adapt this command
|
||||
frontend-i18n-generate: ## Generate the frontend json files used for crowdin
|
||||
frontend-i18n-generate: ## Generate the frontend json files used for Crowdin
|
||||
frontend-i18n-generate: \
|
||||
crowdin-download-sources \
|
||||
frontend-i18n-extract
|
||||
.PHONY: frontend-i18n-generate
|
||||
|
||||
# FIXME : adapt this command
|
||||
frontend-i18n-compile: ## Format the crowin json files used deploy to the apps
|
||||
cd $(PATH_FRONT) && yarn i18n:deploy
|
||||
.PHONY: frontend-i18n-compile
|
||||
|
||||
# -- K8S
|
||||
build-k8s-cluster: ## build the kubernetes cluster using kind
|
||||
./bin/start-kind.sh
|
||||
|
||||
+15
-14
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Your crowdin's credentials
|
||||
#
|
||||
api_token_env: CROWDIN_API_TOKEN
|
||||
api_token_env: CROWDIN_PERSONAL_TOKEN
|
||||
project_id_env: CROWDIN_PROJECT_ID
|
||||
base_path_env: CROWDIN_BASE_PATH
|
||||
|
||||
@@ -14,16 +14,17 @@ preserve_hierarchy: true
|
||||
#
|
||||
# Files configuration
|
||||
#
|
||||
files: [
|
||||
{
|
||||
source : "/backend/locale/django.pot",
|
||||
dest: "/backend-meet.pot",
|
||||
translation : "/backend/locale/%locale_with_underscore%/LC_MESSAGES/django.po"
|
||||
},
|
||||
{
|
||||
source: "/frontend/packages/i18n/locales/impress/translations-crowdin.json",
|
||||
dest: "/frontend-impress.json",
|
||||
translation: "/frontend/packages/i18n/locales/impress/%two_letters_code%/translations.json",
|
||||
skip_untranslated_strings: true,
|
||||
},
|
||||
]
|
||||
files:
|
||||
[
|
||||
{
|
||||
source: "src/backend/locale/django.pot",
|
||||
dest: "/backend-meet.pot",
|
||||
translation: "src/backend/locale/%locale_with_underscore%/LC_MESSAGES/django.po",
|
||||
},
|
||||
{
|
||||
source: "src/frontend/src/locales/fr/**/*",
|
||||
translation: "src/frontend/src/locales/%two_letters_code%/**/%original_file_name%",
|
||||
dest: "/%original_file_name%",
|
||||
skip_untranslated_strings: true,
|
||||
},
|
||||
]
|
||||
|
||||
+1
-1
@@ -100,7 +100,7 @@ services:
|
||||
image: jwilder/dockerize
|
||||
|
||||
crowdin:
|
||||
image: crowdin/cli:3.16.0
|
||||
image: crowdin/cli:4.0.0
|
||||
volumes:
|
||||
- ".:/app"
|
||||
env_file:
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Releasing new version
|
||||
|
||||
Whenever we are cooking a new release (e.g. `4.18.1`) we should follow a standard procedure described below:
|
||||
|
||||
1. Create a new branch named: `release/4.18.1`.
|
||||
2. Bump the release number in the appropriate file, i.e. for backend in the `pyproject.toml` and `package.json` for node-based projects (tips: find and replace all occurrences of the previous version number to ensure consistency across files).
|
||||
3. Update the project's `Changelog` following the [keepachangelog](https://keepachangelog.com/en/0.3.0/) recommendations.
|
||||
4. Update Docker image tag in Helm values files located at `src/helm/env.d` for both `preprod` and `production` environments:
|
||||
```yaml
|
||||
image:
|
||||
repository: lasuite/meet-backend
|
||||
pullPolicy: Always
|
||||
tag: "v4.18.1" # Replace with the latest Docker image tag.
|
||||
```
|
||||
5. Commit your changes with a structured message:
|
||||
- Add a title including the version of the release and respecting the above described format using the 🔖 release emoji.
|
||||
- Paste in the body all changes from the changelog concerned by this release, removing only the Markdown tags and making sure that lines are shorter than 74 characters.
|
||||
|
||||
```
|
||||
🔖(minor) bump release to 4.18.0
|
||||
|
||||
Added:
|
||||
|
||||
- Implement base CLI commands (list, extract, fetch & push) for supported backends
|
||||
- Support for ElasticSearch database backend
|
||||
|
||||
Changed:
|
||||
|
||||
- Replace LDP storage backend by FS storage backend`
|
||||
```
|
||||
|
||||
6. Open a pull request.
|
||||
7. Wait for an approval from your peers.
|
||||
8. Merge your pull request.
|
||||
9. Checkout and pull changes from the `main` branch to ensure you have the latest updates.
|
||||
10. Tag & push your commit: `git tag v4.18.1 && git push origin --tags`
|
||||
11. Manually release your version on GitHub.
|
||||
12. Ensure that the CI Docker job has successfully pushed the newly built Docker images to Docker Hub with the appropriate tags.
|
||||
13. To deploy using ArgoCD, you need to update the `production` or `pre-production` tags. ArgoCD will automatically detect and deploy the new tag.
|
||||
@@ -1,3 +1,3 @@
|
||||
CROWDIN_API_TOKEN=Your-Api-Token
|
||||
CROWDIN_PERSONAL_TOKEN=Your-Api-Token
|
||||
CROWDIN_PROJECT_ID=Your-Project-Id
|
||||
CROWDIN_BASE_PATH=/app/src
|
||||
CROWDIN_BASE_PATH=/app
|
||||
|
||||
+1
-1
Submodule secrets updated: 35ec1ef9fe...9da011f5c2
@@ -122,14 +122,16 @@ class RoomSerializer(serializers.ModelSerializer):
|
||||
if role is not None or instance.is_public:
|
||||
slug = f"{instance.id!s}".replace("-", "")
|
||||
|
||||
username = request.GET.get("username", None)
|
||||
|
||||
output["livekit"] = {
|
||||
"url": settings.LIVEKIT_CONFIGURATION["url"],
|
||||
"room": slug,
|
||||
"token": utils.generate_token(room=slug, user=request.user),
|
||||
"token": utils.generate_token(
|
||||
room=slug, user=request.user, username=username
|
||||
),
|
||||
}
|
||||
|
||||
output["is_administrable"] = is_admin
|
||||
|
||||
# todo - pass properly livekit configuration
|
||||
|
||||
return output
|
||||
|
||||
@@ -188,12 +188,15 @@ class RoomViewSet(
|
||||
if not settings.ALLOW_UNREGISTERED_ROOMS:
|
||||
raise
|
||||
slug = slugify(self.kwargs["pk"])
|
||||
username = request.query_params.get("username", None)
|
||||
data = {
|
||||
"id": None,
|
||||
"livekit": {
|
||||
"url": settings.LIVEKIT_CONFIGURATION["url"],
|
||||
"room": slug,
|
||||
"token": utils.generate_token(room=slug, user=request.user),
|
||||
"token": utils.generate_token(
|
||||
room=slug, user=request.user, username=username
|
||||
),
|
||||
},
|
||||
}
|
||||
else:
|
||||
@@ -206,11 +209,8 @@ class RoomViewSet(
|
||||
user = self.request.user
|
||||
|
||||
if user.is_authenticated:
|
||||
# todo - simplify this queryset
|
||||
queryset = (
|
||||
self.filter_queryset(self.get_queryset())
|
||||
.filter(Q(users=user))
|
||||
.distinct()
|
||||
self.filter_queryset(self.get_queryset()).filter(users=user).distinct()
|
||||
)
|
||||
else:
|
||||
queryset = self.get_queryset().none()
|
||||
|
||||
@@ -111,7 +111,9 @@ def test_api_rooms_retrieve_anonymous_unregistered_allowed(mock_token):
|
||||
},
|
||||
}
|
||||
|
||||
mock_token.assert_called_once_with(room="unregistered-room", user=AnonymousUser())
|
||||
mock_token.assert_called_once_with(
|
||||
room="unregistered-room", user=AnonymousUser(), username=None
|
||||
)
|
||||
|
||||
|
||||
@override_settings(ALLOW_UNREGISTERED_ROOMS=True)
|
||||
@@ -141,7 +143,9 @@ def test_api_rooms_retrieve_anonymous_unregistered_allowed_not_normalized(mock_t
|
||||
},
|
||||
}
|
||||
|
||||
mock_token.assert_called_once_with(room="reunion", user=AnonymousUser())
|
||||
mock_token.assert_called_once_with(
|
||||
room="reunion", user=AnonymousUser(), username=None
|
||||
)
|
||||
|
||||
|
||||
@override_settings(ALLOW_UNREGISTERED_ROOMS=False)
|
||||
@@ -229,7 +233,7 @@ def test_api_rooms_retrieve_authenticated_public(mock_token):
|
||||
"slug": room.slug,
|
||||
}
|
||||
|
||||
mock_token.assert_called_once_with(room=expected_name, user=user)
|
||||
mock_token.assert_called_once_with(room=expected_name, user=user, username=None)
|
||||
|
||||
|
||||
def test_api_rooms_retrieve_authenticated():
|
||||
@@ -327,7 +331,7 @@ def test_api_rooms_retrieve_members(mock_token, django_assert_num_queries):
|
||||
"slug": room.slug,
|
||||
}
|
||||
|
||||
mock_token.assert_called_once_with(room=expected_name, user=user)
|
||||
mock_token.assert_called_once_with(room=expected_name, user=user, username=None)
|
||||
|
||||
|
||||
@mock.patch("core.utils.generate_token", return_value="foo")
|
||||
@@ -400,4 +404,4 @@ def test_api_rooms_retrieve_administrators(mock_token, django_assert_num_queries
|
||||
"slug": room.slug,
|
||||
}
|
||||
|
||||
mock_token.assert_called_once_with(room=expected_name, user=user)
|
||||
mock_token.assert_called_once_with(room=expected_name, user=user, username=None)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Utils functions used in the core app
|
||||
"""
|
||||
import string
|
||||
from typing import Optional
|
||||
from uuid import uuid4
|
||||
|
||||
from django.conf import settings
|
||||
@@ -9,18 +9,19 @@ from django.conf import settings
|
||||
from livekit.api import AccessToken, VideoGrants
|
||||
|
||||
|
||||
def generate_token(room: string, user) -> str:
|
||||
def generate_token(room: str, user, username: Optional[str] = None) -> str:
|
||||
"""Generate a Livekit access token for a user in a specific room.
|
||||
|
||||
Args:
|
||||
room (str): The name of the room.
|
||||
user (User): The user which request the access token.
|
||||
username (Optional[str]): The username to be displayed in the room.
|
||||
If none, a default value will be used.
|
||||
|
||||
Returns:
|
||||
str: The LiveKit JWT access token.
|
||||
"""
|
||||
|
||||
# todo - define the video grants properly based on user and room.
|
||||
video_grants = VideoGrants(
|
||||
room=room,
|
||||
room_join=True,
|
||||
@@ -38,10 +39,12 @@ def generate_token(room: string, user) -> str:
|
||||
).with_grants(video_grants)
|
||||
|
||||
if user.is_anonymous:
|
||||
# todo - allow passing a proper name for not logged-in user
|
||||
token.with_identity(str(uuid4()))
|
||||
default_username = "Anonymous"
|
||||
else:
|
||||
# todo - use user's fullname instead of its email for the displayed name
|
||||
token.with_identity(user.sub).with_name(f"{user!s}")
|
||||
token.with_identity(user.sub)
|
||||
default_username = str(user)
|
||||
|
||||
token.with_name(username or default_username)
|
||||
|
||||
return token.to_jwt()
|
||||
|
||||
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "meet"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"defaultNamespace": "global",
|
||||
"input": ["src/**/*.{ts,tsx}", "!src/styled-system/**/*", "!src/**/*.d.ts"],
|
||||
"output": "src/locales/$LOCALE/$NAMESPACE.json",
|
||||
"createOldCatalogs": false,
|
||||
"locales": ["en", "fr", "de"],
|
||||
"sort": true
|
||||
}
|
||||
Generated
+1506
-41
File diff suppressed because it is too large
Load Diff
@@ -1,23 +1,30 @@
|
||||
{
|
||||
"name": "meet",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "panda codegen && vite",
|
||||
"build": "panda codegen && tsc -b && vite build",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"i18n:extract": "npx i18next -c i18next-parser.config.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@livekit/components-react": "2.3.3",
|
||||
"@livekit/components-styles": "1.0.12",
|
||||
"@pandacss/preset-panda": "0.41.0",
|
||||
"@tanstack/react-query": "5.49.2",
|
||||
"hoofd": "1.7.1",
|
||||
"i18next": "23.12.1",
|
||||
"i18next-browser-languagedetector": "8.0.0",
|
||||
"i18next-parser": "9.0.0",
|
||||
"i18next-resources-to-backend": "1.2.1",
|
||||
"livekit-client": "2.3.1",
|
||||
"react": "18.2.0",
|
||||
"react-aria-components": "1.2.1",
|
||||
"react-dom": "18.2.0",
|
||||
"react-i18next": "14.1.3",
|
||||
"wouter": "3.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -146,6 +146,7 @@ const config: Config = {
|
||||
2: { value: '2' },
|
||||
},
|
||||
radii: {
|
||||
4: { value: '0.25rem' },
|
||||
6: { value: '0.375rem' },
|
||||
8: { value: '0.5rem' },
|
||||
16: { value: '1rem' },
|
||||
@@ -187,7 +188,7 @@ const config: Config = {
|
||||
text: { value: '{colors.white}' },
|
||||
warm: { value: '{colors.blue.300}' },
|
||||
subtle: { value: '{colors.blue.100}' },
|
||||
'subtle-text': { value: '{colors.sky.700}' },
|
||||
'subtle-text': { value: '{colors.blue.700}' },
|
||||
},
|
||||
danger: {
|
||||
DEFAULT: { value: '{colors.red.600}' },
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
import '@livekit/components-styles'
|
||||
import '@/styles/index.css'
|
||||
import { Suspense } from 'react'
|
||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useLang } from 'hoofd'
|
||||
import { Route, Switch } from 'wouter'
|
||||
import { Home } from './routes/Home'
|
||||
import { Screen } from './layout/Screen'
|
||||
import { HomeRoute } from '@/features/home'
|
||||
import { RoomRoute, roomIdRegex } from '@/features/rooms'
|
||||
import { NotFound } from './routes/NotFound'
|
||||
import { RoomRoute } from '@/features/rooms'
|
||||
import './i18n/init'
|
||||
|
||||
const queryClient = new QueryClient()
|
||||
|
||||
function App() {
|
||||
const { i18n } = useTranslation()
|
||||
useLang(i18n.language)
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Switch>
|
||||
<Route path="/" component={Home} />
|
||||
<Route path="/:roomId" component={RoomRoute} />
|
||||
<Route component={NotFound} />
|
||||
</Switch>
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
<Suspense fallback={<Screen />}>
|
||||
<Switch>
|
||||
<Route path="/" component={HomeRoute} />
|
||||
<Route path={roomIdRegex} component={RoomRoute} />
|
||||
<Route component={NotFound} />
|
||||
</Switch>
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
</Suspense>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { fetchApi } from '@/api/fetchApi'
|
||||
import { type ApiUser } from './ApiUser'
|
||||
|
||||
/**
|
||||
* fetch the logged in user from the api.
|
||||
* fetch the logged-in user from the api.
|
||||
*
|
||||
* If the user is not logged in, the api returns a 401 error.
|
||||
* Here our wrapper just returns false in that case, without triggering an error:
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export { useUser } from './api/useUser'
|
||||
export { authUrl } from './utils/authUrl'
|
||||
export { logoutUrl } from './utils/logoutUrl'
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { apiUrl } from '@/api/apiUrl'
|
||||
|
||||
export const logoutUrl = () => {
|
||||
return apiUrl('/logout')
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { navigateToHome } from './navigation/navigateToHome'
|
||||
export { Home as HomeRoute } from './routes/Home'
|
||||
@@ -1,39 +1,35 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { A, Button, Italic, P, Div, H, Box } from '@/primitives'
|
||||
import { useUser } from '@/features/auth'
|
||||
import { apiUrl } from '@/api/apiUrl'
|
||||
import { authUrl, useUser } from '@/features/auth'
|
||||
import { navigateToNewRoom } from '@/features/rooms'
|
||||
import { Screen } from '@/layout/Screen'
|
||||
|
||||
export const Home = () => {
|
||||
const { t } = useTranslation('home')
|
||||
const { isLoggedIn } = useUser()
|
||||
return (
|
||||
<Screen>
|
||||
<Box asScreen>
|
||||
<H lvl={1}>Welcome in Meet</H>
|
||||
<P>What do you want to do? You can either:</P>
|
||||
<Box type="screen">
|
||||
<H lvl={1}>{t('heading')}</H>
|
||||
<P>{t('intro')}</P>
|
||||
<Div marginBottom="gutter">
|
||||
<Box variant="subtle" size="sm">
|
||||
{isLoggedIn ? (
|
||||
<Button variant="primary" onPress={() => navigateToNewRoom()}>
|
||||
Create a conference call
|
||||
{t('createMeeting')}
|
||||
</Button>
|
||||
) : (
|
||||
<p>
|
||||
<A href={apiUrl('/authenticate')}>
|
||||
Login to create a conference call
|
||||
</A>
|
||||
<A href={authUrl()}>{t('login')}</A>
|
||||
</p>
|
||||
)}
|
||||
</Box>
|
||||
</Div>
|
||||
<P>
|
||||
<Italic>Or</Italic>
|
||||
<Italic>{t('or')}</Italic>
|
||||
</P>
|
||||
<Box variant="subtle" size="sm">
|
||||
<p>
|
||||
copy a meeting URL in your browser address bar to join an existing
|
||||
conference call
|
||||
</p>
|
||||
<p>{t('copyMeetingUrl')}</p>
|
||||
</Box>
|
||||
</Box>
|
||||
</Screen>
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useParams } from 'wouter'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import {
|
||||
LiveKitRoom,
|
||||
@@ -7,15 +6,16 @@ import {
|
||||
} from '@livekit/components-react'
|
||||
import { keys } from '@/api/queryKeys'
|
||||
import { QueryAware } from '@/layout/QueryAware'
|
||||
import { navigateToHome } from '@/navigation/navigateToHome'
|
||||
import { navigateToHome } from '@/features/home'
|
||||
import { fetchRoom } from '../api/fetchRoom'
|
||||
|
||||
export const Conference = ({
|
||||
roomId,
|
||||
userConfig,
|
||||
}: {
|
||||
roomId: string
|
||||
userConfig: LocalUserChoices
|
||||
}) => {
|
||||
const { roomId } = useParams()
|
||||
const { status, data } = useQuery({
|
||||
queryKey: [keys.room, roomId, userConfig.username],
|
||||
queryFn: () =>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Box } from '@/layout/Box'
|
||||
import { PreJoin, type LocalUserChoices } from '@livekit/components-react'
|
||||
|
||||
@@ -6,12 +7,11 @@ export const Join = ({
|
||||
}: {
|
||||
onSubmit: (choices: LocalUserChoices) => void
|
||||
}) => {
|
||||
const { t } = useTranslation('rooms')
|
||||
|
||||
return (
|
||||
<Box title="Verify your settings before joining" withBackButton>
|
||||
<PreJoin
|
||||
persistUserChoices
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
<Box title={t('join.heading')} withBackButton>
|
||||
<PreJoin persistUserChoices onSubmit={onSubmit} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export { navigateToNewRoom } from './navigation/navigateToNewRoom'
|
||||
export { Room as RoomRoute } from './routes/Room'
|
||||
export { roomIdRegex } from './utils/generateRoomId'
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import { type LocalUserChoices } from '@livekit/components-react'
|
||||
import { useState } from 'react'
|
||||
import { useParams } from 'wouter'
|
||||
import { Conference } from '../components/Conference'
|
||||
import { Join } from '../components/Join'
|
||||
import { Screen } from '@/layout/Screen'
|
||||
import { ErrorScreen } from '@/layout/ErrorScreen'
|
||||
|
||||
export const Room = () => {
|
||||
const [userConfig, setUserConfig] = useState<LocalUserChoices | null>(null)
|
||||
const { roomId } = useParams()
|
||||
if (!roomId) {
|
||||
return <ErrorScreen />
|
||||
}
|
||||
return (
|
||||
<Screen>
|
||||
{userConfig ? (
|
||||
<Conference userConfig={userConfig} />
|
||||
<Conference roomId={roomId} userConfig={userConfig} />
|
||||
) : (
|
||||
<Join onSubmit={setUserConfig} />
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
import { slugify } from '@/utils/slugify'
|
||||
// Google Meet uses only letters in a room identifier
|
||||
const ROOM_ID_ALLOWED_CHARACTERS = 'abcdefghijklmnopqrstuvwxyz'
|
||||
|
||||
export const generateRoomId = () => {
|
||||
return slugify(crypto.randomUUID())
|
||||
}
|
||||
const getRandomChar = () =>
|
||||
ROOM_ID_ALLOWED_CHARACTERS[
|
||||
Math.floor(Math.random() * ROOM_ID_ALLOWED_CHARACTERS.length)
|
||||
]
|
||||
|
||||
const generateSegment = (length: number): string =>
|
||||
Array.from(Array(length), getRandomChar).join('')
|
||||
|
||||
// Generates a unique room identifier following the Google Meet format
|
||||
export const generateRoomId = () =>
|
||||
[generateSegment(3), generateSegment(4), generateSegment(3)].join('-')
|
||||
|
||||
export const roomIdRegex = /^[/](?<roomId>[a-z]{3}-[a-z]{4}-[a-z]{3})$/
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Button, Popover, PopoverList } from '@/primitives'
|
||||
import { useLanguageLabels } from './useLanguageLabels'
|
||||
|
||||
export const LanguageSelector = () => {
|
||||
const { t, i18n } = useTranslation()
|
||||
const { languagesList, currentLanguage } = useLanguageLabels()
|
||||
return (
|
||||
<Popover aria-label={t('languageSelector.popoverLabel')}>
|
||||
<Button
|
||||
aria-label={t('languageSelector.buttonLabel', {
|
||||
currentLanguage,
|
||||
})}
|
||||
size="sm"
|
||||
variant="primary"
|
||||
outline
|
||||
>
|
||||
{i18n.language}
|
||||
</Button>
|
||||
<PopoverList
|
||||
items={languagesList}
|
||||
onAction={(lang) => {
|
||||
i18n.changeLanguage(lang)
|
||||
}}
|
||||
/>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import i18n from 'i18next'
|
||||
import resourcesToBackend from 'i18next-resources-to-backend'
|
||||
import { initReactI18next } from 'react-i18next'
|
||||
import LanguageDetector from 'i18next-browser-languagedetector'
|
||||
const i18nDefaultNamespace = 'global'
|
||||
|
||||
i18n.setDefaultNamespace(i18nDefaultNamespace)
|
||||
i18n
|
||||
.use(
|
||||
resourcesToBackend((language: string, namespace: string) => {
|
||||
return import(`../locales/${language}/${namespace}.json`)
|
||||
})
|
||||
)
|
||||
.use(initReactI18next)
|
||||
.use(LanguageDetector)
|
||||
i18n.init({
|
||||
supportedLngs: ['en', 'fr'],
|
||||
fallbackLng: 'en',
|
||||
ns: i18nDefaultNamespace,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,20 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const langageLabels: Record<string, string> = {
|
||||
en: 'English',
|
||||
fr: 'Français',
|
||||
de: 'Deutsch',
|
||||
}
|
||||
|
||||
export const useLanguageLabels = () => {
|
||||
const { i18n } = useTranslation()
|
||||
// cimode is a testing value from i18next, don't include it
|
||||
const supportedLanguages = (i18n.options.supportedLngs || []).filter(
|
||||
(lang) => lang !== 'cimode'
|
||||
)
|
||||
const languagesList = supportedLanguages.map((lang) => ({
|
||||
value: lang,
|
||||
label: langageLabels[lang],
|
||||
}))
|
||||
return { languagesList, currentLanguage: langageLabels[i18n.language] }
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Box as BoxDiv, H, Link } from '@/primitives'
|
||||
|
||||
export type BoxProps = {
|
||||
@@ -12,14 +13,15 @@ export const Box = ({
|
||||
title = '',
|
||||
withBackButton = false,
|
||||
}: BoxProps) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<BoxDiv asScreen>
|
||||
<BoxDiv type="screen">
|
||||
{!!title && <H lvl={1}>{title}</H>}
|
||||
{children}
|
||||
{!!withBackButton && (
|
||||
<p>
|
||||
<Link to="/" size="small">
|
||||
Back to homescreen
|
||||
{t('backToHome')}
|
||||
</Link>
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BoxScreen } from './BoxScreen'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export const ErrorScreen = () => {
|
||||
return (
|
||||
<BoxScreen title="An error occured while loading the page" withBackButton />
|
||||
)
|
||||
const { t } = useTranslation()
|
||||
return <BoxScreen title={t('error.heading')} withBackButton />
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { BoxScreen } from './BoxScreen'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export const ForbiddenScreen = () => {
|
||||
return (
|
||||
<BoxScreen
|
||||
title="You don't have the permission to view this page"
|
||||
withBackButton
|
||||
/>
|
||||
)
|
||||
const { t } = useTranslation()
|
||||
return <BoxScreen title={t('forbidden.heading')} withBackButton />
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { Link } from 'wouter'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { flex } from '@/styled-system/patterns'
|
||||
import { apiUrl } from '@/api/apiUrl'
|
||||
import { Stack } from '@/styled-system/jsx'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { LanguageSelector } from '@/i18n/LanguageSelector'
|
||||
import { A, Badge, Text } from '@/primitives'
|
||||
import { useUser } from '@/features/auth/api/useUser'
|
||||
import { authUrl, logoutUrl, useUser } from '@/features/auth'
|
||||
|
||||
export const Header = () => {
|
||||
const { t } = useTranslation()
|
||||
const { user, isLoggedIn } = useUser()
|
||||
return (
|
||||
<div
|
||||
@@ -19,29 +23,27 @@ export const Header = () => {
|
||||
boxShadow: 'box',
|
||||
})}
|
||||
>
|
||||
<header
|
||||
className={flex({
|
||||
justify: 'space-between',
|
||||
align: 'center',
|
||||
})}
|
||||
>
|
||||
<div>
|
||||
<Stack direction="row" justify="space-between" align="center">
|
||||
<header>
|
||||
<Text bold variant="h1" margin={false}>
|
||||
Meet
|
||||
<Link to="/">{t('app')}</Link>
|
||||
</Text>
|
||||
</div>
|
||||
<div>
|
||||
{isLoggedIn === false && <A href={apiUrl('/authenticate')}>Login</A>}
|
||||
{!!user && (
|
||||
<p className={flex({ gap: 1, align: 'center' })}>
|
||||
<Badge>{user.email}</Badge>
|
||||
<A href={apiUrl('/logout')} size="small">
|
||||
Logout
|
||||
</A>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
</header>
|
||||
<nav>
|
||||
<Stack gap={1} direction="row" align="center">
|
||||
<LanguageSelector />
|
||||
{isLoggedIn === false && <A href={authUrl()}>{t('login')}</A>}
|
||||
{!!user && (
|
||||
<p className={flex({ gap: 1, align: 'center' })}>
|
||||
<Badge>{user.email}</Badge>
|
||||
<A href={logoutUrl()} size="small">
|
||||
{t('logout')}
|
||||
</A>
|
||||
</p>
|
||||
)}
|
||||
</Stack>
|
||||
</nav>
|
||||
</Stack>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { BoxScreen } from './BoxScreen'
|
||||
import { Screen } from './Screen'
|
||||
|
||||
export const LoadingScreen = ({ asBox = false }: { asBox?: boolean }) => {
|
||||
const { t } = useTranslation()
|
||||
// show the loading screen only after a little while to prevent flash of texts
|
||||
const [show, setShow] = useState(false)
|
||||
useEffect(() => {
|
||||
@@ -15,7 +17,7 @@ export const LoadingScreen = ({ asBox = false }: { asBox?: boolean }) => {
|
||||
const Container = asBox ? BoxScreen : Screen
|
||||
return (
|
||||
<Container>
|
||||
<p>Loading…</p>
|
||||
<p>{t('loading')}</p>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { BoxScreen } from './BoxScreen'
|
||||
|
||||
export const NotFoundScreen = () => {
|
||||
return <BoxScreen title="Page not found" withBackButton />
|
||||
const { t } = useTranslation()
|
||||
return <BoxScreen title={t('notFound.heading')} withBackButton />
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ReactNode } from 'react'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { Header } from './Header'
|
||||
|
||||
export const Screen = ({ children }: { children: ReactNode }) => {
|
||||
export const Screen = ({ children }: { children?: ReactNode }) => {
|
||||
return (
|
||||
<div
|
||||
className={css({
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"app": "Meet",
|
||||
"backToHome": "",
|
||||
"error": {
|
||||
"heading": ""
|
||||
},
|
||||
"forbidden": {
|
||||
"heading": ""
|
||||
},
|
||||
"languageSelector": {
|
||||
"buttonLabel": "",
|
||||
"popoverLabel": ""
|
||||
},
|
||||
"loading": "",
|
||||
"login": "Anmelden",
|
||||
"logout": "",
|
||||
"notFound": {
|
||||
"heading": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"copyMeetingUrl": "",
|
||||
"createMeeting": "",
|
||||
"heading": "",
|
||||
"intro": "",
|
||||
"login": "",
|
||||
"or": ""
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"join": {
|
||||
"heading": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"app": "Meet",
|
||||
"backToHome": "Back to homescreen",
|
||||
"error": {
|
||||
"heading": "An error occured while loading the page"
|
||||
},
|
||||
"forbidden": {
|
||||
"heading": "You don't have the permission to view this page"
|
||||
},
|
||||
"languageSelector": {
|
||||
"buttonLabel": "Change language (currently {{currentLanguage}})",
|
||||
"popoverLabel": "Choose language"
|
||||
},
|
||||
"loading": "Loading…",
|
||||
"login": "Login",
|
||||
"logout": "Logout",
|
||||
"notFound": {
|
||||
"heading": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"copyMeetingUrl": "copy a meeting URL in your browser address bar to join an existing conference call",
|
||||
"createMeeting": "Create a conference call",
|
||||
"heading": "Welcome in Meet",
|
||||
"intro": "What do you want to do? You can either:",
|
||||
"login": "Login to create a conference call",
|
||||
"or": "Or"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"join": {
|
||||
"heading": "Verify your settings before joining"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"app": "Meet",
|
||||
"backToHome": "Retour à l'accueil",
|
||||
"error": {
|
||||
"heading": "Une erreur est survenue lors du chargement de la page"
|
||||
},
|
||||
"forbidden": {
|
||||
"heading": "Accès interdit"
|
||||
},
|
||||
"languageSelector": {
|
||||
"buttonLabel": "Changer de langue (actuellement {{currentLanguage}})",
|
||||
"popoverLabel": "Choix de la langue"
|
||||
},
|
||||
"loading": "Chargement…",
|
||||
"login": "Se connecter",
|
||||
"logout": "Se déconnecter",
|
||||
"notFound": {
|
||||
"heading": "Page introuvable"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"copyMeetingUrl": "copier une URL de conférence dans votre barre d'adresse pour rejoindre une conférence existante",
|
||||
"createMeeting": "Créer une conférence",
|
||||
"heading": "Bienvenue dans Meet",
|
||||
"intro": "Que voulez vous faire ? Vous pouvez :",
|
||||
"login": "Vous connecter pour créer une conférence",
|
||||
"or": "Ou"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"join": {
|
||||
"heading": "Vérifiez vos paramètres"
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ const link = cva({
|
||||
base: {
|
||||
textDecoration: 'underline',
|
||||
textUnderlineOffset: '2',
|
||||
transition: 'all 200ms',
|
||||
cursor: 'pointer',
|
||||
borderRadius: 2,
|
||||
'_ra-hover': {
|
||||
textDecoration: 'none',
|
||||
},
|
||||
@@ -26,7 +26,7 @@ const link = cva({
|
||||
export type AProps = LinkProps & RecipeVariantProps<typeof link>
|
||||
|
||||
/**
|
||||
* anchor component styled with underline
|
||||
* anchor component styled with underline. Used mostly for external links. Use Link for internal links
|
||||
*/
|
||||
export const A = ({ size, ...props }: AProps) => {
|
||||
return <Link {...props} className={link({ size })} />
|
||||
|
||||
@@ -9,14 +9,18 @@ const box = cva({
|
||||
flex: 1,
|
||||
},
|
||||
variants: {
|
||||
asScreen: {
|
||||
true: {
|
||||
type: {
|
||||
screen: {
|
||||
margin: 'auto',
|
||||
width: '38rem',
|
||||
maxWidth: '100%',
|
||||
marginTop: '6rem',
|
||||
textAlign: 'center',
|
||||
},
|
||||
popover: {
|
||||
padding: 'boxPadding.xs',
|
||||
minWidth: '10rem',
|
||||
},
|
||||
},
|
||||
variant: {
|
||||
default: {
|
||||
@@ -42,7 +46,6 @@ const box = cva({
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
asScreen: false,
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
|
||||
@@ -9,44 +9,69 @@ import { cva, type RecipeVariantProps } from '@/styled-system/css'
|
||||
const button = cva({
|
||||
base: {
|
||||
display: 'inline-block',
|
||||
paddingX: '1',
|
||||
paddingY: '0.625',
|
||||
transition: 'all 200ms',
|
||||
borderRadius: 8,
|
||||
transition: 'background 200ms',
|
||||
cursor: 'pointer',
|
||||
border: '1px solid transparent',
|
||||
color: 'colorPalette.text',
|
||||
backgroundColor: 'colorPalette',
|
||||
'_ra-hover': {
|
||||
backgroundColor: 'colorPalette.hover',
|
||||
},
|
||||
'_ra-pressed': {
|
||||
backgroundColor: 'colorPalette.active',
|
||||
},
|
||||
},
|
||||
variants: {
|
||||
size: {
|
||||
default: {
|
||||
borderRadius: 8,
|
||||
paddingX: '1',
|
||||
paddingY: '0.625',
|
||||
},
|
||||
sm: {
|
||||
borderRadius: 4,
|
||||
paddingX: '0.5',
|
||||
paddingY: '0.25',
|
||||
},
|
||||
},
|
||||
variant: {
|
||||
default: {
|
||||
color: 'control.text',
|
||||
backgroundColor: 'control',
|
||||
'_ra-hover': {
|
||||
backgroundColor: 'control.hover',
|
||||
},
|
||||
'_ra-pressed': {
|
||||
backgroundColor: 'control.active',
|
||||
},
|
||||
colorPalette: 'control',
|
||||
},
|
||||
primary: {
|
||||
color: 'primary.text',
|
||||
backgroundColor: 'primary',
|
||||
colorPalette: 'primary',
|
||||
},
|
||||
},
|
||||
outline: {
|
||||
true: {
|
||||
color: 'colorPalette',
|
||||
backgroundColor: 'transparent!',
|
||||
borderColor: 'currentcolor!',
|
||||
'_ra-hover': {
|
||||
backgroundColor: 'primary.hover',
|
||||
backgroundColor: 'colorPalette.subtle!',
|
||||
},
|
||||
'_ra-pressed': {
|
||||
backgroundColor: 'primary.active',
|
||||
backgroundColor: 'colorPalette.subtle!',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
size: 'default',
|
||||
variant: 'default',
|
||||
outline: false,
|
||||
},
|
||||
})
|
||||
|
||||
type ButtonProps = RecipeVariantProps<typeof button> &
|
||||
(RACButtonsProps | LinkProps)
|
||||
export type ButtonProps = RecipeVariantProps<typeof button> & RACButtonsProps
|
||||
|
||||
export const Button = (props: ButtonProps) => {
|
||||
type LinkButtonProps = RecipeVariantProps<typeof button> & LinkProps
|
||||
|
||||
type ButtonOrLinkProps = ButtonProps | LinkButtonProps
|
||||
|
||||
export const Button = (props: ButtonOrLinkProps) => {
|
||||
const [variantProps, componentProps] = button.splitVariantProps(props)
|
||||
if ((props as LinkProps).href !== undefined) {
|
||||
if ((props as LinkButtonProps).href !== undefined) {
|
||||
return <Link className={button(variantProps)} {...componentProps} />
|
||||
}
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { ReactNode } from 'react'
|
||||
import {
|
||||
DialogProps,
|
||||
DialogTrigger,
|
||||
Popover as RACPopover,
|
||||
Dialog,
|
||||
OverlayArrow,
|
||||
} from 'react-aria-components'
|
||||
import { styled } from '@/styled-system/jsx'
|
||||
import { Box } from './Box'
|
||||
|
||||
const StyledPopover = styled(RACPopover, {
|
||||
base: {
|
||||
'&[data-placement="bottom"]': {
|
||||
marginTop: 0.25,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const StyledOverlayArrow = styled(OverlayArrow, {
|
||||
base: {
|
||||
display: 'block',
|
||||
fill: 'box.bg',
|
||||
stroke: 'box.border',
|
||||
strokeWidth: 1,
|
||||
'&[data-placement="bottom"] svg': {
|
||||
transform: 'rotate(180deg) translateY(-1px)',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* a Popover is a tuple of a trigger component (most usually a Button) that toggles some interactive content in a tooltip around the trigger
|
||||
*/
|
||||
export const Popover = ({
|
||||
children,
|
||||
...dialogProps
|
||||
}: {
|
||||
children: [
|
||||
trigger: ReactNode,
|
||||
popoverContent:
|
||||
| (({ close }: { close: () => void }) => ReactNode)
|
||||
| ReactNode,
|
||||
]
|
||||
} & DialogProps) => {
|
||||
const [trigger, popoverContent] = children
|
||||
return (
|
||||
<DialogTrigger>
|
||||
{trigger}
|
||||
<StyledPopover>
|
||||
<StyledOverlayArrow>
|
||||
<svg width={12} height={12} viewBox="0 0 12 12">
|
||||
<path d="M0 0 L6 6 L12 0" />
|
||||
</svg>
|
||||
</StyledOverlayArrow>
|
||||
<Dialog {...dialogProps}>
|
||||
{({ close }) => (
|
||||
<Box size="sm" type="popover">
|
||||
{typeof popoverContent === 'function'
|
||||
? popoverContent({ close })
|
||||
: popoverContent}
|
||||
</Box>
|
||||
)}
|
||||
</Dialog>
|
||||
</StyledPopover>
|
||||
</DialogTrigger>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { ReactNode, useContext } from 'react'
|
||||
import {
|
||||
ButtonProps,
|
||||
Button,
|
||||
OverlayTriggerStateContext,
|
||||
} from 'react-aria-components'
|
||||
import { styled } from '@/styled-system/jsx'
|
||||
|
||||
const ListItem = styled(Button, {
|
||||
base: {
|
||||
paddingY: 0.125,
|
||||
paddingX: 0.5,
|
||||
textAlign: 'left',
|
||||
width: 'full',
|
||||
borderRadius: 4,
|
||||
cursor: 'pointer',
|
||||
color: 'primary',
|
||||
'_ra-hover': {
|
||||
color: 'primary.subtle-text',
|
||||
backgroundColor: 'primary.subtle',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* render a Button primitive that shows a popover showing a list of pressable items
|
||||
*/
|
||||
export const PopoverList = <T extends string | number = string>({
|
||||
onAction,
|
||||
closeOnAction = true,
|
||||
items = [],
|
||||
}: {
|
||||
closeOnAction?: boolean
|
||||
onAction: (key: T) => void
|
||||
items: Array<string | { value: T; label: ReactNode }>
|
||||
} & ButtonProps) => {
|
||||
const popoverState = useContext(OverlayTriggerStateContext)!
|
||||
return (
|
||||
<ul>
|
||||
{items.map((item) => {
|
||||
const value = typeof item === 'string' ? item : item.value
|
||||
const label = typeof item === 'string' ? item : item.label
|
||||
return (
|
||||
<li>
|
||||
<ListItem
|
||||
key={value}
|
||||
onPress={() => {
|
||||
onAction(value as T)
|
||||
if (closeOnAction) {
|
||||
popoverState.close()
|
||||
}
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</ListItem>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
@@ -9,4 +9,6 @@ export { Hr } from './Hr'
|
||||
export { Italic } from './Italic'
|
||||
export { Link } from './Link'
|
||||
export { P } from './P'
|
||||
export { Popover } from './Popover'
|
||||
export { PopoverList } from './PopoverList'
|
||||
export { Text } from './Text'
|
||||
|
||||
@@ -6,3 +6,15 @@ body,
|
||||
#root {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
* {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
[data-rac][data-focus-visible],
|
||||
:is(a, button, input, select, textarea):not([data-rac]):focus-visible {
|
||||
outline: 2px solid black;
|
||||
outline-offset: 1px;
|
||||
outline-color: Highlight;
|
||||
outline-color: -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
djangoSecretKey: ENC[AES256_GCM,data:TulkgSxtL5YSXuEbmM0vOrSezj+Qod7TAReBs3zoZak782Dr56stTweiSjz5ngtRNh8=,iv:VciiRV5UPvGiNk9yHaw1uT0GEzglQ/Xn2UUxjiycZas=,tag:2LvoFqy6jpSBV679yagA5Q==,type:str]
|
||||
djangoSecretKey: ENC[AES256_GCM,data:dqeL5W5WaTir+tzUAIbivT1DE0zv1IVsq1/nTU7LRKHO2PUefwsyGAy37eYIeC89q9I=,iv:ODTaWFvvYLHNYRkIX//wY2knKD2uIwf7Mo4UhUBEX9c=,tag:MnE3wLdLcK79XUwXV99w4w==,type:str]
|
||||
oidc:
|
||||
clientId: ENC[AES256_GCM,data:7cQzLyVTOqy0OZoL5LEHkBspgqLGzrz02YWma64yzhB+PEj1,iv:vQ49PuVGmms2Y2LIa6uUon0rGO+v0368NuHmpFmqwiE=,tag:39baRMIrS+bvUVpSikgdHg==,type:str]
|
||||
clientSecret: ENC[AES256_GCM,data:e+FMROduwljv1hJp88WtyOFsYcS6r30D8NB5Eg9lrxVLPZlhXE8KOYDFUhqUCEWb5ZvIk5m90V3lngN1nJJHGA==,iv:kY0rWPFoOFHRpOXQIsaipqjBAnaITfNdPVkU9OtJghk=,tag:cP4cDSOl+W8qMMhXpV+vgA==,type:str]
|
||||
clientId: ENC[AES256_GCM,data:X+Qj//JXn9kfVszbe9BNbL8rQ53AJq0Cluq3nG58OyXlv5/y,iv:/Na7zvcsIu5OxJw1uFwiksh8tXfzG9It26jW41E0edQ=,tag:oph41uyMwj3uZMnZPI64ZQ==,type:str]
|
||||
clientSecret: ENC[AES256_GCM,data:2WV+i0pd4wBXiwG8ak+yVROGsmwSaM0X0rEJjFeNeGYtUsHozd5XrurQu6FAB1tQnzTRgRgAU0ob0MqbsMUYhg==,iv:OJZRG+OUD0Fi2HZ0w3Rn+SMFkebMrsQtKvymJkicF28=,tag:PJYzhVvM0gH0xAGrSSPAIQ==,type:str]
|
||||
#ENC[AES256_GCM,data:iZjWgYcHtDeK/c1TrfVVsZaJRmuc1+2XEJnLEOYw1wXMsUUvgswEc5PP/4UmirhbSulwtuMRhFt74sIogMxPl2etMAoCqo3ziKRuu3FrmgSUD/bdXQRl+VQ0nJ3NnWNcKqf+GS8=,iv:5hUaA4hkABLvXoKVE4TNZevOdUCaCy3zYkGajHpmZ5k=,tag:ikiSAyIly8NAgVA0olASRw==,type:comment]
|
||||
livekit:
|
||||
keys:
|
||||
devkey: ENC[AES256_GCM,data:qlvVa2A1,iv:MNxLlo6Hl3/O/p2JqBLjoe3fmSfhvyAZdv34yKHAHRs=,tag:4rO+fpeweGcyhg93kCN0BA==,type:str]
|
||||
devkey: ENC[AES256_GCM,data:deDMTTO9,iv:ZCloCUPRJ7PG2KcGsnhVj3lRDfCmJowwp8PVg4fdTmg=,tag:zCh8lQT29kQbY7LiXHNEiA==,type:str]
|
||||
livekitApi:
|
||||
key: ENC[AES256_GCM,data:fh0zJSUE,iv:yR5siulW5vlO/Ew8qtYFNNtyODOPm3d3NL4SVCzzVcM=,tag:hLtEuonLJoi0eFJmY0RYEg==,type:str]
|
||||
secret: ENC[AES256_GCM,data:81SOHzA6,iv:is7QdB/T58IP41SrPyDdFHtyX57tphb9Gpf/aQ7NnTY=,tag:1E03R+EFYmLtxFoIDcVEHg==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
@@ -14,68 +18,68 @@ sops:
|
||||
- recipient: age15fyxdwmg5mvldtqqus87xspuws2u0cpvwheehrtvkexj4tnsqqysw6re2x
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5LzE5S3h6c3g3WXlpYVRS
|
||||
V3lwZWl4YVVudncwMWxwL09KQ1IrWUFmZFVnCmRMcXM1ZUtISVBtcU1xU0pVVDVx
|
||||
NlBYbDVRbUlNQzU4VS9vZEE1U3p5V1EKLS0tIGhDVldDa1crS2hkY2JkQTMxRDVC
|
||||
SDZXcThPRGJKaXV5aWlheG1NdTY4d1kK5kqtaAlzs8rTYTggpWC0OOk/TOKxxQId
|
||||
ec3ZLFN8g/JZiakPola/+u4hsIxkV+y0fUs2CwlDFJnEBwdkNPpBQw==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwZG8rTjAzcVNtVHdLZmJI
|
||||
Mm4vemNrV1dDUExEZ1R1UGJmWjN3aWw1VXc0CjF1MVhhUTV6ckl1OEFNSjBJUCta
|
||||
MTE3QU04RDJKMWlWcHhDSG1NTmZyTkEKLS0tIHh5UHRqckUxZWZLUDl4d3FDdHJs
|
||||
Y0VMc0llMytmMWNUOW56d3ltTHpwZ2cKEHkn6wuHNeMTk+E6nEMpEJZ6wpdXSi3k
|
||||
FkzXRa6SudAgA4R6K9EieHKPdiNvi0IsCJOhLpiNQtENu9poF5ozqQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age16hnlml8yv4ynwy0seer57g8qww075crd0g7nsundz3pj4wk7m3vqftszg7
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMcU02c2RTcWxIb2gxVVBl
|
||||
N004cXRIT0FOa0JKeTNaSytsa1BBdTdvaTFZCkFzN0tNcEcvZUpjaHdvR0NBUDNp
|
||||
M1pRckcyK2IxSnZid2o3bG5hNXl3YjgKLS0tIDNJSHp2YmovZzgwUEU5ZWRkQUtu
|
||||
ZEFxQ0EzVEJCenVxeVFOMWlrMEd6eTQK8RRV0lbOkJaRrHK1yTTQv8Kly2Bccatu
|
||||
I3ED05o1CaWP+qPYSDmaChzewAW1J2C0AU9k8dpEd94jRIve3nhQMw==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAva3lFTTM2ZUJpcnozNmJt
|
||||
ZHAvRndkSjRCVEZYN29wMWFZVmV5ekNiYW5FCkI3RWUvVTdVZ2dJeWVSY085SW5G
|
||||
UWNOempRQlFCUUlmL0RIR1ZZZVI5cDQKLS0tIEpwZkVodENLM1VnQ1o1TmhRSFFi
|
||||
ZEF5MUNVdHRKcW9aK0M2M0ZVNDBxbjgKw8Wp06PcoStrO6ppsOR5zWjXbYrP64Dv
|
||||
XAEHQMa7vyvuu12YIa/fpyDM4HrsrPq3OWudxuWS6p0X8xmPYrXm6g==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1plkp8td6zzfcavjusmsfrlk54t9vn8jjxm8zaz7cmnr7kzl2nfnsd54hwg
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTcWl1alFtSkIyM1BvSnAw
|
||||
YlAxM2RjOXdzUVRDOTUrUVZIWWFQSmd4dWdBCjQ4SUQ1eXdpSmdzMlM3UnlTTy9D
|
||||
clBOcFFWOWtaZU9OU1JoRXNCbXJ5SVUKLS0tIG1NbUZKajQ5dno3Z2d6NzJtbUNQ
|
||||
WXRDM1dEeXB5SU52UmVVaFUxTUVLbkkKS3oAFk1CYB03vbXm9Xb4P24/Xui0LM+h
|
||||
++ykAOWuHjZ0mHbvPXOeS6VFXchdVWtQiHYW6eo3ya524Pq3C3l34Q==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1OU9mZHZFanpTQjE3aGly
|
||||
VVZDWFh5ZmRHV1YwWUtsZVd6R090SnkxVVNVCmM1UWwwaFlqTHlhSGFRblZBbVhl
|
||||
aEhtWWp1N1lVTDNqOFJBL0swVFl3Y3cKLS0tIG5ZTHAveFdLdGhQeUc2Tzh1d0dw
|
||||
UzRhNGNTZFR3U3BLNFhCdFp6aU5uZVEKL4K5jFjPfMp/fMA8+nQerj6PE5zvGeHW
|
||||
1SuHwnDiglKmksj8Gy+7spwLQCmo11JCnW2gXKktVIe5XOyhortq3Q==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age12g6f5fse25tgrwweleh4jls3qs52hey2edh759smulwmk5lnzadslu2cp3
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWaGtjcUdZWHFSNGttM1Z3
|
||||
VFd3SWQ2OFZBRlc3NEJTMWZUa3BPOW9uMGo0CnZyOHRWazdEVzg0VUdEMVdFVXRy
|
||||
TDFRWW9QYkVzeVoyRVVJNVNIVVRaUHMKLS0tIEFZU1l6THp5bElSMnlCUVN1VitJ
|
||||
RnhnaElLMjNhUUpkQURQZm82ZTJxOVEKXCvgYbPMsYWT13TpFTZevGX3ZBKb2/Yd
|
||||
CaBO0I4Ij2ykyK2X3qqJRAPI1cTKROTVv5GCvilxEZWcq0lLwhfNWQ==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4Z1lXM0tDRzJnMytDZHBV
|
||||
ZlpCcTg0U3RGeEVzN2dBMkFYa0FBMFVKMWw4Cm5KeWMvdFBRNHFJWFNSS3NVWlNQ
|
||||
dnhkc2F2MFVTMmVHT2U2STdUWWcvNzAKLS0tIHkzQXltQW5TWHVrektQU1hES1ll
|
||||
UnVld1laTWRmTlkzWHRKRjJmWVJhbU0KANxoSnhDbxsja+Eo8MVCGv6iThNmlo/m
|
||||
y0RXVNVqNlqTreT0F/SKmP74W3lF30nwsfrOywMQyu75k8p5MGwUuQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1tl80n23wq6zxegupwn70ew0yp225ua5v4dk800x7g2w6pvlxz46qk592pa
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzbkUrMitDdlJzVUVucFIz
|
||||
Q2drWDAxcVFhWE5SSDdhVW9wM09oZjI1K1JVClE1MkpYejZxM0dxSTBFWkt0WjR1
|
||||
Smo5TkEvNzByaDZ0dmMvS25kMUV4QmMKLS0tIEtkaTMyNmw5LzNESVhKTHZKL0E5
|
||||
WnBhdWRZd1N2a1VBdVpuR2JCYUhINk0KaREjyKJ2ieF72TSuFX5as8odtKQacspa
|
||||
S/Bsu2YI2YnCKyuL2BOHVT0ETZXyv29a70yxLAWFgnZNEj/MYXzLFw==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwUm9oT3RYbGZXaW5CM20r
|
||||
QlYyeGZjMG05MFNzdndCU2dUemZqdXF5Um1FCkZWdVlubW1MZ3NwM3V2S3dQRFA1
|
||||
cHVvOXBMMGtMdkF4T2s3Wk9wYkJUZUEKLS0tIFBRM3FWYTRrVDNwZStwQWlpbEYx
|
||||
dXA5WE5udnhhMVdLTk1jMzJuU3VWaTQKaxTpyqi5fjmOFR4qOxm+wSqWDxb/96QI
|
||||
rHGUI+CqMVKZ6w1fMH0uanvCuw7Q9rbmsKB+DsjARMLFGqxzP/psUA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1qy04neuzwpasmvljqrcvhwnf0kz5cpyteze38c8avp0czewskasszv9pyw
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4U0kzTmw0N3hMK0lxVFZX
|
||||
MzRuT2k4WGpxZzZyWmV1dUxvMmhqdHhNekJJCnQ3YXpSVUhoYllKc0FBdStIZ2hK
|
||||
N0o5UWNTMXk3OHlCVldSREJkNTJSR1EKLS0tIEx3NzBjMi9Pa3NsN24yekltaW1h
|
||||
RnVweWw1L0RmTDVURGZoTEg5czkyem8KA/VyWUoqAO4+2MJeIAwtKo5GVOyUK7Mq
|
||||
IDUlHULo5kKZ/DglNwcoErV7MzWFunjXCIseXBPieMrZQ1vC5OJrvA==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwMmVOaHV1Unl4UmUrVXEy
|
||||
REJIYWZhQmt3R0R5RFRveTl4Z0JaZlpKNmd3Ckl0dkFUaG90TE5odVh0YVprMVZj
|
||||
T2tXS0dSRGVVczdhb1dlVlV0L3JZUncKLS0tIGo2RDBPZXNzU2dQQlJhY0NBS2ps
|
||||
RW92OFVTS3d2L3UxOXNrVUFSVFI5TXMKn3pdHbXxBccG6Q+gWPVQK/5wiIKkzdhm
|
||||
HiNzezStrkFHf/lsFS2LNgYkfMMzBQ4rJj7oQkrD1Z8j6qRld3zzLg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age18fgn6j2vwwswqcpv9xpcehq8mrf9zs2sglwkamp3tzwx8d9jq9jsrskrk9
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJQ1Z2UlozTzJsQlBlbnhS
|
||||
UzJiZFZwK2lXUTVnQVBEQUIxOVU0QzVDUjE4CllhMWc0Nzh1Z3FOTU1ESkE1bW5B
|
||||
TFFPUjJRUGxiMnBtUktoYk1LamQ0bmMKLS0tIGdPbWFBdDNUUmpIdnN4dCtmamc2
|
||||
MDJiYzhvNlUvQTlsbUdYdmJkYjNaN3MKdccBEm9dj2Cs3km527bx3w2d3b/rJOi2
|
||||
QzgHJKaRS3rdCRkXj/cxW0rVgI8twvVj/WV1Into1qKRcAKI6f5zfw==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzeWN4ZE5Ya3RPV2VKRFdv
|
||||
UGQ4QTJUZFovSXpQREhNWXdQbktVUHFQaUhjCmM1dXNxbGdlVjBWNjc5L2E5QlBy
|
||||
NjI0S251cjZvYU1nQ2swWXVDdjdhSG8KLS0tIHdFNXptN1VBNytFNW40MUsxTGFM
|
||||
WlVnc3VLY1ZReDFxUVlqWU9rM0xMQ00Kq/ckdP1N4BDBo0pSH9pp36skIacwNm3d
|
||||
utiuCgG16Hqe6YDb3agx81BocuSJ9oMOdpegoztnkkBDDP0F+e/VuQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2024-07-12T08:47:29Z"
|
||||
mac: ENC[AES256_GCM,data:slPiXUVmoLGwg3VbsFNAENY84+bSCDQset6cEtbf7LJjJx30/Jqrdg0VXOv/aFmIJsb5mLaPUlylLt36MTw8IYxRPKYY5Gj3yXWRz2SaSX7QKZAba0k5z9MvGy0FjCDlSVe0Xm+SXUoA37t6NFKHgz54Bh2+CLX1H8i7EbZA1ak=,iv:pyjiNZBvq/4u5kiXAzlqu1C4qbupjCZez5Z9vRXLnIg=,tag:EXzeWMfPJGNy4uKXmCdAzA==,type:str]
|
||||
lastmodified: "2024-07-18T13:08:42Z"
|
||||
mac: ENC[AES256_GCM,data:a/uHyw9V/SMIePV9nPf+wJgPg+YDYLJGYy7NMLBrBgCXtBWHHonSNjzdmtjix1bW2y+cU0gMqodrtqR1cJGBmXr4NRY7NJqgLWE9rEdYfG7BnfqsWmvAaTIrSs7QMZWkEic7ys/bXoA5BZoau3olhVqIO2A/iyBtoMU9Hv7hPlo=,iv:gaqSCUbN7cxWPNrFPDTl7xNxpOZL6GY/swD/MDCiRqk=,tag:Oz0f/DyD3KGV/9Rprj/1Xw==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.8.1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
image:
|
||||
repository: lasuite/meet-backend
|
||||
pullPolicy: Always
|
||||
tag: "v0.1.0"
|
||||
tag: "v0.1.1"
|
||||
|
||||
backend:
|
||||
migrateJobAnnotations:
|
||||
@@ -92,7 +92,7 @@ backend:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: LIVEKIT_API_KEY
|
||||
LIVEKIT_API_URL: https://livekit-docker-1.beta.numerique.gouv.fr
|
||||
LIVEKIT_API_URL: https://livekit-preprod.beta.numerique.gouv.fr
|
||||
|
||||
createsuperuser:
|
||||
command:
|
||||
@@ -106,7 +106,7 @@ frontend:
|
||||
image:
|
||||
repository: lasuite/meet-frontend
|
||||
pullPolicy: Always
|
||||
tag: "v0.1.0"
|
||||
tag: "v0.1.1"
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
image:
|
||||
repository: lasuite/meet-backend
|
||||
pullPolicy: Always
|
||||
tag: "v0.1.0"
|
||||
tag: "v0.1.1"
|
||||
|
||||
backend:
|
||||
migrateJobAnnotations:
|
||||
@@ -92,7 +92,7 @@ backend:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: LIVEKIT_API_KEY
|
||||
LIVEKIT_API_URL: https://livekit-docker-1.beta.numerique.gouv.fr
|
||||
LIVEKIT_API_URL: https://livekit-preprod.beta.numerique.gouv.fr
|
||||
|
||||
createsuperuser:
|
||||
command:
|
||||
@@ -106,7 +106,7 @@ frontend:
|
||||
image:
|
||||
repository: lasuite/meet-frontend
|
||||
pullPolicy: Always
|
||||
tag: "v0.1.0"
|
||||
tag: "v0.1.1"
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
|
||||
@@ -92,7 +92,7 @@ backend:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: LIVEKIT_API_KEY
|
||||
LIVEKIT_API_URL: https://livekit-docker-1.beta.numerique.gouv.fr
|
||||
LIVEKIT_API_URL: https://livekit-preprod.beta.numerique.gouv.fr
|
||||
|
||||
createsuperuser:
|
||||
command:
|
||||
|
||||
@@ -19,6 +19,7 @@ spec:
|
||||
{{- with .Values.backend.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/secrets.yaml") . | sha256sum }}
|
||||
labels:
|
||||
{{- include "meet.common.selectorLabels" (list . $component) | nindent 8 }}
|
||||
spec:
|
||||
|
||||
@@ -2,6 +2,7 @@ apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: backend
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
stringData:
|
||||
DJANGO_SUPERUSER_EMAIL: {{ .Values.djangoSuperUserEmail }}
|
||||
DJANGO_SUPERUSER_PASSWORD: {{ .Values.djangoSuperUserPass }}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mail_mjml",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"description": "An util to generate html and text django's templates from mjml templates",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "meet-openapi-client-ts",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"private": true,
|
||||
"description": "Tool to generate Typescript API client for the Meet application.",
|
||||
"scripts": {
|
||||
|
||||
Reference in New Issue
Block a user