Files
habitica/website/client/store/actions/tasks.js
T
Matteo Pagliazzi 33b249d078 Notifications v2 and Bailey API (#9716)
* Added initial bailey api

* wip

* implement new panel header

* Fixed lint

* add ability to mark notification as seen

* add notification count, remove top badge from user and add ability to mark multiple notifications as seen

* add support dismissall and mark all as read

* do not dismiss actionable notif

* mark as seen when menu is opened instead of closed

* implement ordering, list of actionable notifications

* add groups messages and fix badges count

* add notifications for received cards

* send card received notification to target not sender

* rename notificaion field

* fix integration tests

* mark cards notifications as read and update tests

* add mystery items notifications

* add unallocated stats points notifications

* fix linting

* simplify code

* refactoring and fixes

* fix dropdown opening

* start splitting notifications into their own component

* add notifications for inbox messages

* fix unit tests

* fix default buttons styles

* add initial bailey support

* add title and tests to new stuff notification

* add notification if a group task needs more work

* add tests and fixes for marking a task as needing more work

* make sure user._v is updated

* remove console.log

* notification: hover status and margins

* start styling notifications, add separate files and basic functionalities

* fix tests

* start adding mystery items notification

* wip card notification

* fix cards text

* initial implementation inbox messages

* initial implementation group messages

* disable inbox notifications until mobile is ready

* wip group chat messages

* finish mystery and card notifications

* add bailey notification and fix a lot of stuff

* start adding guilds and parties invitations

* misc invitation fixes

* fix lint issues

* remove old code and add key to notifications

* fix tests

* remove unused code

* add link for public guilds invite

* starts to implement needs work notification design and feature

* fixes to needs work, add group task approved notification

* finish needs work feature

* lots of fixes

* implement quest notification

* bailey fixes and static page

* routing fixes

* fixes #      this.$store.dispatch(guilds:join, {groupId: group.id, type: party});

* read notifications on click

* chat notifications

* fix tests for chat notifications

* fix chat notification test

* fix tests

* fix tests (again)

* try awaiting

* remove only

* more sleep

* add bailey tests

* fix icons alignment

* fix issue with multiple points notifications

* remove merge code

* fix rejecting guild invitation

* make remove area bigger

* fix error with notifications and add migration

* fix migration

* fix typos

* add cleanup migration too

* notifications empty state, new counter color, fix marking messages as seen in guilds

* fixes

* add image and install correct packages

* fix mongoose version

* update bailey

* typo

* make sure chat is marked as read after other requests
2018-01-31 11:55:39 +01:00

239 lines
7.4 KiB
JavaScript

import { loadAsyncResource } from 'client/libs/asyncResource';
import axios from 'axios';
import compact from 'lodash/compact';
import omit from 'lodash/omit';
export function fetchUserTasks (store, options = {}) {
return loadAsyncResource({
store,
path: 'tasks',
url: '/api/v3/tasks/user',
deserialize (response) {
// Wait for the user to be loaded before deserializing
// because user.tasksOrder is necessary
return store.dispatch('user:fetch').then((userResource) => {
return store.dispatch('tasks:order', [response.data.data, userResource.data.tasksOrder]);
});
},
forceLoad: options.forceLoad,
});
}
export async function fetchCompletedTodos (store, forceLoad = false) {
// Wait for the user to be loaded before deserializing
// because user.tasksOrder is necessary
await store.dispatch('tasks:fetchUserTasks');
const loadStatus = store.state.completedTodosStatus;
if (loadStatus === 'NOT_LOADED' || forceLoad) {
store.state.completedTodosStatus = 'LOADING';
const response = await axios.get('/api/v3/tasks/user?type=completedTodos');
const completedTodos = response.data.data;
const tasks = store.state.tasks.data;
// Remove existing completed todos
tasks.todos = tasks.todos.filter(t => !t.completed);
tasks.todos.push(...completedTodos);
store.state.completedTodosStatus = 'LOADED';
} else if (status === 'LOADED') {
return;
} else if (loadStatus === 'LOADING') {
const watcher = store.watch(state => state.completedTodosStatus, (newLoadingStatus) => {
watcher(); // remove the watcher
if (newLoadingStatus === 'LOADED') {
return;
} else {
throw new Error(); // TODO add reason?
}
});
}
}
export async function clearCompletedTodos (store) {
await axios.post('/api/v3/tasks/clearCompletedTodos');
store.state.tasks.data.todos = store.state.tasks.data.todos.filter(task => {
return !task.completed;
});
}
export function order (store, [rawTasks, tasksOrder]) {
const tasks = {
habits: [],
dailys: [],
todos: [],
rewards: [],
};
rawTasks.forEach(task => {
tasks[`${task.type}s`].push(task);
});
Object.keys(tasks).forEach((type) => {
let tasksOfType = tasks[type];
const orderOfType = tasksOrder[type];
const orderedTasks = new Array(tasksOfType.length);
const unorderedTasks = []; // what we want to add later
tasksOfType.forEach((task, index) => {
const taskId = task._id;
const i = orderOfType[index] === taskId ? index : orderOfType.indexOf(taskId);
if (i === -1) {
unorderedTasks.push(task);
} else {
orderedTasks[i] = task;
}
});
tasks[type] = compact(orderedTasks).concat(unorderedTasks);
});
return tasks;
}
function sanitizeChecklist (task) {
if (task.checklist) {
task.checklist = task.checklist.filter((i) => {
return Boolean(i.text);
});
}
}
// Supply an array to create multiple tasks
export async function create (store, createdTask) {
// Treat all create actions as if we are adding multiple tasks
const payload = Array.isArray(createdTask) ? createdTask : [createdTask];
payload.forEach(t => {
const type = `${t.type}s`;
const list = store.state.tasks.data[type];
sanitizeChecklist(t);
list.unshift(t);
store.state.user.data.tasksOrder[type].unshift(t._id);
});
const response = await axios.post('/api/v3/tasks/user', payload);
const data = Array.isArray(response.data.data) ? response.data.data : [response.data.data];
data.forEach(taskRes => {
const taskData = store.state.tasks.data[`${taskRes.type}s`].find(t => t._id === taskRes._id);
Object.assign(taskData, taskRes);
});
}
export async function save (store, editedTask) {
const taskId = editedTask._id;
const type = editedTask.type;
const originalTask = store.state.tasks.data[`${type}s`].find(t => t._id === taskId);
sanitizeChecklist(editedTask);
if (originalTask) Object.assign(originalTask, editedTask);
const taskDataToSend = omit(editedTask, ['history']);
const response = await axios.put(`/api/v3/tasks/${taskId}`, taskDataToSend);
if (originalTask) Object.assign(originalTask, response.data.data);
}
export async function scoreChecklistItem (store, {taskId, itemId}) {
await axios.post(`/api/v3/tasks/${taskId}/checklist/${itemId}/score`);
}
export async function collapseChecklist (store, task) {
task.collapseChecklist = !task.collapseChecklist;
await axios.put(`/api/v3/tasks/${task._id}`, {
collapseChecklist: task.collapseChecklist,
});
}
export async function destroy (store, task) {
const list = store.state.tasks.data[`${task.type}s`];
const taskIndex = list.findIndex(t => t._id === task._id);
if (taskIndex > -1) {
list.splice(taskIndex, 1);
}
await axios.delete(`/api/v3/tasks/${task._id}`);
}
export async function getChallengeTasks (store, payload) {
let response = await axios.get(`/api/v3/tasks/challenge/${payload.challengeId}`);
return response.data.data;
}
export async function createChallengeTasks (store, payload) {
let response = await axios.post(`/api/v3/tasks/challenge/${payload.challengeId}`, payload.tasks);
return response.data.data;
}
export async function getGroupTasks (store, payload) {
let response = await axios.get(`/api/v3/tasks/group/${payload.groupId}`);
return response.data.data;
}
export async function createGroupTasks (store, payload) {
let response = await axios.post(`/api/v3/tasks/group/${payload.groupId}`, payload.tasks);
return response.data.data;
}
export async function assignTask (store, payload) {
let response = await axios.post(`/api/v3/tasks/${payload.taskId}/assign/${payload.userId}`);
return response.data.data;
}
export async function unassignTask (store, payload) {
let response = await axios.post(`/api/v3/tasks/${payload.taskId}/unassign/${payload.userId}`);
return response.data.data;
}
export async function needsWork (store, payload) {
let response = await axios.post(`/api/v3/tasks/${payload.taskId}/needs-work/${payload.userId}`);
return response.data.data;
}
export async function getGroupApprovals (store, payload) {
let response = await axios.get(`/api/v3/approvals/group/${payload.groupId}`);
return response.data.data;
}
export async function approve (store, payload) {
let response = await axios.post(`/api/v3/tasks/${payload.taskId}/approve/${payload.userId}`);
return response.data.data;
}
export async function unlinkOneTask (store, payload) {
if (!payload.keep) payload.keep = 'keep';
let task = payload.task;
const list = store.state.tasks.data[`${task.type}s`];
const taskIndex = list.findIndex(t => t._id === task._id);
if (taskIndex > -1) {
list.splice(taskIndex, 1);
}
let response = await axios.post(`/api/v3/tasks/unlink-one/${payload.task._id}?keep=${payload.keep}`);
return response.data.data;
}
export async function unlinkAllTasks (store, payload) {
if (!payload.keep) payload.keep = 'keep-all';
let response = await axios.post(`/api/v3/tasks/unlink-all/${payload.challengeId}?keep=${payload.keep}`);
return response.data.data;
}
export async function move (store, payload) {
let response = await axios.post(`/api/v3/tasks/${payload.taskId}/move/to/${payload.position}`);
return response.data.data;
}
export async function moveGroupTask (store, payload) {
let response = await axios.post(`/api/v3/group-tasks/${payload.taskId}/move/to/${payload.position}`);
return response.data.data;
}