Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.linkedout.backend.config

import org.springframework.context.annotation.Configuration
import org.springframework.http.codec.ServerCodecConfigurer
import org.springframework.web.reactive.config.WebFluxConfigurer

@Configuration
internal open class WebFluxConfiguration : WebFluxConfigurer {
override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
configurer.defaultCodecs().maxInMemorySize(8 * 1024 * 1024)
}
}
2 changes: 2 additions & 0 deletions backend/api_gateway/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
spring:
codec:
max-in-memory-size: 8MB
security:
oauth2:
resourceserver:
Expand Down
7 changes: 6 additions & 1 deletion front/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
"experiments": {
"tsconfigPaths": true
},
"plugins": ["expo-localization", "expo-font", "expo-secure-store"]
"plugins": [
"expo-localization",
"expo-font",
"expo-secure-store",
"expo-document-picker"
]
}
}
12 changes: 11 additions & 1 deletion front/assets/translations/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,17 @@
"phoneNumber": "Phone number",
"email": "Email",
"references": "References",
"downloadResume": "Download resume",
"uploadPicture": "Upload profile picture",
"uploadResume": "Upload resume",
"shareResume": "Share resume",
"resumeNotFound": {
"title": "Resume not found",
"message": "You have not uploaded a resume yet."
},
"resumeDownloadError": {
"title": "Download error",
"message": "An error occurred while downloading the resume."
},
"information": "Information",
"firstName": "First name",
"lastName": "Last name",
Expand Down
12 changes: 11 additions & 1 deletion front/assets/translations/fr_FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,17 @@
"phoneNumber": "Numéro de téléphone",
"email": "Email",
"references": "Références",
"downloadResume": "Télécharger le CV",
"uploadPicture": "Modifier la photo de profil",
"uploadResume": "Modifier le CV",
"shareResume": "Partager le CV",
"resumeNotFound": {
"title": "Aucun CV trouvé",
"message": "Vous n'avez pas encore ajouté de CV à votre profil."
},
"resumeDownloadError": {
"title": "Erreur de téléchargement",
"message": "Une erreur est survenue lors du téléchargement de votre CV."
},
"information": "Informations",
"firstName": "Prénom",
"lastName": "Nom",
Expand Down
39 changes: 39 additions & 0 deletions front/package-lock.json

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

