1
0

feat: emoji reactions for tasks and comments (#2196)

This PR adds reactions for tasks and comments, similar to what you can do on Gitea, GitHub, Slack and plenty of other tools.

Reviewed-on: https://kolaente.dev/vikunja/vikunja/pulls/2196
Co-authored-by: kolaente <k@knt.li>
Co-committed-by: kolaente <k@knt.li>
This commit is contained in:
kolaente
2024-03-12 19:25:58 +00:00
committed by konrad
parent b9c513f681
commit a5c51d4b1e
43 changed files with 1653 additions and 37 deletions

View File

@ -77,19 +77,25 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
case 'post':
if (this.useUpdateInterceptor()) {
config.data = this.beforeUpdate(config.data)
config.data = objectToSnakeCase(config.data)
if(this.autoTransformBeforePost()) {
config.data = objectToSnakeCase(config.data)
}
}
break
case 'put':
if (this.useCreateInterceptor()) {
config.data = this.beforeCreate(config.data)
config.data = objectToSnakeCase(config.data)
if(this.autoTransformBeforePut()) {
config.data = objectToSnakeCase(config.data)
}
}
break
case 'delete':
if (this.useDeleteInterceptor()) {
config.data = this.beforeDelete(config.data)
config.data = objectToSnakeCase(config.data)
if(this.autoTransformBeforeDelete()) {
config.data = objectToSnakeCase(config.data)
}
}
break
}
@ -119,6 +125,22 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
useDeleteInterceptor(): boolean {
return true
}
autoTransformBeforeSend(): boolean {
return true
}
autoTransformBeforePost(): boolean {
return this.autoTransformBeforeSend()
}
autoTransformBeforePut(): boolean {
return this.autoTransformBeforeSend()
}
autoTransformBeforeDelete(): boolean {
return this.autoTransformBeforeSend()
}
/////////////////
// Helper functions
@ -370,6 +392,7 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
const cancel = this.setLoading()
try {
console.log('post', model.reactions)
const response = await this.http.post(url, model)
const result = this.modelUpdateFactory(response.data)
if (typeof model.maxRight !== 'undefined') {

View File

@ -0,0 +1,32 @@
import AbstractService from '@/services/abstractService'
import type {IAbstract} from '@/modelTypes/IAbstract'
import ReactionModel from '@/models/reaction'
import type {IReactionPerEntity} from '@/modelTypes/IReaction'
import UserModel from '@/models/user'
export default class ReactionService extends AbstractService {
constructor() {
super({
getAll: '{kind}/{id}/reactions',
create: '{kind}/{id}/reactions',
delete: '{kind}/{id}/reactions/delete',
})
}
modelFactory(data: Partial<IAbstract>): ReactionModel {
return new ReactionModel(data)
}
modelGetAllFactory(data: Partial<IReactionPerEntity>): Partial<IReactionPerEntity> {
Object.keys(data).forEach(reaction => {
data[reaction] = data[reaction]?.map(u => new UserModel(u))
})
return data
}
async delete(model: IAbstract) {
const finalUrl = this.getReplacedRoute(this.paths.delete, model)
return super.post(finalUrl, model)
}
}

View File

@ -6,6 +6,7 @@ import LabelService from './label'
import {colorFromHex} from '@/helpers/color/colorFromHex'
import {SECONDS_A_DAY, SECONDS_A_HOUR, SECONDS_A_WEEK} from '@/constants/date'
import {objectToSnakeCase} from '@/helpers/case'
const parseDate = date => {
if (date) {
@ -38,8 +39,12 @@ export default class TaskService extends AbstractService<ITask> {
return this.processModel(model)
}
autoTransformBeforePost(): boolean {
return false
}
processModel(updatedModel) {
const model = { ...updatedModel }
const model = {...updatedModel}
model.title = model.title?.trim()
@ -108,7 +113,15 @@ export default class TaskService extends AbstractService<ITask> {
model.labels = model.labels.map(l => labelService.processModel(l))
}
return model as ITask
const transformed = objectToSnakeCase(model)
// We can't convert emojis to skane case, hence we add them back again
transformed.reactions = {}
Object.keys(updatedModel.reactions || {}).forEach(reaction => {
transformed.reactions[reaction] = updatedModel.reactions[reaction].map(u => objectToSnakeCase(u))
})
return transformed as ITask
}
}

View File

@ -1,6 +1,7 @@
import AbstractService from './abstractService'
import TaskCommentModel from '@/models/taskComment'
import type {ITaskComment} from '@/modelTypes/ITaskComment'
import {objectToSnakeCase} from '@/helpers/case'
export default class TaskCommentService extends AbstractService<ITaskComment> {
constructor() {
@ -16,4 +17,22 @@ export default class TaskCommentService extends AbstractService<ITaskComment> {
modelFactory(data) {
return new TaskCommentModel(data)
}
autoTransformBeforePost(): boolean {
return false
}
beforeUpdate(model: ITaskComment) {
const transformed = objectToSnakeCase({...model})
// We can't convert emojis to skane case, hence we add them back again
transformed.reactions = {}
Object.keys(model.reactions || {}).forEach(reaction => {
transformed.reactions[reaction] = model.reactions[reaction].map(u => objectToSnakeCase(u))
})
console.log()
return transformed as ITaskComment
}
}