CountersUpdate

This commit is contained in:
2025-10-27 14:06:27 +01:00
parent a902062726
commit 839252e3ef
6 changed files with 100 additions and 64 deletions

View File

@@ -23,6 +23,7 @@ function ensureUUIDs(habits) {
});
}
import { supabase, isSupabaseConfigured } from './supabase';
import { calculateStreaks } from './utils-habit';
import * as local from './storage';
const SYNC_FLAG = 'habitgrid_remote_synced_at';
@@ -172,7 +173,10 @@ export async function toggleCompletion(habitId, dateStr) {
const completions = Array.isArray(target.completions) ? [...target.completions] : [];
const idx = completions.indexOf(dateStr);
if (idx > -1) completions.splice(idx, 1); else completions.push(dateStr);
return updateHabit(habitId, { completions });
// Calculate streaks and preserve personal record (do not decrease longest)
const { currentStreak, longestStreak } = calculateStreaks(completions);
const nextLongest = Math.max(longestStreak, target.longestStreak || 0);
return updateHabit(habitId, { completions, currentStreak, longestStreak: nextLongest });
}

View File

@@ -126,65 +126,7 @@ export const toggleCompletion = (habitId, dateStr) => {
});
};
import { getFrozenDays } from './utils-habit.js';
const calculateStreaks = (completions) => {
if (completions.length === 0) {
return { currentStreak: 0, longestStreak: 0 };
}
// Only use frozen days for streak calculation
const frozenDays = getFrozenDays(completions);
const allValid = Array.from(new Set([...completions, ...frozenDays]));
const sortedDates = allValid
.map(d => new Date(d))
.sort((a, b) => b - a);
let currentStreak = 0;
let longestStreak = 0;
let tempStreak = 1;
const today = new Date();
today.setHours(0, 0, 0, 0);
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);
const mostRecent = sortedDates[0];
mostRecent.setHours(0, 0, 0, 0);
if (mostRecent.getTime() === today.getTime() || mostRecent.getTime() === yesterday.getTime()) {
currentStreak = 1;
for (let i = 1; i < sortedDates.length; i++) {
const current = new Date(sortedDates[i]);
current.setHours(0, 0, 0, 0);
const previous = new Date(sortedDates[i - 1]);
previous.setHours(0, 0, 0, 0);
const diffDays = Math.floor((previous - current) / (1000 * 60 * 60 * 24));
if (diffDays === 1) {
currentStreak++;
tempStreak++;
} else {
break;
}
}
}
tempStreak = 1;
for (let i = 1; i < sortedDates.length; i++) {
const current = new Date(sortedDates[i]);
current.setHours(0, 0, 0, 0);
const previous = new Date(sortedDates[i - 1]);
previous.setHours(0, 0, 0, 0);
const diffDays = Math.floor((previous - current) / (1000 * 60 * 60 * 24));
if (diffDays === 1) {
tempStreak++;
longestStreak = Math.max(longestStreak, tempStreak);
} else {
tempStreak = 1;
}
}
longestStreak = Math.max(longestStreak, currentStreak, 1);
return { currentStreak, longestStreak };
}
import { calculateStreaks } from './utils-habit.js';
export const exportData = () => {
const habits = getHabits();

View File

@@ -40,6 +40,71 @@ export const getWeekdayLabel = (dayIndex) => {
const labels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
return labels[dayIndex];
};
// Calculate current and longest streaks from a list of completion date strings (YYYY-MM-DD)
// Rules:
// - Streaks count consecutive days
// - Today or yesterday must be present to have a non-zero current streak
// - We also include "frozen" days (one missed day per month sandwiched by completions)
// - Longest streak is at least 1 if there is at least one completion
export function calculateStreaks(completions) {
if (!Array.isArray(completions) || completions.length === 0) {
return { currentStreak: 0, longestStreak: 0 };
}
// Only use frozen days for streak calculation
const frozenDays = getFrozenDays(completions);
const allValid = Array.from(new Set([...(completions || []), ...frozenDays]));
const sortedDates = allValid
.map(d => new Date(d))
.sort((a, b) => b - a);
let currentStreak = 0;
let longestStreak = 0;
let tempStreak = 1;
const today = new Date();
today.setHours(0, 0, 0, 0);
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);
const mostRecent = sortedDates[0];
mostRecent.setHours(0, 0, 0, 0);
if (mostRecent.getTime() === today.getTime() || mostRecent.getTime() === yesterday.getTime()) {
currentStreak = 1;
for (let i = 1; i < sortedDates.length; i++) {
const current = new Date(sortedDates[i]);
current.setHours(0, 0, 0, 0);
const previous = new Date(sortedDates[i - 1]);
previous.setHours(0, 0, 0, 0);
const diffDays = Math.floor((previous - current) / (1000 * 60 * 60 * 24));
if (diffDays === 1) {
currentStreak++;
tempStreak++;
} else {
break;
}
}
}
tempStreak = 1;
for (let i = 1; i < sortedDates.length; i++) {
const current = new Date(sortedDates[i]);
current.setHours(0, 0, 0, 0);
const previous = new Date(sortedDates[i - 1]);
previous.setHours(0, 0, 0, 0);
const diffDays = Math.floor((previous - current) / (1000 * 60 * 60 * 24));
if (diffDays === 1) {
tempStreak++;
longestStreak = Math.max(longestStreak, tempStreak);
} else {
tempStreak = 1;
}
}
longestStreak = Math.max(longestStreak, currentStreak, 1);
return { currentStreak, longestStreak };
}
// Returns array of frozen days (date strings) for a given completions array
export function getFrozenDays(completions) {
// Map: month string -> frozen day string