6 changes: 5 additions & 1 deletion front/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
"expo-auth-session": "~5.4.0",
"expo-crypto": "~12.8.0",
"expo-font": "~11.10.2",
"expo-image-picker": "~14.7.1",
"expo-localization": "~14.8.3",
"expo-secure-store": "~12.8.1",
"expo-splash-screen": "~0.26.4",
"expo-status-bar": "~1.11.1",
"expo-system-ui": "~2.9.3",
Expand All @@ -44,7 +46,9 @@
"react-native-tab-view": "^3.5.2",
"react-redux": "^9.1.0",
"typescript": "^5.3.3",
"expo-secure-store": "~12.8.1"
"expo-document-picker": "~11.10.1",
"expo-sharing": "~11.10.0",
"expo-file-system": "~16.0.6"
},
"devDependencies": {
"@babel/core": "^7.23.9",
Expand Down
64 changes: 64 additions & 0 deletions front/src/components/profile/ProfileShareResumeButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as FileSystem from 'expo-file-system';
import * as Sharing from 'expo-sharing';
import { useCallback } from 'react';
import { Alert } from 'react-native';
import { Button } from 'react-native-paper';

import { useAppSelector } from '@/store/hooks';
import i18n from '@/utils/i18n';

/**
* Button to share the resume of the user.
* @constructor
*/
export const ProfileShareResumeButton = () => {
// Store hooks
const auth = useAppSelector((state) => state.auth);

// Callbacks
const handleDownloadResume = useCallback(async () => {
if (auth.state !== 'authenticated') {
return;
}

const downloadResult = await FileSystem.downloadAsync(
`${process.env.EXPO_PUBLIC_API_URL}/profile/cv`,
FileSystem.cacheDirectory + 'cv.pdf',
{
headers: {
Authorization: `Bearer ${auth.token}`,
},
},
);

if (downloadResult.status !== 200) {
if (downloadResult.status === 404) {
Alert.alert(
i18n.t('profile.info.resumeNotFound.title'),
i18n.t('profile.info.resumeNotFound.message'),
);
} else {
Alert.alert(
i18n.t('profile.info.resumeDownloadError.title'),
i18n.t('profile.info.resumeDownloadError.message'),
);
}

return;
}

await Sharing.shareAsync(downloadResult.uri);
}, [auth]);

return (
<Button
mode='contained-tonal'
onPress={handleDownloadResume}
icon='file-document-outline'
>
{i18n.t('profile.info.shareResume')}
</Button>
);
};

export default ProfileShareResumeButton;
11 changes: 11 additions & 0 deletions front/src/components/profile/ProfileUpdateInfosForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { FC, useCallback } from 'react';
import { Image, StyleSheet, View, ViewStyle } from 'react-native';
import { Text, TextInput } from 'react-native-paper';

import ProfileUploadPictureButton from '@/components/profile/ProfileUploadPictureButton';
import ProfileUploadResumeButton from '@/components/profile/ProfileUploadResumeButton';
import i18n from '@/utils/i18n';

/**
Expand All @@ -27,6 +29,10 @@ const styles = StyleSheet.create({
textInput: {
marginVertical: 8,
},
uploadButtonContainer: {
gap: 8,
marginVertical: 8,
},
});

/**
Expand Down Expand Up @@ -127,6 +133,11 @@ const ProfileUpdateInfosForm: FC<ProfileUpdateInfosFormProps> = ({
onChangeText={(value) => handleInputChange('shortBiography', value)}
/>

<View style={styles.uploadButtonContainer}>
<ProfileUploadPictureButton />
<ProfileUploadResumeButton />
</View>

<Text variant='headlineMedium' style={styles.sectionTitle}>
{i18n.t('profile.info.contact')}
</Text>
Expand Down
43 changes: 43 additions & 0 deletions front/src/components/profile/ProfileUploadPictureButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as ImagePicker from 'expo-image-picker';
import { useCallback } from 'react';
import { Button } from 'react-native-paper';

import { useUploadProfilePictureMutation } from '@/store/api/profileApiSlice';
import i18n from '@/utils/i18n';

/**
* Button to upload the profile picture of the user.
* @constructor
*/
export const ProfileUploadPictureButton = () => {
// API calls
const [uploadProfilePicture] = useUploadProfilePictureMutation();

// Callbacks
const handleUploadPicture = useCallback(async () => {
const result = await ImagePicker.launchImageLibraryAsync({
allowsEditing: true,
aspect: [1, 1],
exif: false,
quality: 0.4,
});

if (result.canceled === true || result.assets.length === 0) {
return;
}

uploadProfilePicture(result.assets[0].uri);
}, [uploadProfilePicture]);

return (
<Button
mode='contained-tonal'
onPress={handleUploadPicture}
icon='image-plus'
>
{i18n.t('profile.info.uploadPicture')}
</Button>
);
};

export default ProfileUploadPictureButton;
40 changes: 40 additions & 0 deletions front/src/components/profile/ProfileUploadResumeButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as DocumentPicker from 'expo-document-picker';
import { useCallback } from 'react';
import { Button } from 'react-native-paper';

import { useUploadResumeMutation } from '@/store/api/profileApiSlice';
import i18n from '@/utils/i18n';

/**
* Button to upload the resume of the user.
* @constructor
*/
export const ProfileUploadResumeButton = () => {
// API calls
const [uploadResume] = useUploadResumeMutation();

// Callbacks
const handleUploadResume = useCallback(async () => {
const result = await DocumentPicker.getDocumentAsync({
type: 'application/pdf',
});

if (result.canceled === true || result.assets.length === 0) {
return;
}

uploadResume(result.assets[0].uri);
}, [uploadResume]);

return (
<Button
mode='contained-tonal'
onPress={handleUploadResume}
icon='file-upload-outline'
>
{i18n.t('profile.info.uploadResume')}
</Button>
);
};

export default ProfileUploadResumeButton;
8 changes: 2 additions & 6 deletions front/src/components/profile/header/ProfileHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { FC } from 'react';
import { StyleSheet, View } from 'react-native';
import { Button } from 'react-native-paper';

import ProfileShareResumeButton from '@/components/profile/ProfileShareResumeButton';
import ProfileHeaderDescription from '@/components/profile/header/ProfileHeaderDescription';
import ProfileHeaderName from '@/components/profile/header/ProfileHeaderName';
import i18n from '@/utils/i18n';

/**
* The styles for the ProfileHeader component.
Expand Down Expand Up @@ -68,10 +67,7 @@ const ProfileHeader: FC<ProfileHeaderProps> = ({
/>

<ProfileHeaderDescription shortBiography={shortBiography} />

<Button icon='file-document' mode={'contained-tonal'}>
{i18n.t('profile.info.downloadResume')}
</Button>
<ProfileShareResumeButton />
</View>
);
};
Expand Down
10 changes: 10 additions & 0 deletions front/src/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
interface FormDataValue {
uri: string;
name: string;
type: string;
}

interface FormData {
append(name: string, value: FormDataValue, fileName?: string): void;
set(name: string, value: FormDataValue, fileName?: string): void;
}
Loading