This commit is contained in:
“spicecat” 2022-08-18 14:20:19 -04:00
commit fbd04488a8
7 changed files with 148 additions and 8 deletions

View file

@ -7,11 +7,12 @@ import {
ListItemText,
Tooltip,
Typography,
TextField
} from '@mui/material';
import ShareIcon from '@mui/icons-material/Share';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import { Link } from 'react-router-dom';
import { useState } from 'react';
import { Markdown } from 'components';
import { CreationInfoTag } from 'controllers';
import { CommentControl, VoteControl } from 'controllers/QAControllers';
@ -51,8 +52,21 @@ export default function Question({
getVote,
updateVote,
postComment,
canBounty,
handleBounty,
hasBounty,
show,
}) {
const min = 75;
const max = 500;
const [value, setValue] = useState(75);
canBounty = canBounty && !hasBounty
function handleBountyFix(){
handleBounty(value)
}
return (
<>
<Box m={2}>
<Typography variant='h4'>{title}</Typography>
@ -121,11 +135,54 @@ export default function Question({
</span>
</Tooltip>
<Tooltip
title={canBounty ? '' : 'You must be level 4 and this question must be open or protected'}
>
<span>
<Button
disabled={!canBounty}
style={{ marginLeft: '10px' }}
display='inline'
m={1}
variant='contained'
onClick={handleBountyFix}
>
Add Bounty
</Button>
{ show &&
<TextField value = {value} size = "small" type = "number" inputProps={{min,max}} disabled = {!canBounty} label="bounty" onChange={(e) => {
if (e.target.value === "") {
setValue(75);
return;
}
const value = e.target.value;
if (value > max) {
setValue(max);
} else if (value < min) {
setValue(min);
} else {
setValue(value);
}
}}/>
}
</span>
</Tooltip>
{ongoingVote.users.length > 0 && (
<Typography>
{ongoingVote.users.toString()} - voting to {ongoingVote.type} this question{' '}
</Typography>
)}
{hasBounty && (
<Typography>
there is a {hasBounty} point bounty on this question
</Typography>
)}
</Box>
<Divider />

View file

@ -4,7 +4,9 @@ import { Question } from 'components/QAComponents';
import { useQuestion, useUser } from 'contexts';
import { useNavigate, useParams } from 'react-router-dom';
import getPermissions from 'services/getPermissions';
import {
addBounty,
closeQuestion,
getQuestionVote,
openQuestion,
@ -13,17 +15,18 @@ import {
updateQuestion,
updateQuestionVote,
} from 'services/questionsServices';
import { SettingsPhoneTwoTone } from '@mui/icons-material';
export default function QuestionController() {
const navigate = useNavigate();
let canBounty = false;
const { question_id } = useParams();
const { questionData } = useQuestion();
const { protect, close, reopen, status } = questionData;
const { userData } = useUser();
const [ongoingVote, setOngoingVote] = useState({ users: [], type: 'none' });
const { permissions, setPermissions } = useQuestion();
const [show, setShow] = useState(false)
function setVote() {
if (!questionData.loading) {
if (protect.length) setOngoingVote({ users: protect, type: 'protect' });
@ -34,7 +37,9 @@ export default function QuestionController() {
useEffect(() => {
const permissions = getPermissions(questionData, userData, ongoingVote);
if(userData.username)canBounty = (userData.level >= 4 && status !== 'closed' && !questionData.hasBounty && !questionData.hasAcceptedAnswer)
permissions.canBounty = canBounty;
setPermissions(permissions);
setVote();
}, [userData, questionData]);
@ -72,6 +77,17 @@ export default function QuestionController() {
ongoingVoteSet(reopen, 'reopen');
}
}
function handleBounty(amount){
if(show){
const data = {user : userData.username, hasAcceptedAnswer: questionData.hasAcceptedAnswer , amount: amount , isOpen: (questionData.status !== 'closed') , hasBounty: questionData.hasBounty }
addBounty(question_id, data)
questionData.hasBounty = amount;
userData.points -= amount;
setShow(false)
}else{
setShow(true)
}
}
const getVote = () => getQuestionVote(question_id);
const updateVote = (data) => updateQuestionVote(question_id, data);
@ -93,6 +109,9 @@ export default function QuestionController() {
getVote,
updateVote,
postComment,
show,
handleBounty
}}
/>
)}

View file

@ -8,6 +8,13 @@ const postQuestion = async (data) => callQuestionsAPI('post', ``, data);
const getQuestion = async (question_id) => callQuestionsAPI('get', `/${question_id}`);
const addBounty = async (question_id,data) => callQuestionsAPI(
'patch',
`/${question_id}/addBounty`,
data
)
const updateQuestion = async (question_id, data) =>
callQuestionsAPI('patch', `/${question_id}`, data);
@ -103,4 +110,5 @@ export {
closeQuestion,
openQuestion,
protectQuestion,
addBounty
};

View file

@ -22,6 +22,7 @@ const Question = mongoose.Schema(
title: { type: String, required: true },
upvotes: { type: Number, required: true, default: 0 },
views: { type: Number, required: true, default: 0 },
hasBounty: {type: Number, required: false}
},
{ timestamps: { createdAt: false, updatedAt: true } }
);

View file

@ -3,6 +3,7 @@ const router = express.Router();
const tokenAuth = require('server/middleware/tokenAuth');
const HandleBounty = require('./question/HandleBounty');
const CreateAnswer = require('./question/CreateAnswer');
const CreateAnswerComment = require('./question/CreateAnswerComment');
const CreateComment = require('./question/CreateComment');
@ -31,7 +32,7 @@ const GetQuestionVote = require('./question/GetQuestionVote');
const Search = require('./question/Search');
router.get('/search', Search);
router.patch('/:question_id/addBounty', HandleBounty);
router.post('/', tokenAuth, CreateQuestion);
router.get('/:question_id', GetQuestion);
router.patch('/:question_id', tokenAuth, EditQuestion);

View file

@ -11,8 +11,9 @@ async function EditAnswerAccepted(req, res) {
// Find question and verify that it exists
const question = await getQuestion(question_id);
if (!question) return res.status(404).send(config.errorNotFound);
const {hasBounty} = question;
// Verify user owns question and question does not already have an accepted answer
if (question.creator !== user.username || question.hasAccepted)
return res.status(403).send(config.errorForbidden);
@ -41,8 +42,11 @@ async function EditAnswerAccepted(req, res) {
operation: 'increment',
amount: 15,
});
await User.findOneAndUpdate({ username: cachedAnswer.creator }, { $inc: { points: 15 } });
if(hasBounty){
await User.findOneAndUpdate({ username: cachedAnswer.creator }, { $inc: { points: 15 + hasBounty } });
}else{
await User.findOneAndUpdate({ username: cachedAnswer.creator }, { $inc: { points: 15} });
}
return res.sendStatus(200);
}

View file

@ -0,0 +1,50 @@
const Question = require('server/db/models/Question')
const User = require('server/db/models/User')
const createRequest = require('server/utils/api');
async function HandleBounty(req,res){
const {user, hasAcceptedAnswer, amount, isOpen, hasBounty} = req.body;
const {question_id} = req.params;
const userPull = await createRequest(
'get',
`/users/${user}`
);
const questionPull = await createRequest(
'get',
`/questions/${question_id}`
);
const question = questionPull.question;
if(userPull.success){
const {points} = userPull.user;
if(points >= 75 && isOpen && !hasAcceptedAnswer && !hasBounty){
if(amount >= 75 && amount <= 500){
if(points - amount >= 75){
//updates question
await Question.findByIdAndUpdate(question_id, {hasBounty : amount});
//updates user in api
await createRequest('patch', `/users/${user}/points`, {
operation: 'decrement',
amount: amount,
});
//updates user in db
await User.findOneAndUpdate(
{ username: user },
{ $inc: { points: -1 * amount } }
);
}else{
return res.status(403).send("This operation would leave you with less than 75 points")
}
}else{
return res.status(403).send("Must be between 75 and 500 points")
}
}else{
return res.status(403).send("Need to be level 4")
}
}
}
module.exports = HandleBounty;