@ondrej-svec/hog 1.7.0 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +111 -29
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1317,6 +1317,36 @@ async function triggerCompletionActionAsync(action, repoName, issueNumber) {
|
|
|
1317
1317
|
break;
|
|
1318
1318
|
}
|
|
1319
1319
|
}
|
|
1320
|
+
function applyBulkOptimisticStatusUpdates(ids, optionId, repos, config2, mutateData, registerPendingMutation) {
|
|
1321
|
+
for (const id of ids) {
|
|
1322
|
+
const ctx = findIssueContext(repos, id, config2);
|
|
1323
|
+
if (!(ctx.issue && ctx.repoName)) continue;
|
|
1324
|
+
const { issue: ctxIssue, repoName: ctxRepo, statusOptions: ctxOpts } = ctx;
|
|
1325
|
+
mutateData((data) => optimisticSetStatus(data, ctxRepo, ctxIssue.number, ctxOpts, optionId));
|
|
1326
|
+
const ctxStatusName = ctxOpts.find((o) => o.id === optionId)?.name;
|
|
1327
|
+
if (ctxStatusName) {
|
|
1328
|
+
registerPendingMutation?.(ctxRepo, ctxIssue.number, { projectStatus: ctxStatusName });
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
function resolveOptionName(repos, ids, config2, optionId) {
|
|
1333
|
+
for (const id of ids) {
|
|
1334
|
+
const name = findIssueContext(repos, id, config2).statusOptions.find(
|
|
1335
|
+
(o) => o.id === optionId
|
|
1336
|
+
)?.name;
|
|
1337
|
+
if (name) return name;
|
|
1338
|
+
}
|
|
1339
|
+
return optionId;
|
|
1340
|
+
}
|
|
1341
|
+
function clearFailedMutations(failedIds, clearFn) {
|
|
1342
|
+
if (!clearFn) return;
|
|
1343
|
+
for (const failedId of failedIds) {
|
|
1344
|
+
const lastColon = failedId.lastIndexOf(":");
|
|
1345
|
+
const failedRepo = failedId.slice(3, lastColon);
|
|
1346
|
+
const failedIssueNumber = parseInt(failedId.slice(lastColon + 1), 10);
|
|
1347
|
+
clearFn(failedRepo, failedIssueNumber);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1320
1350
|
function optimisticSetStatus(data, repoName, issueNumber, statusOptions, optionId) {
|
|
1321
1351
|
const statusName = statusOptions.find((o) => o.id === optionId)?.name;
|
|
1322
1352
|
if (!statusName) return data;
|
|
@@ -1341,16 +1371,22 @@ function useActions({
|
|
|
1341
1371
|
refresh,
|
|
1342
1372
|
mutateData,
|
|
1343
1373
|
onOverlayDone,
|
|
1344
|
-
pushEntry
|
|
1374
|
+
pushEntry,
|
|
1375
|
+
registerPendingMutation,
|
|
1376
|
+
clearPendingMutation
|
|
1345
1377
|
}) {
|
|
1346
1378
|
const configRef = useRef2(config2);
|
|
1347
1379
|
const reposRef = useRef2(repos);
|
|
1348
1380
|
const selectedIdRef = useRef2(selectedId);
|
|
1349
1381
|
const pushEntryRef = useRef2(pushEntry);
|
|
1382
|
+
const registerPendingMutationRef = useRef2(registerPendingMutation);
|
|
1383
|
+
const clearPendingMutationRef = useRef2(clearPendingMutation);
|
|
1350
1384
|
configRef.current = config2;
|
|
1351
1385
|
reposRef.current = repos;
|
|
1352
1386
|
selectedIdRef.current = selectedId;
|
|
1353
1387
|
pushEntryRef.current = pushEntry;
|
|
1388
|
+
registerPendingMutationRef.current = registerPendingMutation;
|
|
1389
|
+
clearPendingMutationRef.current = clearPendingMutation;
|
|
1354
1390
|
const handlePick = useCallback2(() => {
|
|
1355
1391
|
const ctx = findIssueContext(reposRef.current, selectedIdRef.current, configRef.current);
|
|
1356
1392
|
if (!(ctx.issue && ctx.repoConfig)) return;
|
|
@@ -1433,6 +1469,12 @@ function useActions({
|
|
|
1433
1469
|
mutateData(
|
|
1434
1470
|
(data) => optimisticSetStatus(data, repoName, issue.number, statusOptions, optionId)
|
|
1435
1471
|
);
|
|
1472
|
+
const statusName = statusOptions.find((o) => o.id === optionId)?.name;
|
|
1473
|
+
if (statusName) {
|
|
1474
|
+
registerPendingMutationRef.current?.(repoName, issue.number, {
|
|
1475
|
+
projectStatus: statusName
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1436
1478
|
const t = toast.loading("Moving...");
|
|
1437
1479
|
const projectConfig = {
|
|
1438
1480
|
projectNumber: repoConfig.projectNumber,
|
|
@@ -1472,6 +1514,7 @@ function useActions({
|
|
|
1472
1514
|
status: "error",
|
|
1473
1515
|
ago: Date.now()
|
|
1474
1516
|
});
|
|
1517
|
+
clearPendingMutationRef.current?.(repoName, issue.number);
|
|
1475
1518
|
refresh();
|
|
1476
1519
|
}).finally(() => {
|
|
1477
1520
|
onOverlayDone();
|
|
@@ -1694,15 +1737,14 @@ ${dueLine}` : dueLine;
|
|
|
1694
1737
|
);
|
|
1695
1738
|
const handleBulkStatusChange = useCallback2(
|
|
1696
1739
|
async (ids, optionId) => {
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
}
|
|
1740
|
+
applyBulkOptimisticStatusUpdates(
|
|
1741
|
+
ids,
|
|
1742
|
+
optionId,
|
|
1743
|
+
reposRef.current,
|
|
1744
|
+
configRef.current,
|
|
1745
|
+
mutateData,
|
|
1746
|
+
registerPendingMutationRef.current
|
|
1747
|
+
);
|
|
1706
1748
|
const t = toast.loading(`Moving ${ids.size} issue${ids.size > 1 ? "s" : ""}...`);
|
|
1707
1749
|
const failed = [];
|
|
1708
1750
|
for (const id of ids) {
|
|
@@ -1724,18 +1766,12 @@ ${dueLine}` : dueLine;
|
|
|
1724
1766
|
}
|
|
1725
1767
|
const total = ids.size;
|
|
1726
1768
|
const ok = total - failed.length;
|
|
1727
|
-
const optionName = (
|
|
1728
|
-
for (const id of ids) {
|
|
1729
|
-
const ctx = findIssueContext(reposRef.current, id, configRef.current);
|
|
1730
|
-
const name = ctx.statusOptions.find((o) => o.id === optionId)?.name;
|
|
1731
|
-
if (name) return name;
|
|
1732
|
-
}
|
|
1733
|
-
return optionId;
|
|
1734
|
-
})();
|
|
1769
|
+
const optionName = resolveOptionName(reposRef.current, ids, configRef.current, optionId);
|
|
1735
1770
|
if (failed.length === 0) {
|
|
1736
1771
|
t.resolve(`Moved ${total} issue${total > 1 ? "s" : ""} to ${optionName}`);
|
|
1737
1772
|
} else {
|
|
1738
1773
|
t.reject(`${ok} moved to ${optionName}, ${failed.length} failed`);
|
|
1774
|
+
clearFailedMutations(failed, clearPendingMutationRef.current);
|
|
1739
1775
|
refresh();
|
|
1740
1776
|
}
|
|
1741
1777
|
return failed;
|
|
@@ -1770,6 +1806,24 @@ var init_use_actions = __esm({
|
|
|
1770
1806
|
// src/board/hooks/use-data.ts
|
|
1771
1807
|
import { Worker } from "worker_threads";
|
|
1772
1808
|
import { useCallback as useCallback3, useEffect, useRef as useRef3, useState as useState2 } from "react";
|
|
1809
|
+
function applyPendingMutations(data, pending) {
|
|
1810
|
+
const now = Date.now();
|
|
1811
|
+
for (const [key, m] of pending) {
|
|
1812
|
+
if (m.expiresAt <= now) pending.delete(key);
|
|
1813
|
+
}
|
|
1814
|
+
if (pending.size === 0) return data;
|
|
1815
|
+
return {
|
|
1816
|
+
...data,
|
|
1817
|
+
repos: data.repos.map((rd) => ({
|
|
1818
|
+
...rd,
|
|
1819
|
+
issues: rd.issues.map((issue) => {
|
|
1820
|
+
const mutation = pending.get(`${rd.repo.name}:${issue.number}`);
|
|
1821
|
+
if (!mutation || mutation.expiresAt <= now) return issue;
|
|
1822
|
+
return mutation.projectStatus !== void 0 ? { ...issue, projectStatus: mutation.projectStatus } : issue;
|
|
1823
|
+
})
|
|
1824
|
+
}))
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1773
1827
|
function refreshAgeColor(lastRefresh) {
|
|
1774
1828
|
if (!lastRefresh) return "gray";
|
|
1775
1829
|
const age = Date.now() - lastRefresh.getTime();
|
|
@@ -1782,18 +1836,21 @@ function useData(config2, options, refreshIntervalMs) {
|
|
|
1782
1836
|
const activeRequestRef = useRef3(null);
|
|
1783
1837
|
const workerRef = useRef3(null);
|
|
1784
1838
|
const intervalRef = useRef3(null);
|
|
1839
|
+
const pendingMutationsRef = useRef3(/* @__PURE__ */ new Map());
|
|
1785
1840
|
const configRef = useRef3(config2);
|
|
1786
1841
|
const optionsRef = useRef3(options);
|
|
1787
1842
|
configRef.current = config2;
|
|
1788
1843
|
optionsRef.current = options;
|
|
1789
|
-
const refresh = useCallback3(() => {
|
|
1844
|
+
const refresh = useCallback3((silent = false) => {
|
|
1790
1845
|
if (activeRequestRef.current) {
|
|
1791
1846
|
activeRequestRef.current.canceled = true;
|
|
1792
1847
|
}
|
|
1793
1848
|
workerRef.current?.terminate();
|
|
1794
1849
|
const token = { canceled: false };
|
|
1795
1850
|
activeRequestRef.current = token;
|
|
1796
|
-
|
|
1851
|
+
if (!silent) {
|
|
1852
|
+
setState((prev) => ({ ...prev, isRefreshing: true }));
|
|
1853
|
+
}
|
|
1797
1854
|
const worker = new Worker(
|
|
1798
1855
|
new URL(
|
|
1799
1856
|
import.meta.url.endsWith(".ts") ? "../fetch-worker.ts" : "./fetch-worker.js",
|
|
@@ -1809,11 +1866,12 @@ function useData(config2, options, refreshIntervalMs) {
|
|
|
1809
1866
|
return;
|
|
1810
1867
|
}
|
|
1811
1868
|
if (msg.type === "success" && msg.data) {
|
|
1812
|
-
const
|
|
1813
|
-
|
|
1814
|
-
for (const ev of
|
|
1869
|
+
const raw = msg.data;
|
|
1870
|
+
raw.fetchedAt = new Date(raw.fetchedAt);
|
|
1871
|
+
for (const ev of raw.activity) {
|
|
1815
1872
|
ev.timestamp = new Date(ev.timestamp);
|
|
1816
1873
|
}
|
|
1874
|
+
const data = applyPendingMutations(raw, pendingMutationsRef.current);
|
|
1817
1875
|
setState({
|
|
1818
1876
|
status: "success",
|
|
1819
1877
|
data,
|
|
@@ -1862,7 +1920,7 @@ function useData(config2, options, refreshIntervalMs) {
|
|
|
1862
1920
|
if (refreshIntervalMs <= 0) return;
|
|
1863
1921
|
intervalRef.current = setInterval(() => {
|
|
1864
1922
|
if (!stateRef.current.autoRefreshPaused) {
|
|
1865
|
-
refresh();
|
|
1923
|
+
refresh(true);
|
|
1866
1924
|
}
|
|
1867
1925
|
}, refreshIntervalMs);
|
|
1868
1926
|
return () => {
|
|
@@ -1891,7 +1949,27 @@ function useData(config2, options, refreshIntervalMs) {
|
|
|
1891
1949
|
const resumeAutoRefresh = useCallback3(() => {
|
|
1892
1950
|
setState((prev) => ({ ...prev, autoRefreshPaused: false }));
|
|
1893
1951
|
}, []);
|
|
1894
|
-
|
|
1952
|
+
const registerPendingMutation = useCallback3(
|
|
1953
|
+
(repoName, issueNumber, fields, ttlMs = 9e4) => {
|
|
1954
|
+
pendingMutationsRef.current.set(`${repoName}:${issueNumber}`, {
|
|
1955
|
+
...fields,
|
|
1956
|
+
expiresAt: Date.now() + ttlMs
|
|
1957
|
+
});
|
|
1958
|
+
},
|
|
1959
|
+
[]
|
|
1960
|
+
);
|
|
1961
|
+
const clearPendingMutation = useCallback3((repoName, issueNumber) => {
|
|
1962
|
+
pendingMutationsRef.current.delete(`${repoName}:${issueNumber}`);
|
|
1963
|
+
}, []);
|
|
1964
|
+
return {
|
|
1965
|
+
...state,
|
|
1966
|
+
refresh,
|
|
1967
|
+
mutateData,
|
|
1968
|
+
pauseAutoRefresh,
|
|
1969
|
+
resumeAutoRefresh,
|
|
1970
|
+
registerPendingMutation,
|
|
1971
|
+
clearPendingMutation
|
|
1972
|
+
};
|
|
1895
1973
|
}
|
|
1896
1974
|
var INITIAL_STATE, STALE_THRESHOLDS, MAX_REFRESH_FAILURES;
|
|
1897
1975
|
var init_use_data = __esm({
|
|
@@ -2271,7 +2349,7 @@ function navReducer(state, action) {
|
|
|
2271
2349
|
const collapsedSections = isFirstLoad ? new Set(sections.filter((s) => s === "activity")) : state.collapsedSections;
|
|
2272
2350
|
const selectionValid = state.selectedId != null && action.items.some((i) => i.id === state.selectedId);
|
|
2273
2351
|
if (!isFirstLoad && selectionValid && arraysEqual(sections, state.sections)) {
|
|
2274
|
-
return state;
|
|
2352
|
+
return state.allItems === action.items ? state : { ...state, allItems: action.items };
|
|
2275
2353
|
}
|
|
2276
2354
|
if (selectionValid) {
|
|
2277
2355
|
const selected = action.items.find((i) => i.id === state.selectedId);
|
|
@@ -5197,7 +5275,9 @@ function Dashboard({ config: config2, options, activeProfile }) {
|
|
|
5197
5275
|
refresh,
|
|
5198
5276
|
mutateData,
|
|
5199
5277
|
pauseAutoRefresh,
|
|
5200
|
-
resumeAutoRefresh
|
|
5278
|
+
resumeAutoRefresh,
|
|
5279
|
+
registerPendingMutation,
|
|
5280
|
+
clearPendingMutation
|
|
5201
5281
|
} = useData(config2, options, refreshMs);
|
|
5202
5282
|
const allRepos = useMemo3(() => data?.repos ?? [], [data?.repos]);
|
|
5203
5283
|
const allTasks = useMemo3(
|
|
@@ -5268,7 +5348,9 @@ function Dashboard({ config: config2, options, activeProfile }) {
|
|
|
5268
5348
|
refresh,
|
|
5269
5349
|
mutateData,
|
|
5270
5350
|
onOverlayDone: ui.exitOverlay,
|
|
5271
|
-
pushEntry
|
|
5351
|
+
pushEntry,
|
|
5352
|
+
registerPendingMutation,
|
|
5353
|
+
clearPendingMutation
|
|
5272
5354
|
});
|
|
5273
5355
|
const pendingPickRef = useRef13(null);
|
|
5274
5356
|
const labelCacheRef = useRef13({});
|
|
@@ -6916,7 +6998,7 @@ function resolveProjectId(projectId) {
|
|
|
6916
6998
|
process.exit(1);
|
|
6917
6999
|
}
|
|
6918
7000
|
var program = new Command();
|
|
6919
|
-
program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.7.
|
|
7001
|
+
program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.7.2").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
|
|
6920
7002
|
const opts = thisCommand.opts();
|
|
6921
7003
|
if (opts.json) setFormat("json");
|
|
6922
7004
|
if (opts.human) setFormat("human");
|