mirror of
https://github.com/gearheaded/obsidian-github-contributions.git
synced 2026-07-22 07:43:36 +00:00
feat: auto-refresh panel on focus with 5-minute cooldown
This commit is contained in:
parent
74ffdc95c3
commit
af4aa42472
2 changed files with 93 additions and 30 deletions
56
main.js
56
main.js
|
|
@ -36,6 +36,7 @@ var GITHUB_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|||
var GITHUB_USER_URL = "https://api.github.com/user";
|
||||
var GITHUB_CLIENT_ID = "Ov23litfj5GbQ8mw81VV";
|
||||
var MONTHS_SHORT = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
var AUTO_REFRESH_COOLDOWN_MS = 5 * 60 * 1e3;
|
||||
var DEFAULT_SETTINGS = {
|
||||
authMethod: "oauth",
|
||||
githubToken: "",
|
||||
|
|
@ -471,6 +472,11 @@ var ContributionsView = class extends import_obsidian.ItemView {
|
|||
this.tooltipEl = null;
|
||||
this.resizeObserver = null;
|
||||
this.panelWidth = 0;
|
||||
this.lastFetchTime = 0;
|
||||
// cachedData holds the last successfully fetched result so cooldown-skipped
|
||||
// renders (e.g. refocusing the panel) can redraw from memory instead of
|
||||
// re-fetching from GitHub or re-scanning the filesystem.
|
||||
this.cachedData = null;
|
||||
this.plugin = plugin;
|
||||
this.displayYear = plugin.settings.selectedYear;
|
||||
this.displayMonth = new Date().getMonth();
|
||||
|
|
@ -491,11 +497,17 @@ var ContributionsView = class extends import_obsidian.ItemView {
|
|||
if (Math.abs(w - this.panelWidth) > 5) {
|
||||
this.panelWidth = w;
|
||||
if (this.plugin.settings.sizePreset === "fit")
|
||||
void this.render();
|
||||
void this.render(false);
|
||||
}
|
||||
});
|
||||
this.resizeObserver.observe(this.containerEl);
|
||||
this.panelWidth = this.containerEl.clientWidth;
|
||||
this.registerEvent(
|
||||
this.app.workspace.on("active-leaf-change", (leaf) => {
|
||||
if (leaf === this.leaf)
|
||||
void this.render(false);
|
||||
})
|
||||
);
|
||||
await this.render();
|
||||
}
|
||||
async onClose() {
|
||||
|
|
@ -519,7 +531,10 @@ var ContributionsView = class extends import_obsidian.ItemView {
|
|||
const cell = Math.max(5, Math.floor((available - gap * (cols - 1)) / cols));
|
||||
return { cell, gap };
|
||||
}
|
||||
async render() {
|
||||
// force=true bypasses the auto-refresh cooldown (used by the manual refresh
|
||||
// button and the very first render). force=false is used when the panel
|
||||
// regains focus - if data was fetched recently, the cached result is reused.
|
||||
async render(force = true) {
|
||||
var _a;
|
||||
const container = this.containerEl.children[1];
|
||||
container.empty();
|
||||
|
|
@ -538,22 +553,33 @@ var ContributionsView = class extends import_obsidian.ItemView {
|
|||
this.renderEmpty(container, "local");
|
||||
return;
|
||||
}
|
||||
this.renderSkeleton(container);
|
||||
const withinCooldown = Date.now() - this.lastFetchTime < AUTO_REFRESH_COOLDOWN_MS;
|
||||
const canUseCache = !force && withinCooldown && this.cachedData;
|
||||
if (!canUseCache)
|
||||
this.renderSkeleton(container);
|
||||
try {
|
||||
let repos = [];
|
||||
if (needsLocal && s.localRepoRoot) {
|
||||
repos = await discoverRepos(s.localRepoRoot, s.scanDepth);
|
||||
}
|
||||
let repos;
|
||||
let days;
|
||||
let totalGH = 0, totalLocal = 0;
|
||||
if (s.demoMode) {
|
||||
days = buildDemoData(this.displayYear);
|
||||
totalGH = [...days.values()].reduce((a, d) => a + d.githubCount, 0);
|
||||
let totalGH, totalLocal;
|
||||
if (canUseCache && this.cachedData) {
|
||||
({ days, totalGH, totalLocal, repos } = this.cachedData);
|
||||
} else {
|
||||
const result = await buildDayMap(s, this.displayYear, repos);
|
||||
days = result.days;
|
||||
totalGH = result.totalGH;
|
||||
totalLocal = result.totalLocal;
|
||||
repos = [];
|
||||
if (needsLocal && s.localRepoRoot) {
|
||||
repos = await discoverRepos(s.localRepoRoot, s.scanDepth);
|
||||
}
|
||||
if (s.demoMode) {
|
||||
days = buildDemoData(this.displayYear);
|
||||
totalGH = [...days.values()].reduce((a, d) => a + d.githubCount, 0);
|
||||
totalLocal = 0;
|
||||
} else {
|
||||
const result = await buildDayMap(s, this.displayYear, repos);
|
||||
days = result.days;
|
||||
totalGH = result.totalGH;
|
||||
totalLocal = result.totalLocal;
|
||||
}
|
||||
this.cachedData = { days, totalGH, totalLocal, repos };
|
||||
this.lastFetchTime = Date.now();
|
||||
}
|
||||
container.empty();
|
||||
const streaks = calculateStreaks(days, this.displayYear);
|
||||
|
|
|
|||
67
src/main.ts
67
src/main.ts
|
|
@ -41,6 +41,12 @@ const GITHUB_CLIENT_ID = "Ov23litfj5GbQ8mw81VV";
|
|||
|
||||
const MONTHS_SHORT = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
|
||||
|
||||
// Minimum time between automatic data refreshes when the panel regains focus.
|
||||
// Prevents re-fetching from GitHub's API or re-scanning the filesystem every
|
||||
// time the user simply switches back to this tab. The manual refresh button
|
||||
// always bypasses this cooldown.
|
||||
const AUTO_REFRESH_COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
// ########################################################################
|
||||
// Types
|
||||
// ########################################################################
|
||||
|
|
@ -709,6 +715,7 @@ export class ContributionsView extends ItemView {
|
|||
tooltipEl: HTMLDivElement | null = null;
|
||||
private resizeObserver: ResizeObserver | null = null;
|
||||
private panelWidth = 0;
|
||||
private lastFetchTime = 0;
|
||||
|
||||
constructor(leaf: WorkspaceLeaf, plugin: GitHubContributionsPlugin) {
|
||||
super(leaf);
|
||||
|
|
@ -728,11 +735,21 @@ export class ContributionsView extends ItemView {
|
|||
const w = this.containerEl.clientWidth;
|
||||
if (Math.abs(w - this.panelWidth) > 5) {
|
||||
this.panelWidth = w;
|
||||
if (this.plugin.settings.sizePreset === "fit") void this.render();
|
||||
if (this.plugin.settings.sizePreset === "fit") void this.render(false);
|
||||
}
|
||||
});
|
||||
this.resizeObserver.observe(this.containerEl);
|
||||
this.panelWidth = this.containerEl.clientWidth;
|
||||
|
||||
// Re-render (subject to the cooldown) whenever this panel becomes the
|
||||
// active leaf - e.g. the user switches back to this tab after working
|
||||
// elsewhere. registerEvent() handles cleanup automatically on unload.
|
||||
this.registerEvent(
|
||||
this.app.workspace.on("active-leaf-change", (leaf) => {
|
||||
if (leaf === this.leaf) void this.render(false);
|
||||
})
|
||||
);
|
||||
|
||||
await this.render();
|
||||
}
|
||||
|
||||
|
|
@ -761,7 +778,15 @@ export class ContributionsView extends ItemView {
|
|||
return { cell, gap };
|
||||
}
|
||||
|
||||
private async render() {
|
||||
// cachedData holds the last successfully fetched result so cooldown-skipped
|
||||
// renders (e.g. refocusing the panel) can redraw from memory instead of
|
||||
// re-fetching from GitHub or re-scanning the filesystem.
|
||||
private cachedData: { days: Map<string, DayData>; totalGH: number; totalLocal: number; repos: LocalRepo[] } | null = null;
|
||||
|
||||
// force=true bypasses the auto-refresh cooldown (used by the manual refresh
|
||||
// button and the very first render). force=false is used when the panel
|
||||
// regains focus - if data was fetched recently, the cached result is reused.
|
||||
private async render(force = true) {
|
||||
const container = this.containerEl.children[1] as HTMLElement;
|
||||
container.empty();
|
||||
container.addClass("gh-contributions-view");
|
||||
|
|
@ -781,24 +806,36 @@ export class ContributionsView extends ItemView {
|
|||
this.renderEmpty(container, "local"); return;
|
||||
}
|
||||
|
||||
this.renderSkeleton(container);
|
||||
const withinCooldown = Date.now() - this.lastFetchTime < AUTO_REFRESH_COOLDOWN_MS;
|
||||
const canUseCache = !force && withinCooldown && this.cachedData;
|
||||
|
||||
if (!canUseCache) this.renderSkeleton(container);
|
||||
|
||||
try {
|
||||
// Discover repos if needed
|
||||
let repos: LocalRepo[] = [];
|
||||
if (needsLocal && s.localRepoRoot) {
|
||||
repos = await discoverRepos(s.localRepoRoot, s.scanDepth);
|
||||
}
|
||||
|
||||
let repos: LocalRepo[];
|
||||
let days: Map<string, DayData>;
|
||||
let totalGH = 0, totalLocal = 0;
|
||||
let totalGH: number, totalLocal: number;
|
||||
|
||||
if (s.demoMode) {
|
||||
days = buildDemoData(this.displayYear);
|
||||
totalGH = [...days.values()].reduce((a, d) => a + d.githubCount, 0);
|
||||
if (canUseCache && this.cachedData) {
|
||||
({ days, totalGH, totalLocal, repos } = this.cachedData);
|
||||
} else {
|
||||
const result = await buildDayMap(s, this.displayYear, repos);
|
||||
days = result.days; totalGH = result.totalGH; totalLocal = result.totalLocal;
|
||||
// Discover repos if needed
|
||||
repos = [];
|
||||
if (needsLocal && s.localRepoRoot) {
|
||||
repos = await discoverRepos(s.localRepoRoot, s.scanDepth);
|
||||
}
|
||||
|
||||
if (s.demoMode) {
|
||||
days = buildDemoData(this.displayYear);
|
||||
totalGH = [...days.values()].reduce((a, d) => a + d.githubCount, 0);
|
||||
totalLocal = 0;
|
||||
} else {
|
||||
const result = await buildDayMap(s, this.displayYear, repos);
|
||||
days = result.days; totalGH = result.totalGH; totalLocal = result.totalLocal;
|
||||
}
|
||||
|
||||
this.cachedData = { days, totalGH, totalLocal, repos };
|
||||
this.lastFetchTime = Date.now();
|
||||
}
|
||||
|
||||
container.empty();
|
||||
|
|
|
|||
Loading…
Reference in a new issue