mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
* autoselect badges when rewarding and the suer still have free slots * improve semantics --------- Co-authored-by: Hendrik-cpu <62690517+Hendrik-cpu@users.noreply.github.com> Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
171 lines
5.3 KiB
TypeScript
171 lines
5.3 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
/* eslint-disable @typescript-eslint/require-await */
|
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
import { neo4jgraphql } from 'neo4j-graphql-js'
|
|
|
|
import { TROPHY_BADGES_SELECTED_MAX } from '@constants/badges'
|
|
import { Context } from '@src/server'
|
|
|
|
export const defaultTrophyBadge = {
|
|
id: 'default_trophy',
|
|
type: 'trophy',
|
|
icon: '/img/badges/default_trophy.svg',
|
|
description: '',
|
|
createdAt: '',
|
|
}
|
|
|
|
export const defaultVerificationBadge = {
|
|
id: 'default_verification',
|
|
type: 'verification',
|
|
icon: '/img/badges/default_verification.svg',
|
|
description: '',
|
|
createdAt: '',
|
|
}
|
|
|
|
export default {
|
|
Query: {
|
|
Badge: async (object, args, context, resolveInfo) =>
|
|
neo4jgraphql(object, args, context, resolveInfo),
|
|
},
|
|
|
|
Mutation: {
|
|
setVerificationBadge: async (_object, args, context, _resolveInfo) => {
|
|
const {
|
|
user: { id: currentUserId },
|
|
} = context
|
|
const { badgeId, userId } = args
|
|
const session = context.driver.session()
|
|
|
|
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
|
const response = await transaction.run(
|
|
`
|
|
MATCH (badge:Badge {id: $badgeId, type: 'verification'}), (user:User {id: $userId})
|
|
OPTIONAL MATCH (:Badge {type: 'verification'})-[verify:VERIFIES]->(user)
|
|
DELETE verify
|
|
MERGE (badge)-[relation:VERIFIES {by: $currentUserId}]->(user)
|
|
RETURN relation, user {.*}
|
|
`,
|
|
{
|
|
badgeId,
|
|
userId,
|
|
currentUserId,
|
|
},
|
|
)
|
|
return {
|
|
relation: response.records.map((record) => record.get('relation'))[0],
|
|
user: response.records.map((record) => record.get('user'))[0],
|
|
}
|
|
})
|
|
try {
|
|
const { relation, user } = await writeTxResultPromise
|
|
if (!relation) {
|
|
throw new Error(
|
|
'Could not reward badge! Ensure the user and the badge exist and the badge is of the correct type.',
|
|
)
|
|
}
|
|
return user
|
|
} catch (error) {
|
|
throw new Error(error)
|
|
} finally {
|
|
session.close()
|
|
}
|
|
},
|
|
|
|
rewardTrophyBadge: async (_object, args, context: Context, _resolveInfo) => {
|
|
const {
|
|
user: { id: currentUserId },
|
|
} = context
|
|
const { badgeId, userId } = args
|
|
|
|
// Find used slot
|
|
const userBadges = (
|
|
await context.database.query({
|
|
query: `
|
|
MATCH (rewardedBadge:Badge)-[rewarded:REWARDED]->(user:User {id: $userId})
|
|
OPTIONAL MATCH (rewardedBadge)<-[selected:SELECTED]-(user)
|
|
RETURN collect(rewardedBadge {.*}) AS rewardedBadges, collect(toString(selected.slot)) AS usedSlots
|
|
`,
|
|
variables: { userId },
|
|
})
|
|
).records.map((record) => {
|
|
return {
|
|
rewardedBadges: record.get('rewardedBadges'),
|
|
usedSlots: record.get('usedSlots'),
|
|
}
|
|
})
|
|
|
|
const { rewardedBadges, usedSlots } = userBadges[0]
|
|
|
|
let slot
|
|
if (
|
|
!rewardedBadges.find((item) => item.id === badgeId) && // badge was not rewarded yet
|
|
usedSlots.length < TROPHY_BADGES_SELECTED_MAX // there is free slots left
|
|
) {
|
|
for (slot = 0; slot <= TROPHY_BADGES_SELECTED_MAX; slot++) {
|
|
if (!usedSlots.find((item) => parseInt(item) === slot)) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// reward badge and assign slot
|
|
const users = (
|
|
await context.database.write({
|
|
query: `
|
|
MATCH (badge:Badge {id: $badgeId, type: 'trophy'}), (user:User {id: $userId})
|
|
MERGE (badge)-[:REWARDED {by: $currentUserId}]->(user)
|
|
${slot === undefined ? '' : 'MERGE (badge)<-[:SELECTED {slot: $slot}]-(user)'}
|
|
RETURN user {.*}
|
|
`,
|
|
variables: { badgeId, userId, currentUserId, slot },
|
|
})
|
|
).records.map((record) => record.get('user'))
|
|
|
|
if (users.length !== 1) {
|
|
throw new Error(
|
|
'Could not reward badge! Ensure the user and the badge exist and the badge is of the correct type.',
|
|
)
|
|
}
|
|
|
|
return users[0]
|
|
},
|
|
|
|
revokeBadge: async (_object, args, context, _resolveInfo) => {
|
|
const { badgeId, userId } = args
|
|
const session = context.driver.session()
|
|
|
|
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
|
const response = await transaction.run(
|
|
`
|
|
MATCH (user:User {id: $userId})
|
|
OPTIONAL MATCH (badge:Badge {id: $badgeId})-[rewarded:REWARDED|VERIFIES]->(user)
|
|
OPTIONAL MATCH (user)-[selected:SELECTED]->(badge)
|
|
DELETE rewarded
|
|
DELETE selected
|
|
RETURN user {.*}
|
|
`,
|
|
{
|
|
badgeId,
|
|
userId,
|
|
},
|
|
)
|
|
return response.records.map((record) => record.get('user'))[0]
|
|
})
|
|
try {
|
|
return await writeTxResultPromise
|
|
} catch (error) {
|
|
throw new Error(error)
|
|
} finally {
|
|
session.close()
|
|
}
|
|
},
|
|
},
|
|
Badge: {
|
|
isDefault: async (parent, _params, _context, _resolveInfo) =>
|
|
[defaultTrophyBadge.id, defaultVerificationBadge.id].includes(parent.id),
|
|
},
|
|
}
|