mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-09 05:41:57 -04:00
refactor(frontend): switch to DTOs from @hedgedoc/commons
Co-authored-by: Philip Molares <philip.molares@udo.edu> Signed-off-by: Philip Molares <philip.molares@udo.edu> Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
parent
deee8e885f
commit
e411ddf099
121 changed files with 620 additions and 819 deletions
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { NotePermissions } from '@hedgedoc/commons'
|
||||
import type { NotePermissionsDto } from '@hedgedoc/commons'
|
||||
|
||||
const mockPermissionChangeApiRoutes = (permission: NotePermissions) => {
|
||||
const mockPermissionChangeApiRoutes = (permission: NotePermissionsDto) => {
|
||||
cy.intercept('PUT', 'api/private/notes/mock-note/metadata/permissions/groups/_EVERYONE', {
|
||||
statusCode: 200,
|
||||
body: permission
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { AuthProvider } from '../../src/api/config/types'
|
||||
import { AuthProviderType } from '../../src/api/config/types'
|
||||
import type { ProviderType } from '../../src/api/config/types'
|
||||
import { ProviderType } from '../../src/api/config/types'
|
||||
|
||||
const initLoggedOutTestWithCustomAuthProviders = (cy: Cypress.cy, enabledProviders: AuthProvider[]) => {
|
||||
const initLoggedOutTestWithCustomAuthProviders = (cy: Cypress.cy, enabledProviders: ProviderType[]) => {
|
||||
cy.logOut()
|
||||
cy.loadConfig({
|
||||
authProviders: enabledProviders
|
||||
|
@ -48,7 +48,7 @@ describe('When logged-out ', () => {
|
|||
it('sign-in button points to login route: internal', () => {
|
||||
initLoggedOutTestWithCustomAuthProviders(cy, [
|
||||
{
|
||||
type: AuthProviderType.LOCAL
|
||||
type: ProviderType.LOCAL
|
||||
}
|
||||
])
|
||||
cy.getByCypressId('sign-in-button')
|
||||
|
@ -60,7 +60,7 @@ describe('When logged-out ', () => {
|
|||
it('sign-in button points to login route: ldap', () => {
|
||||
initLoggedOutTestWithCustomAuthProviders(cy, [
|
||||
{
|
||||
type: AuthProviderType.LDAP,
|
||||
type: ProviderType.LDAP,
|
||||
identifier: 'cy-ldap',
|
||||
providerName: 'cy LDAP'
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ describe('When logged-out ', () => {
|
|||
it('sign-in button points to auth-provider', () => {
|
||||
initLoggedOutTestWithCustomAuthProviders(cy, [
|
||||
{
|
||||
type: AuthProviderType.OIDC,
|
||||
type: ProviderType.OIDC,
|
||||
identifier: 'github',
|
||||
providerName: 'GitHub',
|
||||
theme: 'github'
|
||||
|
@ -94,13 +94,13 @@ describe('When logged-out ', () => {
|
|||
it('sign-in button points to login route', () => {
|
||||
initLoggedOutTestWithCustomAuthProviders(cy, [
|
||||
{
|
||||
type: AuthProviderType.OIDC,
|
||||
type: ProviderType.OIDC,
|
||||
identifier: 'github',
|
||||
providerName: 'GitHub',
|
||||
theme: 'github'
|
||||
},
|
||||
{
|
||||
type: AuthProviderType.OIDC,
|
||||
type: ProviderType.OIDC,
|
||||
identifier: 'gitlab',
|
||||
providerName: 'GitLab',
|
||||
theme: 'gitlab'
|
||||
|
@ -117,13 +117,13 @@ describe('When logged-out ', () => {
|
|||
it('sign-in button points to login route', () => {
|
||||
initLoggedOutTestWithCustomAuthProviders(cy, [
|
||||
{
|
||||
type: AuthProviderType.OIDC,
|
||||
type: ProviderType.OIDC,
|
||||
identifier: 'github',
|
||||
providerName: 'GitHub',
|
||||
theme: 'github'
|
||||
},
|
||||
{
|
||||
type: AuthProviderType.LOCAL
|
||||
type: ProviderType.LOCAL
|
||||
}
|
||||
])
|
||||
cy.getByCypressId('sign-in-button')
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { AuthProviderType } from '../../src/api/config/types'
|
||||
import { ProviderType } from '@hedgedoc/commons'
|
||||
import { HttpMethod } from '../../src/handler-utils/respond-to-matching-request'
|
||||
import { IGNORE_MOTD, MOTD_LOCAL_STORAGE_KEY } from '../../src/components/global-dialogs/motd-modal/local-storage-keys'
|
||||
|
||||
|
@ -22,15 +22,15 @@ export const branding = {
|
|||
|
||||
export const authProviders = [
|
||||
{
|
||||
type: AuthProviderType.LOCAL
|
||||
type: ProviderType.LOCAL
|
||||
},
|
||||
{
|
||||
type: AuthProviderType.LDAP,
|
||||
type: ProviderType.LDAP,
|
||||
identifier: 'test-ldap',
|
||||
providerName: 'Test LDAP'
|
||||
},
|
||||
{
|
||||
type: AuthProviderType.OIDC,
|
||||
type: ProviderType.OIDC,
|
||||
identifier: 'test-oidc',
|
||||
providerName: 'Test OIDC'
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { Note } from '../../src/api/notes/types'
|
||||
import type { NoteDto } from '@hedgedoc/commons'
|
||||
|
||||
export const testNoteId = 'test'
|
||||
const mockMetadata = {
|
||||
|
@ -37,6 +37,6 @@ beforeEach(() => {
|
|||
content: '',
|
||||
metadata: mockMetadata,
|
||||
editedByAtPosition: []
|
||||
} as Note)
|
||||
} as NoteDto)
|
||||
cy.intercept(`api/private/notes/${testNoteId}/metadata`, mockMetadata)
|
||||
})
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { DeleteApiRequestBuilder } from '../common/api-request-builder/delete-api-request-builder'
|
||||
import { PostApiRequestBuilder } from '../common/api-request-builder/post-api-request-builder'
|
||||
import { PutApiRequestBuilder } from '../common/api-request-builder/put-api-request-builder'
|
||||
import type { Alias, NewAliasDto, PrimaryAliasDto } from './types'
|
||||
import type { AliasDto, AliasCreateDto, AliasUpdateDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Adds an alias to an existing note.
|
||||
|
@ -16,8 +16,8 @@ import type { Alias, NewAliasDto, PrimaryAliasDto } from './types'
|
|||
* @return Information about the newly created alias.
|
||||
* @throws {Error} when the api request wasn't successful
|
||||
*/
|
||||
export const addAlias = async (noteIdOrAlias: string, newAlias: string): Promise<Alias> => {
|
||||
const response = await new PostApiRequestBuilder<Alias, NewAliasDto>('alias')
|
||||
export const addAlias = async (noteIdOrAlias: string, newAlias: string): Promise<AliasDto> => {
|
||||
const response = await new PostApiRequestBuilder<AliasDto, AliasCreateDto>('alias')
|
||||
.withJsonBody({
|
||||
noteIdOrAlias,
|
||||
newAlias
|
||||
|
@ -34,8 +34,8 @@ export const addAlias = async (noteIdOrAlias: string, newAlias: string): Promise
|
|||
* @return The updated information about the alias.
|
||||
* @throws {Error} when the api request wasn't successfull
|
||||
*/
|
||||
export const markAliasAsPrimary = async (alias: string): Promise<Alias> => {
|
||||
const response = await new PutApiRequestBuilder<Alias, PrimaryAliasDto>('alias/' + alias)
|
||||
export const markAliasAsPrimary = async (alias: string): Promise<AliasDto> => {
|
||||
const response = await new PutApiRequestBuilder<AliasDto, AliasUpdateDto>('alias/' + alias)
|
||||
.withJsonBody({
|
||||
primaryAlias: true
|
||||
})
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
export interface Alias {
|
||||
name: string
|
||||
primaryAlias: boolean
|
||||
noteId: string
|
||||
}
|
||||
|
||||
export interface NewAliasDto {
|
||||
noteIdOrAlias: string
|
||||
newAlias: string
|
||||
}
|
||||
|
||||
export interface PrimaryAliasDto {
|
||||
primaryAlias: boolean
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { DeleteApiRequestBuilder } from '../common/api-request-builder/delete-api-request-builder'
|
||||
import { GetApiRequestBuilder } from '../common/api-request-builder/get-api-request-builder'
|
||||
import { PostApiRequestBuilder } from '../common/api-request-builder/post-api-request-builder'
|
||||
import type { AccessToken, AccessTokenWithSecret, CreateAccessTokenDto } from './types'
|
||||
import type { ApiTokenCreateDto, ApiTokenDto, ApiTokenWithSecretDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Retrieves the access tokens for the current user.
|
||||
|
@ -14,8 +14,8 @@ import type { AccessToken, AccessTokenWithSecret, CreateAccessTokenDto } from '.
|
|||
* @return List of access token metadata.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const getAccessTokenList = async (): Promise<AccessToken[]> => {
|
||||
const response = await new GetApiRequestBuilder<AccessToken[]>('tokens').sendRequest()
|
||||
export const getAccessTokenList = async (): Promise<ApiTokenDto[]> => {
|
||||
const response = await new GetApiRequestBuilder<ApiTokenDto[]>('tokens').sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,8 @@ export const getAccessTokenList = async (): Promise<AccessToken[]> => {
|
|||
* @return The new access token metadata along with its secret.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const postNewAccessToken = async (label: string, validUntil: number): Promise<AccessTokenWithSecret> => {
|
||||
const response = await new PostApiRequestBuilder<AccessTokenWithSecret, CreateAccessTokenDto>('tokens')
|
||||
export const postNewAccessToken = async (label: string, validUntil: Date): Promise<ApiTokenWithSecretDto> => {
|
||||
const response = await new PostApiRequestBuilder<ApiTokenWithSecretDto, ApiTokenCreateDto>('tokens')
|
||||
.withJsonBody({
|
||||
label,
|
||||
validUntil
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { DeleteApiRequestBuilder } from '../common/api-request-builder/delete-api-request-builder'
|
||||
import { PostApiRequestBuilder } from '../common/api-request-builder/post-api-request-builder'
|
||||
import type { LogoutResponseDto, UsernameCheckDto, UsernameCheckResponseDto } from './types'
|
||||
import type { LogoutResponseDto, UsernameCheckDto, UsernameCheckResponseDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Requests to log out the current user.
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { PostApiRequestBuilder } from '../common/api-request-builder/post-api-request-builder'
|
||||
import type { LdapLoginResponseDto, LoginDto } from './types'
|
||||
import type { LdapLoginDto, LdapLoginResponseDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Requests to log in a user via LDAP credentials.
|
||||
|
@ -19,7 +19,7 @@ export const doLdapLogin = async (
|
|||
username: string,
|
||||
password: string
|
||||
): Promise<LdapLoginResponseDto> => {
|
||||
const response = await new PostApiRequestBuilder<LdapLoginResponseDto, LoginDto>(`auth/ldap/${provider}/login`)
|
||||
const response = await new PostApiRequestBuilder<LdapLoginResponseDto, LdapLoginDto>(`auth/ldap/${provider}/login`)
|
||||
.withJsonBody({
|
||||
username: username,
|
||||
password: password
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { PostApiRequestBuilder } from '../common/api-request-builder/post-api-request-builder'
|
||||
import { PutApiRequestBuilder } from '../common/api-request-builder/put-api-request-builder'
|
||||
import type { ChangePasswordDto, LoginDto, RegisterDto } from './types'
|
||||
import type { UpdatePasswordDto, LoginDto, RegisterDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Requests to do a local login with a provided username and password.
|
||||
|
@ -54,7 +54,7 @@ export const doLocalRegister = async (username: string, displayName: string, pas
|
|||
* @throws {AuthError.LOGIN_DISABLED} when local login is disabled on the backend.
|
||||
*/
|
||||
export const doLocalPasswordChange = async (currentPassword: string, newPassword: string): Promise<void> => {
|
||||
await new PutApiRequestBuilder<void, ChangePasswordDto>('auth/local')
|
||||
await new PutApiRequestBuilder<void, UpdatePasswordDto>('auth/local')
|
||||
.withJsonBody({
|
||||
currentPassword,
|
||||
newPassword
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { FullUserInfo } from '../users/types'
|
||||
import type { FullUserInfoDto } from '@hedgedoc/commons'
|
||||
import { GetApiRequestBuilder } from '../common/api-request-builder/get-api-request-builder'
|
||||
import { DeleteApiRequestBuilder } from '../common/api-request-builder/delete-api-request-builder'
|
||||
import type { PendingUserConfirmDto } from './types'
|
||||
import type { PendingUserConfirmationDto } from '@hedgedoc/commons'
|
||||
import { PutApiRequestBuilder } from '../common/api-request-builder/put-api-request-builder'
|
||||
|
||||
/**
|
||||
* Fetches the pending user information.
|
||||
* @returns The pending user information.
|
||||
*/
|
||||
export const getPendingUserInfo = async (): Promise<Partial<FullUserInfo>> => {
|
||||
const response = await new GetApiRequestBuilder<Partial<FullUserInfo>>('auth/pending-user').sendRequest()
|
||||
export const getPendingUserInfo = async (): Promise<FullUserInfoDto> => {
|
||||
const response = await new GetApiRequestBuilder<FullUserInfoDto>('auth/pending-user').sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,8 @@ export const cancelPendingUser = async (): Promise<void> => {
|
|||
* Confirms the pending user with updated user information.
|
||||
* @param updatedUserInfo The updated user information.
|
||||
*/
|
||||
export const confirmPendingUser = async (updatedUserInfo: PendingUserConfirmDto): Promise<void> => {
|
||||
await new PutApiRequestBuilder<void, PendingUserConfirmDto>('auth/pending-user')
|
||||
export const confirmPendingUser = async (updatedUserInfo: PendingUserConfirmationDto): Promise<void> => {
|
||||
await new PutApiRequestBuilder<void, PendingUserConfirmationDto>('auth/pending-user')
|
||||
.withJsonBody(updatedUserInfo)
|
||||
.sendRequest()
|
||||
}
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export interface LoginDto {
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface RegisterDto {
|
||||
username: string
|
||||
password: string
|
||||
displayName: string
|
||||
}
|
||||
|
||||
export interface ChangePasswordDto {
|
||||
currentPassword: string
|
||||
newPassword: string
|
||||
}
|
||||
|
||||
export interface LogoutResponseDto {
|
||||
redirect: string
|
||||
}
|
||||
|
||||
export interface UsernameCheckDto {
|
||||
username: string
|
||||
}
|
||||
|
||||
export interface UsernameCheckResponseDto {
|
||||
usernameAvailable: boolean
|
||||
}
|
||||
|
||||
export interface PendingUserConfirmDto {
|
||||
username: string
|
||||
displayName: string
|
||||
profilePicture: string | undefined
|
||||
}
|
||||
|
||||
export interface LdapLoginResponseDto {
|
||||
newUser: boolean
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { GetApiRequestBuilder } from '../common/api-request-builder/get-api-request-builder'
|
||||
import type { FrontendConfig } from './types'
|
||||
import { isBuildTime } from '../../utils/test-modes'
|
||||
import type { FrontendConfigDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Fetches the frontend config from the backend.
|
||||
|
@ -13,10 +13,10 @@ import { isBuildTime } from '../../utils/test-modes'
|
|||
* @return The frontend config.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const getConfig = async (baseUrl?: string): Promise<FrontendConfig | undefined> => {
|
||||
export const getConfig = async (baseUrl?: string): Promise<FrontendConfigDto | undefined> => {
|
||||
if (isBuildTime) {
|
||||
return undefined
|
||||
}
|
||||
const response = await new GetApiRequestBuilder<FrontendConfig>('config', baseUrl).sendRequest()
|
||||
const response = await new GetApiRequestBuilder<FrontendConfigDto>('config', baseUrl).sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
}
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export interface FrontendConfig {
|
||||
allowRegister: boolean
|
||||
allowProfileEdits: boolean
|
||||
allowChooseUsername: boolean
|
||||
authProviders: AuthProvider[]
|
||||
branding: BrandingConfig
|
||||
guestAccess: GuestAccessLevel
|
||||
useImageProxy: boolean
|
||||
specialUrls: SpecialUrls
|
||||
version: BackendVersion
|
||||
plantumlServer?: string
|
||||
maxDocumentLength: number
|
||||
}
|
||||
|
||||
export enum GuestAccessLevel {
|
||||
DENY = 'deny',
|
||||
READ = 'read',
|
||||
WRITE = 'write',
|
||||
CREATE = 'create'
|
||||
}
|
||||
|
||||
export enum AuthProviderType {
|
||||
OIDC = 'oidc',
|
||||
LDAP = 'ldap',
|
||||
LOCAL = 'local'
|
||||
}
|
||||
|
||||
export type AuthProviderTypeWithCustomName = AuthProviderType.LDAP | AuthProviderType.OIDC
|
||||
|
||||
export type AuthProviderTypeWithoutCustomName = AuthProviderType.LOCAL
|
||||
|
||||
export const authProviderTypeOneClick = [AuthProviderType.OIDC]
|
||||
|
||||
export interface AuthProviderWithCustomName {
|
||||
type: AuthProviderTypeWithCustomName
|
||||
identifier: string
|
||||
providerName: string
|
||||
theme?: string
|
||||
}
|
||||
|
||||
export interface AuthProviderWithoutCustomName {
|
||||
type: AuthProviderTypeWithoutCustomName
|
||||
}
|
||||
|
||||
export type AuthProvider = AuthProviderWithCustomName | AuthProviderWithoutCustomName
|
||||
|
||||
export interface BrandingConfig {
|
||||
name?: string
|
||||
logo?: string
|
||||
}
|
||||
|
||||
export interface BackendVersion {
|
||||
major: number
|
||||
minor: number
|
||||
patch: number
|
||||
preRelease?: string
|
||||
commit?: string
|
||||
}
|
||||
|
||||
export interface SpecialUrls {
|
||||
privacy?: string
|
||||
termsOfUse?: string
|
||||
imprint?: string
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { GetApiRequestBuilder } from '../common/api-request-builder/get-api-request-builder'
|
||||
import type { GroupInfo } from './types'
|
||||
import type { GroupInfoDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Retrieves information about a group with a given name.
|
||||
|
@ -13,7 +13,7 @@ import type { GroupInfo } from './types'
|
|||
* @return Information about the group.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const getGroup = async (groupName: string): Promise<GroupInfo> => {
|
||||
const response = await new GetApiRequestBuilder<GroupInfo>('groups/' + groupName).sendRequest()
|
||||
export const getGroup = async (groupName: string): Promise<GroupInfoDto> => {
|
||||
const response = await new GetApiRequestBuilder<GroupInfoDto>('groups/' + groupName).sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
}
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export interface GroupInfo {
|
||||
name: string
|
||||
displayName: string
|
||||
special: boolean
|
||||
}
|
|
@ -1,13 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { DeleteApiRequestBuilder } from '../common/api-request-builder/delete-api-request-builder'
|
||||
import { GetApiRequestBuilder } from '../common/api-request-builder/get-api-request-builder'
|
||||
import { PostApiRequestBuilder } from '../common/api-request-builder/post-api-request-builder'
|
||||
import type { MediaUpload } from '../media/types'
|
||||
import type { ChangeDisplayNameDto, LoginUserInfo } from './types'
|
||||
import type { UpdateUserInfoDto, LoginUserInfoDto, MediaUploadDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Returns metadata about the currently signed-in user from the API.
|
||||
|
@ -15,8 +14,8 @@ import type { ChangeDisplayNameDto, LoginUserInfo } from './types'
|
|||
* @return The user metadata.
|
||||
* @throws {Error} when the user is not signed-in.
|
||||
*/
|
||||
export const getMe = async (): Promise<LoginUserInfo> => {
|
||||
const response = await new GetApiRequestBuilder<LoginUserInfo>('me').sendRequest()
|
||||
export const getMe = async (): Promise<LoginUserInfoDto> => {
|
||||
const response = await new GetApiRequestBuilder<LoginUserInfoDto>('me').sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
}
|
||||
|
||||
|
@ -33,12 +32,14 @@ export const deleteUser = async (): Promise<void> => {
|
|||
* Changes the display name of the current user.
|
||||
*
|
||||
* @param displayName The new display name to set.
|
||||
* @param email The new email to set.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const updateDisplayName = async (displayName: string): Promise<void> => {
|
||||
await new PostApiRequestBuilder<void, ChangeDisplayNameDto>('me/profile')
|
||||
export const updateUser = async (displayName: string | null, email: string | null): Promise<void> => {
|
||||
await new PostApiRequestBuilder<void, UpdateUserInfoDto>('me/profile')
|
||||
.withJsonBody({
|
||||
displayName
|
||||
displayName,
|
||||
email
|
||||
})
|
||||
.sendRequest()
|
||||
}
|
||||
|
@ -49,7 +50,7 @@ export const updateDisplayName = async (displayName: string): Promise<void> => {
|
|||
* @return List of media object information.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const getMyMedia = async (): Promise<MediaUpload[]> => {
|
||||
const response = await new GetApiRequestBuilder<MediaUpload[]>('me/media').sendRequest()
|
||||
export const getMyMedia = async (): Promise<MediaUploadDto[]> => {
|
||||
const response = await new GetApiRequestBuilder<MediaUploadDto[]>('me/media').sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { UserInfo } from '../users/types'
|
||||
|
||||
export interface LoginUserInfo extends UserInfo {
|
||||
authProvider: string
|
||||
email: string
|
||||
}
|
||||
|
||||
export interface ChangeDisplayNameDto {
|
||||
displayName: string
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { DeleteApiRequestBuilder } from '../common/api-request-builder/delete-api-request-builder'
|
||||
import { PostApiRequestBuilder } from '../common/api-request-builder/post-api-request-builder'
|
||||
import type { ImageProxyRequestDto, ImageProxyResponse, MediaUpload } from './types'
|
||||
import type { ImageProxyRequestDto, ImageProxyResponse } from './types'
|
||||
import type { MediaUploadDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Requests an image-proxy URL from the backend for a given image URL.
|
||||
|
@ -31,10 +32,10 @@ export const getProxiedUrl = async (imageUrl: string): Promise<ImageProxyRespons
|
|||
* @return The URL of the uploaded media object.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const uploadFile = async (noteIdOrAlias: string, media: File): Promise<MediaUpload> => {
|
||||
export const uploadFile = async (noteIdOrAlias: string, media: File): Promise<MediaUploadDto> => {
|
||||
const postData = new FormData()
|
||||
postData.append('file', media)
|
||||
const response = await new PostApiRequestBuilder<MediaUpload, void>('media')
|
||||
const response = await new PostApiRequestBuilder<MediaUploadDto, void>('media')
|
||||
.withHeader('HedgeDoc-Note', noteIdOrAlias)
|
||||
.withBody(postData)
|
||||
.sendRequest()
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
export interface MediaUpload {
|
||||
uuid: string
|
||||
fileName: string
|
||||
noteId: string | null
|
||||
createdAt: string
|
||||
username: string | null
|
||||
}
|
||||
|
||||
export interface ImageProxyResponse {
|
||||
url: string
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { DeleteApiRequestBuilder } from '../common/api-request-builder/delete-api-request-builder'
|
||||
import { GetApiRequestBuilder } from '../common/api-request-builder/get-api-request-builder'
|
||||
import { PostApiRequestBuilder } from '../common/api-request-builder/post-api-request-builder'
|
||||
import type { MediaUpload } from '../media/types'
|
||||
import type { Note, NoteDeletionOptions, NoteMetadata } from './types'
|
||||
import type { MediaUploadDto, NoteDto, NoteMetadataDto } from '@hedgedoc/commons'
|
||||
import type { NoteMediaDeletionDto } from '@hedgedoc/commons/dist/esm'
|
||||
|
||||
/**
|
||||
* Retrieves the content and metadata about the specified note.
|
||||
|
@ -16,8 +16,8 @@ import type { Note, NoteDeletionOptions, NoteMetadata } from './types'
|
|||
* @return Content and metadata of the specified note.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const getNote = async (noteIdOrAlias: string, baseUrl?: string): Promise<Note> => {
|
||||
const response = await new GetApiRequestBuilder<Note>('notes/' + noteIdOrAlias, baseUrl).sendRequest()
|
||||
export const getNote = async (noteIdOrAlias: string, baseUrl?: string): Promise<NoteDto> => {
|
||||
const response = await new GetApiRequestBuilder<NoteDto>('notes/' + noteIdOrAlias, baseUrl).sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,8 @@ export const getNote = async (noteIdOrAlias: string, baseUrl?: string): Promise<
|
|||
* @param noteIdOrAlias The id or alias of the note.
|
||||
* @return Metadata of the specified note.
|
||||
*/
|
||||
export const getNoteMetadata = async (noteIdOrAlias: string): Promise<NoteMetadata> => {
|
||||
const response = await new GetApiRequestBuilder<NoteMetadata>(`notes/${noteIdOrAlias}/metadata`).sendRequest()
|
||||
export const getNoteMetadata = async (noteIdOrAlias: string): Promise<NoteMetadataDto> => {
|
||||
const response = await new GetApiRequestBuilder<NoteMetadataDto>(`notes/${noteIdOrAlias}/metadata`).sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
}
|
||||
|
||||
|
@ -39,8 +39,8 @@ export const getNoteMetadata = async (noteIdOrAlias: string): Promise<NoteMetada
|
|||
* @return List of media object metadata associated with specified note.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const getMediaForNote = async (noteIdOrAlias: string): Promise<MediaUpload[]> => {
|
||||
const response = await new GetApiRequestBuilder<MediaUpload[]>(`notes/${noteIdOrAlias}/media`).sendRequest()
|
||||
export const getMediaForNote = async (noteIdOrAlias: string): Promise<MediaUploadDto[]> => {
|
||||
const response = await new GetApiRequestBuilder<MediaUploadDto[]>(`notes/${noteIdOrAlias}/media`).sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
}
|
||||
|
||||
|
@ -51,8 +51,8 @@ export const getMediaForNote = async (noteIdOrAlias: string): Promise<MediaUploa
|
|||
* @return Content and metadata of the new note.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const createNote = async (markdown: string): Promise<Note> => {
|
||||
const response = await new PostApiRequestBuilder<Note, void>('notes')
|
||||
export const createNote = async (markdown: string): Promise<NoteDto> => {
|
||||
const response = await new PostApiRequestBuilder<NoteDto, void>('notes')
|
||||
.withHeader('Content-Type', 'text/markdown')
|
||||
.withBody(markdown)
|
||||
.sendRequest()
|
||||
|
@ -67,8 +67,8 @@ export const createNote = async (markdown: string): Promise<Note> => {
|
|||
* @return Content and metadata of the new note.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const createNoteWithPrimaryAlias = async (markdown: string, primaryAlias: string): Promise<Note> => {
|
||||
const response = await new PostApiRequestBuilder<Note, void>('notes/' + primaryAlias)
|
||||
export const createNoteWithPrimaryAlias = async (markdown: string, primaryAlias: string): Promise<NoteDto> => {
|
||||
const response = await new PostApiRequestBuilder<NoteDto, void>('notes/' + primaryAlias)
|
||||
.withHeader('Content-Type', 'text/markdown')
|
||||
.withBody(markdown)
|
||||
.sendRequest()
|
||||
|
@ -83,7 +83,7 @@ export const createNoteWithPrimaryAlias = async (markdown: string, primaryAlias:
|
|||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const deleteNote = async (noteIdOrAlias: string, keepMedia: boolean): Promise<void> => {
|
||||
await new DeleteApiRequestBuilder<void, NoteDeletionOptions>('notes/' + noteIdOrAlias)
|
||||
await new DeleteApiRequestBuilder<void, NoteMediaDeletionDto>('notes/' + noteIdOrAlias)
|
||||
.withJsonBody({
|
||||
keepMedia
|
||||
})
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { Alias } from '../alias/types'
|
||||
import type { NotePermissions } from '@hedgedoc/commons'
|
||||
|
||||
export interface Note {
|
||||
content: string
|
||||
metadata: NoteMetadata
|
||||
editedByAtPosition: NoteEdit[]
|
||||
}
|
||||
|
||||
export interface NoteMetadata {
|
||||
id: string
|
||||
aliases: Alias[]
|
||||
primaryAddress: string
|
||||
title: string
|
||||
description: string
|
||||
tags: string[]
|
||||
updatedAt: string
|
||||
updateUsername: string | null
|
||||
viewCount: number
|
||||
createdAt: string
|
||||
editedBy: string[]
|
||||
permissions: NotePermissions
|
||||
version: number
|
||||
}
|
||||
|
||||
export interface NoteEdit {
|
||||
username: string | null
|
||||
startPos: number
|
||||
endPos: number
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface NoteDeletionOptions {
|
||||
keepMedia: boolean
|
||||
}
|
|
@ -1,27 +1,31 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { DeleteApiRequestBuilder } from '../common/api-request-builder/delete-api-request-builder'
|
||||
import { PutApiRequestBuilder } from '../common/api-request-builder/put-api-request-builder'
|
||||
import type { OwnerChangeDto, PermissionSetDto } from './types'
|
||||
import type { NotePermissions } from '@hedgedoc/commons'
|
||||
import type {
|
||||
ChangeNoteOwnerDto,
|
||||
NoteGroupPermissionUpdateDto,
|
||||
NotePermissionsDto,
|
||||
NoteUserPermissionUpdateDto
|
||||
} from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Sets the owner of a note.
|
||||
*
|
||||
* @param noteId The id of the note.
|
||||
* @param newOwner The username of the new owner.
|
||||
* @return The updated {@link NotePermissions}.
|
||||
* @return The updated {@link NotePermissionsDto}.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const setNoteOwner = async (noteId: string, newOwner: string): Promise<NotePermissions> => {
|
||||
const response = await new PutApiRequestBuilder<NotePermissions, OwnerChangeDto>(
|
||||
export const setNoteOwner = async (noteId: string, newOwner: string): Promise<NotePermissionsDto> => {
|
||||
const response = await new PutApiRequestBuilder<NotePermissionsDto, ChangeNoteOwnerDto>(
|
||||
`notes/${noteId}/metadata/permissions/owner`
|
||||
)
|
||||
.withJsonBody({
|
||||
newOwner
|
||||
owner: newOwner
|
||||
})
|
||||
.sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
|
@ -33,15 +37,15 @@ export const setNoteOwner = async (noteId: string, newOwner: string): Promise<No
|
|||
* @param noteId The id of the note.
|
||||
* @param username The username of the user to set the permission for.
|
||||
* @param canEdit true if the user should be able to update the note, false otherwise.
|
||||
* @return The updated {@link NotePermissions}.
|
||||
* @return The updated {@link NotePermissionsDto}.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const setUserPermission = async (
|
||||
noteId: string,
|
||||
username: string,
|
||||
canEdit: boolean
|
||||
): Promise<NotePermissions> => {
|
||||
const response = await new PutApiRequestBuilder<NotePermissions, PermissionSetDto>(
|
||||
): Promise<NotePermissionsDto> => {
|
||||
const response = await new PutApiRequestBuilder<NotePermissionsDto, Pick<NoteUserPermissionUpdateDto, 'canEdit'>>(
|
||||
`notes/${noteId}/metadata/permissions/users/${username}`
|
||||
)
|
||||
.withJsonBody({
|
||||
|
@ -57,15 +61,15 @@ export const setUserPermission = async (
|
|||
* @param noteId The id of the note.
|
||||
* @param groupName The name of the group to set the permission for.
|
||||
* @param canEdit true if the group should be able to update the note, false otherwise.
|
||||
* @return The updated {@link NotePermissions}.
|
||||
* @return The updated {@link NotePermissionsDto}.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const setGroupPermission = async (
|
||||
noteId: string,
|
||||
groupName: string,
|
||||
canEdit: boolean
|
||||
): Promise<NotePermissions> => {
|
||||
const response = await new PutApiRequestBuilder<NotePermissions, PermissionSetDto>(
|
||||
): Promise<NotePermissionsDto> => {
|
||||
const response = await new PutApiRequestBuilder<NotePermissionsDto, Pick<NoteGroupPermissionUpdateDto, 'canEdit'>>(
|
||||
`notes/${noteId}/metadata/permissions/groups/${groupName}`
|
||||
)
|
||||
.withJsonBody({
|
||||
|
@ -80,11 +84,11 @@ export const setGroupPermission = async (
|
|||
*
|
||||
* @param noteId The id of the note.
|
||||
* @param username The name of the user to remove the permission of.
|
||||
* @return The updated {@link NotePermissions}.
|
||||
* @return The updated {@link NotePermissionsDto}.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const removeUserPermission = async (noteId: string, username: string): Promise<NotePermissions> => {
|
||||
const response = await new DeleteApiRequestBuilder<NotePermissions>(
|
||||
export const removeUserPermission = async (noteId: string, username: string): Promise<NotePermissionsDto> => {
|
||||
const response = await new DeleteApiRequestBuilder<NotePermissionsDto>(
|
||||
`notes/${noteId}/metadata/permissions/users/${username}`
|
||||
).sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
|
@ -95,11 +99,11 @@ export const removeUserPermission = async (noteId: string, username: string): Pr
|
|||
*
|
||||
* @param noteId The id of the note.
|
||||
* @param groupName The name of the group to remove the permission of.
|
||||
* @return The updated {@link NotePermissions}.
|
||||
* @return The updated {@link NotePermissionsDto}.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const removeGroupPermission = async (noteId: string, groupName: string): Promise<NotePermissions> => {
|
||||
const response = await new DeleteApiRequestBuilder<NotePermissions>(
|
||||
export const removeGroupPermission = async (noteId: string, groupName: string): Promise<NotePermissionsDto> => {
|
||||
const response = await new DeleteApiRequestBuilder<NotePermissionsDto>(
|
||||
`notes/${noteId}/metadata/permissions/groups/${groupName}`
|
||||
).sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
export interface OwnerChangeDto {
|
||||
newOwner: string
|
||||
}
|
||||
|
||||
export interface PermissionSetDto {
|
||||
canEdit: boolean
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { DeleteApiRequestBuilder } from '../common/api-request-builder/delete-api-request-builder'
|
||||
import { GetApiRequestBuilder } from '../common/api-request-builder/get-api-request-builder'
|
||||
import type { RevisionDetails, RevisionMetadata } from './types'
|
||||
import type { RevisionDto, RevisionMetadataDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Retrieves a note revision while using a cache for often retrieved revisions.
|
||||
|
@ -15,10 +15,8 @@ import type { RevisionDetails, RevisionMetadata } from './types'
|
|||
* @return The revision.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const getRevision = async (noteId: string, revisionId: number): Promise<RevisionDetails> => {
|
||||
const response = await new GetApiRequestBuilder<RevisionDetails>(
|
||||
`notes/${noteId}/revisions/${revisionId}`
|
||||
).sendRequest()
|
||||
export const getRevision = async (noteId: string, revisionId: number): Promise<RevisionDto> => {
|
||||
const response = await new GetApiRequestBuilder<RevisionDto>(`notes/${noteId}/revisions/${revisionId}`).sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
}
|
||||
|
||||
|
@ -29,8 +27,8 @@ export const getRevision = async (noteId: string, revisionId: number): Promise<R
|
|||
* @return A list of revision ids.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const getAllRevisions = async (noteId: string): Promise<RevisionMetadata[]> => {
|
||||
const response = await new GetApiRequestBuilder<RevisionMetadata[]>(`notes/${noteId}/revisions`).sendRequest()
|
||||
export const getAllRevisions = async (noteId: string): Promise<RevisionMetadataDto[]> => {
|
||||
const response = await new GetApiRequestBuilder<RevisionMetadataDto[]>(`notes/${noteId}/revisions`).sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { NoteEdit } from '../notes/types'
|
||||
|
||||
export interface RevisionDetails extends RevisionMetadata {
|
||||
content: string
|
||||
patch: string
|
||||
edits: NoteEdit[]
|
||||
}
|
||||
|
||||
export interface RevisionMetadata {
|
||||
id: number
|
||||
createdAt: string
|
||||
length: number
|
||||
authorUsernames: string[]
|
||||
anonymousAuthorCount: number
|
||||
title: string
|
||||
tags: string[]
|
||||
description: string
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export interface AccessToken {
|
||||
label: string
|
||||
validUntil: string
|
||||
keyId: string
|
||||
createdAt: string
|
||||
lastUsedAt: string | null
|
||||
}
|
||||
|
||||
export interface AccessTokenWithSecret extends AccessToken {
|
||||
secret: string
|
||||
}
|
||||
|
||||
export interface CreateAccessTokenDto {
|
||||
label: string
|
||||
validUntil: number
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { GetApiRequestBuilder } from '../common/api-request-builder/get-api-request-builder'
|
||||
import type { UserInfo } from './types'
|
||||
import type { UserInfoDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Retrieves information about a specific user while using a cache to avoid many requests for the same username.
|
||||
|
@ -13,7 +13,7 @@ import type { UserInfo } from './types'
|
|||
* @return Metadata about the requested user.
|
||||
* @throws {Error} when the api request wasn't successful.
|
||||
*/
|
||||
export const getUserInfo = async (username: string): Promise<UserInfo> => {
|
||||
const response = await new GetApiRequestBuilder<UserInfo>(`users/profile/${username}`).sendRequest()
|
||||
export const getUserInfo = async (username: string): Promise<UserInfoDto> => {
|
||||
const response = await new GetApiRequestBuilder<UserInfoDto>(`users/profile/${username}`).sendRequest()
|
||||
return response.asParsedJsonObject()
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export interface UserInfo {
|
||||
username: string
|
||||
displayName: string
|
||||
photoUrl?: string
|
||||
}
|
||||
|
||||
export interface FullUserInfo extends UserInfo {
|
||||
email: string
|
||||
}
|
|
@ -1,16 +1,15 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { mockI18n } from '../../../../../test-utils/mock-i18n'
|
||||
import { EditorAppBar } from './editor-app-bar'
|
||||
import type { NoteGroupPermissionEntry, NoteUserPermissionEntry } from '@hedgedoc/commons'
|
||||
import type { LoginUserInfoDto, NoteGroupPermissionEntryDto, NoteUserPermissionEntryDto } from '@hedgedoc/commons'
|
||||
import { render } from '@testing-library/react'
|
||||
import type { PropsWithChildren } from 'react'
|
||||
import React from 'react'
|
||||
import { mockAppState } from '../../../../../test-utils/mock-app-state'
|
||||
import type { LoginUserInfo } from '../../../../../api/me/types'
|
||||
|
||||
jest.mock('../../../../../components/layout/app-bar/base-app-bar', () => ({
|
||||
__esModule: true,
|
||||
|
@ -34,13 +33,13 @@ const mockedCommonAppState = {
|
|||
groupName: '_EVERYONE',
|
||||
canEdit: false
|
||||
}
|
||||
] as NoteGroupPermissionEntry[],
|
||||
sharedToUsers: [] as NoteUserPermissionEntry[]
|
||||
] as NoteGroupPermissionEntryDto[],
|
||||
sharedToUsers: [] as NoteUserPermissionEntryDto[]
|
||||
}
|
||||
},
|
||||
user: {
|
||||
username: 'test'
|
||||
} as LoginUserInfo
|
||||
} as LoginUserInfoDto
|
||||
}
|
||||
|
||||
describe('app bar', () => {
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
'use client'
|
||||
|
||||
/*
|
||||
|
@ -5,7 +11,7 @@
|
|||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { AuthProviderType } from '../../../api/config/types'
|
||||
import { ProviderType } from '@hedgedoc/commons'
|
||||
import { Redirect } from '../../../components/common/redirect'
|
||||
import { LandingLayout } from '../../../components/landing-layout/landing-layout'
|
||||
import { ProfileAccessTokens } from '../../../components/profile-page/access-tokens/profile-access-tokens'
|
||||
|
@ -34,7 +40,7 @@ const ProfilePage: NextPage = () => {
|
|||
<Row className='h-100 flex justify-content-center'>
|
||||
<Col lg={6}>
|
||||
<ProfileDisplayName />
|
||||
{userProvider === (AuthProviderType.LOCAL as string) && <ProfileChangePassword />}
|
||||
{userProvider === ProviderType.LOCAL && <ProfileChangePassword />}
|
||||
<ProfileAccessTokens />
|
||||
<ProfileAccountManagement />
|
||||
</Col>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -20,7 +20,7 @@ describe('Copy to clipboard button', () => {
|
|||
beforeAll(async () => {
|
||||
await mockI18n()
|
||||
originalClipboard = window.navigator.clipboard
|
||||
jest.spyOn(uuidModule, 'v4').mockReturnValue(uuidMock)
|
||||
jest.spyOn(uuidModule, 'v4').mockReturnValue(Buffer.from(uuidMock))
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { FrontendConfig } from '../../../api/config/types'
|
||||
import type { FrontendConfigDto } from '@hedgedoc/commons'
|
||||
import * as UseFrontendConfigMock from '../frontend-config-context/use-frontend-config'
|
||||
import { CustomBranding } from './custom-branding'
|
||||
import { render } from '@testing-library/react'
|
||||
|
@ -12,10 +12,10 @@ import { Mock } from 'ts-mockery'
|
|||
jest.mock('../frontend-config-context/use-frontend-config')
|
||||
|
||||
describe('custom branding', () => {
|
||||
const mockFrontendConfigHook = (logo?: string, name?: string) => {
|
||||
const mockFrontendConfigHook = (logo: string | null = null, name: string | null = null) => {
|
||||
jest
|
||||
.spyOn(UseFrontendConfigMock, 'useFrontendConfig')
|
||||
.mockReturnValue(Mock.of<FrontendConfig>({ branding: { logo, name } }))
|
||||
.mockReturnValue(Mock.of<FrontendConfigDto>({ branding: { logo, name } }))
|
||||
}
|
||||
|
||||
it("doesn't show anything if no branding is defined", () => {
|
||||
|
@ -32,7 +32,7 @@ describe('custom branding', () => {
|
|||
})
|
||||
|
||||
it('shows an text if branding text is defined', () => {
|
||||
mockFrontendConfigHook(undefined, 'mockedBranding')
|
||||
mockFrontendConfigHook(null, 'mockedBranding')
|
||||
const view = render(<CustomBranding inline={inline} />)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -36,7 +36,12 @@ export const CustomBranding: React.FC<BrandingProps> = ({ inline = false }) => {
|
|||
} else if (branding.logo) {
|
||||
return (
|
||||
/* eslint-disable-next-line @next/next/no-img-element */
|
||||
<img src={branding.logo} alt={branding.name} title={branding.name} className={className} />
|
||||
<img
|
||||
src={branding.logo}
|
||||
alt={branding.name !== null ? branding.name : undefined}
|
||||
title={branding.name !== null ? branding.name : undefined}
|
||||
className={className}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return <span className={className}>{branding.name}</span>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { BrandingConfig } from '../../../api/config/types'
|
||||
import type { BrandingDto } from '@hedgedoc/commons'
|
||||
import { useFrontendConfig } from '../frontend-config-context/use-frontend-config'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
|
@ -12,10 +12,10 @@ import { useMemo } from 'react'
|
|||
*
|
||||
* @return the branding configuration or null if no branding has been configured
|
||||
*/
|
||||
export const useBrandingDetails = (): null | BrandingConfig => {
|
||||
export const useBrandingDetails = (): null | BrandingDto => {
|
||||
const branding = useFrontendConfig().branding
|
||||
|
||||
return useMemo(() => {
|
||||
return !branding.name && !branding.logo ? null : branding
|
||||
return branding.name === null && branding.logo === null ? null : branding
|
||||
}, [branding])
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -18,7 +18,7 @@ export enum ProfilePictureChoice {
|
|||
export interface ProfilePictureSelectFieldProps extends CommonFieldProps<ProfilePictureChoice> {
|
||||
onChange: (choice: ProfilePictureChoice) => void
|
||||
value: ProfilePictureChoice
|
||||
pictureUrl?: string
|
||||
photoUrl: string | null
|
||||
username: string
|
||||
}
|
||||
|
||||
|
@ -31,11 +31,15 @@ export interface ProfilePictureSelectFieldProps extends CommonFieldProps<Profile
|
|||
*/
|
||||
export const ProfilePictureSelectField: React.FC<ProfilePictureSelectFieldProps> = ({
|
||||
onChange,
|
||||
pictureUrl,
|
||||
photoUrl,
|
||||
username,
|
||||
value
|
||||
}) => {
|
||||
const fallbackUrl = useAvatarUrl(undefined, username)
|
||||
const fallbackUrl = useAvatarUrl({
|
||||
username,
|
||||
photoUrl,
|
||||
displayName: username
|
||||
})
|
||||
const profileEditsAllowed = useFrontendConfig().allowProfileEdits
|
||||
const onSetProviderPicture = useCallback(() => {
|
||||
if (value !== ProfilePictureChoice.PROVIDER) {
|
||||
|
@ -57,7 +61,7 @@ export const ProfilePictureSelectField: React.FC<ProfilePictureSelectFieldProps>
|
|||
<Form.Label>
|
||||
<Trans i18nKey='profile.selectProfilePicture.title' />
|
||||
</Form.Label>
|
||||
{pictureUrl && (
|
||||
{photoUrl && (
|
||||
<Form.Check className={'d-flex gap-2 align-items-center mb-3'} type='radio'>
|
||||
<Form.Check.Input
|
||||
type={'radio'}
|
||||
|
@ -66,7 +70,7 @@ export const ProfilePictureSelectField: React.FC<ProfilePictureSelectFieldProps>
|
|||
/>
|
||||
<Form.Check.Label>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={pictureUrl} alt={'Profile picture provided by the identity provider'} height={48} width={48} />
|
||||
<img src={photoUrl} alt={'Profile picture provided by the identity provider'} height={48} width={48} />
|
||||
</Form.Check.Label>
|
||||
</Form.Check>
|
||||
)}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { FrontendConfig } from '../../../api/config/types'
|
||||
import type { FrontendConfigDto } from '@hedgedoc/commons'
|
||||
import { createContext } from 'react'
|
||||
|
||||
export const frontendConfigContext = createContext<FrontendConfig | undefined>(undefined)
|
||||
export const frontendConfigContext = createContext<FrontendConfigDto | undefined>(undefined)
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
'use client'
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { FrontendConfig } from '../../../api/config/types'
|
||||
import type { FrontendConfigDto } from '@hedgedoc/commons'
|
||||
import { frontendConfigContext } from './context'
|
||||
import type { PropsWithChildren } from 'react'
|
||||
import React from 'react'
|
||||
|
||||
interface FrontendConfigContextProviderProps extends PropsWithChildren {
|
||||
config?: FrontendConfig
|
||||
config?: FrontendConfigDto
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { FrontendConfig } from '../../../api/config/types'
|
||||
import type { FrontendConfigDto } from '@hedgedoc/commons'
|
||||
import { frontendConfigContext } from './context'
|
||||
import { Optional } from '@mrdrogdrog/optional'
|
||||
import { useContext } from 'react'
|
||||
|
@ -11,7 +11,7 @@ import { useContext } from 'react'
|
|||
/**
|
||||
* Retrieves the current frontend config from the next react context.
|
||||
*/
|
||||
export const useFrontendConfig = (): FrontendConfig => {
|
||||
export const useFrontendConfig = (): FrontendConfigDto => {
|
||||
return Optional.ofNullable(useContext(frontendConfigContext)).orElseThrow(
|
||||
() => new Error('No frontend config context found. Did you forget to use the provider component?')
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -12,7 +12,7 @@ import React, { useCallback } from 'react'
|
|||
import { FileEarmarkPlus as IconPlus } from 'react-bootstrap-icons'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { useFrontendConfig } from '../frontend-config-context/use-frontend-config'
|
||||
import { GuestAccessLevel } from '../../../api/config/types'
|
||||
import { GuestAccess } from '@hedgedoc/commons'
|
||||
import { useIsLoggedIn } from '../../../hooks/common/use-is-logged-in'
|
||||
|
||||
/**
|
||||
|
@ -34,7 +34,7 @@ export const NewNoteButton: React.FC = () => {
|
|||
})
|
||||
}, [router, showErrorNotification])
|
||||
|
||||
if (!isLoggedIn && guestAccessLevel !== GuestAccessLevel.CREATE) {
|
||||
if (!isLoggedIn && guestAccessLevel !== GuestAccess.CREATE) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import * as createNoteWithPrimaryAliasModule from '../../../api/notes'
|
||||
import type { Note, NoteMetadata } from '../../../api/notes/types'
|
||||
import { mockI18n } from '../../../test-utils/mock-i18n'
|
||||
import { CreateNonExistingNoteHint } from './create-non-existing-note-hint'
|
||||
import type { NoteDto, NoteMetadataDto } from '@hedgedoc/commons'
|
||||
import { waitForOtherPromisesToFinish } from '@hedgedoc/commons'
|
||||
import { act, render, screen, waitFor } from '@testing-library/react'
|
||||
import { Mock } from 'ts-mockery'
|
||||
|
@ -20,20 +20,20 @@ describe('create non existing note hint', () => {
|
|||
const mockCreateNoteWithPrimaryAlias = () => {
|
||||
jest
|
||||
.spyOn(createNoteWithPrimaryAliasModule, 'createNoteWithPrimaryAlias')
|
||||
.mockImplementation(async (markdown, primaryAlias): Promise<Note> => {
|
||||
.mockImplementation(async (markdown, primaryAlias): Promise<NoteDto> => {
|
||||
expect(markdown).toBe('')
|
||||
expect(primaryAlias).toBe(mockedNoteId)
|
||||
const metadata: NoteMetadata = Mock.of<NoteMetadata>({ primaryAddress: 'mockedPrimaryAlias' })
|
||||
const metadata: NoteMetadataDto = Mock.of<NoteMetadataDto>({ primaryAddress: 'mockedPrimaryAlias' })
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
await waitForOtherPromisesToFinish()
|
||||
return Mock.of<Note>({ metadata })
|
||||
return Mock.of<NoteDto>({ metadata })
|
||||
})
|
||||
}
|
||||
|
||||
const mockFailingCreateNoteWithPrimaryAlias = () => {
|
||||
jest
|
||||
.spyOn(createNoteWithPrimaryAliasModule, 'createNoteWithPrimaryAlias')
|
||||
.mockImplementation(async (markdown, primaryAlias): Promise<Note> => {
|
||||
.mockImplementation(async (markdown, primaryAlias): Promise<NoteDto> => {
|
||||
expect(markdown).toBe('')
|
||||
expect(primaryAlias).toBe(mockedNoteId)
|
||||
await waitForOtherPromisesToFinish()
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiError } from '../../../api/common/api-error'
|
||||
import * as getNoteModule from '../../../api/notes'
|
||||
import type { Note } from '../../../api/notes/types'
|
||||
import * as LoadingScreenModule from '../../../components/application-loader/loading-screen/loading-screen'
|
||||
import * as setNoteDataFromServerModule from '../../../redux/note-details/methods'
|
||||
import { mockI18n } from '../../../test-utils/mock-i18n'
|
||||
|
@ -16,6 +15,7 @@ import { NoteLoadingBoundary } from './note-loading-boundary'
|
|||
import { render, screen } from '@testing-library/react'
|
||||
import { Fragment } from 'react'
|
||||
import { Mock } from 'ts-mockery'
|
||||
import type { NoteDto } from '@hedgedoc/commons'
|
||||
|
||||
jest.mock('../../../hooks/common/use-single-string-url-parameter')
|
||||
jest.mock('../../../api/notes')
|
||||
|
@ -65,7 +65,7 @@ describe('Note loading boundary', () => {
|
|||
})
|
||||
})
|
||||
|
||||
const mockGetNoteApiCall = (returnValue: Note) => {
|
||||
const mockGetNoteApiCall = (returnValue: NoteDto) => {
|
||||
jest.spyOn(getNoteModule, 'getNote').mockImplementation((id) => {
|
||||
expect(id).toBe(mockedNoteId)
|
||||
return new Promise((resolve) => {
|
||||
|
@ -83,14 +83,14 @@ describe('Note loading boundary', () => {
|
|||
})
|
||||
}
|
||||
|
||||
const mockSetNoteInRedux = (expectedNote: Note): jest.SpyInstance<void, [apiResponse: Note]> => {
|
||||
const mockSetNoteInRedux = (expectedNote: NoteDto): jest.SpyInstance<void, [apiResponse: NoteDto]> => {
|
||||
return jest.spyOn(setNoteDataFromServerModule, 'setNoteDataFromServer').mockImplementation((givenNote) => {
|
||||
expect(givenNote).toBe(expectedNote)
|
||||
})
|
||||
}
|
||||
|
||||
it('loads a note', async () => {
|
||||
const mockedNote: Note = Mock.of<Note>()
|
||||
const mockedNote: NoteDto = Mock.of<NoteDto>()
|
||||
mockGetNoteApiCall(mockedNote)
|
||||
const setNoteInReduxFunctionMock = mockSetNoteInRedux(mockedNote)
|
||||
|
||||
|
@ -106,7 +106,7 @@ describe('Note loading boundary', () => {
|
|||
})
|
||||
|
||||
it('shows an error', async () => {
|
||||
const mockedNote: Note = Mock.of<Note>()
|
||||
const mockedNote: NoteDto = Mock.of<NoteDto>()
|
||||
mockCrashingNoteApiCall()
|
||||
const setNoteInReduxFunctionMock = mockSetNoteInRedux(mockedNote)
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ exports[`UserAvatar uses custom photo component if provided 1`] = `
|
|||
<span
|
||||
class="ms-2 me-1 user-line-name"
|
||||
>
|
||||
test
|
||||
No face user
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -133,7 +133,7 @@ exports[`UserAvatar uses custom photo component preferred over photoUrl 1`] = `
|
|||
<span
|
||||
class="ms-2 me-1 user-line-name"
|
||||
>
|
||||
test
|
||||
Boaty McBoatFace
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -155,7 +155,7 @@ exports[`UserAvatar uses identicon when empty photoUrl is given 1`] = `
|
|||
<span
|
||||
class="ms-2 me-1 user-line-name"
|
||||
>
|
||||
test
|
||||
Empty
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -177,7 +177,7 @@ exports[`UserAvatar uses identicon when no photoUrl is given 1`] = `
|
|||
<span
|
||||
class="ms-2 me-1 user-line-name"
|
||||
>
|
||||
test
|
||||
No face user
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -1,21 +1,29 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import React from 'react'
|
||||
import type { UserAvatarProps } from './user-avatar'
|
||||
import { UserAvatar } from './user-avatar'
|
||||
import { useTranslatedText } from '../../../hooks/common/use-translated-text'
|
||||
import { Person as IconPerson } from 'react-bootstrap-icons'
|
||||
|
||||
export type GuestUserAvatarProps = Omit<UserAvatarProps, 'displayName' | 'photoUrl' | 'username'>
|
||||
import type { CommonUserAvatarProps } from './types'
|
||||
|
||||
/**
|
||||
* The avatar component for an anonymous user.
|
||||
* @param props The properties of the guest user avatar ({@link UserAvatarProps})
|
||||
*/
|
||||
export const GuestUserAvatar: React.FC<GuestUserAvatarProps> = (props) => {
|
||||
export const GuestUserAvatar: React.FC<CommonUserAvatarProps> = (props) => {
|
||||
const label = useTranslatedText('common.guestUser')
|
||||
return <UserAvatar displayName={label} photoComponent={<IconPerson />} {...props} />
|
||||
return (
|
||||
<UserAvatar
|
||||
user={{
|
||||
username: '',
|
||||
photoUrl: null,
|
||||
displayName: label
|
||||
}}
|
||||
photoComponent={<IconPerson />}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,28 +1,30 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useMemo } from 'react'
|
||||
import { createAvatar } from '@dicebear/core'
|
||||
import * as identicon from '@dicebear/identicon'
|
||||
import type { UserInfoDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Returns the correct avatar url for a user.
|
||||
* When an empty or no photoUrl is given, a random avatar is generated from the displayName.
|
||||
* When the user has no photoUrl, a random avatar is generated from the display name.
|
||||
*
|
||||
* @param photoUrl The photo url of the user to use. Maybe empty or not set.
|
||||
* @param username The username of the user to use as input to the random avatar.
|
||||
* @return The correct avatar url for the user.
|
||||
* @param user The user for which to get the avatar URL
|
||||
* @return The correct avatar url for the user
|
||||
*/
|
||||
export const useAvatarUrl = (photoUrl: string | undefined, username: string): string => {
|
||||
export const useAvatarUrl = (user: UserInfoDto): string => {
|
||||
const { photoUrl, displayName } = user
|
||||
|
||||
return useMemo(() => {
|
||||
if (photoUrl && photoUrl.trim() !== '') {
|
||||
return photoUrl
|
||||
}
|
||||
const avatar = createAvatar(identicon, {
|
||||
seed: username
|
||||
seed: displayName
|
||||
})
|
||||
return avatar.toDataUri()
|
||||
}, [photoUrl, username])
|
||||
}, [photoUrl, displayName])
|
||||
}
|
||||
|
|
14
frontend/src/components/common/user-avatar/types.ts
Normal file
14
frontend/src/components/common/user-avatar/types.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type React from 'react'
|
||||
|
||||
export interface CommonUserAvatarProps {
|
||||
size?: 'sm' | 'lg'
|
||||
additionalClasses?: string
|
||||
showName?: boolean
|
||||
photoComponent?: React.ReactNode
|
||||
overrideDisplayName?: string
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { UserInfo } from '../../../api/users/types'
|
||||
import type { UserAvatarProps } from './user-avatar'
|
||||
import { UserAvatar } from './user-avatar'
|
||||
import React from 'react'
|
||||
|
||||
export interface UserAvatarForUserProps extends Omit<UserAvatarProps, 'photoUrl' | 'displayName'> {
|
||||
user: UserInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the avatar image of a user, optionally altogether with their name.
|
||||
*
|
||||
* @param user The user object with the display name and photo.
|
||||
* @param props remaining avatar props
|
||||
*/
|
||||
export const UserAvatarForUser: React.FC<UserAvatarForUserProps> = ({ user, ...props }) => {
|
||||
return <UserAvatar displayName={user.displayName} photoUrl={user.photoUrl} username={user.username} {...props} />
|
||||
}
|
|
@ -1,18 +1,18 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { getUserInfo } from '../../../api/users'
|
||||
import { AsyncLoadingBoundary } from '../async-loading-boundary/async-loading-boundary'
|
||||
import type { UserAvatarProps } from './user-avatar'
|
||||
import { UserAvatar } from './user-avatar'
|
||||
import React, { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useAsync } from 'react-use'
|
||||
import type { UserInfo } from '../../../api/users/types'
|
||||
import type { CommonUserAvatarProps } from './types'
|
||||
import type { UserInfoDto } from '@hedgedoc/commons'
|
||||
|
||||
export interface UserAvatarForUsernameProps extends Omit<UserAvatarProps, 'photoUrl' | 'displayName'> {
|
||||
export interface UserAvatarForUsernameProps extends CommonUserAvatarProps {
|
||||
username: string | null
|
||||
}
|
||||
|
||||
|
@ -27,12 +27,13 @@ export interface UserAvatarForUsernameProps extends Omit<UserAvatarProps, 'photo
|
|||
*/
|
||||
export const UserAvatarForUsername: React.FC<UserAvatarForUsernameProps> = ({ username, ...props }) => {
|
||||
const { t } = useTranslation()
|
||||
const { error, value, loading } = useAsync(async (): Promise<UserInfo> => {
|
||||
const { error, value, loading } = useAsync(async (): Promise<UserInfoDto> => {
|
||||
return username
|
||||
? await getUserInfo(username)
|
||||
: {
|
||||
displayName: t('common.guestUser'),
|
||||
username: ''
|
||||
username: '',
|
||||
photoUrl: null
|
||||
}
|
||||
}, [username, t])
|
||||
|
||||
|
@ -40,8 +41,8 @@ export const UserAvatarForUsername: React.FC<UserAvatarForUsernameProps> = ({ us
|
|||
if (!value) {
|
||||
return null
|
||||
}
|
||||
return <UserAvatar displayName={value.displayName} photoUrl={value.photoUrl} username={username} {...props} />
|
||||
}, [props, value, username])
|
||||
return <UserAvatar user={value} {...props} />
|
||||
}, [props, value])
|
||||
|
||||
return (
|
||||
<AsyncLoadingBoundary loading={loading || !value} error={error} componentName={'UserAvatarForUsername'}>
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { UserInfo } from '../../../api/users/types'
|
||||
import { mockI18n } from '../../../test-utils/mock-i18n'
|
||||
import { UserAvatarForUser } from './user-avatar-for-user'
|
||||
import { render } from '@testing-library/react'
|
||||
import { UserAvatar } from './user-avatar'
|
||||
import type { UserInfoDto } from '@hedgedoc/commons'
|
||||
|
||||
jest.mock('@dicebear/identicon', () => null)
|
||||
jest.mock('@dicebear/core', () => ({
|
||||
|
@ -17,58 +16,68 @@ jest.mock('@dicebear/core', () => ({
|
|||
}))
|
||||
|
||||
describe('UserAvatar', () => {
|
||||
const user: UserInfo = {
|
||||
const user: UserInfoDto = {
|
||||
username: 'boatface',
|
||||
displayName: 'Boaty McBoatFace',
|
||||
photoUrl: 'https://example.com/test.png'
|
||||
}
|
||||
|
||||
const userWithoutPhoto: UserInfoDto = {
|
||||
username: 'pictureless',
|
||||
displayName: 'No face user',
|
||||
photoUrl: null
|
||||
}
|
||||
|
||||
const userWithEmptyPhoto: UserInfoDto = {
|
||||
username: 'void',
|
||||
displayName: 'Empty',
|
||||
photoUrl: ''
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await mockI18n()
|
||||
})
|
||||
|
||||
it('renders the user avatar correctly', () => {
|
||||
const view = render(<UserAvatarForUser user={user} />)
|
||||
const view = render(<UserAvatar user={user} />)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
})
|
||||
describe('renders the user avatar in size', () => {
|
||||
it('sm', () => {
|
||||
const view = render(<UserAvatarForUser user={user} size={'sm'} />)
|
||||
const view = render(<UserAvatar user={user} size={'sm'} />)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
})
|
||||
it('lg', () => {
|
||||
const view = render(<UserAvatarForUser user={user} size={'lg'} />)
|
||||
const view = render(<UserAvatar user={user} size={'lg'} />)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
it('adds additionalClasses props to wrapping span', () => {
|
||||
const view = render(<UserAvatarForUser user={user} additionalClasses={'testClass'} />)
|
||||
const view = render(<UserAvatar user={user} additionalClasses={'testClass'} />)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
})
|
||||
it('does not show names if showName prop is false', () => {
|
||||
const view = render(<UserAvatarForUser user={user} showName={false} />)
|
||||
const view = render(<UserAvatar user={user} showName={false} />)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('uses identicon when no photoUrl is given', () => {
|
||||
const view = render(<UserAvatar displayName={'test'} />)
|
||||
const view = render(<UserAvatar user={userWithoutPhoto} />)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('uses identicon when empty photoUrl is given', () => {
|
||||
const view = render(<UserAvatar displayName={'test'} photoUrl={''} />)
|
||||
const view = render(<UserAvatar user={userWithEmptyPhoto} />)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('uses custom photo component if provided', () => {
|
||||
const view = render(<UserAvatar displayName={'test'} photoComponent={<div>Custom Photo</div>} />)
|
||||
const view = render(<UserAvatar user={userWithoutPhoto} photoComponent={<div>Custom Photo</div>} />)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('uses custom photo component preferred over photoUrl', () => {
|
||||
const view = render(
|
||||
<UserAvatar displayName={'test'} photoComponent={<div>Custom Photo</div>} photoUrl={user.photoUrl} />
|
||||
)
|
||||
const view = render(<UserAvatar user={user} photoComponent={<div>Custom Photo</div>} />)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -7,15 +7,11 @@ import { useTranslatedText } from '../../../hooks/common/use-translated-text'
|
|||
import styles from './user-avatar.module.scss'
|
||||
import React, { useMemo } from 'react'
|
||||
import { useAvatarUrl } from './hooks/use-avatar-url'
|
||||
import type { UserInfoDto } from '@hedgedoc/commons'
|
||||
import type { CommonUserAvatarProps } from './types'
|
||||
|
||||
export interface UserAvatarProps {
|
||||
size?: 'sm' | 'lg'
|
||||
additionalClasses?: string
|
||||
showName?: boolean
|
||||
photoUrl?: string
|
||||
displayName: string
|
||||
username?: string | null
|
||||
photoComponent?: React.ReactNode
|
||||
interface UserAvatarProps extends CommonUserAvatarProps {
|
||||
user: UserInfoDto
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,17 +21,16 @@ export interface UserAvatarProps {
|
|||
* @param size The size in which the user image should be shown.
|
||||
* @param additionalClasses Additional CSS classes that will be added to the container.
|
||||
* @param showName true when the name should be displayed alongside the image, false otherwise. Defaults to true.
|
||||
* @param username The username to use for generating the fallback avatar image.
|
||||
* @param photoComponent A custom component to use as the user's photo.
|
||||
* @param overrideDisplayName Used to override the used display name, for example for setting random guest names
|
||||
*/
|
||||
export const UserAvatar: React.FC<UserAvatarProps> = ({
|
||||
photoUrl,
|
||||
displayName,
|
||||
size,
|
||||
additionalClasses = '',
|
||||
showName = true,
|
||||
username,
|
||||
photoComponent
|
||||
photoComponent,
|
||||
user,
|
||||
overrideDisplayName
|
||||
}) => {
|
||||
const imageSize = useMemo(() => {
|
||||
switch (size) {
|
||||
|
@ -48,13 +43,21 @@ export const UserAvatar: React.FC<UserAvatarProps> = ({
|
|||
}
|
||||
}, [size])
|
||||
|
||||
const avatarUrl = useAvatarUrl(photoUrl, username ?? displayName)
|
||||
const modifiedUser: UserInfoDto = useMemo(
|
||||
() => ({
|
||||
...user,
|
||||
displayName: overrideDisplayName ?? user.displayName
|
||||
}),
|
||||
[user, overrideDisplayName]
|
||||
)
|
||||
|
||||
const avatarUrl = useAvatarUrl(modifiedUser)
|
||||
|
||||
const imageTranslateOptions = useMemo(
|
||||
() => ({
|
||||
name: displayName
|
||||
name: modifiedUser.displayName
|
||||
}),
|
||||
[displayName]
|
||||
[modifiedUser.displayName]
|
||||
)
|
||||
const imgDescription = useTranslatedText('common.avatarOf', imageTranslateOptions)
|
||||
|
||||
|
@ -71,7 +74,7 @@ export const UserAvatar: React.FC<UserAvatarProps> = ({
|
|||
width={imageSize}
|
||||
/>
|
||||
)}
|
||||
{showName && <span className={`ms-2 me-1 ${styles['user-line-name']}`}>{displayName}</span>}
|
||||
{showName && <span className={`ms-2 me-1 ${styles['user-line-name']}`}>{modifiedUser.displayName}</span>}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { LoginUserInfo } from '../../../../api/me/types'
|
||||
import { useDisconnectOnUserLoginStatusChange } from './use-disconnect-on-user-login-status-change'
|
||||
import type { MessageTransporter } from '@hedgedoc/commons'
|
||||
import type { LoginUserInfoDto, MessageTransporter } from '@hedgedoc/commons'
|
||||
import { render } from '@testing-library/react'
|
||||
import React, { Fragment } from 'react'
|
||||
import { Mock } from 'ts-mockery'
|
||||
|
@ -21,7 +20,7 @@ describe('use logout on user change', () => {
|
|||
|
||||
const mockUseApplicationState = (userLoggedIn: boolean) => {
|
||||
mockAppState({
|
||||
user: userLoggedIn ? Mock.of<LoginUserInfo>({}) : null
|
||||
user: userLoggedIn ? Mock.of<LoginUserInfoDto>({}) : null
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -36,7 +36,8 @@ describe('frontend websocket', () => {
|
|||
mockSocket()
|
||||
const handler = jest.fn((reason?: DisconnectReason) => console.log(reason))
|
||||
|
||||
let modifiedHandler: (event: CloseEvent) => void = jest.fn()
|
||||
let modifiedHandler: EventListenerOrEventListenerObject = jest.fn()
|
||||
|
||||
jest.spyOn(mockedSocket, 'addEventListener').mockImplementation((event, handler_) => {
|
||||
modifiedHandler = handler_
|
||||
})
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { addLink } from './add-link'
|
||||
import type { ContentEdits } from './changes'
|
||||
import type { ContentEdits } from './types/changes'
|
||||
|
||||
describe('add link', () => {
|
||||
describe('without to-cursor', () => {
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import * as AliasModule from '../../../../../../api/alias'
|
||||
import type { Alias } from '../../../../../../api/alias/types'
|
||||
import * as NoteDetailsReduxModule from '../../../../../../redux/note-details/methods'
|
||||
import { mockI18n } from '../../../../../../test-utils/mock-i18n'
|
||||
import { mockNotePermissions } from '../../../../../../test-utils/mock-note-permissions'
|
||||
|
@ -12,6 +11,7 @@ import { AliasesListEntry } from './aliases-list-entry'
|
|||
import { act, render, screen } from '@testing-library/react'
|
||||
import React from 'react'
|
||||
import { mockUiNotifications } from '../../../../../../test-utils/mock-ui-notifications'
|
||||
import type { AliasDto } from '@hedgedoc/commons'
|
||||
|
||||
jest.mock('../../../../../../api/alias')
|
||||
jest.mock('../../../../../../redux/note-details/methods')
|
||||
|
@ -37,7 +37,7 @@ describe('AliasesListEntry', () => {
|
|||
|
||||
it('renders an AliasesListEntry that is primary', async () => {
|
||||
mockNotePermissions('test', 'test')
|
||||
const testAlias: Alias = {
|
||||
const testAlias: AliasDto = {
|
||||
name: 'test-primary',
|
||||
primaryAlias: true,
|
||||
noteId: 'test-note-id'
|
||||
|
@ -55,7 +55,7 @@ describe('AliasesListEntry', () => {
|
|||
|
||||
it("adds aliasPrimaryBadge & removes aliasButtonMakePrimary in AliasesListEntry if it's primary", () => {
|
||||
mockNotePermissions('test2', 'test')
|
||||
const testAlias: Alias = {
|
||||
const testAlias: AliasDto = {
|
||||
name: 'test-primary',
|
||||
primaryAlias: true,
|
||||
noteId: 'test-note-id'
|
||||
|
@ -66,7 +66,7 @@ describe('AliasesListEntry', () => {
|
|||
|
||||
it('renders an AliasesListEntry that is not primary', async () => {
|
||||
mockNotePermissions('test', 'test')
|
||||
const testAlias: Alias = {
|
||||
const testAlias: AliasDto = {
|
||||
name: 'test-non-primary',
|
||||
primaryAlias: false,
|
||||
noteId: 'test-note-id'
|
||||
|
@ -91,7 +91,7 @@ describe('AliasesListEntry', () => {
|
|||
|
||||
it("removes aliasPrimaryBadge & adds aliasButtonMakePrimary in AliasesListEntry if it's not primary", () => {
|
||||
mockNotePermissions('test2', 'test')
|
||||
const testAlias: Alias = {
|
||||
const testAlias: AliasDto = {
|
||||
name: 'test-primary',
|
||||
primaryAlias: false,
|
||||
noteId: 'test-note-id'
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { deleteAlias, markAliasAsPrimary } from '../../../../../../api/alias'
|
||||
import type { Alias } from '../../../../../../api/alias/types'
|
||||
import { useIsOwner } from '../../../../../../hooks/common/use-is-owner'
|
||||
import { useTranslatedText } from '../../../../../../hooks/common/use-translated-text'
|
||||
import { updateMetadata } from '../../../../../../redux/note-details/methods'
|
||||
|
@ -16,9 +15,10 @@ import { Badge } from 'react-bootstrap'
|
|||
import { Button } from 'react-bootstrap'
|
||||
import { Star as IconStar, X as IconX } from 'react-bootstrap-icons'
|
||||
import { useTranslation, Trans } from 'react-i18next'
|
||||
import type { AliasDto } from '@hedgedoc/commons'
|
||||
|
||||
export interface AliasesListEntryProps {
|
||||
alias: Alias
|
||||
alias: AliasDto
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useApplicationState } from '../../../../../../hooks/common/use-application-state'
|
||||
import type { Alias } from '../../../../../../api/alias/types'
|
||||
import type { ApplicationState } from '../../../../../../redux'
|
||||
import { AliasesListEntry } from './aliases-list-entry'
|
||||
import React, { Fragment, useMemo } from 'react'
|
||||
import type { AliasDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Renders the list of aliases.
|
||||
|
@ -18,8 +18,8 @@ export const AliasesList: React.FC = () => {
|
|||
return aliases === undefined
|
||||
? null
|
||||
: Object.assign([], aliases)
|
||||
.sort((a: Alias, b: Alias) => a.name.localeCompare(b.name))
|
||||
.map((alias: Alias) => <AliasesListEntry alias={alias} key={alias.name} />)
|
||||
.sort((a: AliasDto, b: AliasDto) => a.name.localeCompare(b.name))
|
||||
.map((alias: AliasDto) => <AliasesListEntry alias={alias} key={alias.name} />)
|
||||
}, [aliases])
|
||||
|
||||
return <Fragment>{aliasesDom}</Fragment>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -15,9 +15,9 @@ import { useApplicationState } from '../../../../../hooks/common/use-application
|
|||
import { getMediaForNote } from '../../../../../api/notes'
|
||||
import { AsyncLoadingBoundary } from '../../../../common/async-loading-boundary/async-loading-boundary'
|
||||
import { MediaEntry } from './media-entry'
|
||||
import type { MediaUpload } from '../../../../../api/media/types'
|
||||
import { MediaEntryDeletionModal } from './media-entry-deletion-modal'
|
||||
import { MediaBrowserEmpty } from './media-browser-empty'
|
||||
import type { MediaUploadDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Renders the media browser "menu" for the sidebar.
|
||||
|
@ -35,7 +35,7 @@ export const MediaBrowserSidebarMenu: React.FC<SpecificSidebarMenuProps> = ({
|
|||
}) => {
|
||||
useTranslation()
|
||||
const noteId = useApplicationState((state) => state.noteDetails?.id ?? '')
|
||||
const [mediaEntryForDeletion, setMediaEntryForDeletion] = useState<MediaUpload | null>(null)
|
||||
const [mediaEntryForDeletion, setMediaEntryForDeletion] = useState<MediaUploadDto | null>(null)
|
||||
|
||||
const hide = selectedMenuId !== DocumentSidebarMenuSelection.NONE && selectedMenuId !== menuId
|
||||
const expand = selectedMenuId === menuId
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import type { MediaUpload } from '../../../../../api/media/types'
|
||||
import { useBaseUrl } from '../../../../../hooks/common/use-base-url'
|
||||
import { Button, ButtonGroup } from 'react-bootstrap'
|
||||
import {
|
||||
|
@ -20,10 +19,11 @@ import { UserAvatarForUsername } from '../../../../common/user-avatar/user-avata
|
|||
import { useChangeEditorContentCallback } from '../../../change-content-context/use-change-editor-content-callback'
|
||||
import { replaceSelection } from '../../../editor-pane/tool-bar/formatters/replace-selection'
|
||||
import styles from './media-entry.module.css'
|
||||
import type { MediaUploadDto } from '@hedgedoc/commons'
|
||||
|
||||
export interface MediaEntryProps {
|
||||
entry: MediaUpload
|
||||
onDelete: (entry: MediaUpload) => void
|
||||
entry: MediaUploadDto
|
||||
onDelete: (entry: MediaUploadDto) => void
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useTranslatedText } from '../../../../../../hooks/common/use-translated-text'
|
||||
import { UiIcon } from '../../../../../common/icons/ui-icon'
|
||||
import type { PermissionDisabledProps } from './permission-disabled.prop'
|
||||
import { AccessLevel } from '@hedgedoc/commons'
|
||||
import { GuestAccess } from '@hedgedoc/commons'
|
||||
import React, { useMemo } from 'react'
|
||||
import { Button, ToggleButtonGroup } from 'react-bootstrap'
|
||||
import { Eye as IconEye, Pencil as IconPencil, X as IconX } from 'react-bootstrap-icons'
|
||||
|
@ -24,7 +24,7 @@ export enum PermissionType {
|
|||
|
||||
export interface PermissionEntryButtonsProps {
|
||||
type: PermissionType
|
||||
currentSetting: AccessLevel
|
||||
currentSetting: GuestAccess
|
||||
name: string
|
||||
onSetReadOnly: () => void
|
||||
onSetWriteable: () => void
|
||||
|
@ -79,14 +79,14 @@ export const PermissionEntryButtons: React.FC<PermissionEntryButtonsProps & Perm
|
|||
<Button
|
||||
disabled={disabled}
|
||||
title={setReadOnlyTitle}
|
||||
variant={currentSetting === AccessLevel.READ_ONLY ? 'secondary' : 'outline-secondary'}
|
||||
variant={currentSetting === GuestAccess.READ ? 'secondary' : 'outline-secondary'}
|
||||
onClick={onSetReadOnly}>
|
||||
<UiIcon icon={IconEye} />
|
||||
</Button>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
title={setWritableTitle}
|
||||
variant={currentSetting === AccessLevel.WRITEABLE ? 'secondary' : 'outline-secondary'}
|
||||
variant={currentSetting === GuestAccess.WRITE ? 'secondary' : 'outline-secondary'}
|
||||
onClick={onSetWriteable}>
|
||||
<UiIcon icon={IconPencil} />
|
||||
</Button>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -10,7 +10,7 @@ import { setNotePermissionsFromServer } from '../../../../../../redux/note-detai
|
|||
import { IconButton } from '../../../../../common/icon-button/icon-button'
|
||||
import { useUiNotifications } from '../../../../../notifications/ui-notification-boundary'
|
||||
import type { PermissionDisabledProps } from './permission-disabled.prop'
|
||||
import { AccessLevel, SpecialGroup } from '@hedgedoc/commons'
|
||||
import { GuestAccess, SpecialGroup } from '@hedgedoc/commons'
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import { ToggleButtonGroup } from 'react-bootstrap'
|
||||
import { Eye as IconEye, Pencil as IconPencil, SlashCircle as IconSlashCircle } from 'react-bootstrap-icons'
|
||||
|
@ -19,7 +19,7 @@ import { PermissionInconsistentAlert } from './permission-inconsistent-alert'
|
|||
import { cypressId } from '../../../../../../utils/cypress-attribute'
|
||||
|
||||
export interface PermissionEntrySpecialGroupProps {
|
||||
level: AccessLevel
|
||||
level: GuestAccess
|
||||
type: SpecialGroup
|
||||
inconsistent?: boolean
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ export const PermissionEntrySpecialGroup: React.FC<PermissionEntrySpecialGroupPr
|
|||
<IconButton
|
||||
icon={IconSlashCircle}
|
||||
title={denyGroupText}
|
||||
variant={level === AccessLevel.NONE ? 'secondary' : 'outline-secondary'}
|
||||
variant={level === GuestAccess.DENY ? 'secondary' : 'outline-secondary'}
|
||||
onClick={onSetEntryDenied}
|
||||
disabled={disabled}
|
||||
className={'p-1'}
|
||||
|
@ -107,7 +107,7 @@ export const PermissionEntrySpecialGroup: React.FC<PermissionEntrySpecialGroupPr
|
|||
<IconButton
|
||||
icon={IconEye}
|
||||
title={viewOnlyGroupText}
|
||||
variant={level === AccessLevel.READ_ONLY ? 'secondary' : 'outline-secondary'}
|
||||
variant={level === GuestAccess.READ ? 'secondary' : 'outline-secondary'}
|
||||
onClick={onSetEntryReadOnly}
|
||||
disabled={disabled}
|
||||
className={'p-1'}
|
||||
|
@ -116,7 +116,7 @@ export const PermissionEntrySpecialGroup: React.FC<PermissionEntrySpecialGroupPr
|
|||
<IconButton
|
||||
icon={IconPencil}
|
||||
title={editGroupText}
|
||||
variant={level === AccessLevel.WRITEABLE ? 'secondary' : 'outline-secondary'}
|
||||
variant={level === GuestAccess.WRITE ? 'secondary' : 'outline-secondary'}
|
||||
onClick={onSetEntryWriteable}
|
||||
disabled={disabled}
|
||||
className={'p-1'}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -7,20 +7,20 @@ import { removeUserPermission, setUserPermission } from '../../../../../../api/p
|
|||
import { getUserInfo } from '../../../../../../api/users'
|
||||
import { useApplicationState } from '../../../../../../hooks/common/use-application-state'
|
||||
import { setNotePermissionsFromServer } from '../../../../../../redux/note-details/methods'
|
||||
import { UserAvatarForUser } from '../../../../../common/user-avatar/user-avatar-for-user'
|
||||
import { useUiNotifications } from '../../../../../notifications/ui-notification-boundary'
|
||||
import type { PermissionDisabledProps } from './permission-disabled.prop'
|
||||
import { PermissionEntryButtons, PermissionType } from './permission-entry-buttons'
|
||||
import type { NoteUserPermissionEntry } from '@hedgedoc/commons'
|
||||
import { AccessLevel, SpecialGroup } from '@hedgedoc/commons'
|
||||
import type { NoteUserPermissionEntryDto } from '@hedgedoc/commons'
|
||||
import { GuestAccess, SpecialGroup } from '@hedgedoc/commons'
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import { useAsync } from 'react-use'
|
||||
import { PermissionInconsistentAlert } from './permission-inconsistent-alert'
|
||||
import { useGetSpecialPermissions } from './hooks/use-get-special-permissions'
|
||||
import { AsyncLoadingBoundary } from '../../../../../common/async-loading-boundary/async-loading-boundary'
|
||||
import { UserAvatar } from '../../../../../common/user-avatar/user-avatar'
|
||||
|
||||
export interface PermissionEntryUserProps {
|
||||
entry: NoteUserPermissionEntry
|
||||
entry: NoteUserPermissionEntryDto
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,12 +89,12 @@ export const PermissionEntryUser: React.FC<PermissionEntryUserProps & Permission
|
|||
return (
|
||||
<AsyncLoadingBoundary loading={loading} error={error} componentName={'PermissionEntryUser'}>
|
||||
<li className={'list-group-item d-flex flex-row justify-content-between align-items-center'}>
|
||||
<UserAvatarForUser user={value} />
|
||||
<UserAvatar user={value} />
|
||||
<div className={'d-flex flex-row align-items-center'}>
|
||||
<PermissionInconsistentAlert show={permissionInconsistent ?? false} />
|
||||
<PermissionEntryButtons
|
||||
type={PermissionType.USER}
|
||||
currentSetting={entry.canEdit ? AccessLevel.WRITEABLE : AccessLevel.READ_ONLY}
|
||||
currentSetting={entry.canEdit ? GuestAccess.WRITE : GuestAccess.READ}
|
||||
name={value.displayName}
|
||||
onSetReadOnly={onSetEntryReadOnly}
|
||||
onSetWriteable={onSetEntryWriteable}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useIsOwner } from '../../../../../../hooks/common/use-is-owner'
|
||||
import type { PermissionDisabledProps } from './permission-disabled.prop'
|
||||
import { PermissionEntrySpecialGroup } from './permission-entry-special-group'
|
||||
import { AccessLevel, SpecialGroup } from '@hedgedoc/commons'
|
||||
import { GuestAccess, SpecialGroup } from '@hedgedoc/commons'
|
||||
import React, { Fragment, useMemo } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useGetSpecialPermissions } from './hooks/use-get-special-permissions'
|
||||
|
@ -23,16 +23,8 @@ export const PermissionSectionSpecialGroups: React.FC<PermissionDisabledProps> =
|
|||
|
||||
const specialGroupEntries = useMemo(() => {
|
||||
return {
|
||||
everyoneLevel: groupEveryone
|
||||
? groupEveryone.canEdit
|
||||
? AccessLevel.WRITEABLE
|
||||
: AccessLevel.READ_ONLY
|
||||
: AccessLevel.NONE,
|
||||
loggedInLevel: groupLoggedIn
|
||||
? groupLoggedIn.canEdit
|
||||
? AccessLevel.WRITEABLE
|
||||
: AccessLevel.READ_ONLY
|
||||
: AccessLevel.NONE,
|
||||
everyoneLevel: groupEveryone ? (groupEveryone.canEdit ? GuestAccess.WRITE : GuestAccess.READ) : GuestAccess.DENY,
|
||||
loggedInLevel: groupLoggedIn ? (groupLoggedIn.canEdit ? GuestAccess.WRITE : GuestAccess.READ) : GuestAccess.DENY,
|
||||
loggedInInconsistentAlert: groupEveryone && (!groupLoggedIn || (groupEveryone.canEdit && !groupLoggedIn.canEdit))
|
||||
}
|
||||
}, [groupEveryone, groupLoggedIn])
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { RevisionMetadata } from '../../../../../../api/revisions/types'
|
||||
import type { RevisionMetadataDto } from '@hedgedoc/commons'
|
||||
import { UiIcon } from '../../../../../common/icons/ui-icon'
|
||||
import { UserAvatarForUser } from '../../../../../common/user-avatar/user-avatar-for-user'
|
||||
import { WaitSpinner } from '../../../../../common/wait-spinner/wait-spinner'
|
||||
import { useUiNotifications } from '../../../../../notifications/ui-notification-boundary'
|
||||
import styles from './revision-list-entry.module.scss'
|
||||
|
@ -21,11 +20,12 @@ import {
|
|||
} from 'react-bootstrap-icons'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useAsync } from 'react-use'
|
||||
import { UserAvatar } from '../../../../../common/user-avatar/user-avatar'
|
||||
|
||||
export interface RevisionListEntryProps {
|
||||
active: boolean
|
||||
onSelect: () => void
|
||||
revision: RevisionMetadata
|
||||
revision: RevisionMetadataDto
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,7 +47,7 @@ export const RevisionListEntry: React.FC<RevisionListEntryProps> = ({ active, on
|
|||
try {
|
||||
const authorDetails = await getUserDataForRevision(revision.authorUsernames)
|
||||
return authorDetails.map((author) => (
|
||||
<UserAvatarForUser user={author} key={author.username} showName={false} additionalClasses={'mx-1'} />
|
||||
<UserAvatar user={author} key={author.username} showName={false} additionalClasses={'mx-1'} />
|
||||
))
|
||||
} catch (error) {
|
||||
showErrorNotification('editor.modal.revision.errorUser')(error as Error)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { RevisionMetadata } from '../../../../../../api/revisions/types'
|
||||
import type { RevisionMetadataDto } from '@hedgedoc/commons'
|
||||
import { cypressId } from '../../../../../../utils/cypress-attribute'
|
||||
import { AsyncLoadingBoundary } from '../../../../../common/async-loading-boundary/async-loading-boundary'
|
||||
import { RevisionListEntry } from './revision-list-entry'
|
||||
|
@ -13,7 +13,7 @@ import { ListGroup } from 'react-bootstrap'
|
|||
|
||||
interface RevisionListProps {
|
||||
selectedRevisionId?: number
|
||||
revisions?: RevisionMetadata[]
|
||||
revisions?: RevisionMetadataDto[]
|
||||
loadingRevisions: boolean
|
||||
error?: Error | boolean
|
||||
onRevisionSelect: (selectedRevisionId: number) => void
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { RevisionDetails } from '../../../../../../api/revisions/types'
|
||||
import { getUserInfo } from '../../../../../../api/users'
|
||||
import type { UserInfo } from '../../../../../../api/users/types'
|
||||
import { download } from '../../../../../common/download/download'
|
||||
import type { RevisionDto, UserInfoDto } from '@hedgedoc/commons'
|
||||
|
||||
const DISPLAY_MAX_USERS_PER_REVISION = 9
|
||||
|
||||
|
@ -16,7 +15,7 @@ const DISPLAY_MAX_USERS_PER_REVISION = 9
|
|||
* @param noteId The id of the note from which to download the revision.
|
||||
* @param revision The revision details object containing the content to download.
|
||||
*/
|
||||
export const downloadRevision = (noteId: string, revision: RevisionDetails | null): void => {
|
||||
export const downloadRevision = (noteId: string, revision: RevisionDto | null): void => {
|
||||
if (!revision) {
|
||||
return
|
||||
}
|
||||
|
@ -30,8 +29,8 @@ export const downloadRevision = (noteId: string, revision: RevisionDetails | nul
|
|||
* @throws {Error} in case the user-data request failed.
|
||||
* @return An array of user details.
|
||||
*/
|
||||
export const getUserDataForRevision = async (usernames: string[]): Promise<UserInfo[]> => {
|
||||
const users: UserInfo[] = []
|
||||
export const getUserDataForRevision = async (usernames: string[]): Promise<UserInfoDto[]> => {
|
||||
const users: UserInfoDto[] = []
|
||||
const usersToFetch = Math.min(usernames.length, DISPLAY_MAX_USERS_PER_REVISION) - 1
|
||||
for (let i = 0; i <= usersToFetch; i++) {
|
||||
const user = await getUserInfo(usernames[i])
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { UserAvatar } from '../../../../../common/user-avatar/user-avatar'
|
||||
import { UserAvatarForUsername } from '../../../../../common/user-avatar/user-avatar-for-username'
|
||||
import { createCursorCssClass } from '../../../../editor-pane/codemirror-extensions/remote-cursors/create-cursor-css-class'
|
||||
import { ActiveIndicator } from '../active-indicator'
|
||||
|
@ -12,6 +11,7 @@ import React, { useMemo } from 'react'
|
|||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Incognito as IconIncognito } from 'react-bootstrap-icons'
|
||||
import { useTranslatedText } from '../../../../../../hooks/common/use-translated-text'
|
||||
import { GuestUserAvatar } from '../../../../../common/user-avatar/guest-user-avatar'
|
||||
|
||||
export interface UserLineProps {
|
||||
username: string | null
|
||||
|
@ -38,7 +38,10 @@ export const UserLine: React.FC<UserLineProps> = ({ username, displayName, activ
|
|||
return username ? (
|
||||
<UserAvatarForUsername username={username} additionalClasses={'flex-fill overflow-hidden px-2 text-nowrap'} />
|
||||
) : (
|
||||
<UserAvatar displayName={displayName} additionalClasses={'flex-fill overflow-hidden px-2 text-nowrap'} />
|
||||
<GuestUserAvatar
|
||||
overrideDisplayName={displayName}
|
||||
additionalClasses={'flex-fill overflow-hidden px-2 text-nowrap'}
|
||||
/>
|
||||
)
|
||||
}, [displayName, username])
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -61,18 +61,56 @@ describe('Splitter', () => {
|
|||
const view = render(<Splitter left={<>left</>} right={<>right</>} />)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
const divider = await screen.findByTestId('splitter-divider')
|
||||
const target: EventTarget = Mock.of<EventTarget>()
|
||||
const defaultTouchEvent: Omit<Touch, 'clientX'> = {
|
||||
clientY: 0,
|
||||
target: target,
|
||||
identifier: 0,
|
||||
pageX: 0,
|
||||
pageY: 0,
|
||||
screenX: 0,
|
||||
screenY: 0,
|
||||
force: 0,
|
||||
radiusX: 0,
|
||||
radiusY: 0,
|
||||
rotationAngle: 0
|
||||
}
|
||||
|
||||
fireEvent.touchStart(divider, {})
|
||||
fireEvent.touchMove(window, Mock.of<TouchEvent>({ touches: [{ clientX: 1920 }, { clientX: 200 }] }))
|
||||
fireEvent.touchMove(
|
||||
window,
|
||||
Mock.of<TouchEvent>({
|
||||
touches: [
|
||||
{ ...defaultTouchEvent, clientX: 1920 },
|
||||
{ ...defaultTouchEvent, clientX: 200 }
|
||||
]
|
||||
})
|
||||
)
|
||||
fireEvent.touchEnd(window)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
|
||||
fireEvent.touchStart(divider, {})
|
||||
fireEvent.touchMove(window, Mock.of<TouchEvent>({ touches: [{ clientX: 0 }, { clientX: 100 }] }))
|
||||
fireEvent.touchMove(
|
||||
window,
|
||||
Mock.of<TouchEvent>({
|
||||
touches: [
|
||||
{ ...defaultTouchEvent, clientX: 0 },
|
||||
{ ...defaultTouchEvent, clientX: 100 }
|
||||
]
|
||||
})
|
||||
)
|
||||
fireEvent.touchCancel(window)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
|
||||
fireEvent.touchMove(window, Mock.of<TouchEvent>({ touches: [{ clientX: 500 }, { clientX: 900 }] }))
|
||||
fireEvent.touchMove(
|
||||
window,
|
||||
Mock.of<TouchEvent>({
|
||||
touches: [
|
||||
{ ...defaultTouchEvent, clientX: 500 },
|
||||
{ ...defaultTouchEvent, clientX: 900 }
|
||||
]
|
||||
})
|
||||
)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { BackendVersion } from '../../../api/config/types'
|
||||
import type { ServerVersionDto } from '@hedgedoc/commons'
|
||||
import links from '../../../links.json'
|
||||
import { cypressId } from '../../../utils/cypress-attribute'
|
||||
import { CopyableField } from '../../common/copyable/copyable-field/copyable-field'
|
||||
|
@ -21,7 +21,7 @@ import { Modal } from 'react-bootstrap'
|
|||
* @param show If the modal should be shown.
|
||||
*/
|
||||
export const VersionInfoModal: React.FC<CommonModalProps> = ({ onHide, show }) => {
|
||||
const serverVersion: BackendVersion = useFrontendConfig().version
|
||||
const serverVersion: ServerVersionDto = useFrontendConfig().version
|
||||
const backendVersion = useMemo(() => {
|
||||
const version = `${serverVersion.major}.${serverVersion.minor}.${serverVersion.patch}`
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -7,13 +7,13 @@ import { useApplicationState } from '../../../hooks/common/use-application-state
|
|||
import { useOutlineButtonVariant } from '../../../hooks/dark-mode/use-outline-button-variant'
|
||||
import { cypressId } from '../../../utils/cypress-attribute'
|
||||
import { UiIcon } from '../../common/icons/ui-icon'
|
||||
import { UserAvatarForUser } from '../../common/user-avatar/user-avatar-for-user'
|
||||
import { SignOutDropdownButton } from './sign-out-dropdown-button'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import { Dropdown } from 'react-bootstrap'
|
||||
import { Person as IconPerson } from 'react-bootstrap-icons'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { UserAvatar } from '../../common/user-avatar/user-avatar'
|
||||
|
||||
/**
|
||||
* Renders a dropdown menu with user-relevant actions.
|
||||
|
@ -34,7 +34,7 @@ export const UserDropdown: React.FC = () => {
|
|||
size='sm'
|
||||
variant={buttonVariant}
|
||||
className={'d-flex align-items-center'}>
|
||||
<UserAvatarForUser user={user} />
|
||||
<UserAvatar user={user} />
|
||||
</Dropdown.Toggle>
|
||||
|
||||
<Dropdown.Menu className='text-start'>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -28,13 +28,13 @@ export const LegalSubmenu: React.FC = (): null | ReactElement => {
|
|||
<Fragment>
|
||||
<Dropdown.Divider />
|
||||
<DropdownHeader i18nKey={'appbar.help.legal.header'} />
|
||||
{specialUrls.privacy !== undefined && (
|
||||
{specialUrls.privacy !== null && (
|
||||
<TranslatedDropdownItem href={specialUrls.privacy} i18nKey={'appbar.help.legal.privacy'} />
|
||||
)}
|
||||
{specialUrls.termsOfUse !== undefined && (
|
||||
{specialUrls.termsOfUse !== null && (
|
||||
<TranslatedDropdownItem href={specialUrls.termsOfUse} i18nKey={'appbar.help.legal.termsOfUse'} />
|
||||
)}
|
||||
{specialUrls.imprint !== undefined && (
|
||||
{specialUrls.imprint !== null && (
|
||||
<TranslatedDropdownItem href={specialUrls.imprint} i18nKey={'appbar.help.legal.imprint'} />
|
||||
)}
|
||||
</Fragment>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -10,7 +10,7 @@ import { NewNoteButton } from '../../common/new-note-button/new-note-button'
|
|||
import { HistoryButton } from '../../layout/app-bar/app-bar-elements/help-dropdown/history-button'
|
||||
import { useFrontendConfig } from '../../common/frontend-config-context/use-frontend-config'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { GuestAccessLevel } from '../../../api/config/types'
|
||||
import { GuestAccess } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Renders the card with the options for not logged-in users.
|
||||
|
@ -20,7 +20,7 @@ export const GuestCard: React.FC = () => {
|
|||
|
||||
useTranslation()
|
||||
|
||||
if (guestAccessLevel === GuestAccessLevel.DENY) {
|
||||
if (guestAccessLevel === GuestAccess.DENY) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ export const GuestCard: React.FC = () => {
|
|||
<NewNoteButton />
|
||||
<HistoryButton />
|
||||
</div>
|
||||
{guestAccessLevel !== GuestAccessLevel.CREATE && (
|
||||
{guestAccessLevel !== GuestAccess.CREATE && (
|
||||
<div className={'text-muted mt-2 small'}>
|
||||
<Trans i18nKey={'login.guest.noteCreationDisabled'} />
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import React, { Fragment, useMemo } from 'react'
|
||||
import { useFrontendConfig } from '../../common/frontend-config-context/use-frontend-config'
|
||||
import type { AuthProviderWithCustomName } from '../../../api/config/types'
|
||||
import { AuthProviderType } from '../../../api/config/types'
|
||||
import type { AuthProviderWithCustomNameDto } from '@hedgedoc/commons'
|
||||
import { ProviderType } from '@hedgedoc/commons'
|
||||
import { LdapLoginCard } from './ldap-login-card'
|
||||
|
||||
/**
|
||||
|
@ -18,9 +18,9 @@ export const LdapLoginCards: React.FC = () => {
|
|||
|
||||
const ldapProviders = useMemo(() => {
|
||||
return authProviders
|
||||
.filter((provider) => provider.type === AuthProviderType.LDAP)
|
||||
.filter((provider) => provider.type === ProviderType.LDAP)
|
||||
.map((provider) => {
|
||||
const ldapProvider = provider as AuthProviderWithCustomName
|
||||
const ldapProvider = provider as AuthProviderWithCustomNameDto
|
||||
return (
|
||||
<LdapLoginCard
|
||||
providerName={ldapProvider.providerName}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -7,7 +7,7 @@
|
|||
import React, { useMemo } from 'react'
|
||||
import { Card } from 'react-bootstrap'
|
||||
import { useFrontendConfig } from '../../common/frontend-config-context/use-frontend-config'
|
||||
import { AuthProviderType } from '../../../api/config/types'
|
||||
import { ProviderType } from '@hedgedoc/commons'
|
||||
import { LocalLoginCardBody } from './local-login-card-body'
|
||||
import { LocalRegisterCardBody } from './register/local-register-card-body'
|
||||
|
||||
|
@ -18,7 +18,7 @@ export const LocalLoginCard: React.FC = () => {
|
|||
const frontendConfig = useFrontendConfig()
|
||||
|
||||
const localLoginEnabled = useMemo(() => {
|
||||
return frontendConfig.authProviders.some((provider) => provider.type === AuthProviderType.LOCAL)
|
||||
return frontendConfig.authProviders.some((provider) => provider.type === ProviderType.LOCAL)
|
||||
}, [frontendConfig])
|
||||
|
||||
if (!localLoginEnabled) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -40,10 +40,15 @@ export const NewUserCard: React.FC = () => {
|
|||
const submitUserdata = useCallback(
|
||||
(event: FormEvent) => {
|
||||
event.preventDefault()
|
||||
let profilePicture: string | null = null
|
||||
if (pictureChoice === ProfilePictureChoice.PROVIDER && value) {
|
||||
profilePicture = value.photoUrl
|
||||
}
|
||||
|
||||
confirmPendingUser({
|
||||
username,
|
||||
displayName,
|
||||
profilePicture: pictureChoice === ProfilePictureChoice.PROVIDER ? value?.photoUrl : undefined
|
||||
profilePicture
|
||||
})
|
||||
.then(() => fetchAndSetUser())
|
||||
.then(() => {
|
||||
|
@ -51,7 +56,7 @@ export const NewUserCard: React.FC = () => {
|
|||
})
|
||||
.catch(showErrorNotification('login.welcome.error'))
|
||||
},
|
||||
[username, displayName, pictureChoice, router, showErrorNotification, value?.photoUrl]
|
||||
[pictureChoice, value, username, displayName, showErrorNotification, router]
|
||||
)
|
||||
|
||||
const cancelUserCreation = useCallback(() => {
|
||||
|
@ -111,7 +116,7 @@ export const NewUserCard: React.FC = () => {
|
|||
<ProfilePictureSelectField
|
||||
onChange={setPictureChoice}
|
||||
value={pictureChoice}
|
||||
pictureUrl={value?.photoUrl}
|
||||
photoUrl={value?.photoUrl ?? null}
|
||||
username={username}
|
||||
/>
|
||||
<div className={'d-flex gap-3'}>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -16,8 +16,8 @@ import {
|
|||
Mastodon as IconMastodon
|
||||
} from 'react-bootstrap-icons'
|
||||
import { Logger } from '../../../utils/logger'
|
||||
import type { AuthProvider } from '../../../api/config/types'
|
||||
import { AuthProviderType } from '../../../api/config/types'
|
||||
import type { AuthProviderDto } from '@hedgedoc/commons'
|
||||
import { ProviderType } from '@hedgedoc/commons'
|
||||
import { IconGitlab } from '../../common/icons/additional/icon-gitlab'
|
||||
import styles from './one-click-login-button.module.scss'
|
||||
|
||||
|
@ -36,8 +36,8 @@ const logger = new Logger('GetOneClickProviderMetadata')
|
|||
* @param provider The provider for which to retrieve the metadata.
|
||||
* @return Name, icon, URL and CSS class of the given provider for rendering a login button.
|
||||
*/
|
||||
export const getOneClickProviderMetadata = (provider: AuthProvider): OneClickMetadata => {
|
||||
if (provider.type !== AuthProviderType.OIDC) {
|
||||
export const getOneClickProviderMetadata = (provider: AuthProviderDto): OneClickMetadata => {
|
||||
if (provider.type !== ProviderType.OIDC) {
|
||||
logger.warn('Metadata for one-click-provider does not exist', provider)
|
||||
return {
|
||||
name: '',
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { AuthProvider, AuthProviderWithCustomName } from '../../../api/config/types'
|
||||
import type { AuthProviderDto, AuthProviderWithCustomNameDto } from '@hedgedoc/commons'
|
||||
import { IconButton } from '../../common/icon-button/icon-button'
|
||||
import React, { useMemo } from 'react'
|
||||
import { getOneClickProviderMetadata } from './get-one-click-provider-metadata'
|
||||
|
||||
export interface ViaOneClickProps {
|
||||
provider: AuthProvider
|
||||
provider: AuthProviderDto
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,7 +19,7 @@ export interface ViaOneClickProps {
|
|||
*/
|
||||
export const OneClickLoginButton: React.FC<ViaOneClickProps> = ({ provider }) => {
|
||||
const { className, icon, url, name } = useMemo(() => getOneClickProviderMetadata(provider), [provider])
|
||||
const text = (provider as AuthProviderWithCustomName).providerName || name
|
||||
const text = (provider as AuthProviderWithCustomNameDto).providerName || name
|
||||
|
||||
return (
|
||||
<IconButton className={className} icon={icon} href={url} title={text} border={true}>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import type { AuthProvider } from '../../../api/config/types'
|
||||
import { authProviderTypeOneClick } from '../../../api/config/types'
|
||||
import type { AuthProviderDto } from '@hedgedoc/commons'
|
||||
import { ProviderType } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Filters the given auth providers to one-click providers only.
|
||||
* @param authProviders The auth providers to filter
|
||||
* @return only one click auth providers
|
||||
*/
|
||||
export const filterOneClickProviders = (authProviders: AuthProvider[]) => {
|
||||
return authProviders.filter((provider: AuthProvider): boolean => authProviderTypeOneClick.includes(provider.type))
|
||||
export const filterOneClickProviders = (authProviders: AuthProviderDto[]) => {
|
||||
return authProviders.filter((provider: AuthProviderDto): boolean => provider.type === ProviderType.OIDC)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { AccessTokenWithSecret } from '../../../api/tokens/types'
|
||||
import { cypressId } from '../../../utils/cypress-attribute'
|
||||
import { CopyableField } from '../../common/copyable/copyable-field/copyable-field'
|
||||
import type { ModalVisibilityProps } from '../../common/modals/common-modal'
|
||||
|
@ -11,9 +10,10 @@ import { CommonModal } from '../../common/modals/common-modal'
|
|||
import React from 'react'
|
||||
import { Button, Modal } from 'react-bootstrap'
|
||||
import { Trans } from 'react-i18next'
|
||||
import type { ApiTokenWithSecretDto } from '@hedgedoc/commons'
|
||||
|
||||
export interface AccessTokenCreatedModalProps extends ModalVisibilityProps {
|
||||
tokenWithSecret?: AccessTokenWithSecret
|
||||
tokenWithSecret?: ApiTokenWithSecretDto
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { AccessTokenWithSecret } from '../../../../api/tokens/types'
|
||||
import { AccessTokenCreatedModal } from '../access-token-created-modal'
|
||||
import type { AccessTokenUpdateProps } from '../profile-access-tokens'
|
||||
import { AccessTokenCreationFormExpiryField } from './access-token-creation-form-expiry-field'
|
||||
|
@ -15,6 +14,7 @@ import type { ChangeEvent } from 'react'
|
|||
import React, { Fragment, useCallback, useMemo, useState } from 'react'
|
||||
import { Form } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import type { ApiTokenWithSecretDto } from '@hedgedoc/commons'
|
||||
|
||||
interface NewTokenFormValues {
|
||||
label: string
|
||||
|
@ -38,7 +38,7 @@ export const AccessTokenCreationForm: React.FC<AccessTokenUpdateProps> = ({ onUp
|
|||
}, [expiryDates])
|
||||
|
||||
const [formValues, setFormValues] = useState<NewTokenFormValues>(() => formValuesInitialState)
|
||||
const [newTokenWithSecret, setNewTokenWithSecret] = useState<AccessTokenWithSecret>()
|
||||
const [newTokenWithSecret, setNewTokenWithSecret] = useState<ApiTokenWithSecretDto>()
|
||||
|
||||
const onHideCreatedModal = useCallback(() => {
|
||||
setFormValues(formValuesInitialState)
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { postNewAccessToken } from '../../../../../api/tokens'
|
||||
import type { AccessTokenWithSecret } from '../../../../../api/tokens/types'
|
||||
import { postNewAccessToken } from '../../../../../api/api-tokens'
|
||||
import { useUiNotifications } from '../../../../notifications/ui-notification-boundary'
|
||||
import { DateTime } from 'luxon'
|
||||
import type { FormEvent } from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import type { ApiTokenWithSecretDto } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Callback for requesting a new access token from the API and returning the response token and secret.
|
||||
*
|
||||
* @param label The label for the new access token.
|
||||
* @param expiryDate The expiry date of the new access token.
|
||||
* @param expiryDateStr The expiry date of the new access token.
|
||||
* @param setNewTokenWithSecret Callback to set the new access token with the secret from the API.
|
||||
* @return Callback that can be called when the new access token should be requested.
|
||||
*/
|
||||
export const useOnCreateToken = (
|
||||
label: string,
|
||||
expiryDate: string,
|
||||
setNewTokenWithSecret: (token: AccessTokenWithSecret) => void
|
||||
expiryDateStr: string,
|
||||
setNewTokenWithSecret: (token: ApiTokenWithSecretDto) => void
|
||||
): ((event: FormEvent) => void) => {
|
||||
const { showErrorNotification } = useUiNotifications()
|
||||
|
||||
return useCallback(
|
||||
(event: FormEvent) => {
|
||||
event.preventDefault()
|
||||
const expiryInMillis = DateTime.fromFormat(expiryDate, 'yyyy-MM-dd').toMillis()
|
||||
postNewAccessToken(label, expiryInMillis)
|
||||
const expiryDate = DateTime.fromFormat(expiryDateStr, 'yyyy-MM-dd').toJSDate()
|
||||
postNewAccessToken(label, expiryDate)
|
||||
.then((tokenWithSecret) => {
|
||||
setNewTokenWithSecret(tokenWithSecret)
|
||||
})
|
||||
.catch(showErrorNotification('profile.accessTokens.creationFailed'))
|
||||
},
|
||||
[expiryDate, label, setNewTokenWithSecret, showErrorNotification]
|
||||
[expiryDateStr, label, setNewTokenWithSecret, showErrorNotification]
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { deleteAccessToken } from '../../../api/tokens'
|
||||
import type { AccessToken } from '../../../api/tokens/types'
|
||||
import { deleteAccessToken } from '../../../api/api-tokens'
|
||||
import { cypressId } from '../../../utils/cypress-attribute'
|
||||
import type { ModalVisibilityProps } from '../../common/modals/common-modal'
|
||||
import { CommonModal } from '../../common/modals/common-modal'
|
||||
|
@ -12,9 +11,10 @@ import { useUiNotifications } from '../../notifications/ui-notification-boundary
|
|||
import React, { useCallback } from 'react'
|
||||
import { Button, Modal } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import type { ApiTokenDto } from '@hedgedoc/commons'
|
||||
|
||||
export interface AccessTokenDeletionModalProps extends ModalVisibilityProps {
|
||||
token: AccessToken
|
||||
token: ApiTokenDto
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { AccessToken } from '../../../api/tokens/types'
|
||||
import { useBooleanState } from '../../../hooks/common/use-boolean-state'
|
||||
import { cypressId } from '../../../utils/cypress-attribute'
|
||||
import { IconButton } from '../../common/icon-button/icon-button'
|
||||
|
@ -14,9 +13,10 @@ import React, { useCallback, useMemo } from 'react'
|
|||
import { Col, ListGroup, Row } from 'react-bootstrap'
|
||||
import { Trash as IconTrash } from 'react-bootstrap-icons'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import type { ApiTokenDto } from '@hedgedoc/commons'
|
||||
|
||||
export interface AccessTokenListEntryProps {
|
||||
token: AccessToken
|
||||
token: ApiTokenDto
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { getAccessTokenList } from '../../../api/tokens'
|
||||
import type { AccessToken } from '../../../api/tokens/types'
|
||||
import { getAccessTokenList } from '../../../api/api-tokens'
|
||||
import { useUiNotifications } from '../../notifications/ui-notification-boundary'
|
||||
import { AccessTokenCreationForm } from './access-token-creation-form/access-token-creation-form'
|
||||
import { AccessTokenListEntry } from './access-token-list-entry'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Card, ListGroup } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import type { ApiTokenDto } from '@hedgedoc/commons'
|
||||
|
||||
export interface AccessTokenUpdateProps {
|
||||
onUpdateList: () => void
|
||||
|
@ -21,7 +21,7 @@ export interface AccessTokenUpdateProps {
|
|||
*/
|
||||
export const ProfileAccessTokens: React.FC = () => {
|
||||
useTranslation()
|
||||
const [accessTokens, setAccessTokens] = useState<AccessToken[]>([])
|
||||
const [accessTokens, setAccessTokens] = useState<ApiTokenDto[]>([])
|
||||
const { showErrorNotification } = useUiNotifications()
|
||||
|
||||
const refreshAccessTokens = useCallback(() => {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { updateDisplayName } from '../../../api/me'
|
||||
import { updateUser } from '../../../api/me'
|
||||
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
||||
import { useOnInputChange } from '../../../hooks/common/use-on-input-change'
|
||||
import { DisplayNameField } from '../../common/fields/display-name-field'
|
||||
|
@ -27,7 +27,7 @@ export const ProfileDisplayName: React.FC = () => {
|
|||
const onSubmitNameChange = useCallback(
|
||||
(event: FormEvent) => {
|
||||
event.preventDefault()
|
||||
updateDisplayName(displayName)
|
||||
updateUser(displayName, null)
|
||||
.then(fetchAndSetUser)
|
||||
.catch(showErrorNotification('profile.changeDisplayNameFailed'))
|
||||
},
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { FrontendConfig } from '../../api/config/types'
|
||||
import type { FrontendConfigDto } from '@hedgedoc/commons'
|
||||
import type { CheatsheetExtension } from '../../components/cheatsheet/cheatsheet-extension'
|
||||
import type { Linter } from '../../components/editor-page/editor-pane/linter/linter'
|
||||
import type { MarkdownRendererExtension } from '../../components/markdown-renderer/extensions/_base-classes/markdown-renderer-extension'
|
||||
|
@ -14,7 +14,7 @@ import type React from 'react'
|
|||
import { Fragment } from 'react'
|
||||
|
||||
export interface MarkdownRendererExtensionOptions {
|
||||
frontendConfig: FrontendConfig
|
||||
frontendConfig: FrontendConfigDto
|
||||
eventEmitter: EventEmitter2
|
||||
rendererType: RendererType
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -21,7 +21,7 @@ import type { CompletionSource } from '@codemirror/autocomplete'
|
|||
*/
|
||||
export class PlantumlAppExtension extends AppExtension {
|
||||
buildMarkdownRendererExtensions(options: MarkdownRendererExtensionOptions): MarkdownRendererExtension[] {
|
||||
return [new PlantumlMarkdownExtension(options.frontendConfig.plantumlServer)]
|
||||
return [new PlantumlMarkdownExtension(options.frontendConfig.plantUmlServer)]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -33,7 +33,7 @@ describe('PlantUML markdown extensions', () => {
|
|||
it('renders an error if no server is defined', () => {
|
||||
const view = render(
|
||||
<TestMarkdownRenderer
|
||||
extensions={[new PlantumlMarkdownExtension(undefined)]}
|
||||
extensions={[new PlantumlMarkdownExtension(null)]}
|
||||
content={'```plantuml\nclass Example\n```'}
|
||||
/>
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -19,7 +19,7 @@ import type Token from 'markdown-it/lib/token'
|
|||
* @see https://plantuml.com
|
||||
*/
|
||||
export class PlantumlMarkdownExtension extends MarkdownRendererExtension {
|
||||
constructor(private plantumlServerUrl: string | undefined) {
|
||||
constructor(private plantumlServerUrl: string | null) {
|
||||
super()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -30,8 +30,8 @@ export const respondToMatchingRequest = <T>(
|
|||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
response: T,
|
||||
statusCode = 200,
|
||||
respondMethodNotAllowedOnMismatch = true
|
||||
statusCode: number = 200,
|
||||
respondMethodNotAllowedOnMismatch: boolean = true
|
||||
): boolean => {
|
||||
if (!isMockMode) {
|
||||
res.status(404).send('Mock API is disabled')
|
||||
|
|
|
@ -1,27 +1,25 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useTranslatedText } from './use-translated-text'
|
||||
import { renderHook } from '@testing-library/react'
|
||||
import type { Namespace } from 'i18next'
|
||||
import * as ReactI18NextModule from 'react-i18next'
|
||||
import type { UseTranslationResponse } from 'react-i18next'
|
||||
import { Mock } from 'ts-mockery'
|
||||
import type { UseTranslationResponse } from 'react-i18next'
|
||||
|
||||
jest.mock('react-i18next')
|
||||
|
||||
describe('useTranslatedText', () => {
|
||||
const mockTranslation = 'mockTranslation'
|
||||
const mockKey = 'mockKey'
|
||||
let translateFunction: jest.Mock
|
||||
const translateFunction = jest.fn(() => mockTranslation)
|
||||
|
||||
beforeEach(() => {
|
||||
translateFunction = jest.fn(() => mockTranslation)
|
||||
const useTranslateMock = Mock.of<UseTranslationResponse<Namespace, unknown>>({
|
||||
const useTranslateMock = Mock.of({
|
||||
t: translateFunction
|
||||
})
|
||||
}) as unknown as UseTranslationResponse<never, never>
|
||||
jest.spyOn(ReactI18NextModule, 'useTranslation').mockReturnValue(useTranslateMock)
|
||||
})
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { FrontendConfig } from '../../../api/config/types'
|
||||
import { AuthProviderType, GuestAccessLevel } from '../../../api/config/types'
|
||||
import type { FrontendConfigDto } from '@hedgedoc/commons'
|
||||
import { ProviderType, GuestAccess } from '@hedgedoc/commons'
|
||||
import {
|
||||
HttpMethod,
|
||||
respondToMatchingRequest,
|
||||
|
@ -13,7 +13,7 @@ import {
|
|||
import { isTestMode } from '../../../utils/test-modes'
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
const initialConfig: FrontendConfig = {
|
||||
const initialConfig: FrontendConfigDto = {
|
||||
allowRegister: true,
|
||||
allowProfileEdits: true,
|
||||
allowChooseUsername: true,
|
||||
|
@ -21,7 +21,7 @@ const initialConfig: FrontendConfig = {
|
|||
name: 'DEMO Corp',
|
||||
logo: '/public/img/demo.png'
|
||||
},
|
||||
guestAccess: GuestAccessLevel.WRITE,
|
||||
guestAccess: GuestAccess.WRITE,
|
||||
useImageProxy: false,
|
||||
specialUrls: {
|
||||
privacy: 'https://example.com/privacy',
|
||||
|
@ -33,31 +33,34 @@ const initialConfig: FrontendConfig = {
|
|||
minor: 0,
|
||||
patch: 0,
|
||||
preRelease: isTestMode ? undefined : '',
|
||||
commit: 'mock'
|
||||
commit: 'mock',
|
||||
fullString: `${isTestMode ? 0 : 2}.0.0`
|
||||
},
|
||||
plantumlServer: isTestMode ? 'http://mock-plantuml.local' : 'https://www.plantuml.com/plantuml',
|
||||
plantUmlServer: isTestMode ? 'http://mock-plantuml.local' : 'https://www.plantuml.com/plantuml',
|
||||
maxDocumentLength: isTestMode ? 200 : 1000000,
|
||||
authProviders: [
|
||||
{
|
||||
type: AuthProviderType.LOCAL
|
||||
type: ProviderType.LOCAL
|
||||
},
|
||||
{
|
||||
type: AuthProviderType.LDAP,
|
||||
type: ProviderType.LDAP,
|
||||
identifier: 'test-ldap',
|
||||
providerName: 'Test LDAP'
|
||||
providerName: 'Test LDAP',
|
||||
theme: null
|
||||
},
|
||||
{
|
||||
type: AuthProviderType.OIDC,
|
||||
type: ProviderType.OIDC,
|
||||
identifier: 'test-oidc',
|
||||
providerName: 'Test OIDC'
|
||||
providerName: 'Test OIDC',
|
||||
theme: null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
let currentConfig: FrontendConfig = initialConfig
|
||||
let currentConfig: FrontendConfigDto = initialConfig
|
||||
|
||||
const handler = (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const responseSuccessful = respondToMatchingRequest<FrontendConfig>(
|
||||
const responseSuccessful = respondToMatchingRequest<FrontendConfigDto>(
|
||||
HttpMethod.GET,
|
||||
req,
|
||||
res,
|
||||
|
@ -66,10 +69,10 @@ const handler = (req: NextApiRequest, res: NextApiResponse) => {
|
|||
false
|
||||
)
|
||||
if (!responseSuccessful) {
|
||||
respondToTestRequest<FrontendConfig>(req, res, () => {
|
||||
respondToTestRequest<FrontendConfigDto>(req, res, () => {
|
||||
currentConfig = {
|
||||
...initialConfig,
|
||||
...(req.body as FrontendConfig)
|
||||
...(req.body as FrontendConfigDto)
|
||||
}
|
||||
return currentConfig
|
||||
})
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { GroupInfo } from '../../../../api/group/types'
|
||||
import { HttpMethod, respondToMatchingRequest } from '../../../../handler-utils/respond-to-matching-request'
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import type { GroupInfoDto } from '@hedgedoc/commons'
|
||||
|
||||
const handler = (req: NextApiRequest, res: NextApiResponse) => {
|
||||
respondToMatchingRequest<GroupInfo>(HttpMethod.GET, req, res, {
|
||||
respondToMatchingRequest<GroupInfoDto>(HttpMethod.GET, req, res, {
|
||||
name: '_EVERYONE',
|
||||
displayName: 'Everyone',
|
||||
special: true
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { GroupInfo } from '../../../../api/group/types'
|
||||
import { HttpMethod, respondToMatchingRequest } from '../../../../handler-utils/respond-to-matching-request'
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import type { GroupInfoDto } from '@hedgedoc/commons'
|
||||
|
||||
const handler = (req: NextApiRequest, res: NextApiResponse) => {
|
||||
respondToMatchingRequest<GroupInfo>(HttpMethod.GET, req, res, {
|
||||
respondToMatchingRequest<GroupInfoDto>(HttpMethod.GET, req, res, {
|
||||
name: '_LOGGED_IN',
|
||||
displayName: 'All registered users',
|
||||
special: true
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { GroupInfo } from '../../../../api/group/types'
|
||||
import { HttpMethod, respondToMatchingRequest } from '../../../../handler-utils/respond-to-matching-request'
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import type { GroupInfoDto } from '@hedgedoc/commons'
|
||||
|
||||
const handler = (req: NextApiRequest, res: NextApiResponse) => {
|
||||
respondToMatchingRequest<GroupInfo>(HttpMethod.GET, req, res, {
|
||||
respondToMatchingRequest<GroupInfoDto>(HttpMethod.GET, req, res, {
|
||||
name: 'hedgedoc-devs',
|
||||
displayName: 'HedgeDoc devs',
|
||||
special: true
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { LoginUserInfo } from '../../../../api/me/types'
|
||||
import { HttpMethod, respondToMatchingRequest } from '../../../../handler-utils/respond-to-matching-request'
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import type { LoginUserInfoDto } from '@hedgedoc/commons'
|
||||
import { ProviderType } from '@hedgedoc/commons'
|
||||
|
||||
const handler = (req: NextApiRequest, res: NextApiResponse): void => {
|
||||
const cookieSet = req.headers?.['cookie']?.split(';').find((value) => value.trim() === 'mock-session=1') !== undefined
|
||||
|
@ -13,11 +14,11 @@ const handler = (req: NextApiRequest, res: NextApiResponse): void => {
|
|||
res.status(403).json({})
|
||||
return
|
||||
}
|
||||
respondToMatchingRequest<LoginUserInfo>(HttpMethod.GET, req, res, {
|
||||
respondToMatchingRequest<LoginUserInfoDto>(HttpMethod.GET, req, res, {
|
||||
username: 'mock',
|
||||
photoUrl: '/public/img/avatar.png',
|
||||
displayName: 'Mock User',
|
||||
authProvider: 'local',
|
||||
authProvider: ProviderType.LOCAL,
|
||||
email: 'mock@hedgedoc.test'
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { MediaUpload } from '../../../../api/media/types'
|
||||
import { HttpMethod, respondToMatchingRequest } from '../../../../handler-utils/respond-to-matching-request'
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import type { MediaUploadDto } from '@hedgedoc/commons'
|
||||
|
||||
const handler = (req: NextApiRequest, res: NextApiResponse) => {
|
||||
respondToMatchingRequest<MediaUpload[]>(HttpMethod.GET, req, res, [
|
||||
respondToMatchingRequest<MediaUploadDto[]>(HttpMethod.GET, req, res, [
|
||||
{
|
||||
username: 'tilman',
|
||||
createdAt: '2022-03-20T20:36:32Z',
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { MediaUpload } from '../../../api/media/types'
|
||||
import { HttpMethod, respondToMatchingRequest } from '../../../handler-utils/respond-to-matching-request'
|
||||
import { isMockMode, isTestMode } from '../../../utils/test-modes'
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import type { MediaUploadDto } from '@hedgedoc/commons'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
|
||||
if (isMockMode && !isTestMode) {
|
||||
|
@ -15,7 +15,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse): Promise<void>
|
|||
})
|
||||
}
|
||||
|
||||
respondToMatchingRequest<MediaUpload>(
|
||||
respondToMatchingRequest<MediaUploadDto>(
|
||||
HttpMethod.POST,
|
||||
req,
|
||||
res,
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,14 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { RevisionDetails } from '../../../../../../api/revisions/types'
|
||||
import type { RevisionDto } from '@hedgedoc/commons'
|
||||
import { HttpMethod, respondToMatchingRequest } from '../../../../../../handler-utils/respond-to-matching-request'
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
const handler = (req: NextApiRequest, res: NextApiResponse): void => {
|
||||
respondToMatchingRequest<RevisionDetails>(HttpMethod.GET, req, res, {
|
||||
respondToMatchingRequest<RevisionDto>(HttpMethod.GET, req, res, {
|
||||
id: 0,
|
||||
createdAt: '2021-12-21T16:59:42.000Z',
|
||||
title: 'Features',
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { RevisionDetails } from '../../../../../../api/revisions/types'
|
||||
import type { RevisionDto } from '@hedgedoc/commons'
|
||||
import { HttpMethod, respondToMatchingRequest } from '../../../../../../handler-utils/respond-to-matching-request'
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
const handler = (req: NextApiRequest, res: NextApiResponse): void => {
|
||||
respondToMatchingRequest<RevisionDetails>(HttpMethod.GET, req, res, {
|
||||
respondToMatchingRequest<RevisionDto>(HttpMethod.GET, req, res, {
|
||||
id: 1,
|
||||
createdAt: '2021-12-29T17:54:11.000Z',
|
||||
title: 'Features',
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue