diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 38be91f..619e6de 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -25,7 +25,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v2
with:
- node-version: "16"
+ node-version: "20"
- name: Install dependencies
run: npm ci
diff --git a/locales/en.json b/locales/en.json
index 68ea126..f505f65 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -17,6 +17,7 @@
"hidden": "Hidden",
"administrator": "Administrator",
"moderator": "Moderator",
+ "donor": "Donator",
"play_log": "Play Log",
"registered_on": "Registered",
@@ -43,6 +44,8 @@
"select_font": "Select Font",
"images": "Images",
+ "donators": "Donator Benefits",
+ "name_color": "Name Colors",
"home": "Home",
"profile": "Profile",
diff --git a/locales/en_uk.json b/locales/en_uk.json
index cee5152..bc1c675 100644
--- a/locales/en_uk.json
+++ b/locales/en_uk.json
@@ -17,6 +17,7 @@
"hidden": "Hidden",
"administrator": "Administrator",
"moderator": "Moderator",
+ "donor": "Donator",
"play_log": "Play Log",
"registered_on": "Registered",
@@ -43,6 +44,8 @@
"select_font": "Select Font",
"images": "Images",
+ "donators": "Donator Benefits",
+ "name_color": "Name Colours",
"home": "Home",
"profile": "Profile",
diff --git a/locales/jp.json b/locales/jp.json
index eb3f74e..211258f 100644
--- a/locales/jp.json
+++ b/locales/jp.json
@@ -16,6 +16,7 @@
"hidden": "非表示",
"administrator": "管理者",
"moderator": "モデレーター",
+ "donor": "寄贈者",
"play_log": "プレイログ",
"registered_on": "登録日",
@@ -42,6 +43,8 @@
"select_font": "フォントを選択",
"images": "画像",
+ "donators": "寄贈者",
+ "name_color": "名前カラー",
"home": "ホーム",
"leaderboard": "リーダーボード",
diff --git a/prisma/migrations/20240801065036_donors/migration.sql b/prisma/migrations/20240801065036_donors/migration.sql
new file mode 100644
index 0000000..9f105d0
--- /dev/null
+++ b/prisma/migrations/20240801065036_donors/migration.sql
@@ -0,0 +1,3 @@
+-- AlterTable
+ALTER TABLE "user" ADD COLUMN "isDonor" BOOLEAN NOT NULL DEFAULT false;
+ALTER TABLE "user" ADD COLUMN "nameColor" TEXT NOT NULL DEFAULT '#000000';
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index c99015a..223ca69 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -126,10 +126,12 @@ model user {
created_at DateTime @default(dbgenerated("CURRENT_TIMESTAMP(3)"))
updated_at DateTime @default(dbgenerated("CURRENT_TIMESTAMP(3)"))
badge String? @db.VarChar(50)
+ language String @default("en") @db.VarChar(11)
isBanned Int @default(0) @db.SmallInt
isPublic Int @default(1) @db.SmallInt
publicOverride Int? @db.SmallInt
- language String @default("en") @db.VarChar(11)
+ isDonor Boolean @default(false)
+ nameColor String @default("#000000")
accounts accounts[]
banned_user banned_user[]
game_sessions game_sessions[]
diff --git a/src/components/account/DonorButton.jsx b/src/components/account/DonorButton.jsx
new file mode 100644
index 0000000..78b87a2
--- /dev/null
+++ b/src/components/account/DonorButton.jsx
@@ -0,0 +1,42 @@
+import { React } from 'react'
+import { useRouter } from 'next/router'
+import { toast } from 'react-toastify'
+import useInfo from '@/lib/swr-hooks/useInfo'
+import { Button } from 'react-bootstrap'
+import PropTypes from 'prop-types'
+
+export default function DonorButton ({ isDonor, id }) {
+ const router = useRouter()
+ const { mutate } = useInfo()
+
+ const setDonor = async (status) => {
+ const response = await fetch('/api/account/donor', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ status,
+ user: id
+ })
+ })
+ if (response.status === 200) {
+ toast.success('The account\'s donor status has been updated.')
+ mutate()
+ router.reload()
+ } else {
+ toast.error('An error occured, please try again later.')
+ }
+ }
+
+ return (
+
+ )
+}
+
+DonorButton.propTypes = {
+ id: PropTypes.number.isRequired,
+ isDonor: PropTypes.bool.isRequired
+}
diff --git a/src/components/edit/DonorsCard.jsx b/src/components/edit/DonorsCard.jsx
new file mode 100644
index 0000000..bc78afb
--- /dev/null
+++ b/src/components/edit/DonorsCard.jsx
@@ -0,0 +1,81 @@
+import React, { useState } from 'react'
+import PropTypes from 'prop-types'
+import { Alert, Card, Col, Row } from 'react-bootstrap'
+import LocalizedString from '../shared/LocalizedString'
+import LanguageContext from '../shared/LanguageContext'
+
+import styles from './DonorsCard.module.css'
+
+function ColorBox ({ color, onClick, current }) {
+ return (
+
onClick(e, color)} />
+ )
+}
+
+function ColorPicker ({ current, setColor }) {
+ const [color, setColorState] = useState(current)
+
+ return (
+
+
{ setColorState(e.target.value); setColor(e, e.target.value) }} />
+
Custom
+
+ )
+}
+
+ColorPicker.propTypes = {
+ current: PropTypes.string.isRequired,
+ setColor: PropTypes.func.isRequired
+}
+
+ColorBox.propTypes = {
+ color: PropTypes.string.isRequired,
+ onClick: PropTypes.func.isRequired,
+ current: PropTypes.string.isRequired
+}
+
+function DonorsCard ({ values, errors, handleChange }) {
+ const setColor = (e, color) => {
+ values.nameColor = color
+ handleChange(e)
+ }
+
+ return (
+
+ {(lang) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {errors.overlay && (
+
+ {errors.overlay}
+
+ )}
+
+
+
+
+ )}
+
+ )
+}
+
+DonorsCard.propTypes = {
+ values: PropTypes.object.isRequired,
+ errors: PropTypes.object.isRequired,
+ handleChange: PropTypes.func.isRequired
+}
+
+export default DonorsCard
diff --git a/src/components/edit/DonorsCard.module.css b/src/components/edit/DonorsCard.module.css
new file mode 100644
index 0000000..c80233a
--- /dev/null
+++ b/src/components/edit/DonorsCard.module.css
@@ -0,0 +1,31 @@
+.colorBox {
+ width: 48px;
+ height: 48px;
+ margin-left: 8px;
+ margin-top: 8px;
+ border-radius: 8px;
+}
+
+.colorPicker {
+ width: 48px;
+ height: 48px;
+ margin-left: 8px;
+ margin-top: 8px;
+}
+
+.colorPicker input {
+ height: 100%
+}
+
+.colorPicker p {
+ font-size: 10px;
+ text-align: center;
+ width: 48px;
+ color: #DDD;
+}
+
+.colorBoxes {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap
+}
diff --git a/src/components/edit/ImagesCard.jsx b/src/components/edit/ImagesCard.jsx
index 96986e0..bd5da0f 100644
--- a/src/components/edit/ImagesCard.jsx
+++ b/src/components/edit/ImagesCard.jsx
@@ -19,7 +19,7 @@ const backgrounds = BACKGROUNDS.map((background) => ({
label: background
}))
-function ImagesCard ({ values, errors, handleChange }) {
+function ImagesCard ({ values, errors, handleChange, username }) {
return (
{(lang) => (
@@ -66,12 +66,35 @@ function ImagesCard ({ values, errors, handleChange }) {
{errors.background}
)}
+
+ {
+ const formData = new FormData()
+ formData.append('file', event.currentTarget.files[0])
+
+ values.background = `${username}.png`
+
+ return fetch('/api/account/background', {
+ method: 'POST',
+ body: formData
+ })
+ }}
+ />
+
+
+ Please ensure that your image is 1200x450 and is in PNG format.
+
+
@@ -134,7 +157,8 @@ function ImagesCard ({ values, errors, handleChange }) {
ImagesCard.propTypes = {
values: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired,
- handleChange: PropTypes.func.isRequired
+ handleChange: PropTypes.func.isRequired,
+ username: PropTypes.string.isRequired
}
export default ImagesCard
diff --git a/src/components/shared/AppNavbar.jsx b/src/components/shared/AppNavbar.jsx
index 72ee9d5..a604077 100644
--- a/src/components/shared/AppNavbar.jsx
+++ b/src/components/shared/AppNavbar.jsx
@@ -41,7 +41,7 @@ function AppNavbar () {
diff --git a/src/components/user/UserInformationCard.jsx b/src/components/user/UserInformationCard.jsx
index c6c34bb..2a9e999 100644
--- a/src/components/user/UserInformationCard.jsx
+++ b/src/components/user/UserInformationCard.jsx
@@ -17,6 +17,7 @@ import BanAccountButton from '../account/BanAccountButton'
import ForceHiddenAccountButton from '../account/ForceHiddenAccountButton'
import LanguageContext from '../shared/LanguageContext'
import LocalizedString from '../shared/LocalizedString'
+import DonorButton from '@/components/account/DonorButton'
function UserInformationCard ({ user, isLoggedIn, isAdmin, isMod }) {
return (
@@ -29,6 +30,7 @@ function UserInformationCard ({ user, isLoggedIn, isAdmin, isMod }) {
{(user.publicOverride === 0 || (user.publicOverride === 1 && isMod)) && ()}
{user.role === 'admin' && ()}
{user.role === 'mod' && ()}
+ {user.isDonor === true && ()}
-
: {user.display_name}
@@ -37,7 +39,7 @@ function UserInformationCard ({ user, isLoggedIn, isAdmin, isMod }) {
: {OVERLAYS.find((overlay) => overlay.value === user.overlay).label}
-
- : {user.background}
+ : {!Number.isNaN(Number(user.background.replace(/.*\//, '').replace(/\.png$/, ''))) ? 'Custom' : user.background}
-
: {COINS.find((coin) => coin.value === user.coin).label}
@@ -71,6 +73,7 @@ function UserInformationCard ({ user, isLoggedIn, isAdmin, isMod }) {
+
)}
diff --git a/src/lib/constants/filePaths.js b/src/lib/constants/filePaths.js
index 236de4e..a669676 100644
--- a/src/lib/constants/filePaths.js
+++ b/src/lib/constants/filePaths.js
@@ -12,7 +12,8 @@ export const CACHE = Object.freeze({
COVER: path.resolve(CACHE_PATH, 'cover'),
MIIS: path.resolve(CACHE_PATH, 'mii', 'user'),
TAGS: path.resolve(CACHE_PATH, 'tags'),
- WADS: path.resolve(CACHE_PATH, 'wads')
+ WADS: path.resolve(CACHE_PATH, 'wads'),
+ BACKGROUNDS: path.resolve(CACHE_PATH, 'backgrounds')
})
export const PUBLIC = Object.freeze({
diff --git a/src/lib/riitag/neo/std/Background.ts b/src/lib/riitag/neo/std/Background.ts
index 5c55391..4523523 100644
--- a/src/lib/riitag/neo/std/Background.ts
+++ b/src/lib/riitag/neo/std/Background.ts
@@ -1,4 +1,4 @@
-import { PUBLIC } from '@/lib/constants/filePaths'
+import { PUBLIC, CACHE } from '@/lib/constants/filePaths'
import path from 'node:path'
import Canvas from 'canvas'
import fs from 'node:fs'
@@ -7,7 +7,7 @@ import logger from '@/lib/logger'
export default class Background extends ModuleBase {
async render (ctx: Canvas.CanvasRenderingContext2D, user): Promise {
- const bgPath = path.resolve(PUBLIC.BACKGROUND, user.background)
+ const bgPath = path.resolve(!Number.isNaN(Number(user.background.replace(/.*\//, '').replace(/\.png$/, ''))) ? CACHE.BACKGROUNDS : PUBLIC.BACKGROUND, user.background)
if (!fs.existsSync(bgPath)) {
logger.error(`Background image does not exist: ${bgPath}`)
diff --git a/src/lib/riitag/neo/std/Username.ts b/src/lib/riitag/neo/std/Username.ts
index 28320c6..f18af36 100644
--- a/src/lib/riitag/neo/std/Username.ts
+++ b/src/lib/riitag/neo/std/Username.ts
@@ -33,6 +33,7 @@ export default class Username extends ModuleBase {
logger.info(`User Font: ${user.font}`)
logger.info(`Font Info: ${this.font.name} ${this.font.size} ${this.font.style} ${this.font.color} ${this.font.force}`)
+ if (user.isDonor === true) this.font.color = user.nameColor
drawText(ctx, this.font, user.display_name, this.x, this.y, this.align)
}
}
diff --git a/src/lib/utils/fileUtils.ts b/src/lib/utils/fileUtils.ts
index b246dd9..9d321c2 100644
--- a/src/lib/utils/fileUtils.ts
+++ b/src/lib/utils/fileUtils.ts
@@ -3,11 +3,12 @@ import path from 'node:path'
import logger from '@/lib/logger'
import { Readable } from 'stream'
import { finished } from 'node:stream/promises'
+import { Buffer } from 'buffer'
-export const exists = async (filename) =>
+export const exists = async (filename: string) =>
!!(await fs.promises.stat(filename).catch(() => null))
-export async function saveFile (filepath, file: any | null) {
+export async function saveFile (filepath: string, file: any | null) {
if (file == null) return
if (!(await exists(filepath))) {
@@ -20,3 +21,17 @@ export async function saveFile (filepath, file: any | null) {
logger.info('File saved successfully')
}
+
+export async function saveFileBuffer (filepath: string, file: Buffer) {
+ logger.info(`Saving file to ${filepath}`)
+ if (!(await exists(filepath))) {
+ await fs.promises.mkdir(path.dirname(filepath), { recursive: true })
+ }
+
+ try {
+ await fs.promises.writeFile(filepath, file)
+ logger.info('File saved successfully')
+ } catch (error) {
+ logger.error('Error saving the file:', error)
+ }
+}
diff --git a/src/pages/api/account/background.ts b/src/pages/api/account/background.ts
new file mode 100644
index 0000000..70dd3af
--- /dev/null
+++ b/src/pages/api/account/background.ts
@@ -0,0 +1,112 @@
+import { IncomingForm, Fields, Files } from 'formidable'
+import { readFile } from 'node:fs/promises'
+import path from 'node:path'
+import { ncWithSession } from '@/lib/routing'
+import HTTP_CODE from '@/lib/constants/httpStatusCodes'
+import { saveFileBuffer } from '@/lib/utils/fileUtils'
+import { CACHE } from '@/lib/constants/filePaths'
+import prisma from '@/lib/db'
+import { makeBanner } from '@/lib/riitag/banner'
+import logger from '@/lib/logger'
+import { Request, Response } from 'express'
+import { setFileHeaders } from '../../../lib/utils/utils'
+import fs from 'node:fs'
+
+async function postBackground (request: Request, response: Response) {
+ if (request.socket.bytesRead > 2_107_638) {
+ return response
+ .status(HTTP_CODE.REQUEST_ENTITY_TOO_LARGE)
+ .send({ error: 'Request entity too large.' })
+ }
+
+ // @ts-ignore
+ const username: string = request.session?.username
+
+ if (!username) {
+ return response
+ .status(HTTP_CODE.UNAUTHORIZED)
+ .json({ error: 'Unauthorized' })
+ }
+
+ const data: unknown = await new Promise((resolve, reject): void => {
+ const form = new IncomingForm()
+
+ form.parse(request, (error, fields: Fields, files: Files): void => {
+ if (error) return reject(error)
+ return resolve({ fields, files })
+ })
+ })
+ .catch((error) => {
+ logger.error(error)
+ return response
+ .status(HTTP_CODE.BAD_REQUEST)
+ .send({ error: 'Invalid data' })
+ })
+
+ // @ts-ignore
+ const { file } = data.files
+
+ if (file.mimetype !== 'image/png') {
+ return response
+ .status(HTTP_CODE.BAD_REQUEST)
+ .send({ error: 'Invalid data' })
+ }
+
+ // Hard cap of 2MBs for custom backgrounds
+ if (file.size > 2_000_000) {
+ return response
+ .status(HTTP_CODE.REQUEST_ENTITY_TOO_LARGE)
+ .send({ error: 'Request entity too large.' })
+ }
+
+ let user: {username: string} = await prisma.user.findFirst({
+ where: {
+ username
+ },
+ select: {
+ username: true
+ }
+ })
+
+ const filepath: string = path.resolve(CACHE.BACKGROUNDS, `${user.username}.png`)
+ await saveFileBuffer(filepath, await readFile(file.filepath))
+
+ user = await prisma.user.update({
+ where: {
+ username
+ },
+ data: {
+ background: `${user.username}.png`
+ }
+ })
+
+ await makeBanner(user)
+ return response.status(HTTP_CODE.OK).send()
+}
+
+async function getBackground (request: Request, response: Response) {
+ // @ts-ignore
+ const username = request.session?.username
+
+ if (!username) {
+ return response
+ .status(HTTP_CODE.UNAUTHORIZED)
+ .json({ error: 'Unauthorized' })
+ }
+
+ response.setHeader('Content-Type', 'image/png')
+ setFileHeaders(response, `${username}.png`)
+ return response
+ .status(HTTP_CODE.OK)
+ .send(await fs.promises.readFile(path.resolve(CACHE.BACKGROUNDS, username + '.png')))
+}
+
+const handler = ncWithSession().post(postBackground).get(getBackground)
+
+export const config = {
+ api: {
+ bodyParser: false
+ }
+}
+
+export default handler
diff --git a/src/pages/api/account/donor.js b/src/pages/api/account/donor.js
new file mode 100644
index 0000000..aeead83
--- /dev/null
+++ b/src/pages/api/account/donor.js
@@ -0,0 +1,50 @@
+import HTTP_CODE from '@/lib/constants/httpStatusCodes'
+import { ncWithSession } from '@/lib/routing'
+import { userIsMod } from '@/lib/utils/databaseUtils'
+import prisma from '@/lib/db'
+import { isBlank } from '@/lib/utils/utils'
+import { doRender } from '@/lib/riitag/neo/renderer'
+
+async function exportData (request, response) {
+ const loggedInUser = request.session?.username
+ const {
+ status,
+ user
+ } = request.body
+
+ if (
+ isBlank(String(status)) || isBlank(String(user))
+ ) {
+ return response
+ .status(HTTP_CODE.BAD_REQUEST)
+ .send({ error: 'Invalid data' })
+ }
+
+ if (!loggedInUser) {
+ return response
+ .status(HTTP_CODE.UNAUTHORIZED)
+ .json({ error: 'Unauthorized' })
+ }
+
+ if (!(await userIsMod(loggedInUser))) {
+ return response
+ .status(HTTP_CODE.UNAUTHORIZED)
+ .json({ error: 'Unauthorized' })
+ }
+
+ const userObj = await prisma.user.update({
+ data: {
+ isDonor: status
+ },
+ where: {
+ id: user
+ }
+ })
+
+ doRender(userObj)
+ return response.status(HTTP_CODE.OK).send(null)
+}
+
+const handler = ncWithSession().post(exportData)
+
+export default handler
diff --git a/src/pages/api/account/tag.js b/src/pages/api/account/tag.js
index c159056..b416278 100644
--- a/src/pages/api/account/tag.js
+++ b/src/pages/api/account/tag.js
@@ -4,7 +4,6 @@ import { isBlank, isBoolean } from '@/lib/utils/utils'
import { isValidCoverRegion } from '@/lib/constants/forms/coverRegions'
import { isValidCoverType } from '@/lib/constants/forms/coverTypes'
import { isValidOverlay } from '@/lib/constants/forms/overlays'
-import { BACKGROUNDS } from '@/lib/constants/forms/backgrounds'
import { isValidFlag } from '@/lib/constants/forms/flags'
import { isValidCoin } from '@/lib/constants/forms/coins'
import { isValidFont } from '@/lib/constants/forms/fonts'
@@ -24,30 +23,31 @@ async function updateTagSettings (request, response) {
background,
flag,
coin,
- font
+ font,
+ nameColor
} = request.body
const username = request.session?.username
- function validateFriendCode () {
- if (!request.body.comment) {
- return true
- }
-
- return true
- }
-
- if (!validateFriendCode()) {
- return response
- .status(HTTP_CODE.BAD_REQUEST)
- .send({ error: 'Invalid data' })
- }
-
if (!username) {
return response
.status(HTTP_CODE.UNAUTHORIZED)
.json({ error: 'Unauthorized' })
}
+ if (nameColor) {
+ if (nameColor.length !== 7) {
+ return response
+ .status(HTTP_CODE.BAD_REQUEST)
+ .send({ error: 'Invalid data' })
+ }
+
+ if (!(/(#[a-fA-F0-9]{6})/.test(nameColor))) {
+ return response
+ .status(HTTP_CODE.BAD_REQUEST)
+ .send({ error: 'Invalid data' })
+ }
+ }
+
if (
isBlank(nameOnRiiTag) ||
isBlank(coverRegion) ||
@@ -62,12 +62,12 @@ async function updateTagSettings (request, response) {
!isValidCoverType(coverType) ||
!isValidCoverRegion(coverRegion) ||
!isValidOverlay(overlay) ||
- BACKGROUNDS.includes(background) === false ||
!isValidFlag(flag) ||
!isValidCoin(coin) ||
!isValidFont(font) ||
!isBoolean(showAvatar) ||
- !isBoolean(showMii)
+ !isBoolean(showMii) ||
+ isBlank(nameColor)
) {
return response
.status(HTTP_CODE.BAD_REQUEST)
@@ -90,7 +90,8 @@ async function updateTagSettings (request, response) {
coin,
font,
show_avatar: +showAvatar,
- show_mii: +showMii
+ show_mii: +showMii,
+ nameColor
}
})
await renderTag(user)
diff --git a/src/pages/edit.jsx b/src/pages/edit.jsx
index 4ed0e7c..d7b07fd 100644
--- a/src/pages/edit.jsx
+++ b/src/pages/edit.jsx
@@ -14,13 +14,13 @@ import { isValidOverlay } from '@/lib/constants/forms/overlays'
import { isValidFlag } from '@/lib/constants/forms/flags'
import { isValidCoin } from '@/lib/constants/forms/coins'
import { isValidFont } from '@/lib/constants/forms/fonts'
-import { BACKGROUNDS } from '@/lib/constants/forms/backgrounds'
import GeneralCard from '@/components/edit/GeneralCard'
import FontCard from '@/components/edit/FontCard'
import ImagesCard from '@/components/edit/ImagesCard'
import ENV from '@/lib/constants/environmentVariables'
import LanguageContext from '@/components/shared/LanguageContext'
import AppNavbar from '@/components/shared/AppNavbar'
+import DonorsCard from '@/components/edit/DonorsCard'
export const getServerSideProps = withSession(async ({ req }) => {
// get the current user session
@@ -43,7 +43,9 @@ export const getServerSideProps = withSession(async ({ req }) => {
coin: true,
font: true,
show_avatar: true,
- show_mii: true
+ show_mii: true,
+ nameColor: true,
+ isDonor: true
}
})
: null
@@ -57,10 +59,10 @@ export const getServerSideProps = withSession(async ({ req }) => {
}
}
- return { props: { tagInfo: session, language: session?.language || 'en' } }
+ return { props: { tagInfo: session, user: sessionAccount, language: session?.language || 'en' } }
})
-function EditPage ({ tagInfo, language }) {
+function EditPage ({ tagInfo, username, language }) {
tagInfo.show_avatar = Boolean(tagInfo.show_avatar)
tagInfo.show_mii = Boolean(tagInfo.show_mii)
@@ -79,7 +81,8 @@ function EditPage ({ tagInfo, language }) {
background: tagInfo.background,
flag: tagInfo.flag,
coin: tagInfo.coin,
- font: tagInfo.font
+ font: tagInfo.font,
+ nameColor: tagInfo.nameColor
}}
validate={(values) => {
const errors = {}
@@ -114,8 +117,6 @@ function EditPage ({ tagInfo, language }) {
if (!values.background) {
errors.background = 'Required'
- } else if (BACKGROUNDS.includes(values.background) === false) {
- errors.background = 'Invalid Background'
}
if (!values.flag) {
@@ -153,12 +154,12 @@ function EditPage ({ tagInfo, language }) {
render ({ data, toastProps }) {
if (data.status !== 200) {
toastProps.type = 'error'
- return 'An error occured, please try again later'
+ return 'An error occurred, please try again later'
}
return 'Saved!'
}
},
- error: 'An error occured, please try again later.'
+ error: 'An error occurred, please try again later.'
}
)
@@ -204,9 +205,17 @@ function EditPage ({ tagInfo, language }) {
+
+ {tagInfo.isDonor &&
+ }
@@ -219,6 +228,7 @@ function EditPage ({ tagInfo, language }) {
EditPage.propTypes = {
tagInfo: PropTypes.object.isRequired,
+ username: PropTypes.string.isRequired,
language: PropTypes.string.isRequired
}
diff --git a/src/pages/user/[username]/index.jsx b/src/pages/user/[username]/index.jsx
index 6c1d748..fa07ef4 100644
--- a/src/pages/user/[username]/index.jsx
+++ b/src/pages/user/[username]/index.jsx
@@ -41,6 +41,7 @@ export const getServerSideProps = withSession(async ({ req, query }) => {
role: true,
isBanned: true,
isPublic: true,
+ isDonor: true,
publicOverride: true,
banned_user: true,
playlog: {
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..d55eb30
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "compilerOptions": {
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": false,
+ "noEmit": true,
+ "incremental": true,
+ "module": "esnext",
+ "esModuleInterop": true,
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve"
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}