mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-13 16:22:59 +10:00
- implement donation link, in about section
This commit is contained in:
@ -7,29 +7,38 @@ function timeout(ms) {
|
|||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.printSinglePageResume = functions.https.onCall(
|
exports.printResume = functions.https.onCall(async ({ id, type }, { auth }) => {
|
||||||
async ({ id }, { auth }) => {
|
if (!id) {
|
||||||
if (!id) {
|
throw new functions.https.HttpsError(
|
||||||
throw new functions.https.HttpsError(
|
'invalid-argument',
|
||||||
'invalid-argument',
|
'The function must be called with argument "id" containing the resume ID.',
|
||||||
'The function must be called with one arguments "id" containing the resume ID.',
|
);
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!auth) {
|
if (!type || type !== 'single' || type !== 'multi') {
|
||||||
throw new functions.https.HttpsError(
|
throw new functions.https.HttpsError(
|
||||||
'failed-precondition',
|
'invalid-argument',
|
||||||
'The function must be called while authenticated.',
|
'The function must be called with argument "type" containing the type of resume.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const browser = await puppeteer.launch({
|
if (!auth) {
|
||||||
headless: true,
|
throw new functions.https.HttpsError(
|
||||||
});
|
'failed-precondition',
|
||||||
const page = await browser.newPage();
|
'The function must be called while authenticated.',
|
||||||
await page.goto(BASE_URL + id);
|
);
|
||||||
await timeout(5000);
|
}
|
||||||
await page.emulateMediaType('print');
|
|
||||||
|
const browser = await puppeteer.launch({
|
||||||
|
headless: true,
|
||||||
|
});
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.goto(BASE_URL + id);
|
||||||
|
await timeout(5000);
|
||||||
|
await page.emulateMediaType('print');
|
||||||
|
let pdf;
|
||||||
|
|
||||||
|
if (type === 'single') {
|
||||||
const height = await page.evaluate(() => {
|
const height = await page.evaluate(() => {
|
||||||
const { body } = document;
|
const { body } = document;
|
||||||
const html = document.documentElement;
|
const html = document.documentElement;
|
||||||
@ -44,45 +53,19 @@ exports.printSinglePageResume = functions.https.onCall(
|
|||||||
|
|
||||||
return maxHeight;
|
return maxHeight;
|
||||||
});
|
});
|
||||||
const pdf = await page.pdf({
|
pdf = await page.pdf({
|
||||||
printBackground: true,
|
printBackground: true,
|
||||||
width: `21cm`,
|
width: `21cm`,
|
||||||
height: `${height}px`,
|
height: `${height}px`,
|
||||||
pageRanges: '1',
|
pageRanges: '1',
|
||||||
});
|
});
|
||||||
await browser.close();
|
} else if (type === 'multi') {
|
||||||
return Buffer.from(pdf).toString('base64');
|
pdf = await page.pdf({
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
exports.printMultiPageResume = functions.https.onCall(
|
|
||||||
async ({ id }, { auth }) => {
|
|
||||||
if (!id) {
|
|
||||||
throw new functions.https.HttpsError(
|
|
||||||
'invalid-argument',
|
|
||||||
'The function must be called with one arguments "id" containing the resume ID.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!auth) {
|
|
||||||
throw new functions.https.HttpsError(
|
|
||||||
'failed-precondition',
|
|
||||||
'The function must be called while authenticated.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const browser = await puppeteer.launch({
|
|
||||||
headless: true,
|
|
||||||
});
|
|
||||||
const page = await browser.newPage();
|
|
||||||
await page.goto(BASE_URL + id);
|
|
||||||
await timeout(5000);
|
|
||||||
await page.emulateMediaType('print');
|
|
||||||
const pdf = await page.pdf({
|
|
||||||
format: 'A4',
|
format: 'A4',
|
||||||
printBackground: true,
|
printBackground: true,
|
||||||
});
|
});
|
||||||
await browser.close();
|
}
|
||||||
return Buffer.from(pdf).toString('base64');
|
|
||||||
},
|
await browser.close();
|
||||||
);
|
return Buffer.from(pdf).toString('base64');
|
||||||
|
});
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import Layout from './sections/Layout';
|
|||||||
import Templates from './sections/Templates';
|
import Templates from './sections/Templates';
|
||||||
import Actions from './sections/Actions';
|
import Actions from './sections/Actions';
|
||||||
import Settings from './sections/Settings';
|
import Settings from './sections/Settings';
|
||||||
|
import About from './sections/About';
|
||||||
|
|
||||||
const getComponent = (id) => {
|
const getComponent = (id) => {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
@ -24,6 +25,8 @@ const getComponent = (id) => {
|
|||||||
return Actions;
|
return Actions;
|
||||||
case 'settings':
|
case 'settings':
|
||||||
return Settings;
|
return Settings;
|
||||||
|
case 'about':
|
||||||
|
return About;
|
||||||
default:
|
default:
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
|
|||||||
44
src/components/builder/right/sections/About.js
Normal file
44
src/components/builder/right/sections/About.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import React, { memo } from 'react';
|
||||||
|
import { FaCoffee } from 'react-icons/fa';
|
||||||
|
import Button from '../../../shared/Button';
|
||||||
|
import Heading from '../../../shared/Heading';
|
||||||
|
import styles from './About.module.css';
|
||||||
|
|
||||||
|
const About = () => {
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<Heading>About</Heading>
|
||||||
|
|
||||||
|
<div className={styles.container}>
|
||||||
|
<h5>Donate to Reactive Resume</h5>
|
||||||
|
|
||||||
|
<p className="leading-loose">
|
||||||
|
As you know, every nook and cranny of this app is free and
|
||||||
|
open-source, but servers don't pay for themselves.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className="leading-loose">
|
||||||
|
I try to do what I can, but if you found the app helpful, or
|
||||||
|
you're in a better position than the others who depend on this
|
||||||
|
project for their first job, please consider donating{' '}
|
||||||
|
<span className="font-semibold">
|
||||||
|
as little as $5 to help keep the project alive
|
||||||
|
</span>{' '}
|
||||||
|
:)
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="mt-4 flex">
|
||||||
|
<a
|
||||||
|
href="https://www.buymeacoffee.com/AmruthPillai"
|
||||||
|
rel="noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<Button icon={FaCoffee}>Buy me a coffee!</Button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(About);
|
||||||
11
src/components/builder/right/sections/About.module.css
Normal file
11
src/components/builder/right/sections/About.module.css
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
.container {
|
||||||
|
@apply bg-primary-100 rounded grid gap-5 p-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container h5 {
|
||||||
|
@apply text-lg font-semibold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container p {
|
||||||
|
@apply text-sm font-medium;
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ import {
|
|||||||
MdDashboard,
|
MdDashboard,
|
||||||
MdFontDownload,
|
MdFontDownload,
|
||||||
MdImportExport,
|
MdImportExport,
|
||||||
|
MdInfo,
|
||||||
MdSettings,
|
MdSettings,
|
||||||
MdStyle,
|
MdStyle,
|
||||||
} from 'react-icons/md';
|
} from 'react-icons/md';
|
||||||
@ -38,4 +39,9 @@ export default [
|
|||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
icon: MdSettings,
|
icon: MdSettings,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'about',
|
||||||
|
name: 'About',
|
||||||
|
icon: MdInfo,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -38,20 +38,16 @@ const ExportModal = () => {
|
|||||||
|
|
||||||
const handleSinglePageDownload = async () => {
|
const handleSinglePageDownload = async () => {
|
||||||
setLoadingSingle(true);
|
setLoadingSingle(true);
|
||||||
const printSinglePageResume = firebase
|
const printResume = firebase.functions().httpsCallable('printResume');
|
||||||
.functions()
|
const { data } = await printResume({ id: state.id, type: 'single' });
|
||||||
.httpsCallable('printSinglePageResume');
|
|
||||||
const { data } = await printSinglePageResume({ id: state.id });
|
|
||||||
const blob = b64toBlob(data, 'application/pdf');
|
const blob = b64toBlob(data, 'application/pdf');
|
||||||
openFile(blob);
|
openFile(blob);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMultiPageDownload = async () => {
|
const handleMultiPageDownload = async () => {
|
||||||
setLoadingMulti(true);
|
setLoadingMulti(true);
|
||||||
const printMultiPageResume = firebase
|
const printResume = firebase.functions().httpsCallable('printResume');
|
||||||
.functions()
|
const { data } = await printResume({ id: state.id, type: 'multi' });
|
||||||
.httpsCallable('printMultiPageResume');
|
|
||||||
const { data } = await printMultiPageResume({ id: state.id });
|
|
||||||
const blob = b64toBlob(data, 'application/pdf');
|
const blob = b64toBlob(data, 'application/pdf');
|
||||||
openFile(blob);
|
openFile(blob);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user