rvote-online/src/lib/voting.ts

103 lines
2.4 KiB
TypeScript

import { differenceInDays, addDays } from "date-fns";
// Votes start decaying after this many days
export const DECAY_START_DAYS = 30;
// Votes are fully decayed (0 weight) after this many days
export const DECAY_END_DAYS = 60;
// Score threshold to promote proposal to voting stage
export const PROMOTION_THRESHOLD = 100;
// Voting period duration in days
export const VOTING_PERIOD_DAYS = 7;
/**
* Calculate the effective weight of a vote after decay
* Returns a value between 0 and the original weight
*/
export function getEffectiveWeight(
originalWeight: number,
createdAt: Date
): number {
const age = differenceInDays(new Date(), createdAt);
// No decay if vote is younger than decay start
if (age < DECAY_START_DAYS) {
return originalWeight;
}
// Fully decayed if older than decay end
if (age >= DECAY_END_DAYS) {
return 0;
}
// Linear decay between start and end
const decayProgress =
(age - DECAY_START_DAYS) / (DECAY_END_DAYS - DECAY_START_DAYS);
return Math.round(originalWeight * (1 - decayProgress));
}
/**
* Calculate the decay percentage (0-100) for display
*/
export function getDecayPercentage(createdAt: Date): number {
const age = differenceInDays(new Date(), createdAt);
if (age < DECAY_START_DAYS) {
return 0;
}
if (age >= DECAY_END_DAYS) {
return 100;
}
const decayProgress =
(age - DECAY_START_DAYS) / (DECAY_END_DAYS - DECAY_START_DAYS);
return Math.round(decayProgress * 100);
}
/**
* Calculate when a vote will start decaying
*/
export function getDecayStartDate(createdAt: Date): Date {
return addDays(createdAt, DECAY_START_DAYS);
}
/**
* Calculate when a vote will be fully decayed
*/
export function getFullDecayDate(createdAt: Date): Date {
return addDays(createdAt, DECAY_END_DAYS);
}
/**
* Check if a proposal should be promoted to voting stage
*/
export function shouldPromote(score: number): boolean {
return score >= PROMOTION_THRESHOLD;
}
/**
* Calculate the voting end date from promotion time
*/
export function getVotingEndDate(promotedAt: Date = new Date()): Date {
return addDays(promotedAt, VOTING_PERIOD_DAYS);
}
/**
* Calculate the result of a pass/fail vote
*/
export function calculateVoteResult(
yesVotes: number,
noVotes: number
): "PASSED" | "FAILED" | "TIE" {
if (yesVotes > noVotes) {
return "PASSED";
}
if (noVotes > yesVotes) {
return "FAILED";
}
return "TIE";
}