Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added import and export functionality #274

Closed
wants to merge 1 commit into from
Closed
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
108 changes: 103 additions & 5 deletions components/Nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@ import { useTranslation } from 'next-i18next'
import Menu from './icons/Menu'
import Close from './icons/Close'
import useDeviceDetect from '../hooks/useDeviceDetect'
import { useEffect, useState, useRef } from 'react'
import useLocalStorage from '../hooks/useLocalStorage'

export const Nav = ({
sectionTemplates,
selectedSectionSlugs,
setSelectedSectionSlugs,
setSectionSlugs,
templates,
setTemplates,
setShowModal,
getTemplate,
onMenuClick,
Expand All @@ -15,6 +22,20 @@ export const Nav = ({
setDarkMode,
focusedSectionSlug,
}) => {
const [files, setFiles] = useState([])
const { saveBackup } = useLocalStorage()
const fileInput = useRef(null)

/**
* Enum for handling supported file type exports.
* @readonly
* @enum string
*/
const FileExports = Object.freeze({
JSON: 'json',
MD: 'md',
})

const markdown = selectedSectionSlugs.reduce((acc, section) => {
const template = getTemplate(section)
if (template) {
Expand All @@ -26,18 +47,67 @@ export const Nav = ({

const { isMobile } = useDeviceDetect()

const downloadMarkdownFile = () => {
const downloadFile = (fileExtension) => {
const selected_slugs = localStorage.getItem('current-slug-list')
const export_template = templates.filter((t) => selected_slugs.includes(t.slug))

const a = document.createElement('a')
const blob = new Blob([markdown])
const blob = new Blob(
fileExtension == 'md'
? [markdown]
: fileExtension == 'json'
? [JSON.stringify(export_template)]
: []
)

a.href = URL.createObjectURL(blob)
a.download = 'README.md'
a.download = `README.${fileExtension}`
a.click()

if (isMobile && isDrawerOpen) {
onMenuClick()
}
setShowModal(true)
}

const uploadJSON = (e) => {
const fileReader = new FileReader()
fileReader.readAsText(e.target.files[0], 'UTF-8')
fileReader.onload = (e) => {
setFiles(JSON.parse(e.target.result))
}
}

const validate_files = () => {
return files.filter((f) => {
const required_keys = ['slug', 'name', 'markdown']
const keys = Object.keys(f)

if (keys.length != required_keys.length) {
return false
}

return keys.every((key) => required_keys.includes(key))
})
}

useEffect(() => {
if (files.length > 0) {
const files = validate_files()
const selected_slugs = files.map((f) => f.slug)
const slugs = sectionTemplates.map((s) => s.slug).filter((s) => !selected_slugs.includes(s))

const temp = templates.filter((t) => !selected_slugs.includes(t.slug))
templates = [...temp, ...files]

localStorage.setItem('current-slug-list', selected_slugs.join(','))
setSelectedSectionSlugs(selected_slugs)
setSectionSlugs(slugs)
saveBackup(templates)
setTemplates(templates)
}
}, [files])

const { t } = useTranslation('editor')

return (
Expand All @@ -47,7 +117,7 @@ export const Nav = ({
<img className="w-auto h-12" src="readme.svg" alt="readme.so logo" />
</a>
</Link>
<div className="flex flex-row-reverse md:flex-row">
<div className="flex flex-row-reverse gap-3 md:flex-row">
{/* visible for sm only */}
<button
className="focus:outline-none focus:ring-2 focus:ring-emerald-400"
Expand Down Expand Up @@ -76,11 +146,39 @@ export const Nav = ({
</button>
)}

<input
type="file"
accept=".json"
className="hidden"
ref={fileInput}
onChange={uploadJSON}
/>

<button
type="button"
aria-label="Import"
className="flex flex-row relative items-center mr-4 md:mr-0 px-4 py-2 text-sm font-bold tracking-wide text-white border border-transparent rounded-md shadow-sm bg-emerald-500 hover:bg-emerald-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-emerald-500"
onClick={() => fileInput.current.click()}
>
<img className="w-auto h-6 cursor-pointer" src="download.svg" />
<span className="hidden md:inline-block ml-2">{t('nav-import')}</span>
</button>

<button
type="button"
aria-label="Export"
className="flex flex-row relative items-center mr-4 md:mr-0 px-4 py-2 text-sm font-bold tracking-wide text-white border border-transparent rounded-md shadow-sm bg-emerald-500 hover:bg-emerald-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-emerald-500"
onClick={() => downloadFile(FileExports.JSON)}
>
<img className="w-auto h-6 cursor-pointer" src="download.svg" />
<span className="hidden md:inline-block ml-2">{t('nav-export')}</span>
</button>

<button
type="button"
aria-label="Download Markdown"
className="flex flex-row relative items-center mr-4 md:mr-0 px-4 py-2 text-sm font-bold tracking-wide text-white border border-transparent rounded-md shadow-sm bg-emerald-500 hover:bg-emerald-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-emerald-500"
onClick={downloadMarkdownFile}
onClick={() => downloadFile(FileExports.MD)}
>
<img className="w-auto h-6 cursor-pointer" src="download.svg" />
<span className="hidden md:inline-block ml-2">{t('nav-download')}</span>
Expand Down
5 changes: 5 additions & 0 deletions pages/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,18 @@ export default function Editor({ sectionTemplates }) {
</Head>

<Nav
sectionTemplates={sectionTemplates}
selectedSectionSlugs={selectedSectionSlugs}
setSelectedSectionSlugs={setSelectedSectionSlugs}
setSectionSlugs={setSectionSlugs}
setShowModal={setShowModal}
setTemplates={setTemplates}
getTemplate={getTemplate}
onMenuClick={() => toggleDrawer(!showDrawer)}
isDrawerOpen={showDrawer}
darkMode={darkMode}
setDarkMode={setDarkMode}
templates={templates}
focusedSectionSlug={focusedSectionSlug}
/>
{showModal && <DownloadModal setShowModal={setShowModal} />}
Expand Down
2 changes: 2 additions & 0 deletions public/locales/en/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"editor-desktop-optimized": "This site is optimized for desktop",
"editor-visit-desktop": "Please visit readme.so on a desktop to create your readme!",
"nav-download": "Download",
"nav-export": "Export",
"nav-import": "Import",
"download-readme-generated": "Readme Generated!",
"download-reach-out": "Thanks for using readme.so! Feel free to reach out to me on",
"download-feedback": "with any feedback.",
Expand Down