You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1550 lines
44 KiB
1550 lines
44 KiB
2 years ago
|
// Mutations are state builders.
|
||
|
// The current state is the result of all the events in the system fed through the mutation functions.
|
||
|
// `server/state.js` for server; `modules/*` for vuex.
|
||
|
|
||
|
// const Vue = require('vue')
|
||
|
import { createHash } from './crypto.js'
|
||
|
import {
|
||
|
blankCard,
|
||
|
blankPinboard,
|
||
|
getTask,
|
||
|
getTaskBy,
|
||
|
atomicCardPlay,
|
||
|
unpinTasksOutOfBounds,
|
||
|
discardTaskFromZone,
|
||
|
taskExists,
|
||
|
seeTask,
|
||
|
clearPassesTo,
|
||
|
changeGiftCount,
|
||
|
grabTask,
|
||
|
dropTask,
|
||
|
addParent,
|
||
|
removeParentIfNotParent,
|
||
|
filterFromSubpiles,
|
||
|
clearSeenExcept,
|
||
|
addSubTask,
|
||
|
putTaskInTask,
|
||
|
addPriority,
|
||
|
stashTask,
|
||
|
unstashTask,
|
||
|
addPotential,
|
||
|
checkPotential,
|
||
|
clearPotential,
|
||
|
updateLastUsed,
|
||
|
safeMerge,
|
||
|
registerDuplicateTaskId,
|
||
|
POTENTIALS_TO_EXECUTE,
|
||
|
} from './cards.js'
|
||
|
|
||
|
function aoMuts(aos, ev) {
|
||
|
switch (ev.type) {
|
||
|
//case 'ao-linked':
|
||
|
// aos.forEach((ao, i) => {
|
||
|
// if (ao.address === ev.address) {
|
||
|
// ao.links.push(ev.taskId)
|
||
|
// }
|
||
|
// })
|
||
|
// break
|
||
|
case 'ao-inbound-connected':
|
||
|
let inAddressConnect = aos.some(a => {
|
||
|
if (a.address === ev.address) {
|
||
|
a.inboundSecret = ev.secret
|
||
|
a.lastContact = Date.now()
|
||
|
return true
|
||
|
}
|
||
|
})
|
||
|
if (!inAddressConnect) {
|
||
|
let newEv = {
|
||
|
address: ev.address,
|
||
|
outboundSecret: false,
|
||
|
inboundSecret: ev.secret,
|
||
|
lastContact: Date.now(),
|
||
|
}
|
||
|
aos.push(newEv)
|
||
|
}
|
||
|
break
|
||
|
case 'ao-outbound-connected':
|
||
|
let outAddressConnect = aos.some(a => {
|
||
|
if (a.address === ev.address) {
|
||
|
a.outboundSecret = ev.secret
|
||
|
a.lastContact = Date.now()
|
||
|
return true
|
||
|
}
|
||
|
})
|
||
|
if (!outAddressConnect) {
|
||
|
let newEv = {
|
||
|
address: ev.address.trim(),
|
||
|
outboundSecret: ev.secret,
|
||
|
inboundSecret: false,
|
||
|
lastContact: Date.now(),
|
||
|
}
|
||
|
aos.push(newEv)
|
||
|
}
|
||
|
break
|
||
|
case 'ao-disconnected':
|
||
|
aos.forEach((ao, i) => {
|
||
|
if (ao.address.trim() === ev.address.trim()) {
|
||
|
aos.splice(i, 1)
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function cashMuts(cash, ev) {
|
||
|
switch (ev.type) {
|
||
|
case 'ao-named':
|
||
|
cash.alias = ev.alias
|
||
|
break
|
||
|
case 'spot-updated':
|
||
|
cash.spot = ev.spot
|
||
|
break
|
||
|
case 'currency-switched':
|
||
|
cash.currency = ev.currency
|
||
|
break
|
||
|
case 'rent-set':
|
||
|
cash.rent = parseFloat(ev.amount)
|
||
|
break
|
||
|
case 'cap-set':
|
||
|
cash.cap = ev.amount
|
||
|
break
|
||
|
case 'funds-set':
|
||
|
cash.outputs = ev.outputs
|
||
|
cash.channels = ev.channels
|
||
|
break
|
||
|
case 'quorum-set':
|
||
|
cash.quorum = ev.quorum
|
||
|
break
|
||
|
case 'task-boosted':
|
||
|
if (ev.txid) cash.usedTxIds.push(ev.txid)
|
||
|
break
|
||
|
case 'task-boosted-lightning':
|
||
|
cash.pay_index = ev.pay_index
|
||
|
break
|
||
|
case 'get-node-info':
|
||
|
cash.info = ev.info
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function membersMuts(members, ev) {
|
||
|
switch (ev.type) {
|
||
|
case 'ao-outbound-connected':
|
||
|
break
|
||
|
case 'ao-disconnected':
|
||
|
break
|
||
|
case 'member-created':
|
||
|
updateLastUsed(ev, ev.timestamp)
|
||
|
ev.muted = true
|
||
|
ev.p0wned = true
|
||
|
members.push(ev)
|
||
|
break
|
||
|
case 'member-activated':
|
||
|
members.forEach(member => {
|
||
|
if (member.memberId === ev.memberId && !member.banned) {
|
||
|
if (member.active < 0) {
|
||
|
member.active = -1 * member.active
|
||
|
} else {
|
||
|
member.active++
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'task-boosted':
|
||
|
members.forEach(member => {
|
||
|
if (member.memberId === ev.taskId) {
|
||
|
if (member.active < 0) {
|
||
|
member.active = -1 * member.active
|
||
|
} else {
|
||
|
member.active++
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'task-boosted-lightning':
|
||
|
members.forEach(member => {
|
||
|
if (member.memberId === ev.taskId) {
|
||
|
if (member.active < 0) {
|
||
|
member.active = -1 * member.active
|
||
|
} else {
|
||
|
member.active++
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'task-visited':
|
||
|
members.forEach(member => {
|
||
|
if (member.memberId === ev.memberId) {
|
||
|
updateLastUsed(member, ev.timestamp)
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'member-deactivated':
|
||
|
members.forEach(member => {
|
||
|
if (member.memberId === ev.memberId) {
|
||
|
if (member.active >= 0) {
|
||
|
member.active = -1 * member.active - 1
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'member-secret-reset':
|
||
|
members.forEach(member => {
|
||
|
if (member.memberId === ev.kohaiId) {
|
||
|
const newSig = {
|
||
|
memberId: ev.senpaiId,
|
||
|
timestamp: ev.timestamp,
|
||
|
opinion: ev.type,
|
||
|
}
|
||
|
|
||
|
addPotential(member, newSig)
|
||
|
|
||
|
if (checkPotential(member, 'member-secret-reset')) {
|
||
|
member.p0wned = true
|
||
|
member.secret = createHash(member.name)
|
||
|
clearPotential(member, 'member-secret-reset')
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'member-promoted':
|
||
|
let toIndex
|
||
|
let fromIndex
|
||
|
if (
|
||
|
members.some((member, i) => {
|
||
|
if (member.memberId === ev.senpaiId) {
|
||
|
toIndex = i
|
||
|
return true
|
||
|
}
|
||
|
}) &&
|
||
|
members.some((member, i) => {
|
||
|
if (member.memberId === ev.kohaiId) {
|
||
|
fromIndex = i
|
||
|
return true
|
||
|
}
|
||
|
})
|
||
|
) {
|
||
|
members.splice(toIndex, 0, members.splice(fromIndex, 1)[0])
|
||
|
}
|
||
|
case 'member-banned':
|
||
|
members.forEach(member => {
|
||
|
if (member.memberId === ev.kohaiId) {
|
||
|
const newSig = {
|
||
|
memberId: ev.senpaiId,
|
||
|
timestamp: ev.timestamp,
|
||
|
opinion: ev.type,
|
||
|
}
|
||
|
|
||
|
addPotential(member, newSig)
|
||
|
|
||
|
if (checkPotential(member, 'member-banned')) {
|
||
|
member.banned = true
|
||
|
if (member.active >= 0) {
|
||
|
member.active = -1 * member.active - 1
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'member-unbanned':
|
||
|
members.forEach(member => {
|
||
|
if (
|
||
|
member.memberId === ev.kohaiId &&
|
||
|
member.hasOwnProperty('potentials') &&
|
||
|
member.potentials.length >= 1
|
||
|
) {
|
||
|
const beforeBans = member.potentials.filter(
|
||
|
p => p.opinion === 'member-banned'
|
||
|
).length
|
||
|
|
||
|
member.potentials = member.potentials.filter(
|
||
|
p => !(p.opinion === 'member-banned' && p.memberId === ev.senpaiId)
|
||
|
)
|
||
|
|
||
|
const afterBans = member.potentials.filter(
|
||
|
p => p.opinion === 'member-banned'
|
||
|
).length
|
||
|
|
||
|
if (
|
||
|
beforeBans >= POTENTIALS_TO_EXECUTE &&
|
||
|
afterBans < POTENTIALS_TO_EXECUTE
|
||
|
) {
|
||
|
member.banned = false
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'member-purged':
|
||
|
for (let i = members.length - 1; i >= 0; i--) {
|
||
|
const member = members[i]
|
||
|
if (member.memberId === ev.memberId) {
|
||
|
const newSig = {
|
||
|
memberId: ev.blame,
|
||
|
timestamp: ev.timestamp,
|
||
|
opinion: ev.type,
|
||
|
}
|
||
|
|
||
|
//addPotential(member, newSig)
|
||
|
|
||
|
//if (testPotential(member, 'member-purged')) {
|
||
|
members.splice(i, 1)
|
||
|
//}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break
|
||
|
case 'resource-used':
|
||
|
members.forEach(member => {
|
||
|
if (member.memberId === ev.memberId) {
|
||
|
updateLastUsed(member, ev.timestamp)
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
|
||
|
case 'member-field-updated':
|
||
|
members.forEach(member => {
|
||
|
if (member.memberId === ev.memberId) {
|
||
|
member[ev.field] = ev.newfield
|
||
|
if (ev.field === 'secret') {
|
||
|
member.p0wned = false
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
|
||
|
case 'member-ticker-set':
|
||
|
members.forEach(member => {
|
||
|
if (member.memberId === ev.memberId) {
|
||
|
if (!member.tickers) {
|
||
|
member.tickers = []
|
||
|
}
|
||
|
if (
|
||
|
!ev.fromCoin ||
|
||
|
ev.fromCoin.trim().length < 1 ||
|
||
|
!ev.toCoin ||
|
||
|
ev.toCoin.trim().length < 1
|
||
|
) {
|
||
|
member.tickers.splice(ev.index, 1)
|
||
|
} else {
|
||
|
member.tickers[ev.index] = {
|
||
|
from: ev.fromCoin.trim().toLowerCase(),
|
||
|
to: ev.toCoin.trim().toLowerCase(),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
|
||
|
case 'doge-barked':
|
||
|
members.forEach(member => {
|
||
|
// this should only bump up for mutual doges
|
||
|
if (member.memberId === ev.memberId) {
|
||
|
updateLastUsed(member, ev.timestamp)
|
||
|
// then bark
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function resourcesMuts(resources, ev) {
|
||
|
switch (ev.type) {
|
||
|
case 'resource-created':
|
||
|
let resourceIds = resources.map(r => r.resourceId)
|
||
|
if (resourceIds.indexOf(ev.resourceId) === -1) {
|
||
|
resources.push(ev)
|
||
|
} else {
|
||
|
console.log(
|
||
|
'BAD data duplicate resource rejected in mutation, dup resource task likely created'
|
||
|
)
|
||
|
}
|
||
|
break
|
||
|
case 'resource-used':
|
||
|
resources.forEach(resource => {
|
||
|
if (resource.resourceId == ev.resourceId) {
|
||
|
resource.stock -= parseInt(ev.amount)
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'resource-purged':
|
||
|
resources.forEach((r, i) => {
|
||
|
if (r.resourceId === ev.resourceId) {
|
||
|
resources.splice(i, 1)
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'resource-stocked':
|
||
|
resources.forEach(resource => {
|
||
|
if (resource.resourceId == ev.resourceId) {
|
||
|
resource.stock += parseInt(ev.amount)
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'channel-created':
|
||
|
resources.forEach((r, i) => {
|
||
|
if (r.resourceId == ev.resourceId) {
|
||
|
r.pubkey = ev.pubkey
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function memesMuts(memes, ev) {
|
||
|
switch (ev.type) {
|
||
|
case 'meme-added':
|
||
|
const fileHash = ev.data
|
||
|
if (
|
||
|
!memes.some(file => {
|
||
|
return file.hash === ev.hash
|
||
|
})
|
||
|
) {
|
||
|
memes.push({
|
||
|
memeId: ev.taskId,
|
||
|
filename: ev.filename,
|
||
|
hash: ev.hash,
|
||
|
filetype: ev.filetype,
|
||
|
})
|
||
|
// console.log('added meme file: ', ev.filename)
|
||
|
} else {
|
||
|
// console.log('meme file already in state: ', ev.filename)
|
||
|
}
|
||
|
break
|
||
|
case 'task-removed':
|
||
|
for (let i = memes.length - 1; i >= 0; i--) {
|
||
|
const meme = memes[i]
|
||
|
if (meme.memeId === ev.taskId) {
|
||
|
memes.splice(i, 1)
|
||
|
}
|
||
|
}
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function sessionsMuts(sessions, ev) {
|
||
|
switch (ev.type) {
|
||
|
case 'session-created':
|
||
|
let idHasSession = sessions.some(session => {
|
||
|
// replace that sessions creds,
|
||
|
let match = false
|
||
|
if (session.ownerId === ev.ownerId) {
|
||
|
match = true
|
||
|
Object.assign(session, session, ev)
|
||
|
}
|
||
|
return match // true terminates the some loop & idHasSession->true too
|
||
|
})
|
||
|
|
||
|
if (idHasSession) {
|
||
|
// edited in session
|
||
|
} else {
|
||
|
// id didn't previously have session
|
||
|
sessions.push(ev)
|
||
|
}
|
||
|
break
|
||
|
case 'session-killed':
|
||
|
for (let i = sessions.length - 1; i >= 0; i--) {
|
||
|
const s = sessions[i]
|
||
|
if (s.session == ev.session) {
|
||
|
sessions.splice(i, 1)
|
||
|
}
|
||
|
}
|
||
|
break
|
||
|
case 'ao-outbound-connected':
|
||
|
sessions.push({
|
||
|
ownerId: ev.address,
|
||
|
token: ev.secret,
|
||
|
session: ev.address,
|
||
|
})
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let missingTaskIds = []
|
||
|
function tasksMuts(tasks, ev) {
|
||
|
let theTask
|
||
|
let inTask
|
||
|
let memberTask
|
||
|
|
||
|
// Most tasks have a taskId and memberId, and many have an inId, so pull these out in a standard way
|
||
|
const memberTaskId = ev.memberId || ev.blame
|
||
|
if (
|
||
|
memberTaskId &&
|
||
|
memberTaskId !== 'cleanup' &&
|
||
|
typeof memberTaskId === 'string' &&
|
||
|
!memberTaskId.includes('.onion')
|
||
|
) {
|
||
|
memberTask = getTask(tasks, memberTaskId)
|
||
|
if (
|
||
|
!memberTask &&
|
||
|
ev.type !== 'member-created' &&
|
||
|
ev.type !== 'member-purged'
|
||
|
) {
|
||
|
if (!missingTaskIds.includes(memberTaskId)) {
|
||
|
missingTaskIds.push(memberTaskId)
|
||
|
console.log(
|
||
|
ev.type + ': first missing member task for memberId',
|
||
|
memberTaskId,
|
||
|
'(' + missingTaskIds.length + ')'
|
||
|
)
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const theTaskId =
|
||
|
ev.taskId ||
|
||
|
ev.subTask ||
|
||
|
ev.resourceId ||
|
||
|
ev?.from?.taskId ||
|
||
|
ev?.to?.taskId
|
||
|
if (theTaskId) {
|
||
|
theTask = getTask(tasks, theTaskId)
|
||
|
if (
|
||
|
!theTask &&
|
||
|
ev.type !== 'task-created' &&
|
||
|
ev.type !== 'grid-created' &&
|
||
|
ev.type !== 'resource-created' &&
|
||
|
ev.type !== 'meme-added'
|
||
|
) {
|
||
|
if (!missingTaskIds.includes(theTaskId)) {
|
||
|
missingTaskIds.push(theTaskId)
|
||
|
//console.log(ev.type + ': first missing task event for taskId', theTaskId, '(' + missingTaskIds.length + ') ev:', ev)
|
||
|
}
|
||
|
return // continuing may crash, but returning may cause further inconsistencies going forward
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ev.inId) {
|
||
|
inTask = getTask(tasks, ev.inId)
|
||
|
if (!inTask && ev.type !== 'task-created') {
|
||
|
if (!missingTaskIds.includes(ev.inId)) {
|
||
|
missingTaskIds.push(ev.inId)
|
||
|
//console.log(ev.type + ': first missing task event for inId', ev.inId, '(' + missingTaskIds.length + ') ev:', ev)
|
||
|
}
|
||
|
return // continuing may crash, but returning may cause further inconsistencies going forward
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch (ev.type) {
|
||
|
case 'highlighted':
|
||
|
tasks.forEach(task => {
|
||
|
if (task.taskId === ev.taskId) {
|
||
|
let didUpdateInline = false
|
||
|
task.highlights.forEach((h, i) => {
|
||
|
if (h.memberId === ev.memberId) {
|
||
|
didUpdateInline = true
|
||
|
if (h.valence === ev.valence) {
|
||
|
task.highlights.splice(i, 1)
|
||
|
} else {
|
||
|
h.valence = ev.valence
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
if (!didUpdateInline) {
|
||
|
task.highlights.push({
|
||
|
memberId: ev.memberId,
|
||
|
valence: ev.valence,
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'ao-outbound-connected':
|
||
|
tasks.push(blankCard(ev.address, ev.address, 'purple', ev.timestamp))
|
||
|
break
|
||
|
case 'ao-disconnected':
|
||
|
break
|
||
|
case 'resource-created':
|
||
|
tasks.push(blankCard(ev.resourceId, ev.resourceId, 'red', ev.timestamp))
|
||
|
break
|
||
|
case 'member-created':
|
||
|
tasks.push(blankCard(ev.memberId, ev.memberId, 'blue', ev.timestamp))
|
||
|
break
|
||
|
case 'member-purged':
|
||
|
// This is terribly redundant since the same potential builds up on the member.
|
||
|
// Maybe the potentials system should be abstracted out to the spec or validation layer;
|
||
|
// Attempts to call limited functions instead produce an action-potential event.
|
||
|
// The original idea was potentials would only build up on members, not tasks.
|
||
|
let purgedMemberCard = false
|
||
|
for (let i = tasks.length - 1; i >= 0; i--) {
|
||
|
const task = tasks[i]
|
||
|
if (task.taskId === ev.memberId) {
|
||
|
let newSig = {
|
||
|
memberId: ev.blame,
|
||
|
timestamp: ev.timestamp,
|
||
|
opinion: ev.type,
|
||
|
}
|
||
|
|
||
|
addPotential(task, newSig)
|
||
|
|
||
|
if (checkPotential(task, 'member-purged')) {
|
||
|
tasks.splice(i, 1)
|
||
|
purgedMemberCard = true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (purgedMemberCard) {
|
||
|
tasks.forEach((t, j) => {
|
||
|
t.subTasks = t.subTasks.filter(st => st !== ev.memberId)
|
||
|
t.priorities = t.priorities.filter(st => st !== ev.memberId)
|
||
|
t.completed.filter(st => st !== ev.memberId)
|
||
|
t.claimed = t.claimed.filter(st => st !== ev.memberId)
|
||
|
t.deck = t.deck.filter(st => st !== ev.memberId)
|
||
|
clearPassesTo(tasks, t, ev.memberId, true)
|
||
|
if (t.pins && t.pins.length >= 1) {
|
||
|
t.pins.forEach((pin, i) => {
|
||
|
const cell = pin.taskId
|
||
|
if (cell === ev.memberId) {
|
||
|
tasks[j].pins.spice(i, 1)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
break
|
||
|
case 'meme-added':
|
||
|
// console.log('meme-added taskId is', ev.taskId)
|
||
|
if (!tasks.some(t => t.taskId === ev.taskId)) {
|
||
|
// console.log('adding meme', ev.taskId)
|
||
|
tasks.push(blankCard(ev.taskId, ev.filename, 'yellow', ev.timestamp))
|
||
|
}
|
||
|
break
|
||
|
case 'task-created':
|
||
|
const foundExistingTask = getTaskBy(tasks, ev.name, 'name')
|
||
|
if (foundExistingTask?.taskId) {
|
||
|
registerDuplicateTaskId(foundExistingTask.taskId, ev.taskId)
|
||
|
break
|
||
|
}
|
||
|
tasks.push(
|
||
|
blankCard(
|
||
|
ev.taskId,
|
||
|
ev.name,
|
||
|
ev.color,
|
||
|
ev.timestamp,
|
||
|
ev.deck,
|
||
|
ev.inId ? [ev.inId] : []
|
||
|
)
|
||
|
)
|
||
|
if (inTask) {
|
||
|
if (ev.prioritized) {
|
||
|
addPriority(inTask, ev.taskId)
|
||
|
} else {
|
||
|
addSubTask(inTask, ev.taskId)
|
||
|
}
|
||
|
addParent(inTask, ev.inId)
|
||
|
clearSeenExcept(inTask, ev.deck.length >= 1 ? [ev.deck[0]] : undefined) // The very font of novelty
|
||
|
}
|
||
|
break
|
||
|
case 'address-updated':
|
||
|
tasks.forEach(t => {
|
||
|
if (t.taskId === ev.taskId) {
|
||
|
t.address = ev.address
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'task-passed':
|
||
|
let pass = [ev.fromMemberId, ev.toMemberId]
|
||
|
|
||
|
if (
|
||
|
!theTask.passed.some(p => {
|
||
|
if (p[0] === pass[0] && p[1] === pass[1]) {
|
||
|
return true
|
||
|
}
|
||
|
})
|
||
|
) {
|
||
|
theTask.passed.push(pass)
|
||
|
const recipient = getTask(tasks, ev.toMemberId)
|
||
|
if (recipient) {
|
||
|
changeGiftCount(recipient, 1)
|
||
|
}
|
||
|
}
|
||
|
break
|
||
|
case 'task-grabbed':
|
||
|
grabTask(tasks, theTask, ev.memberId)
|
||
|
break
|
||
|
case 'task-seen':
|
||
|
tasks.forEach(task => {
|
||
|
if (task.taskId === ev.taskId) {
|
||
|
if (!task.seen) {
|
||
|
task.seen = []
|
||
|
}
|
||
|
if (
|
||
|
!task.seen.some(t => {
|
||
|
return t.memberId === ev.memberId
|
||
|
})
|
||
|
) {
|
||
|
task.seen.push({ memberId: ev.memberId, timestamp: Date.now() })
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'task-started':
|
||
|
const tsFound = theTask
|
||
|
|
||
|
if (tsFound) {
|
||
|
if (!tsFound.timelog) {
|
||
|
tsFound.timelog = []
|
||
|
}
|
||
|
// console.log('task-started pre timelog is', tsFound.timelog)
|
||
|
tsFound.timelog.push({
|
||
|
memberId: ev.memberId,
|
||
|
taskId: ev.taskId,
|
||
|
inId: ev.inId,
|
||
|
start: ev.timestamp,
|
||
|
stop: null,
|
||
|
})
|
||
|
}
|
||
|
// console.log('task-started post timelog is', tsFound.timelog)
|
||
|
break
|
||
|
case 'task-stopped':
|
||
|
// console.log('task-stopped 1')
|
||
|
const tstFound = theTask
|
||
|
// console.log('task-stopped 2')
|
||
|
if (tstFound) {
|
||
|
// console.log('task-stopped 3')
|
||
|
// console.log('task-stopped pre timelog is', tstFound.timelog)
|
||
|
if (!tstFound.timelog) {
|
||
|
return
|
||
|
}
|
||
|
// console.log('task-stopped 4')
|
||
|
for (var i = tstFound.timelog.length - 1; i >= 0; i--) {
|
||
|
// console.log('task-stopped 5')
|
||
|
if (tstFound.timelog[i].memberId === ev.memberId) {
|
||
|
// console.log('task-stopped 6')
|
||
|
if (
|
||
|
tstFound.timelog[i].stop &&
|
||
|
tstFound.timelog[i].stop > tstFound.timelog[i].start
|
||
|
) {
|
||
|
// console.log(
|
||
|
// 'task-stopped 7 stop is',
|
||
|
// tstFound.timelog[i].stop,
|
||
|
// ' and start is',
|
||
|
// tstFound.timelog[i].start
|
||
|
// )
|
||
|
console.log(
|
||
|
'Stop time already set for most recent start time, triggering this event should not be possible in the GUI'
|
||
|
)
|
||
|
} else {
|
||
|
// console.log('task-stopped 8')
|
||
|
tstFound.timelog[i].stop = ev.timestamp
|
||
|
// tstFound.timelog.push({
|
||
|
// memberId: 'test',
|
||
|
// taskId: 'testId',
|
||
|
// inId: null,
|
||
|
// start: null,
|
||
|
// stop: null,
|
||
|
// })
|
||
|
}
|
||
|
}
|
||
|
// console.log('task-stopped post timelog is', tstFound.timelog)
|
||
|
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
break
|
||
|
case 'task-time-clocked':
|
||
|
tasks.forEach(task => {
|
||
|
if (task.taskId === ev.taskId) {
|
||
|
let found = task.time.find(t => {
|
||
|
return t.memberId === ev.memberId
|
||
|
})
|
||
|
if (!found) {
|
||
|
task.time.push({
|
||
|
memberId: ev.memberId,
|
||
|
timelog: [ev.seconds],
|
||
|
date: [ev.date],
|
||
|
})
|
||
|
} else {
|
||
|
if (!found.timelog) {
|
||
|
found.timelog = []
|
||
|
}
|
||
|
if (!found.date) {
|
||
|
found.date = []
|
||
|
if (found.timelog.length > found.date.length) {
|
||
|
let count = found.timelog.length - found.date.length
|
||
|
while (count > 0) {
|
||
|
found.date.push(null)
|
||
|
count--
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
found.timelog.push(ev.seconds)
|
||
|
found.date.push(ev.date)
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'task-signed':
|
||
|
tasks.forEach(task => {
|
||
|
if (task.taskId === ev.taskId) {
|
||
|
clearPassesTo(tasks, task, ev.memberId)
|
||
|
if (task.deck.indexOf(ev.memberId) === -1) {
|
||
|
task.deck.push(ev.memberId)
|
||
|
}
|
||
|
let newSig = {
|
||
|
memberId: ev.memberId,
|
||
|
timestamp: ev.timestamp,
|
||
|
opinion: ev.opinion,
|
||
|
}
|
||
|
if (!task.signed) {
|
||
|
task.signed = []
|
||
|
}
|
||
|
task.signed.push(newSig)
|
||
|
if (task.guild && task.guild.length >= 1) {
|
||
|
if (
|
||
|
ev.opinion === 1 &&
|
||
|
(!task.hasOwnProperty('memberships') ||
|
||
|
!Array.isArray(task.memberships))
|
||
|
) {
|
||
|
if (!task.memberships) {
|
||
|
task.memberships = []
|
||
|
}
|
||
|
task.memberships.push({ memberId: ev.memberId, level: 2 })
|
||
|
} else if (
|
||
|
ev.opinion === 0 &&
|
||
|
task.hasOwnProperty('memberships') &&
|
||
|
Array.isArray(task.memberships)
|
||
|
) {
|
||
|
task.memberships.filter(memb => memb.memberId !== ev.memberId)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'task-membership':
|
||
|
tasks.forEach(task => {
|
||
|
if (task.taskId === ev.taskId) {
|
||
|
if (task.guild && task.guild.length >= 1) {
|
||
|
// The member must have signed the task affirmatively
|
||
|
if (!task.signed || !task.signed.length || task.signed.length < 1) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
let mostRecentOpinion
|
||
|
for (let i = task.signed.length - 1; i--; i >= 0) {
|
||
|
const signature = task.signed[i]
|
||
|
if (signature.memberId === ev.memberId) {
|
||
|
mostRecentOpinion = signature.opinion
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mostRecentOpinion < 1) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (
|
||
|
!task.memberships ||
|
||
|
!task.memberships.length ||
|
||
|
task.memberships.length < 1
|
||
|
) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
const promoterLevel = task.memberships.find(
|
||
|
membership => membership.memberId === ev.blame
|
||
|
)?.level
|
||
|
|
||
|
if (!promoterLevel || promoterLevel < 1) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
const promotedLevel =
|
||
|
task.memberships.find(
|
||
|
membership => membership.memberId === ev.memberId
|
||
|
)?.level || 0
|
||
|
|
||
|
let maxLevel = 0
|
||
|
task.memberships.forEach(membership => {
|
||
|
maxLevel = Math.max(maxLevel, membership.level)
|
||
|
})
|
||
|
|
||
|
// The promoter must be a member at least one level higher
|
||
|
// Or, the highest-level member of a group can promote themselves
|
||
|
const canPromote =
|
||
|
(promoterLevel > promotedLevel &&
|
||
|
promoterLevel >= ev.level + 1) ||
|
||
|
(ev.memberId === ev.blame &&
|
||
|
promoterLevel === maxLevel &&
|
||
|
maxLevel >= 1)
|
||
|
|
||
|
if (!canPromote) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
task.memberships = task.memberships.filter(
|
||
|
memb => memb.memberId !== ev.memberId
|
||
|
)
|
||
|
if (ev.level !== 0) {
|
||
|
task.memberships.push({
|
||
|
memberId: ev.memberId,
|
||
|
level: ev.level,
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'task-stashed':
|
||
|
// I think the spec is only run on event creation, not load from database,
|
||
|
// so make sure the task exists before linking to it from another card
|
||
|
const toStash = theTask
|
||
|
if (toStash) {
|
||
|
grabTask(tasks, toStash, ev.blame)
|
||
|
addParent(toStash, ev.inId)
|
||
|
|
||
|
tasks.forEach(task => {
|
||
|
if (task.taskId === ev.inId) {
|
||
|
stashTask(task, ev.taskId, ev.level)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
break
|
||
|
case 'task-unstashed':
|
||
|
// I think the spec is only run on event creation, not load from database,
|
||
|
// so make sure the task exists before linking to it from another card
|
||
|
const toUnstash = theTask
|
||
|
const unstashParentCard = inTask
|
||
|
if (toUnstash && unstashParentCard) {
|
||
|
grabTask(tasks, toUnstash, ev.blame)
|
||
|
|
||
|
tasks.forEach(task => {
|
||
|
if (task.taskId === ev.inId) {
|
||
|
unstashTask(task, ev.taskId, ev.level)
|
||
|
removeParentIfNotParent(task, unstashParentCard)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
break
|
||
|
case 'pile-grabbed':
|
||
|
if (!ev.memberId) {
|
||
|
break
|
||
|
}
|
||
|
tasks.forEach(task => {
|
||
|
if (task.taskId === ev.taskId) {
|
||
|
clearPassesTo(tasks, task, ev.memberId)
|
||
|
let crawler = [ev.taskId]
|
||
|
let history = []
|
||
|
let newCards = []
|
||
|
do {
|
||
|
newCards = []
|
||
|
crawler.forEach(t => {
|
||
|
if (history.indexOf(t) >= 0) return
|
||
|
let subTask = tasks.filter(pst => pst.taskId === t)
|
||
|
if (subTask.length < 1) {
|
||
|
// console.log(
|
||
|
// 'missing subtask, this is messy. parent task name: ',
|
||
|
// task.name
|
||
|
// )
|
||
|
return
|
||
|
}
|
||
|
if (subTask.length > 1) {
|
||
|
console.log('duplicate task found, this is very bad')
|
||
|
}
|
||
|
subTask = subTask[0]
|
||
|
if (
|
||
|
subTask === undefined ||
|
||
|
subTask.subTasks === undefined ||
|
||
|
subTask.priorities === undefined ||
|
||
|
subTask.completed === undefined
|
||
|
) {
|
||
|
console.log('invalid task data found, this is very bad')
|
||
|
return
|
||
|
}
|
||
|
|
||
|
history.push(t)
|
||
|
|
||
|
if (
|
||
|
subTask.deck.indexOf(ev.memberId) === -1 &&
|
||
|
ev.taskId !== ev.memberId
|
||
|
) {
|
||
|
clearPassesTo(tasks, subTask, ev.memberId)
|
||
|
subTask.deck.push(ev.memberId)
|
||
|
}
|
||
|
newCards = newCards
|
||
|
.concat(subTask.subTasks)
|
||
|
.concat(subTask.priorities)
|
||
|
.concat(subTask.completed)
|
||
|
})
|
||
|
crawler = newCards
|
||
|
} while (crawler.length > 0)
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'task-dropped':
|
||
|
theTask.deck = theTask.deck.filter(d => d !== ev.memberId)
|
||
|
clearPassesTo(tasks, theTask, ev.memberId)
|
||
|
break
|
||
|
case 'pile-dropped':
|
||
|
if (!ev.memberId) {
|
||
|
break
|
||
|
}
|
||
|
tasks.forEach(task => {
|
||
|
if (task.taskId === ev.taskId) {
|
||
|
clearPassesTo(tasks, task, ev.memberId)
|
||
|
let crawler = [ev.taskId]
|
||
|
let history = []
|
||
|
let newCards = []
|
||
|
do {
|
||
|
newCards = []
|
||
|
crawler.forEach(t => {
|
||
|
if (history.indexOf(t) >= 0) return
|
||
|
let subTask = tasks.filter(pst => pst.taskId === t)
|
||
|
if (subTask.length < 1) {
|
||
|
console.log('missing subtask, this is messy')
|
||
|
return
|
||
|
}
|
||
|
if (subTask.length > 1) {
|
||
|
console.log('duplicate task found, this is very bad')
|
||
|
}
|
||
|
subTask = subTask[0]
|
||
|
if (
|
||
|
subTask === undefined ||
|
||
|
subTask.subTasks === undefined ||
|
||
|
subTask.priorities === undefined ||
|
||
|
subTask.completed === undefined
|
||
|
) {
|
||
|
console.log('invalid task data found, this is very bad')
|
||
|
return
|
||
|
}
|
||
|
|
||
|
history.push(t)
|
||
|
|
||
|
if (
|
||
|
subTask.deck.indexOf(ev.memberId) >= 0 &&
|
||
|
ev.taskId !== ev.memberId
|
||
|
) {
|
||
|
clearPassesTo(tasks, subTask, ev.memberId)
|
||
|
dropTask(subTask, ev.memberId)
|
||
|
}
|
||
|
newCards = newCards
|
||
|
.concat(subTask.subTasks)
|
||
|
.concat(subTask.priorities)
|
||
|
.concat(subTask.completed)
|
||
|
})
|
||
|
crawler = newCards
|
||
|
} while (crawler.length > 0)
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'task-removed':
|
||
|
for (let i = tasks.length - 1; i >= 0; i--) {
|
||
|
const task = tasks[i]
|
||
|
if (task.taskId === ev.taskId) {
|
||
|
tasks.splice(i, 1)
|
||
|
}
|
||
|
}
|
||
|
tasks.forEach((t, i) => {
|
||
|
t.subTasks = t.subTasks.filter(st => st !== ev.taskId)
|
||
|
t.priorities = t.priorities.filter(st => st !== ev.taskId)
|
||
|
t.completed = t.completed.filter(st => st !== ev.taskId)
|
||
|
t.pins = t.pins.filter(pin => pin.taskId !== ev.taskId)
|
||
|
})
|
||
|
break
|
||
|
case 'tasks-removed':
|
||
|
for (let i = tasks.length - 1; i >= 0; i--) {
|
||
|
const task = tasks[i]
|
||
|
if (ev.taskIds.includes(task.taskId)) {
|
||
|
tasks.splice(i, 1)
|
||
|
}
|
||
|
}
|
||
|
tasks.forEach((t, i) => {
|
||
|
t.subTasks = t.subTasks.filter(st => !ev.taskIds.includes(st))
|
||
|
t.priorities = t.priorities.filter(st => !ev.taskIds.includes(st))
|
||
|
t.completed = t.completed.filter(st => !ev.taskIds.includes(st))
|
||
|
t.pins =
|
||
|
t.pins && Array.isArray(t.pins)
|
||
|
? t.pins.filter(pin => !ev.taskIds.includes(pin.taskId))
|
||
|
: []
|
||
|
})
|
||
|
break
|
||
|
case 'pile-prioritized':
|
||
|
inTask.priorities = inTask.priorities.concat(inTask.subTasks)
|
||
|
inTask.subTasks = []
|
||
|
break
|
||
|
case 'task-refocused':
|
||
|
atomicCardPlay(
|
||
|
tasks,
|
||
|
{ taskId: ev.taskId, inId: ev.inId, zone: 'priorities' },
|
||
|
{ taskId: ev.taskId, inId: ev.inId, zone: 'subTasks' },
|
||
|
ev.blame
|
||
|
)
|
||
|
break
|
||
|
case 'pile-refocused':
|
||
|
tasks.forEach(task => {
|
||
|
if (task.taskId === ev.inId) {
|
||
|
task.priorities.forEach(stId => {
|
||
|
tasks.forEach(st => {
|
||
|
if (st.taskId === stId) {
|
||
|
if (st.claimed && st.claimed.length >= 1) {
|
||
|
task.completed.push(stId)
|
||
|
} else {
|
||
|
task.subTasks.push(stId)
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
task.priorities = []
|
||
|
if (task.allocations && Array.isArray(task.allocations)) {
|
||
|
task.allocations.forEach(allocation => {
|
||
|
task.boost += allocation.amount
|
||
|
})
|
||
|
task.allocations = []
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'task-played':
|
||
|
atomicCardPlay(tasks, ev.from, ev.to, ev.memberId)
|
||
|
break
|
||
|
case 'task-sub-tasked':
|
||
|
atomicCardPlay(
|
||
|
tasks,
|
||
|
{ taskId: ev.subTask },
|
||
|
{ taskId: ev.subTask, inId: ev.taskId, zone: 'subTasks' },
|
||
|
ev.memberId
|
||
|
)
|
||
|
break
|
||
|
case 'task-de-sub-tasked':
|
||
|
atomicCardPlay(
|
||
|
tasks,
|
||
|
{ taskId: ev.subTask, inId: ev.taskId, zone: 'subTasks' },
|
||
|
{ taskId: ev.subTask, zone: 'discard' },
|
||
|
ev.memberId
|
||
|
)
|
||
|
break
|
||
|
case 'task-prioritized':
|
||
|
atomicCardPlay(
|
||
|
tasks,
|
||
|
{ taskId: ev.taskId, inId: ev.inId, zone: 'card' },
|
||
|
{ taskId: ev.taskId, inId: ev.inId, zone: 'priorities' },
|
||
|
ev.memberId
|
||
|
)
|
||
|
break
|
||
|
case 'grid-pin':
|
||
|
if (typeof ev.y === 'string') {
|
||
|
ev.y = parseInt(ev.y)
|
||
|
}
|
||
|
if (typeof ev.x === 'string') {
|
||
|
ev.x = parseInt(ev.x)
|
||
|
}
|
||
|
atomicCardPlay(
|
||
|
tasks,
|
||
|
{ taskId: ev.taskId, inId: ev.inId, zone: 'subTasks' },
|
||
|
{
|
||
|
taskId: ev.taskId,
|
||
|
inId: ev.inId,
|
||
|
zone: 'grid',
|
||
|
coords: { y: parseInt(ev.y), x: parseInt(ev.x) },
|
||
|
},
|
||
|
ev?.memberId
|
||
|
)
|
||
|
break
|
||
|
case 'grid-unpin':
|
||
|
//console.log("event type is", ev.type)
|
||
|
atomicCardPlay(
|
||
|
tasks,
|
||
|
{
|
||
|
taskId: ev.taskId,
|
||
|
inId: ev.inId,
|
||
|
zone: 'grid',
|
||
|
coords: { y: parseInt(ev.y), x: parseInt(ev.x) },
|
||
|
},
|
||
|
{
|
||
|
taskId: ev.taskId,
|
||
|
inId: ev.inId,
|
||
|
zone: 'subTasks',
|
||
|
coords: { y: 0 },
|
||
|
},
|
||
|
ev.memberId
|
||
|
)
|
||
|
break
|
||
|
case 'task-emptied':
|
||
|
let updateParents = []
|
||
|
const emptiedParent = theTask
|
||
|
updateParents = [...theTask.priorities, ...theTask.subTasks]
|
||
|
theTask.priorities = []
|
||
|
theTask.subTasks = []
|
||
|
tasks.forEach(task => {
|
||
|
if (updateParents.indexOf(task.taskId) >= 0) {
|
||
|
removeParentIfNotParent(task, emptiedParent)
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'task-guilded':
|
||
|
theTask.guild = ev.guild
|
||
|
break
|
||
|
case 'task-property-set':
|
||
|
let properties = ev.property.split('.')
|
||
|
|
||
|
if (properties?.length >= 2) {
|
||
|
if (
|
||
|
!theTask.hasOwnProperty(properties[0]) ||
|
||
|
typeof theTask[properties[0]] != 'object'
|
||
|
) {
|
||
|
theTask[properties[0]] = {}
|
||
|
}
|
||
|
theTask[properties[0]][properties[1]] = ev.value
|
||
|
} else {
|
||
|
theTask[ev.property] = ev.value
|
||
|
}
|
||
|
if (ev.property === 'pinboard.spread') {
|
||
|
unpinTasksOutOfBounds(tasks, theTask)
|
||
|
}
|
||
|
break
|
||
|
case 'task-colored':
|
||
|
theTask.color = ev.color
|
||
|
|
||
|
if (ev.inId) {
|
||
|
addSubTask(inTask, ev.taskId)
|
||
|
}
|
||
|
break
|
||
|
case 'task-claimed':
|
||
|
let paid = parseFloat(ev.paid) > 0 ? parseFloat(ev.paid) : 0
|
||
|
let bounty = 0
|
||
|
tasks.forEach(task => {
|
||
|
let found = false
|
||
|
task.priorities.some(taskId => {
|
||
|
if (taskId !== ev.taskId) {
|
||
|
return false
|
||
|
} else {
|
||
|
found = true
|
||
|
return true
|
||
|
}
|
||
|
})
|
||
|
|
||
|
task.subTasks.some(taskId => {
|
||
|
if (taskId !== ev.taskId) {
|
||
|
return false
|
||
|
} else {
|
||
|
found = true
|
||
|
return true
|
||
|
}
|
||
|
})
|
||
|
|
||
|
if (found) {
|
||
|
if (task.priorities.indexOf(ev.taskId) === -1) {
|
||
|
task.subTasks = task.subTasks.filter(tId => tId !== ev.subTask)
|
||
|
task.completed = task.completed.filter(tId => tId !== ev.subTask)
|
||
|
task.completed.push(ev.taskId)
|
||
|
}
|
||
|
// let alloc = false
|
||
|
if (task.allocations && Array.isArray(task.allocations)) {
|
||
|
task.allocations = task.allocations.filter(al => {
|
||
|
if (al.allocatedId === ev.taskId) {
|
||
|
bounty += al.amount
|
||
|
return false
|
||
|
}
|
||
|
return true
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
if (task.taskId === ev.taskId) {
|
||
|
clearPassesTo(tasks, task, ev.memberId)
|
||
|
if (task.deck.indexOf(ev.memberId) === -1) {
|
||
|
if (ev.taskId !== ev.memberId && ev.memberId) {
|
||
|
task.deck.push(ev.memberId)
|
||
|
}
|
||
|
}
|
||
|
if (task.claimed.indexOf(ev.memberId) === -1) {
|
||
|
task.claimed.push(ev.memberId)
|
||
|
}
|
||
|
task.lastClaimed = ev.timestamp
|
||
|
}
|
||
|
})
|
||
|
tasks.forEach(task => {
|
||
|
if (task.taskId === ev.memberId) {
|
||
|
task.boost += paid + bounty
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case 'task-unclaimed':
|
||
|
theTask.claimed = theTask.claimed.filter(mId => mId !== ev.memberId)
|
||
|
if (theTask.claimed.length < 1) {
|
||
|
tasks.forEach(p => {
|
||
|
if (
|
||
|
p.priorities.indexOf(ev.taskId) === -1 &&
|
||
|
p.completed.indexOf(ev.taskId) > -1
|
||
|
) {
|
||
|
p.completed = p.completed.filter(taskId => taskId !== ev.taskId)
|
||
|
addSubTask(p, ev.taskId)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
break
|
||
|
case 'task-reset':
|
||
|
const clearAllCheckmarksFromTask = taskToReset => {
|
||
|
if (!taskToReset) return
|
||
|
taskToReset.claimed = []
|
||
|
taskToReset.lastClaimed = ev.timestamp
|
||
|
}
|
||
|
if (theTask.uncheckThisCard) {
|
||
|
clearAllCheckmarksFromTask(theTask)
|
||
|
}
|
||
|
if (
|
||
|
theTask.uncheckPriorities &&
|
||
|
theTask.priorities &&
|
||
|
theTask.priorities.length >= 1
|
||
|
) {
|
||
|
theTask.priorities.forEach(tId => {
|
||
|
const priorityCard = getTask(tasks, tId)
|
||
|
clearAllCheckmarksFromTask(priorityCard)
|
||
|
})
|
||
|
}
|
||
|
if (theTask.uncheckPinned && theTask.pins && theTask.pins.length >= 1) {
|
||
|
theTask.pins.forEach(pin => {
|
||
|
const pinnedCard = getTask(tasks, pin.taskId)
|
||
|
clearAllCheckmarksFromTask(pinnedCard)
|
||
|
})
|
||
|
}
|
||
|
break
|
||
|
case 'task-boosted':
|
||
|
let amount = parseFloat(ev.amount)
|
||
|
let boost = parseFloat(theTask.boost)
|
||
|
if (amount > 0) {
|
||
|
theTask.boost = amount + boost
|
||
|
theTask.address = ''
|
||
|
}
|
||
|
break
|
||
|
case 'task-boosted-lightning':
|
||
|
const taskToBoostLightning = getTaskBy(
|
||
|
tasks,
|
||
|
ev.payment_hash,
|
||
|
'payment_hash'
|
||
|
)
|
||
|
let amountToBoost = parseFloat(ev.amount)
|
||
|
let boostLightning = parseFloat(taskToBoostLightning.boost)
|
||
|
if (amountToBoost > 0) {
|
||
|
taskToBoostLightning.boost = amountToBoost + boostLightning
|
||
|
taskToBoostLightning.bolt11 = ''
|
||
|
taskToBoostLightning.payment_hash = ''
|
||
|
}
|
||
|
break
|
||
|
case 'task-allocated':
|
||
|
if (!Number.isInteger(ev.amount) || ev.amount < 0) {
|
||
|
break
|
||
|
}
|
||
|
if (
|
||
|
!theTask.hasOwnProperty('allocations') ||
|
||
|
!Array.isArray(theTask.allocations)
|
||
|
) {
|
||
|
theTask.allocations = []
|
||
|
}
|
||
|
if (ev.amount === 0) {
|
||
|
theTask.allocations = theTask.allocations.filter(als => {
|
||
|
if (als.allocatedId == ev.allocatedId) {
|
||
|
theTask.boost += als.amount
|
||
|
return false
|
||
|
}
|
||
|
return true
|
||
|
})
|
||
|
break
|
||
|
}
|
||
|
const alreadyPointed = theTask.allocations.some(als => {
|
||
|
const diff = ev.amount - als.amount
|
||
|
if (als.allocatedId !== ev.allocatedId) return false
|
||
|
if (diff > theTask.boost) return true
|
||
|
theTask.boost -= diff
|
||
|
als.amount = ev.amount
|
||
|
return true
|
||
|
})
|
||
|
if (!alreadyPointed) {
|
||
|
theTask.allocations.push(ev)
|
||
|
theTask.boost -= ev.amount
|
||
|
}
|
||
|
addPriority(theTask, ev.allocatedId)
|
||
|
break
|
||
|
case 'resource-booked':
|
||
|
theTask.book = ev
|
||
|
break
|
||
|
case 'resource-used':
|
||
|
const charged = parseFloat(ev.charged)
|
||
|
if (charged > 0) {
|
||
|
memberTask.boost -= charged
|
||
|
|
||
|
const resourceCard = theTask
|
||
|
resourceCard.boost += charged
|
||
|
}
|
||
|
break
|
||
|
case 'invoice-created':
|
||
|
theTask.payment_hash = ev.payment_hash
|
||
|
theTask.bolt11 = ev.bolt11
|
||
|
break
|
||
|
case 'task-swapped':
|
||
|
let task
|
||
|
tasks.forEach(t => {
|
||
|
if (t.taskId === ev.taskId) {
|
||
|
task = t
|
||
|
}
|
||
|
})
|
||
|
|
||
|
if (task) {
|
||
|
let originalIndex = task.subTasks.indexOf(ev.swapId1)
|
||
|
let swapIndex = task.subTasks.indexOf(ev.swapId2)
|
||
|
|
||
|
let originalIndexCompleted = task.completed.indexOf(ev.swapId1)
|
||
|
let swapIndexCompleted = task.completed.indexOf(ev.swapId2)
|
||
|
|
||
|
if (originalIndex > -1 && swapIndex > -1) {
|
||
|
let newST = task.subTasks.slice()
|
||
|
newST[originalIndex] = ev.swapId2
|
||
|
newST[swapIndex] = ev.swapId1
|
||
|
task.subTasks = newST
|
||
|
}
|
||
|
|
||
|
if (originalIndexCompleted > -1 && swapIndexCompleted > -1) {
|
||
|
let newCompleted = task.completed.slice()
|
||
|
newCompleted[originalIndexCompleted] = ev.swapId2
|
||
|
newCompleted[swapIndexCompleted] = ev.swapId1
|
||
|
task.completed = newCompleted
|
||
|
}
|
||
|
}
|
||
|
break
|
||
|
case 'task-bumped':
|
||
|
let taskB
|
||
|
tasks.forEach(t => {
|
||
|
if (t.taskId === ev.taskId) {
|
||
|
taskB = t
|
||
|
}
|
||
|
})
|
||
|
|
||
|
if (taskB) {
|
||
|
let originalIndex = taskB.subTasks.indexOf(ev.bumpId)
|
||
|
let originalIndexCompleted = taskB.completed.indexOf(ev.bumpId)
|
||
|
if (
|
||
|
originalIndex === taskB.subTasks.length - 1 &&
|
||
|
ev.direction === -1
|
||
|
) {
|
||
|
let newST = [ev.bumpId]
|
||
|
newST = newST.concat(
|
||
|
taskB.subTasks.slice(0, taskB.subTasks.length - 1)
|
||
|
)
|
||
|
taskB.subTasks = newST
|
||
|
}
|
||
|
|
||
|
if (originalIndex === 0 && ev.direction === 1) {
|
||
|
let newST = taskB.subTasks.slice(1)
|
||
|
newST.push(ev.bumpId)
|
||
|
taskB.subTasks = newST
|
||
|
}
|
||
|
}
|
||
|
break
|
||
|
case 'tasks-received':
|
||
|
ev.tasks.forEach(newT => {
|
||
|
let existingTask = getTask(tasks, newT.taskId)
|
||
|
if (!existingTask) {
|
||
|
existingTask = blankCard(
|
||
|
newT.taskId,
|
||
|
newT.name,
|
||
|
newT.color,
|
||
|
newT.timestamp,
|
||
|
newT.parents,
|
||
|
newT.height,
|
||
|
newT.width
|
||
|
)
|
||
|
tasks.push(existingTask)
|
||
|
}
|
||
|
safeMerge(existingTask, newT)
|
||
|
})
|
||
|
|
||
|
// Loop through the new cards and remove invalid references to cards that don't exist on this server
|
||
|
/*changedIndexes.forEach(tId => {
|
||
|
const t = tasks[tId]
|
||
|
let beforeLength = t.subTasks.length
|
||
|
t.subTasks = t.subTasks.filter(stId => taskExists(tasks, stId))
|
||
|
t.priorities = t.priorities.filter(stId => taskExists(tasks, stId))
|
||
|
t.completed = t.completed.filter(stId => taskExists(tasks, stId))
|
||
|
t.deck = t.deck.filter(stId =>
|
||
|
tasks.some(sst => sst.taskId === stId && sst.taskId === sst.name)
|
||
|
)
|
||
|
if (t?.grid?.rows && Object.keys(t.grid.rows).length >= 1) {
|
||
|
let filteredRows = {}
|
||
|
Object.entries(t.grid.rows).forEach(([x, row]) => {
|
||
|
let filteredRow = {}
|
||
|
|
||
|
if (row) {
|
||
|
Object.entries(row).forEach(([y, stId]) => {
|
||
|
if (taskExists(tasks, stId)) {
|
||
|
filteredRow[y] = stId
|
||
|
}
|
||
|
})
|
||
|
if (Object.keys(filteredRow).length < 1) {
|
||
|
filteredRows[x] = {}
|
||
|
} else {
|
||
|
filteredRows[x] = filteredRow
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
t.grid.rows = filteredRows
|
||
|
}
|
||
|
})*/
|
||
|
break
|
||
|
case 'task-visited':
|
||
|
// Remove the avatar from everywhere else
|
||
|
tasks.forEach(task => {
|
||
|
if (task.hasOwnProperty('avatars')) {
|
||
|
task.avatars = task.avatars.filter(
|
||
|
avatarLocation => avatarLocation.memberId !== ev.memberId
|
||
|
)
|
||
|
}
|
||
|
})
|
||
|
if (!theTask.hasOwnProperty('avatars')) {
|
||
|
theTask.avatars = []
|
||
|
}
|
||
|
theTask.avatars.push({
|
||
|
memberId: ev.memberId,
|
||
|
timestamp: ev.timestamp,
|
||
|
area: ev.area,
|
||
|
})
|
||
|
theTask.lastClaimed = ev.timestamp
|
||
|
break
|
||
|
case 'member-charged':
|
||
|
memberTask.boost -= parseFloat(ev.charged)
|
||
|
if (memberTask.boost < 0) {
|
||
|
memberTask.boost = 0
|
||
|
}
|
||
|
break
|
||
|
case 'grid-created':
|
||
|
tasks.push(
|
||
|
blankCard(
|
||
|
ev.taskId,
|
||
|
ev.name,
|
||
|
ev.color,
|
||
|
ev.timestamp,
|
||
|
ev.deck,
|
||
|
ev.height,
|
||
|
ev.width
|
||
|
)
|
||
|
)
|
||
|
break
|
||
|
case 'grid-added':
|
||
|
theTask.pinboard = blankPinboard(ev.height, ev.width, ev.spread)
|
||
|
break
|
||
|
case 'grid-removed':
|
||
|
theTask.pins = []
|
||
|
theTask.pinboard = false
|
||
|
break
|
||
|
case 'grid-resized':
|
||
|
if (!theTask.pinboard) {
|
||
|
theTask.pinboard = blankPinboard(ev.height, ev.width)
|
||
|
}
|
||
|
theTask.pinboard.height = ev.height
|
||
|
theTask.pinboard.width = ev.width
|
||
|
theTask.pinboard.size = ev.size || 9
|
||
|
unpinTasksOutOfBounds(tasks, theTask)
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export default {
|
||
|
aoMuts,
|
||
|
cashMuts,
|
||
|
membersMuts,
|
||
|
resourcesMuts,
|
||
|
memesMuts,
|
||
|
sessionsMuts,
|
||
|
tasksMuts,
|
||
|
POTENTIALS_TO_EXECUTE,
|
||
|
}
|