mirror of
https://gitlab.com/MrFry/qmining-page
synced 2025-04-01 20:23:44 +02:00
606 lines
17 KiB
JavaScript
606 lines
17 KiB
JavaScript
import React, { useState, useEffect, useCallback } from 'react'
|
||
import Link from 'next/link'
|
||
|
||
import LoadingIndicator from '../components/LoadingIndicator'
|
||
import Modal from '../components/modal'
|
||
import SearchBar from '../components/searchBar'
|
||
import UpDownVote from '../components/upDownVote'
|
||
import Header from '../components/header'
|
||
|
||
import styles from './userfiles.module.css'
|
||
import constants from '../constants.json'
|
||
|
||
function vote(to, item) {
|
||
return new Promise((resolve, reject) => {
|
||
fetch(`${constants.apiUrl}voteFile`, {
|
||
method: 'POST',
|
||
credentials: 'include',
|
||
headers: {
|
||
Accept: 'application/json',
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
path: item.path,
|
||
to: to,
|
||
}),
|
||
})
|
||
.then((resp) => {
|
||
return resp.json()
|
||
})
|
||
.then((res) => {
|
||
if (res.success) {
|
||
resolve(res)
|
||
} else {
|
||
reject(res)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
function listUserDir(subdir) {
|
||
return new Promise((resolve, reject) => {
|
||
fetch(
|
||
`${constants.apiUrl}listUserDir${subdir ? `?subdir=${subdir}` : ''}`,
|
||
{
|
||
credentials: 'include',
|
||
}
|
||
)
|
||
.then((resp) => {
|
||
return resp.json()
|
||
})
|
||
.then((res) => {
|
||
if (res.success) {
|
||
resolve(res)
|
||
} else {
|
||
reject(res)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
function FileUploader({ onChange, uploading }) {
|
||
return (
|
||
<div>
|
||
<div className={styles.text}>
|
||
<b>
|
||
Fontos a névtelenség, ezért figyelj rá milyen személyes adatokat
|
||
tartalmaz a feltöltött dokumentum!
|
||
</b>
|
||
<p />
|
||
Amit feltöltesz minden felhasználó lát. Csak ide illő tartalmat ossz
|
||
meg.
|
||
<p />
|
||
</div>
|
||
{uploading ? (
|
||
<LoadingIndicator />
|
||
) : (
|
||
<input type="file" name="file" onChange={onChange} />
|
||
)}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
function SubjNameInput({ onChange, uploading }) {
|
||
return (
|
||
<>
|
||
<div className={styles.text}>
|
||
Tárgyat kérlek úgy nevezd el, hogy: [tárgy neve] - [egyetem rövid
|
||
azonosító], szóval pl: Elektronika - OE.
|
||
<p />
|
||
Üres mappák időnként törölve lesznek.
|
||
</div>
|
||
<p />
|
||
{uploading ? (
|
||
<LoadingIndicator />
|
||
) : (
|
||
<div>
|
||
<input
|
||
autoFocus
|
||
placeholder={'Új tárgy neve'}
|
||
type="text"
|
||
onChange={(e) => {
|
||
onChange(e.target.value)
|
||
}}
|
||
/>
|
||
</div>
|
||
)}
|
||
</>
|
||
)
|
||
}
|
||
|
||
function deleteFile(currDir, name) {
|
||
return new Promise((resolve) => {
|
||
fetch(constants.apiUrl + 'deleteUserFile', {
|
||
method: 'POST',
|
||
credentials: 'include',
|
||
headers: {
|
||
Accept: 'application/json',
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
dir: currDir,
|
||
fname: name,
|
||
}),
|
||
})
|
||
.then((res) => {
|
||
return res.json()
|
||
})
|
||
.then((res) => {
|
||
if (res.success) {
|
||
resolve(res)
|
||
} else {
|
||
alert(res.msg)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
function newSubj(name) {
|
||
return new Promise((resolve) => {
|
||
fetch(constants.apiUrl + 'newUserDir', {
|
||
method: 'POST',
|
||
credentials: 'include',
|
||
headers: {
|
||
Accept: 'application/json',
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
name: name,
|
||
}),
|
||
})
|
||
.then((res) => {
|
||
return res.json()
|
||
})
|
||
.then((res) => {
|
||
if (res.success) {
|
||
resolve(res)
|
||
} else {
|
||
alert(res.msg)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
function uploadFile(dir, file) {
|
||
return new Promise((resolve) => {
|
||
const formData = new FormData() // eslint-disable-line
|
||
formData.append('file', file)
|
||
formData.append('dir', dir)
|
||
|
||
fetch(constants.apiUrl + 'uploadUserFile', {
|
||
method: 'POST',
|
||
credentials: 'include',
|
||
headers: {
|
||
Accept: 'application/json',
|
||
},
|
||
body: formData,
|
||
})
|
||
.then((res) => {
|
||
return res.json()
|
||
})
|
||
.then((res) => {
|
||
resolve(res)
|
||
})
|
||
})
|
||
}
|
||
|
||
export default function UserFiles({
|
||
router,
|
||
globalData,
|
||
globalState,
|
||
setGlobalState,
|
||
}) {
|
||
const userId = globalData.userId
|
||
const [dirs, setDirs] = useState()
|
||
const [sortBy, setSortBy] = useState('name')
|
||
const [sortDirection, setSortDirection] = useState(true)
|
||
const [addingNew, setAddingNew] = useState()
|
||
const [newSubjName, setNewSubjName] = useState()
|
||
const [file, setFile] = useState()
|
||
const [searchTerm, setSearchTerm] = useState()
|
||
const [uploading, setUploading] = useState(false)
|
||
const [votingDisabled, setVotingDisabled] = useState(false)
|
||
|
||
const currDir = router.query.dir ? decodeURIComponent(router.query.dir) : ''
|
||
|
||
const goBack = useCallback(() => {
|
||
router.back()
|
||
// FIXME: consistend going back with browser back button
|
||
//
|
||
// back button works like broser back button, unless it would result in site leave
|
||
// history: nothing > opened site/usrFiles?dir=...
|
||
// history: site > site/userFiles > site/usrFiles?dir=...
|
||
router.push(`${router.pathname}`, undefined, {
|
||
shallow: true,
|
||
})
|
||
}, [router])
|
||
|
||
const deleteDirectory = useCallback(
|
||
(dirName) => {
|
||
fetch(constants.apiUrl + 'deleteDir', {
|
||
method: 'POST',
|
||
credentials: 'include',
|
||
headers: {
|
||
Accept: 'application/json',
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
name: dirName,
|
||
}),
|
||
})
|
||
.then((res) => {
|
||
return res.json()
|
||
})
|
||
.then((res) => {
|
||
console.info(res)
|
||
if (res.succes) {
|
||
getRootDir(true)
|
||
alert(`a(z) '${dirName}' mappa törölve!`)
|
||
} else {
|
||
alert('Hiba törlés közben!')
|
||
}
|
||
})
|
||
.catch((e) => {
|
||
alert('Hálózati hiba!')
|
||
})
|
||
},
|
||
[router]
|
||
)
|
||
|
||
useEffect(() => {
|
||
router.replace(`${router.asPath.replace('.html', '')}`, undefined, {
|
||
shallow: true,
|
||
})
|
||
}, [])
|
||
|
||
useEffect(() => {
|
||
const dir = router.query.dir ? decodeURIComponent(router.query.dir) : ''
|
||
setDirs(null)
|
||
setSearchTerm()
|
||
|
||
if (router.query.dir) {
|
||
getDir(dir)
|
||
} else {
|
||
getRootDir()
|
||
}
|
||
}, [router.query.dir])
|
||
|
||
const getDir = (dir, nocache) => {
|
||
if (!nocache && globalState.userFiles && globalState.userFiles[dir]) {
|
||
setDirs(globalState.userFiles[dir])
|
||
} else {
|
||
listUserDir(dir)
|
||
.then((res) => {
|
||
setDirs(res.files)
|
||
setGlobalState({
|
||
userFiles: {
|
||
...globalState.userFiles,
|
||
[dir]: res.files,
|
||
},
|
||
})
|
||
})
|
||
.catch((res) => {
|
||
alert(res.msg)
|
||
})
|
||
}
|
||
}
|
||
|
||
const getRootDir = (nocache) => {
|
||
if (!nocache && globalState.userFilesRoot) {
|
||
setDirs(globalState.userFilesRoot)
|
||
} else {
|
||
listUserDir()
|
||
.then((res) => {
|
||
setGlobalState({
|
||
userFilesRoot: res.dirs,
|
||
})
|
||
setDirs(res.dirs)
|
||
})
|
||
.catch((res) => {
|
||
alert(res.msg)
|
||
})
|
||
}
|
||
}
|
||
|
||
const dirSorter = (a, b) => {
|
||
if (a[sortBy] < b[sortBy]) {
|
||
return sortDirection ? -1 : 1
|
||
} else if (a[sortBy] > b[sortBy]) {
|
||
return sortDirection ? 1 : -1
|
||
} else {
|
||
return 0
|
||
}
|
||
}
|
||
|
||
const handleVote = (type, dir) => {
|
||
setVotingDisabled(true)
|
||
vote(type, dir)
|
||
.then((res) => {
|
||
setDirs(res.files)
|
||
setVotingDisabled(false)
|
||
})
|
||
.catch((res) => {
|
||
alert(res.msg)
|
||
setVotingDisabled(false)
|
||
})
|
||
}
|
||
|
||
const renderNewButton = () => (
|
||
<div className={`buttonContainer ${styles.newButton}`}>
|
||
<div
|
||
onClick={() => {
|
||
setAddingNew(currDir ? 'file' : 'dir')
|
||
}}
|
||
>
|
||
{currDir ? 'Új fájl feltöltése...' : '➕ Új tárgy hozzáadása...'}
|
||
</div>
|
||
</div>
|
||
)
|
||
|
||
const renderDirList = (dirs) => {
|
||
return (
|
||
<div>
|
||
{currDir && (
|
||
<div className={styles.title}>
|
||
{currDir && <div className={styles.currDir}>{currDir}</div>}
|
||
</div>
|
||
)}
|
||
<div style={{ display: 'flex' }}>
|
||
<div style={{ flexGrow: 1 }}>
|
||
<SearchBar
|
||
searchTerm={searchTerm}
|
||
onChange={(e) => {
|
||
setSearchTerm(e)
|
||
}}
|
||
/>
|
||
</div>
|
||
{renderNewButton()}
|
||
</div>
|
||
<div className={`${styles.tableContainer} ${styles.header}`}>
|
||
<div
|
||
onClick={(e) => {
|
||
const name = e.target.getAttribute('name')
|
||
if (name) {
|
||
if (sortBy === name) {
|
||
setSortDirection(!sortDirection)
|
||
} else {
|
||
setSortDirection(true)
|
||
}
|
||
setSortBy(name)
|
||
}
|
||
}}
|
||
>
|
||
<div name="name">Fájl név</div>
|
||
<div name="date">Feltöltés dátuma</div>
|
||
<div name="size">Méret</div>
|
||
<div name="upCount">Hasznos</div>
|
||
<div name="views">Letöltések</div>
|
||
<div name="user">Feltöltő user</div>
|
||
</div>
|
||
</div>
|
||
{dirs ? (
|
||
<div className={`${styles.tableContainer} ${styles.rows}`}>
|
||
{currDir && (
|
||
<div
|
||
style={{ height: 40, justifyContent: "center", display: "flex" }}
|
||
onClick={() => {
|
||
goBack()
|
||
}}
|
||
>
|
||
{'Vissza'}
|
||
</div>
|
||
)}
|
||
{dirs.length !== 0 ? (
|
||
<>
|
||
{dirs.sort(dirSorter).map((dir) => {
|
||
const {
|
||
name,
|
||
date,
|
||
path,
|
||
size,
|
||
views,
|
||
user,
|
||
upvotes,
|
||
downvotes,
|
||
} = dir
|
||
|
||
if (
|
||
searchTerm &&
|
||
!name.toLowerCase().includes(searchTerm.toLowerCase())
|
||
) {
|
||
return null
|
||
}
|
||
return (
|
||
<div
|
||
title={name}
|
||
key={name}
|
||
onClick={() => {
|
||
if (path) {
|
||
window.open(`${constants.apiUrl}${path}`, '_blank')
|
||
} else {
|
||
setDirs(undefined)
|
||
router.push(
|
||
`${router.pathname}?dir=${encodeURIComponent(
|
||
name
|
||
)}`,
|
||
undefined,
|
||
{ shallow: true }
|
||
)
|
||
}
|
||
}}
|
||
>
|
||
<div>{name}</div>
|
||
<div title={new Date(date).toLocaleString()}>
|
||
{new Date(date).toLocaleDateString()}
|
||
</div>
|
||
<div>
|
||
{currDir
|
||
? (size / 1000000).toFixed(2).toString() + ' MB'
|
||
: size + ' fájl'}
|
||
</div>
|
||
<div>
|
||
{Array.isArray(upvotes) && Array.isArray(downvotes) && (
|
||
<UpDownVote
|
||
onUp={() => {
|
||
handleVote('up', dir)
|
||
}}
|
||
onDown={() => {
|
||
handleVote('down', dir)
|
||
}}
|
||
onClear={() => {
|
||
handleVote('clear', dir)
|
||
}}
|
||
upvotes={upvotes}
|
||
downvotes={downvotes}
|
||
userId={userId}
|
||
disabled={votingDisabled}
|
||
/>
|
||
)}
|
||
</div>
|
||
<div>{!isNaN(views) && `${views} db`}</div>
|
||
<div>
|
||
{user &&
|
||
user !== -1 &&
|
||
(userId === user ? (
|
||
<div
|
||
className={styles.deleteButton}
|
||
onClick={(e) => {
|
||
e.stopPropagation()
|
||
if (confirm(`Biztos törlöd '${name}'-t ?`)) {
|
||
deleteFile(currDir, name).then(() => {
|
||
getDir(currDir, true)
|
||
})
|
||
}
|
||
}}
|
||
>
|
||
Törlés
|
||
</div>
|
||
) : (
|
||
<Link href={`/chat?user=${user}`}>
|
||
<a
|
||
title={`Chat #${user}-el`}
|
||
onClick={(e) => {
|
||
e.stopPropagation()
|
||
}}
|
||
className={'userId'}
|
||
>{`#${user}`}</a>
|
||
</Link>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)
|
||
})}
|
||
</>
|
||
) : (
|
||
<div
|
||
title={'A megnyitott mappa törlése, mivel üres'}
|
||
onClick={() => {
|
||
if (
|
||
window.confirm(
|
||
`Biztosan törlöd a(z) '${currDir}' üres mappát?`
|
||
)
|
||
) {
|
||
deleteDirectory(currDir)
|
||
goBack()
|
||
}
|
||
}}
|
||
>
|
||
{'Mappa törlése'}
|
||
</div>
|
||
)}
|
||
</div>
|
||
) : (
|
||
<LoadingIndicator />
|
||
)}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div>
|
||
<Header title={'ZH-k/Vizsgák, segédanyagok'} />
|
||
<div className="pageHeader">
|
||
<h1>ZH-k/Vizsgák, segédanyagok</h1>
|
||
</div>
|
||
{!currDir && (
|
||
<div className={styles.description}>
|
||
Itt lehet megosztani mindenféle tanulást segítő doksit (kidolgozott
|
||
tételektől puskáig mindent).
|
||
<br />
|
||
Ez gyakorlatilag egy fájl böngésző, a tárgyak a mappák, és azon belül
|
||
lehet fájlokat feltölteni.
|
||
<br />A feltöltött fájlok ellenőrizve vannak, hogy ide valóak-e, de{' '}
|
||
<b>
|
||
hibás információért, vírusokért és hasonlókért semmi felelősség
|
||
vállalás nincs.
|
||
</b>{' '}
|
||
Ha valami nem idevalót látsz, azt a{' '}
|
||
<Link href="/contact">
|
||
<a>Kapcsolat</a>
|
||
</Link>{' '}
|
||
oldalon jelezd kérlek. Tudatos károkozásért ban jár.
|
||
</div>
|
||
)}
|
||
<hr />
|
||
{renderDirList(dirs)}
|
||
{addingNew && (
|
||
<Modal
|
||
closeClick={() => {
|
||
setAddingNew(null)
|
||
}}
|
||
>
|
||
<div className={styles.uploadContainer}>
|
||
{addingNew === 'file' ? (
|
||
<>
|
||
<FileUploader
|
||
uploading={uploading}
|
||
onChange={(e) => {
|
||
setFile(e.target.files[0])
|
||
}}
|
||
/>
|
||
<div
|
||
className="buttonContainer"
|
||
onClick={() => {
|
||
setUploading(true)
|
||
uploadFile(currDir, file).then(() => {
|
||
setUploading(false)
|
||
setAddingNew(null)
|
||
getDir(currDir, true)
|
||
})
|
||
}}
|
||
>
|
||
<div>Feltöltés</div>
|
||
</div>
|
||
</>
|
||
) : (
|
||
<>
|
||
<SubjNameInput
|
||
uploading={uploading}
|
||
onChange={(e) => {
|
||
setNewSubjName(e)
|
||
}}
|
||
/>
|
||
<div
|
||
className="buttonContainer"
|
||
onClick={() => {
|
||
setUploading(true)
|
||
newSubj(newSubjName).then(() => {
|
||
setUploading(false)
|
||
setAddingNew(null)
|
||
getRootDir(true)
|
||
alert(`'${newSubjName}' létrehozva!`)
|
||
})
|
||
}}
|
||
>
|
||
<div>
|
||
{addingNew === 'file' ? 'OK' : 'Új mappa létrehozása'}
|
||
</div>
|
||
</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
</Modal>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|