Compare commits

..

7 Commits

Author SHA1 Message Date
Anthony LC ac86a4e7f7 🔖(minor) release 1.4.0
Added:
- (backend) Add link public/authenticated/restricted
access with read/editor roles
- (frontend) add copy link button
- 🛂(frontend) access public docs without being logged

Changed:
- ♻️(backend) Allow null titles on documents
for easier creation
- 🛂(backend) stop to list public doc to everyone
- 🚚(frontend) change visibility in share modal
- ️(frontend) Improve summary

Fixed:
- 🐛(backend) Fix forcing ID when creating a
document via API endpoint
- 🐛 Rebuild frontend dev container from makefile
2024-09-18 12:01:52 +02:00
Anthony LC bbe5501297 🌐(frontend) translate last features
Translate:
- doc visibility
- doc table of contents
2024-09-18 11:18:29 +02:00
Anthony LC b37acf3138 💄(frontend) improve ui table of contents
- keep correctly the text on the left side
- improve accuracy highlightment heading when scrolling
- display full heading text when text transform is applied
- fix typo
2024-09-18 11:18:29 +02:00
Anthony LC 5bd78b8068 🚚(frontend) rename feature summary to table of content
We rename the feature summary to table of content
to better reflect the feature purpose.
2024-09-17 15:06:37 +02:00
Anthony LC ed39c01608 ♻️(frontent) improve summary feature
- Change Summary to Table of content
- No dash before the title
- Change font-size depend the type of heading
- If more than 2 headings the panel is open
by default
- improve sticky
- highligth the title where you are in the page
2024-09-17 15:06:37 +02:00
Anthony LC 748ebc8f26 🔧(helm) change conf helm dev
Some frontend env vars were added on the frontend
side, we need to add them to the dev helm chart.
2024-09-17 15:06:37 +02:00
renovate[bot] 03262878c4 ⬆️(dependencies) update js dependencies 2024-09-16 14:30:29 +02:00
37 changed files with 12624 additions and 13685 deletions
+139 -139
View File
@@ -9,52 +9,52 @@ on:
- "*"
jobs:
# lint-git:
# runs-on: ubuntu-latest
# if: github.event_name == 'pull_request' # Makes sense only for pull requests
# steps:
# - name: Checkout repository
# uses: actions/checkout@v2
# with:
# fetch-depth: 0
# - name: show
# run: git log
# - name: Enforce absence of print statements in code
# run: |
# ! git diff origin/${{ github.event.pull_request.base.ref }}..HEAD -- . ':(exclude)**/impress.yml' | grep "print("
# - name: Check absence of fixup commits
# run: |
# ! git log | grep 'fixup!'
# - name: Install gitlint
# run: pip install --user requests gitlint
# - name: Lint commit messages added to main
# run: ~/.local/bin/gitlint --commits origin/${{ github.event.pull_request.base.ref }}..HEAD
lint-git:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' # Makes sense only for pull requests
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: show
run: git log
- name: Enforce absence of print statements in code
run: |
! git diff origin/${{ github.event.pull_request.base.ref }}..HEAD -- . ':(exclude)**/impress.yml' | grep "print("
- name: Check absence of fixup commits
run: |
! git log | grep 'fixup!'
- name: Install gitlint
run: pip install --user requests gitlint
- name: Lint commit messages added to main
run: ~/.local/bin/gitlint --commits origin/${{ github.event.pull_request.base.ref }}..HEAD
# check-changelog:
# runs-on: ubuntu-latest
# if: |
# contains(github.event.pull_request.labels.*.name, 'noChangeLog') == false &&
# github.event_name == 'pull_request'
# steps:
# - name: Checkout repository
# uses: actions/checkout@v3
# with:
# fetch-depth: 50
# - name: Check that the CHANGELOG has been modified in the current branch
# run: git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.after }} | grep 'CHANGELOG.md'
check-changelog:
runs-on: ubuntu-latest
if: |
contains(github.event.pull_request.labels.*.name, 'noChangeLog') == false &&
github.event_name == 'pull_request'
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 50
- name: Check that the CHANGELOG has been modified in the current branch
run: git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.after }} | grep 'CHANGELOG.md'
# lint-changelog:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout repository
# uses: actions/checkout@v2
# - name: Check CHANGELOG max line length
# run: |
# max_line_length=$(cat CHANGELOG.md | grep -Ev "^\[.*\]: https://github.com" | wc -L)
# if [ $max_line_length -ge 80 ]; then
# echo "ERROR: CHANGELOG has lines longer than 80 characters."
# exit 1
# fi
lint-changelog:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Check CHANGELOG max line length
run: |
max_line_length=$(cat CHANGELOG.md | grep -Ev "^\[.*\]: https://github.com" | wc -L)
if [ $max_line_length -ge 80 ]; then
echo "ERROR: CHANGELOG has lines longer than 80 characters."
exit 1
fi
build-mails:
runs-on: ubuntu-latest
@@ -96,112 +96,112 @@ jobs:
path: "src/backend/core/templates/mail"
key: mail-templates-${{ hashFiles('src/mail/mjml') }}
# lint-back:
# runs-on: ubuntu-latest
# defaults:
# run:
# working-directory: src/backend
# steps:
# - name: Checkout repository
# uses: actions/checkout@v2
# - name: Install Python
# uses: actions/setup-python@v3
# with:
# python-version: "3.10"
# - name: Install development dependencies
# run: pip install --user .[dev]
# - name: Check code formatting with ruff
# run: ~/.local/bin/ruff format . --diff
# - name: Lint code with ruff
# run: ~/.local/bin/ruff check .
# - name: Lint code with pylint
# run: ~/.local/bin/pylint .
lint-back:
runs-on: ubuntu-latest
defaults:
run:
working-directory: src/backend
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Python
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install development dependencies
run: pip install --user .[dev]
- name: Check code formatting with ruff
run: ~/.local/bin/ruff format . --diff
- name: Lint code with ruff
run: ~/.local/bin/ruff check .
- name: Lint code with pylint
run: ~/.local/bin/pylint .
# test-back:
# runs-on: ubuntu-latest
# needs: build-mails
test-back:
runs-on: ubuntu-latest
needs: build-mails
# defaults:
# run:
# working-directory: src/backend
defaults:
run:
working-directory: src/backend
# services:
# postgres:
# image: postgres:16
# env:
# POSTGRES_DB: impress
# POSTGRES_USER: dinum
# POSTGRES_PASSWORD: pass
# ports:
# - 5432:5432
# # needed because the postgres container does not provide a healthcheck
# options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: impress
POSTGRES_USER: dinum
POSTGRES_PASSWORD: pass
ports:
- 5432:5432
# needed because the postgres container does not provide a healthcheck
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
# env:
# DJANGO_CONFIGURATION: Test
# DJANGO_SETTINGS_MODULE: impress.settings
# DJANGO_SECRET_KEY: ThisIsAnExampleKeyForTestPurposeOnly
# OIDC_OP_JWKS_ENDPOINT: /endpoint-for-test-purpose-only
# DB_HOST: localhost
# DB_NAME: impress
# DB_USER: dinum
# DB_PASSWORD: pass
# DB_PORT: 5432
# STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
# AWS_S3_ENDPOINT_URL: http://localhost:9000
# AWS_S3_ACCESS_KEY_ID: impress
# AWS_S3_SECRET_ACCESS_KEY: password
env:
DJANGO_CONFIGURATION: Test
DJANGO_SETTINGS_MODULE: impress.settings
DJANGO_SECRET_KEY: ThisIsAnExampleKeyForTestPurposeOnly
OIDC_OP_JWKS_ENDPOINT: /endpoint-for-test-purpose-only
DB_HOST: localhost
DB_NAME: impress
DB_USER: dinum
DB_PASSWORD: pass
DB_PORT: 5432
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
AWS_S3_ENDPOINT_URL: http://localhost:9000
AWS_S3_ACCESS_KEY_ID: impress
AWS_S3_SECRET_ACCESS_KEY: password
# steps:
# - name: Checkout repository
# uses: actions/checkout@v4
steps:
- name: Checkout repository
uses: actions/checkout@v4
# - name: Create writable /data
# run: |
# sudo mkdir -p /data/media && \
# sudo mkdir -p /data/static
- name: Create writable /data
run: |
sudo mkdir -p /data/media && \
sudo mkdir -p /data/static
# - name: Restore the mail templates
# uses: actions/cache@v4
# id: mail-templates
# with:
# path: "src/backend/core/templates/mail"
# key: mail-templates-${{ hashFiles('src/mail/mjml') }}
- name: Restore the mail templates
uses: actions/cache@v4
id: mail-templates
with:
path: "src/backend/core/templates/mail"
key: mail-templates-${{ hashFiles('src/mail/mjml') }}
# - name: Start Minio
# run: |
# docker pull minio/minio
# docker run -d --name minio \
# -p 9000:9000 \
# -e "MINIO_ACCESS_KEY=impress" \
# -e "MINIO_SECRET_KEY=password" \
# -v /data/media:/data \
# minio/minio server --console-address :9001 /data
- name: Start Minio
run: |
docker pull minio/minio
docker run -d --name minio \
-p 9000:9000 \
-e "MINIO_ACCESS_KEY=impress" \
-e "MINIO_SECRET_KEY=password" \
-v /data/media:/data \
minio/minio server --console-address :9001 /data
# - name: Configure MinIO
# run: |
# MINIO=$(docker ps | grep minio/minio | sed -E 's/.*\s+([a-zA-Z0-9_-]+)$/\1/')
# docker exec ${MINIO} sh -c \
# "mc alias set impress http://localhost:9000 impress password && \
# mc alias ls && \
# mc mb impress/impress-media-storage && \
# mc version enable impress/impress-media-storage"
- name: Configure MinIO
run: |
MINIO=$(docker ps | grep minio/minio | sed -E 's/.*\s+([a-zA-Z0-9_-]+)$/\1/')
docker exec ${MINIO} sh -c \
"mc alias set impress http://localhost:9000 impress password && \
mc alias ls && \
mc mb impress/impress-media-storage && \
mc version enable impress/impress-media-storage"
# - name: Install Python
# uses: actions/setup-python@v3
# with:
# python-version: "3.10"
- name: Install Python
uses: actions/setup-python@v3
with:
python-version: "3.10"
# - name: Install development dependencies
# run: pip install --user .[dev]
- name: Install development dependencies
run: pip install --user .[dev]
# - name: Install gettext (required to compile messages)
# run: |
# sudo apt-get update
# sudo apt-get install -y gettext pandoc
- name: Install gettext (required to compile messages)
run: |
sudo apt-get update
sudo apt-get install -y gettext pandoc
# - name: Generate a MO file from strings extracted from the project
# run: python manage.py compilemessages
- name: Generate a MO file from strings extracted from the project
run: python manage.py compilemessages
# - name: Run tests
# run: ~/.local/bin/pytest -n 2
- name: Run tests
run: ~/.local/bin/pytest -n 2
+8 -3
View File
@@ -9,6 +9,9 @@ and this project adheres to
## [Unreleased]
## [1.4.0] - 2024-09-17
## Added
- ✨Add link public/authenticated/restricted access with read/editor roles #234
@@ -17,13 +20,14 @@ and this project adheres to
## Changed
- ♻️ Allow null titles on documents for easier creation #234
- ♻️(backend) Allow null titles on documents for easier creation #234
- 🛂(backend) stop to list public doc to everyone #234
- 🚚(frontend) change visibility in share modal #235
- ⚡️(frontend) Improve summary #244
## Fixed
- 🐛 Fix forcing ID when creating a document via API endpoint #234
- 🐛(backend) Fix forcing ID when creating a document via API endpoint #234
- 🐛 Rebuild frontend dev container from makefile #248
@@ -149,7 +153,8 @@ and this project adheres to
- 🚀 Impress, project to manage your documents easily and collaboratively.
[unreleased]: https://github.com/numerique-gouv/impress/compare/v1.3.0...main
[unreleased]: https://github.com/numerique-gouv/impress/compare/v1.4.0...main
[1.4.0]: https://github.com/numerique-gouv/impress/releases/v1.4.0
[1.3.0]: https://github.com/numerique-gouv/impress/releases/v1.3.0
[1.2.1]: https://github.com/numerique-gouv/impress/releases/v1.2.1
[1.2.0]: https://github.com/numerique-gouv/impress/releases/v1.2.0
+2 -2
View File
@@ -16,8 +16,8 @@ class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = ["id", "sub", "email"]
read_only_fields = ["id", "sub", "email"]
fields = ["id", "email"]
read_only_fields = ["id", "email"]
class BaseAccessSerializer(serializers.ModelSerializer):
+1 -1
View File
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "impress"
version = "1.3.0"
version = "1.4.0"
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -1,64 +0,0 @@
import { expect, test } from '@playwright/test';
import { createDoc } from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test.describe('Doc Summary', () => {
test('it checks the doc summary', async ({ page, browserName }) => {
const [randomDoc] = await createDoc(page, 'doc-summary', browserName, 1);
await expect(page.locator('h2').getByText(randomDoc)).toBeVisible();
await page.getByLabel('Open the document options').click();
await page
.getByRole('button', {
name: 'Summary',
})
.click();
const panel = page.getByLabel('Document panel');
const editor = page.locator('.ProseMirror');
await editor.locator('.bn-block-outer').last().fill('/');
await page.getByText('Heading 1').click();
await page.keyboard.type('Hello World');
await page.locator('.bn-block-outer').last().click();
// Create space to fill the viewport
for (let i = 0; i < 6; i++) {
await page.keyboard.press('Enter');
}
await editor.locator('.bn-block-outer').last().fill('/');
await page.getByText('Heading 2').click();
await page.keyboard.type('Super World');
await page.locator('.bn-block-outer').last().click();
// Create space to fill the viewport
for (let i = 0; i < 4; i++) {
await page.keyboard.press('Enter');
}
await editor.locator('.bn-block-outer').last().fill('/');
await page.getByText('Heading 3').click();
await page.keyboard.type('Another World');
await expect(panel.getByText('Hello World')).toBeVisible();
await expect(panel.getByText('Super World')).toBeVisible();
await panel.getByText('Another World').click();
await expect(editor.getByText('Hello World')).not.toBeInViewport();
await panel.getByText('Back to top').click();
await expect(editor.getByText('Hello World')).toBeInViewport();
await panel.getByText('Go to bottom').click();
await expect(editor.getByText('Hello World')).not.toBeInViewport();
});
});
@@ -0,0 +1,95 @@
import { expect, test } from '@playwright/test';
import { createDoc } from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test.describe('Doc Table Content', () => {
test('it checks the doc table content', async ({ page, browserName }) => {
const [randomDoc] = await createDoc(
page,
'doc-table-content',
browserName,
1,
);
await expect(page.locator('h2').getByText(randomDoc)).toBeVisible();
await page.getByLabel('Open the document options').click();
await page
.getByRole('button', {
name: 'Table of contents',
})
.click();
const panel = page.getByLabel('Document panel');
const editor = page.locator('.ProseMirror');
await editor.locator('.bn-block-outer').last().fill('/');
await page.getByText('Heading 1').click();
await page.keyboard.type('Hello World');
await editor.getByText('Hello').dblclick();
await page.getByRole('button', { name: 'Strike' }).click();
await page.locator('.bn-block-outer').last().click();
// Create space to fill the viewport
for (let i = 0; i < 5; i++) {
await page.keyboard.press('Enter');
}
await editor.locator('.bn-block-outer').last().fill('/');
await page.getByText('Heading 2').click();
await page.keyboard.type('Super World', { delay: 100 });
await page.locator('.bn-block-outer').last().click();
// Create space to fill the viewport
for (let i = 0; i < 5; i++) {
await page.keyboard.press('Enter');
}
await editor.locator('.bn-block-outer').last().fill('/');
await page.getByText('Heading 3').click();
await page.keyboard.type('Another World');
const hello = panel.getByText('Hello World');
const superW = panel.getByText('Super World');
const another = panel.getByText('Another World');
await expect(hello).toBeVisible();
await expect(hello).toHaveCSS('font-size', /19/);
await expect(hello).toHaveAttribute('aria-selected', 'true');
await expect(superW).toBeVisible();
await expect(superW).toHaveCSS('font-size', /16/);
await expect(superW).toHaveAttribute('aria-selected', 'false');
await expect(another).toBeVisible();
await expect(another).toHaveCSS('font-size', /12/);
await expect(another).toHaveAttribute('aria-selected', 'false');
await hello.click();
await expect(editor.getByText('Hello World')).toBeInViewport();
await expect(hello).toHaveAttribute('aria-selected', 'true');
await expect(superW).toHaveAttribute('aria-selected', 'false');
await another.click();
await expect(editor.getByText('Hello World')).not.toBeInViewport();
await expect(hello).toHaveAttribute('aria-selected', 'false');
await expect(superW).toHaveAttribute('aria-selected', 'true');
await panel.getByText('Back to top').click();
await expect(editor.getByText('Hello World')).toBeInViewport();
await expect(hello).toHaveAttribute('aria-selected', 'true');
await expect(superW).toHaveAttribute('aria-selected', 'false');
await panel.getByText('Go to bottom').click();
await expect(editor.getByText('Hello World')).not.toBeInViewport();
await expect(superW).toHaveAttribute('aria-selected', 'true');
});
});
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "app-e2e",
"version": "1.3.0",
"version": "1.4.0",
"private": true,
"scripts": {
"lint": "eslint . --ext .ts",
@@ -12,7 +12,7 @@
"test:ui::chromium": "yarn test:ui --project=chromium"
},
"devDependencies": {
"@playwright/test": "1.47.0",
"@playwright/test": "1.47.1",
"@types/node": "*",
"@types/pdf-parse": "1.1.4",
"eslint-config-impress": "*",
+7 -10
View File
@@ -1,6 +1,6 @@
{
"name": "app-impress",
"version": "1.3.0",
"version": "1.4.0",
"private": true,
"scripts": {
"dev": "next dev",
@@ -21,19 +21,16 @@
"@gouvfr-lasuite/integration": "1.0.2",
"@hocuspocus/provider": "2.13.5",
"@openfun/cunningham-react": "2.9.4",
"@socialgouv/e2esdk-client": "1.0.0-beta.28",
"@socialgouv/e2esdk-devtools": "1.0.0-beta.38",
"@socialgouv/e2esdk-react": "1.0.0-beta.28",
"@tanstack/react-query": "5.55.4",
"@tanstack/react-query": "5.56.2",
"i18next": "23.15.1",
"idb": "8.0.0",
"lodash": "4.17.21",
"luxon": "3.5.0",
"next": "14.2.9",
"next": "14.2.11",
"react": "*",
"react-aria-components": "1.3.3",
"react-dom": "*",
"react-i18next": "15.0.1",
"react-i18next": "15.0.2",
"react-select": "5.8.0",
"styled-components": "6.1.13",
"y-protocols": "1.0.6",
@@ -42,16 +39,16 @@
},
"devDependencies": {
"@svgr/webpack": "8.1.0",
"@tanstack/react-query-devtools": "5.55.4",
"@tanstack/react-query-devtools": "5.56.2",
"@testing-library/dom": "10.4.0",
"@testing-library/jest-dom": "6.5.0",
"@testing-library/react": "16.0.1",
"@testing-library/user-event": "14.5.2",
"@types/jest": "29.5.12",
"@types/jest": "29.5.13",
"@types/lodash": "4.17.7",
"@types/luxon": "3.4.2",
"@types/node": "*",
"@types/react": "18.3.5",
"@types/react": "18.3.6",
"@types/react-dom": "*",
"cross-env": "*",
"dotenv": "16.4.5",
@@ -5,7 +5,7 @@ import { Box, Card, IconBG, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
interface PanelProps {
title: string;
title?: string;
setIsPanelOpen: (isOpen: boolean) => void;
}
@@ -36,10 +36,10 @@ export const Panel = ({
$width="100%"
$maxWidth="20rem"
$position="sticky"
$maxHeight="96vh"
$maxHeight="99vh"
$height="100%"
$css={`
top: 2vh;
top: 0vh;
transition: ${transition};
${
!isOpen &&
@@ -53,11 +53,14 @@ export const Panel = ({
{...closedOverridingStyles}
>
<Box
$overflow="hidden"
$overflow="inherit"
$position="sticky"
$css={`
top: 0;
opacity: ${isOpen ? '1' : '0'};
transition: ${transition};
`}
$maxHeight="100%"
>
<Box
$padding={{ all: 'small' }}
@@ -90,9 +93,11 @@ export const Panel = ({
}}
$radius="2px"
/>
<Text $weight="bold" $size="l" $theme="primary">
{title}
</Text>
{title && (
<Text $weight="bold" $size="l" $theme="primary">
{title}
</Text>
)}
</Box>
{children}
</Box>
@@ -1,7 +1,5 @@
import { CunninghamProvider } from '@openfun/cunningham-react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { E2ESDKClientProvider } from '@socialgouv/e2esdk-react';
import { e2esdkClient } from './auth/useAuthStore';
import { useCunninghamTheme } from '@/cunningham';
import '@/i18n/initI18n';
@@ -29,9 +27,7 @@ export function AppProvider({ children }: { children: React.ReactNode }) {
return (
<QueryClientProvider client={queryClient}>
<CunninghamProvider theme={theme}>
<E2ESDKClientProvider client={e2esdkClient}>
<Auth>{children}</Auth>
</E2ESDKClientProvider>
<Auth>{children}</Auth>
</CunninghamProvider>
</QueryClientProvider>
);
@@ -2,12 +2,10 @@
* Represents user retrieved from the API.
* @interface User
* @property {string} id - The id of the user.
* @property {string} sub - The `sub` field of OIDC
* @property {string} email - The email of the user.
* @property {string} name - The name of the user.
*/
export interface User {
id: string;
sub: string;
email: string;
}
@@ -5,31 +5,18 @@ import { baseApiUrl } from '@/core/conf';
import { User, getMe } from './api';
import { PATH_AUTH_LOCAL_STORAGE } from './conf';
import { Client, PublicUserIdentity } from '@socialgouv/e2esdk-client';
import { identity } from 'lodash';
export const e2esdkClient = new Client({
// Point it to where your server is listening
serverURL: 'https://app-a5a1b445-32e0-4cf4-a478-821a48f86ccf.cleverapps.io',
// Pass the signature public key you configured for the server
serverSignaturePublicKey: 'ayfva9SUh0mfgmifUtxcdLp4HriHJiqefEKnvYgY4qM',
});
interface AuthStore {
initiated: boolean;
authenticated: boolean;
readyForEncryption: boolean;
initAuth: () => void;
logout: () => void;
login: () => void;
endToEndData?: PublicUserIdentity;
userData?: User;
}
const initialState = {
initiated: false,
authenticated: false,
readyForEncryption: false,
userData: undefined,
};
@@ -37,51 +24,22 @@ export const useAuthStore = create<AuthStore>((set) => ({
initiated: initialState.initiated,
authenticated: initialState.authenticated,
userData: initialState.userData,
readyForEncryption: initialState.readyForEncryption,
initAuth: () => {
getMe()
.then(
(data: User) => {
// If a path is stored in the local storage, we redirect to it
const path_auth = localStorage.getItem(PATH_AUTH_LOCAL_STORAGE);
if (path_auth) {
localStorage.removeItem(PATH_AUTH_LOCAL_STORAGE);
window.location.replace(path_auth);
return;
}
.then((data: User) => {
// If a path is stored in the local storage, we redirect to it
const path_auth = localStorage.getItem(PATH_AUTH_LOCAL_STORAGE);
if (path_auth) {
localStorage.removeItem(PATH_AUTH_LOCAL_STORAGE);
window.location.replace(path_auth);
return;
}
set({ authenticated: true, userData: data });
return e2esdkClient
.signup(data.sub)
.then(() => data)
.catch(() => data);
},
() => {},
)
.then(
(data) => {
set({ readyForEncryption: true });
if (data) {
return e2esdkClient.login(data.sub);
}
},
(e) => {
throw e;
//if (data) {
// return e2esdkClient.login(data.sub);
//}
//fail
},
)
.then((publicIdentity: PublicUserIdentity | null | undefined) => {
if (!publicIdentity) throw Error('exploding');
console.log('publicIdentity', publicIdentity);
set({ endToEndData: publicIdentity });
set({ authenticated: true, userData: data });
})
.catch(() => {})
.finally(() => {
console.log('finally');
set({ initiated: true });
});
},
@@ -1,4 +1,4 @@
import { Block, BlockNoteEditor as BlockNoteEditorCore } from '@blocknote/core';
import { BlockNoteEditor as BlockNoteEditorCore } from '@blocknote/core';
import '@blocknote/core/fonts/inter.css';
import { BlockNoteView } from '@blocknote/mantine';
import '@blocknote/mantine/style.css';
@@ -17,7 +17,6 @@ import { useDocStore } from '../stores';
import { randomColor } from '../utils';
import { BlockNoteToolbar } from './BlockNoteToolbar';
import { useE2ESDKClient } from '@socialgouv/e2esdk-react';
const cssEditor = `
&, & > .bn-container, & .ProseMirror {
@@ -72,8 +71,7 @@ export const BlockNoteContent = ({
const { userData } = useAuthStore();
const { setStore, docsStore } = useDocStore();
const canSave = doc.abilities.partial_update && !isVersion;
const e2eClient = useE2ESDKClient();
useSaveDoc(doc.id, provider.document, canSave);
const storedEditor = docsStore?.[storeId]?.editor;
const {
mutateAsync: createDocAttachment,
@@ -101,39 +99,18 @@ export const BlockNoteContent = ({
return storedEditor;
}
// TODO decrypt doc.content
//localStorage.getItem('KEY');
const docId = 'uuid-du-doc';
const purpose = `doc:${docId}`;
const key = e2eClient.findKeyByPurpose(purpose);
if (!key) {
alert('probleme de key');
// return;
} else {
const decryptedMessage = e2eClient.decrypt(
doc.content,
key.keychainFingerprint,
);
console.log('decryptedMessage', decryptedMessage);
}
return BlockNoteEditorCore.create({
// collaboration: {
// provider,
// fragment: provider.document.getXmlFragment('document-store'),
// user: {
// name: userData?.email || 'Anonymous',
// color: randomColor(),
// },
// },
collaboration: {
provider,
fragment: provider.document.getXmlFragment('document-store'),
user: {
name: userData?.email || 'Anonymous',
color: randomColor(),
},
},
uploadFile,
initialContent: JSON.parse(doc.content),
});
}, [doc.content, storedEditor, uploadFile]);
useSaveDoc(doc.id, provider.document, canSave, editor);
}, [provider, storedEditor, uploadFile, userData?.email]);
useEffect(() => {
setStore(storeId, { editor });
@@ -9,7 +9,7 @@ import { Panel } from '@/components/Panel';
import { useCunninghamTheme } from '@/cunningham';
import { DocHeader } from '@/features/docs/doc-header';
import { Doc } from '@/features/docs/doc-management';
import { Summary, useDocSummaryStore } from '@/features/docs/doc-summary';
import { TableContent } from '@/features/docs/doc-table-content';
import {
VersionList,
Versions,
@@ -28,8 +28,6 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
query: { versionId },
} = useRouter();
const { isPanelVersionOpen, setIsPanelVersionOpen } = useDocVersionStore();
const { isPanelSummaryOpen, setIsPanelSummaryOpen } = useDocSummaryStore();
const { t } = useTranslation();
const isVersion = versionId && typeof versionId === 'string';
@@ -72,11 +70,7 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
<VersionList doc={doc} />
</Panel>
)}
{isPanelSummaryOpen && (
<Panel title={t('SUMMARY')} setIsPanelOpen={setIsPanelSummaryOpen}>
<Summary doc={doc} />
</Panel>
)}
<TableContent doc={doc} />
</Box>
</>
);
@@ -1,4 +1,3 @@
import { BlockNoteEditor } from '@blocknote/core';
import { useRouter } from 'next/router';
import { useCallback, useEffect, useRef, useState } from 'react';
import * as Y from 'yjs';
@@ -7,18 +6,11 @@ import { useUpdateDoc } from '@/features/docs/doc-management/';
import { KEY_LIST_DOC_VERSIONS } from '@/features/docs/doc-versioning';
import { toBase64 } from '../utils';
import { useE2ESDKClient } from '@socialgouv/e2esdk-react';
const useSaveDoc = (
docId: string,
doc: Y.Doc,
canSave: boolean,
editor: BlockNoteEditor,
) => {
const useSaveDoc = (docId: string, doc: Y.Doc, canSave: boolean) => {
const { mutate: updateDoc } = useUpdateDoc({
listInvalideQueries: [KEY_LIST_DOC_VERSIONS],
});
const e2eClient = useE2ESDKClient();
const [initialDoc, setInitialDoc] = useState<string>(
toBase64(Y.encodeStateAsUpdate(doc)),
);
@@ -64,32 +56,14 @@ const useSaveDoc = (
}, [canSave, hasChanged]);
const saveDoc = useCallback(() => {
const newDoc = JSON.stringify(editor.document);
//const newDoc = toBase64(Y.encodeStateAsUpdate(doc));
// TODO encode the content
const docId = 'uuid-du-doc';
const purpose = `doc:${docId}`;
const key = e2eClient.findKeyByPurpose(purpose);
if (!key) {
alert('probleme de key');
return;
}
const encrypted = e2eClient.encrypt(newDoc, key.keychainFingerprint);
console.log('encrypted', encrypted);
// todo
const newDoc = toBase64(Y.encodeStateAsUpdate(doc));
setInitialDoc(newDoc);
updateDoc({
id: docId,
content: newDoc,
});
}, [docId, editor?.document, updateDoc]);
}, [doc, docId, updateDoc]);
const timeout = useRef<NodeJS.Timeout>();
const router = useRouter();
@@ -26,9 +26,9 @@ export const useDocStore = create<UseDocStore>((set, get) => ({
guid: storeId,
});
// if (initialDoc) {
// Y.applyUpdate(doc, Buffer.from(initialDoc, 'base64'));
// }
if (initialDoc) {
Y.applyUpdate(doc, Buffer.from(initialDoc, 'base64'));
}
const provider = new HocuspocusProvider({
url: providerUrl(storeId),
@@ -9,7 +9,7 @@ import {
ModalShare,
ModalUpdateDoc,
} from '@/features/docs/doc-management';
import { useDocSummaryStore } from '@/features/docs/doc-summary';
import { useDocTableContentStore } from '@/features/docs/doc-table-content';
import { useDocVersionStore } from '@/features/docs/doc-versioning';
import { ModalPDF } from './ModalExport';
@@ -26,7 +26,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
const [isModalPDFOpen, setIsModalPDFOpen] = useState(false);
const [isDropOpen, setIsDropOpen] = useState(false);
const { setIsPanelVersionOpen } = useDocVersionStore();
const { setIsPanelSummaryOpen } = useDocSummaryStore();
const { setIsPanelTableContentOpen } = useDocTableContentStore();
return (
<Box
@@ -83,7 +83,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
)}
<Button
onClick={() => {
setIsPanelSummaryOpen(true);
setIsPanelTableContentOpen(true);
setIsPanelVersionOpen(false);
setIsDropOpen(false);
}}
@@ -91,7 +91,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
icon={<span className="material-icons">summarize</span>}
size="small"
>
<Text $theme="primary">{t('Summary')}</Text>
<Text $theme="primary">{t('Table of contents')}</Text>
</Button>
<Button
onClick={() => {
@@ -1,104 +0,0 @@
import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, BoxButton, Text } from '@/components';
import { useDocStore } from '../../doc-editor';
import { Doc } from '../../doc-management';
import { useDocSummaryStore } from '../stores';
interface SummaryProps {
doc: Doc;
}
export const Summary = ({ doc }: SummaryProps) => {
const { docsStore } = useDocStore();
const { t } = useTranslation();
const editor = docsStore?.[doc.id]?.editor;
const headingFiltering = useCallback(
() => editor?.document.filter((block) => block.type === 'heading'),
[editor?.document],
);
const [headings, setHeadings] = useState(headingFiltering());
const { setIsPanelSummaryOpen } = useDocSummaryStore();
useEffect(() => {
return () => {
setIsPanelSummaryOpen(false);
};
}, [setIsPanelSummaryOpen]);
if (!editor) {
return null;
}
editor.onEditorContentChange(() => {
setHeadings(headingFiltering());
});
return (
<Box $overflow="auto" $padding="small">
{headings?.map((heading) => (
<BoxButton
key={heading.id}
onClick={() => {
editor.focus();
editor?.setTextCursorPosition(heading.id, 'end');
document
.querySelector(`[data-id="${heading.id}"]`)
?.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
}}
style={{ textAlign: 'left' }}
>
<Text $theme="primary" $padding={{ vertical: 'xtiny' }}>
{heading.content?.[0]?.type === 'text' && heading.content?.[0]?.text
? `- ${heading.content[0].text}`
: ''}
</Text>
</BoxButton>
))}
<Box
$height="1px"
$width="auto"
$background="#e5e5e5"
$margin={{ vertical: 'small' }}
$css="flex: none;"
/>
<BoxButton
onClick={() => {
editor.focus();
document.querySelector(`.bn-editor`)?.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
}}
>
<Text $theme="primary" $padding={{ vertical: 'xtiny' }}>
{t('Back to top')}
</Text>
</BoxButton>
<BoxButton
onClick={() => {
editor.focus();
document
.querySelector(
`.bn-editor > .bn-block-group > .bn-block-outer:last-child`,
)
?.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
}}
>
<Text $theme="primary" $padding={{ vertical: 'xtiny' }}>
{t('Go to bottom')}
</Text>
</BoxButton>
</Box>
);
};
@@ -1 +0,0 @@
export * from './Summary';
@@ -1 +0,0 @@
export * from './useDocSummaryStore';
@@ -1,13 +0,0 @@
import { create } from 'zustand';
export interface UseDocSummaryStore {
isPanelSummaryOpen: boolean;
setIsPanelSummaryOpen: (isOpen: boolean) => void;
}
export const useDocSummaryStore = create<UseDocSummaryStore>((set) => ({
isPanelSummaryOpen: false,
setIsPanelSummaryOpen: (isPanelSummaryOpen) => {
set(() => ({ isPanelSummaryOpen }));
},
}));
@@ -0,0 +1,67 @@
import { BlockNoteEditor } from '@blocknote/core';
import { useState } from 'react';
import { BoxButton, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
const sizeMap: { [key: number]: string } = {
1: '1.2rem',
2: '1rem',
3: '0.8rem',
};
export type HeadingsHighlight = {
headingId: string;
isVisible: boolean;
}[];
interface HeadingProps {
editor: BlockNoteEditor;
level: number;
text: string;
headingId: string;
isHighlight: boolean;
}
export const Heading = ({
headingId,
editor,
isHighlight,
level,
text,
}: HeadingProps) => {
const [isHover, setIsHover] = useState(isHighlight);
const { colorsTokens } = useCunninghamTheme();
return (
<BoxButton
key={headingId}
onMouseOver={() => setIsHover(true)}
onMouseLeave={() => setIsHover(false)}
onClick={() => {
editor.focus();
editor.setTextCursorPosition(headingId, 'end');
document.querySelector(`[data-id="${headingId}"]`)?.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
}}
$css="text-align: left;"
>
<Text
$theme="primary"
$padding={{ vertical: 'xtiny', left: 'tiny' }}
$size={sizeMap[level]}
$hasTransition
$css={
isHover || isHighlight
? `box-shadow: -2px 0px 0px ${colorsTokens()[isHighlight ? 'primary-500' : 'primary-400']};`
: ''
}
aria-selected={isHighlight}
>
{text}
</Text>
</BoxButton>
);
};
@@ -0,0 +1,197 @@
import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, BoxButton, Text } from '@/components';
import { Panel } from '@/components/Panel';
import { useDocStore } from '@/features/docs/doc-editor';
import { Doc } from '@/features/docs/doc-management';
import { useDocTableContentStore } from '../stores';
import { Heading } from './Heading';
const recursiveTextContent = (content: HeadingBlock['content']): string => {
if (!content) {
return '';
}
return content.reduce((acc, content) => {
if (content.type === 'text') {
return acc + content.text;
} else if (content.type === 'link') {
return acc + recursiveTextContent(content.content);
}
return acc;
}, '');
};
type HeadingBlock = {
id: string;
type: string;
text: string;
content: HeadingBlock[];
contentText: string;
props: {
level: number;
};
};
interface TableContentProps {
doc: Doc;
}
export const TableContent = ({ doc }: TableContentProps) => {
const { docsStore } = useDocStore();
const { t } = useTranslation();
const editor = docsStore?.[doc.id]?.editor;
const headingFiltering = useCallback(
() =>
editor?.document
.filter((block) => block.type === 'heading')
.map((block) => ({
...block,
contentText: recursiveTextContent(
block.content as unknown as HeadingBlock['content'],
),
})) as unknown as HeadingBlock[],
[editor?.document],
);
const [headings, setHeadings] = useState<HeadingBlock[]>();
const { setIsPanelTableContentOpen, isPanelTableContentOpen } =
useDocTableContentStore();
const [hasBeenClose, setHasBeenClose] = useState(false);
const setClosePanel = () => {
setHasBeenClose(true);
setIsPanelTableContentOpen(false);
};
const [headingIdHighlight, setHeadingIdHighlight] = useState<string>();
// Open the panel if there are more than 1 heading
useEffect(() => {
if (headings?.length && headings.length > 1 && !hasBeenClose) {
setIsPanelTableContentOpen(true);
}
}, [setIsPanelTableContentOpen, headings, hasBeenClose]);
// Close the panel unmount
useEffect(() => {
return () => {
setIsPanelTableContentOpen(false);
};
}, [setIsPanelTableContentOpen]);
// To highlight the first heading in the viewport
useEffect(() => {
const handleScroll = () => {
if (!headings) {
return;
}
for (const heading of headings) {
const elHeading = document.body.querySelector(
`.bn-block-outer[data-id="${heading.id}"] [data-content-type="heading"]:first-child`,
);
if (!elHeading) {
return;
}
const rect = elHeading.getBoundingClientRect();
const isVisible =
rect.top + rect.height >= 1 &&
rect.bottom <=
(window.innerHeight || document.documentElement.clientHeight);
if (isVisible) {
setHeadingIdHighlight(heading.id);
break;
}
}
};
window.addEventListener('scroll', () => {
setTimeout(() => {
handleScroll();
}, 300);
});
handleScroll();
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, [headings, setHeadingIdHighlight]);
if (!editor) {
return null;
}
// Update the headings when the editor content changes
editor?.onEditorContentChange(() => {
setHeadings(headingFiltering());
});
if (!isPanelTableContentOpen) {
return null;
}
return (
<Panel setIsPanelOpen={setClosePanel}>
<Box $padding="small" $maxHeight="95%">
<Box $overflow="auto">
{headings?.map((heading) => (
<Heading
editor={editor}
headingId={heading.id}
level={heading.props.level}
text={heading.contentText}
key={heading.id}
isHighlight={headingIdHighlight === heading.id}
/>
))}
</Box>
<Box
$height="1px"
$width="auto"
$background="#e5e5e5"
$margin={{ vertical: 'small' }}
$css="flex: none;"
/>
<BoxButton
onClick={() => {
editor.focus();
document.querySelector(`.bn-editor`)?.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
}}
>
<Text $theme="primary" $padding={{ vertical: 'xtiny' }}>
{t('Back to top')}
</Text>
</BoxButton>
<BoxButton
onClick={() => {
editor.focus();
document
.querySelector(
`.bn-editor > .bn-block-group > .bn-block-outer:last-child`,
)
?.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
}}
>
<Text $theme="primary" $padding={{ vertical: 'xtiny' }}>
{t('Go to bottom')}
</Text>
</BoxButton>
</Box>
</Panel>
);
};
@@ -0,0 +1 @@
export * from './TableContent';
@@ -0,0 +1 @@
export * from './useDocTableContentStore';
@@ -0,0 +1,15 @@
import { create } from 'zustand';
export interface UseDocTableContentStore {
isPanelTableContentOpen: boolean;
setIsPanelTableContentOpen: (isOpen: boolean) => void;
}
export const useDocTableContentStore = create<UseDocTableContentStore>(
(set) => ({
isPanelTableContentOpen: false,
setIsPanelTableContentOpen: (isPanelTableContentOpen) => {
set(() => ({ isPanelTableContentOpen }));
},
}),
);
@@ -20,6 +20,7 @@
"Content modal to delete document": "Contenu modal pour supprimer le document",
"Content modal to export the document": "Contenu modal pour exporter le document",
"Cookies placed": "Cookies déposés",
"Copy link": "Copier le lien",
"Copyright": "Copyright",
"Create a new document": "Créer un nouveau document",
"Create the document": "Créer le document",
@@ -29,9 +30,10 @@
"Defender of Rights - Free response - 71120 75342 Paris CEDEX 07": "Défenseur des droits - Réponse gratuite - 71120 75342 Paris CEDEX 07",
"Delete document": "Supprimer le document",
"Deleting the document \"{{title}}\"": "Suppression du document \"{{title}}\"",
"Doc visibility card": "Carte de visibilité du doc",
"Docs": "Docs",
"Docs Description": "Description de Docs",
"Docs Logo": "Logo Docs",
"Docs: Your new companion to collaborate on documents efficiently, intuitively, and securely.": "Docs : Votre nouveau compagnon pour collaborer sur des documents efficacement, intuitivement et en toute sécurité.",
"Document icon": "Icône du document",
"Document name": "Nom du document",
"Document panel": "Panneau du document",
@@ -45,6 +47,7 @@
"Export": "Exporter",
"Export your document, it will be inserted in the selected template.": "Exportez votre document, il sera inséré dans le modèle sélectionné.",
"Failed to add the member in the document.": "Impossible d'ajouter le membre dans le document.",
"Failed to copy link": "Échec de la copie du lien",
"Failed to create the invitation for {{email}}.": "Impossible de créer l'invitation pour {{email}}.",
"Find a member to add to the document": "Trouver un membre à ajouter au document",
"French Interministerial Directorate for Digital Affairs (DINUM), 20 avenue de Ségur 75007 Paris.": "Direction interministérielle des affaires numériques (DINUM), 20 avenue de Segur 75007 Paris.",
@@ -56,7 +59,6 @@
"Invitation sent to {{email}}.": "Invitation envoyée à {{email}}.",
"Invite new members to {{title}}": "Invitez de nouveaux membres à rejoindre {{title}}",
"Invited": "Invité",
"Is it public ?": "Est-ce public?",
"It is the card information about the document.": "Il s'agit de la carte d'information du document.",
"It seems that the page you are looking for does not exist or cannot be displayed correctly.": "Il semble que la page que vous cherchez n'existe pas ou ne puisse pas être affichée correctement.",
"It's true, you didn't have to click on a block that covers half the page to say you agree to the placement of cookies — even if you don't know what it means!": "C'est vrai, vous n'avez pas à cliquer sur un bloc qui couvre la moitié de la page pour dire que vous acceptez le placement de cookies — même si vous ne savez pas ce que cela signifie !",
@@ -64,13 +66,14 @@
"Language Icon": "Icône de langue",
"Legal Notice": "Mentions Legales",
"Legal notice": "Mention légale",
"Link Copied !": "Lien copié !",
"List invitation card": "Carte de liste d'invitation",
"List members card": "Carte liste des membres",
"Login": "Connexion",
"Logout": "Se déconnecter",
"Members": "Membres",
"Modal confirmation to restore the version": "Modale de confirmation pour restaurer la version",
"More info?": "Plus d'infos ?",
"My account": "Mon compte",
"No editor found": "Pas d'éditeur trouvé",
"Nothing exceptional, no special privileges related to a .gouv.fr.": "Rien d'exceptionnel, pas de privilèges spéciaux liés à un .gouv.fr.",
"Offline ?!": "Hors-ligne ?!",
@@ -94,16 +97,16 @@
"Restore this version": "Restaurer cette version",
"Restore this version?": "Restaurer cette version?",
"Role": "Rôle",
"SUMMARY": "RÉSUMÉ",
"Search by email": "Recherche par email",
"Send a letter by post (free of charge, no stamp needed):": "Envoyer un courrier par la poste (gratuit, ne pas mettre de timbre):",
"Share": "Partager",
"Something bad happens, please retry.": "Une erreur inattendue s'est produite, veuillez réessayer.",
"Stéphanie Schaer: Interministerial Digital Director (DINUM).": "Stéphanie Schaer: Directrice numérique interministériel (DINUM).",
"Summary": "Résumé",
"Table of contents": "Table des matières",
"Template": "Template",
"The document has been deleted.": "Le document a bien été supprimé.",
"The document has been updated.": "Le document a été mis à jour.",
"The document visiblitity has been updated.": "La visibilité du document a été mise à jour.",
"The invitation has been removed.": "L'invitation a été supprimée.",
"The member has been removed from the document": "Le membre a été retiré du document",
"The role has been updated": "Le rôle a été mis à jour",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "impress",
"version": "1.3.0",
"version": "1.4.0",
"private": true,
"workspaces": {
"packages": [
@@ -1,24 +1,24 @@
{
"name": "eslint-config-impress",
"version": "1.3.0",
"version": "1.4.0",
"license": "MIT",
"scripts": {
"lint": "eslint --ext .js ."
},
"dependencies": {
"@next/eslint-plugin-next": "14.2.9",
"@tanstack/eslint-plugin-query": "5.53.0",
"@next/eslint-plugin-next": "14.2.11",
"@tanstack/eslint-plugin-query": "5.56.1",
"@typescript-eslint/eslint-plugin": "*",
"@typescript-eslint/parser": "*",
"eslint": "*",
"eslint-config-next": "14.2.9",
"eslint-config-next": "14.2.11",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-import": "2.30.0",
"eslint-plugin-jest": "28.8.3",
"eslint-plugin-jsx-a11y": "6.10.0",
"eslint-plugin-playwright": "1.6.2",
"eslint-plugin-prettier": "5.2.1",
"eslint-plugin-react": "7.35.2",
"eslint-plugin-react": "7.36.1",
"eslint-plugin-testing-library": "6.3.0",
"prettier": "3.3.3"
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "packages-i18n",
"version": "1.3.0",
"version": "1.4.0",
"private": true,
"scripts": {
"extract-translation": "yarn extract-translation:impress",
@@ -11,7 +11,7 @@
"test": "jest"
},
"dependencies": {
"@types/jest": "29.5.12",
"@types/jest": "29.5.13",
"@types/node": "*",
"eslint-config-impress": "*",
"eslint-plugin-import": "2.30.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "server-y-provider",
"version": "1.3.0",
"version": "1.4.0",
"description": "Y.js provider for docs",
"repository": "https://github.com/numerique-gouv/impress",
"license": "MIT",
+12018 -13180
View File
File diff suppressed because it is too large Load Diff
@@ -72,7 +72,8 @@ frontend:
envVars:
PORT: 8080
NEXT_PUBLIC_API_ORIGIN: https://impress.127.0.0.1.nip.io
NEXT_PUBLIC_SIGNALING_URL: wss://impress.127.0.0.1.nip.io/ws
NEXT_PUBLIC_Y_PROVIDER_URL: wss://impress.127.0.0.1.nip.io/ws
NEXT_PUBLIC_MEDIA_URL: https://impress.127.0.0.1.nip.io
replicas: 1
command:
@@ -1,7 +1,7 @@
image:
repository: lasuite/impress-backend
pullPolicy: Always
tag: "v1.3.0-preprod"
tag: "v1.4.0-preprod"
backend:
migrateJobAnnotations:
@@ -124,13 +124,13 @@ frontend:
image:
repository: lasuite/impress-frontend
pullPolicy: Always
tag: "v1.3.0-preprod"
tag: "v1.4.0-preprod"
yProvider:
image:
repository: lasuite/impress-y-provider
pullPolicy: Always
tag: "v1.3.0-preprod"
tag: "v1.4.0-preprod"
ingress:
enabled: true
@@ -1,7 +1,7 @@
image:
repository: lasuite/impress-backend
pullPolicy: Always
tag: "v1.3.0"
tag: "v1.4.0"
backend:
migrateJobAnnotations:
@@ -124,13 +124,13 @@ frontend:
image:
repository: lasuite/impress-frontend
pullPolicy: Always
tag: "v1.3.0"
tag: "v1.4.0"
yProvider:
image:
repository: lasuite/impress-y-provider
pullPolicy: Always
tag: "v1.3.0"
tag: "v1.4.0"
ingress:
enabled: true
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mail_mjml",
"version": "1.3.0",
"version": "1.4.0",
"description": "An util to generate html and text django's templates from mjml templates",
"type": "module",
"dependencies": {