tab to a refresh section on issue list
This commit is contained in:
parent
56dde66aca
commit
dcc2bbaa82
|
|
@ -9,6 +9,7 @@ export interface SavedConfig {
|
||||||
apiKey?: string;
|
apiKey?: string;
|
||||||
repo?: string;
|
repo?: string;
|
||||||
repository?: string;
|
repository?: string;
|
||||||
|
autoRefreshInterval?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LOCAL_CONFIG_PATH = path.join(process.cwd(), 'fjtui.json');
|
export const LOCAL_CONFIG_PATH = path.join(process.cwd(), 'fjtui.json');
|
||||||
|
|
@ -18,7 +19,7 @@ export const GLOBAL_CONFIG_PATH = path.join(os.homedir(), '.config', 'fjtui', 'f
|
||||||
* Loads configuration from either local or global fjtui.json.
|
* Loads configuration from either local or global fjtui.json.
|
||||||
* Local configuration overrides global configuration.
|
* Local configuration overrides global configuration.
|
||||||
*/
|
*/
|
||||||
export function loadSavedConfig(): { url: string; userid: string; token: string; repo: string } {
|
export function loadSavedConfig(): { url: string; userid: string; token: string; repo: string; autoRefreshInterval: number } {
|
||||||
let localConfig: SavedConfig = {};
|
let localConfig: SavedConfig = {};
|
||||||
let globalConfig: SavedConfig = {};
|
let globalConfig: SavedConfig = {};
|
||||||
|
|
||||||
|
|
@ -48,13 +49,14 @@ export function loadSavedConfig(): { url: string; userid: string; token: string;
|
||||||
userid: merged.userid || '',
|
userid: merged.userid || '',
|
||||||
token: merged.token || merged.apiKey || '',
|
token: merged.token || merged.apiKey || '',
|
||||||
repo: merged.repo || merged.repository || '',
|
repo: merged.repo || merged.repository || '',
|
||||||
|
autoRefreshInterval: merged.autoRefreshInterval || 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves settings to the global configuration file.
|
* Saves settings to the global configuration file.
|
||||||
*/
|
*/
|
||||||
export function saveGlobalConfig(config: { url: string; userid: string; token: string | null; repo: string }) {
|
export function saveGlobalConfig(config: { url: string; userid: string; token: string | null; repo: string; autoRefreshInterval?: number }) {
|
||||||
try {
|
try {
|
||||||
const dir = path.dirname(GLOBAL_CONFIG_PATH);
|
const dir = path.dirname(GLOBAL_CONFIG_PATH);
|
||||||
if (!fs.existsSync(dir)) {
|
if (!fs.existsSync(dir)) {
|
||||||
|
|
@ -64,7 +66,8 @@ export function saveGlobalConfig(config: { url: string; userid: string; token: s
|
||||||
url: config.url,
|
url: config.url,
|
||||||
userid: config.userid,
|
userid: config.userid,
|
||||||
token: config.token || '',
|
token: config.token || '',
|
||||||
repo: config.repo
|
repo: config.repo,
|
||||||
|
autoRefreshInterval: config.autoRefreshInterval || 0
|
||||||
}, null, 2);
|
}, null, 2);
|
||||||
fs.writeFileSync(GLOBAL_CONFIG_PATH, data, 'utf-8');
|
fs.writeFileSync(GLOBAL_CONFIG_PATH, data, 'utf-8');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,14 @@ async function bootstrap() {
|
||||||
previousScreen: 'list',
|
previousScreen: 'list',
|
||||||
focusedPane: 'list',
|
focusedPane: 'list',
|
||||||
selectedSettingIndex: 0,
|
selectedSettingIndex: 0,
|
||||||
|
autoRefreshInterval: savedConfig.autoRefreshInterval || 0,
|
||||||
config: {
|
config: {
|
||||||
url: '',
|
url: '',
|
||||||
token: null,
|
token: null,
|
||||||
userid: '',
|
userid: '',
|
||||||
owner: '',
|
owner: '',
|
||||||
repo: '',
|
repo: '',
|
||||||
|
autoRefreshInterval: savedConfig.autoRefreshInterval || 0,
|
||||||
},
|
},
|
||||||
issues: [],
|
issues: [],
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
|
|
@ -128,6 +130,7 @@ async function bootstrap() {
|
||||||
userid: config.userid,
|
userid: config.userid,
|
||||||
token: config.token,
|
token: config.token,
|
||||||
repo: repo,
|
repo: repo,
|
||||||
|
autoRefreshInterval: state.autoRefreshInterval,
|
||||||
});
|
});
|
||||||
console.log(chalk.green('Settings saved successfully to global config file!'));
|
console.log(chalk.green('Settings saved successfully to global config file!'));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
68
src/tui.ts
68
src/tui.ts
|
|
@ -199,6 +199,42 @@ export class TuiEngine {
|
||||||
private launchFrameIndex = 0;
|
private launchFrameIndex = 0;
|
||||||
private launchInterval: NodeJS.Timeout | null = null;
|
private launchInterval: NodeJS.Timeout | null = null;
|
||||||
private maxLaunchFrames = 60;
|
private maxLaunchFrames = 60;
|
||||||
|
private autoRefreshTimer: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
|
private restartAutoRefresh() {
|
||||||
|
if (this.autoRefreshTimer) {
|
||||||
|
clearInterval(this.autoRefreshTimer);
|
||||||
|
this.autoRefreshTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.autoRefreshInterval && this.state.autoRefreshInterval > 0) {
|
||||||
|
this.autoRefreshTimer = setInterval(() => {
|
||||||
|
// We need a way to check if input is active, but isTextInputActive isn't accessible here if we didn't add it.
|
||||||
|
// Wait, isTextInputActive is a method of TuiEngine, or I reverted it?
|
||||||
|
// Ah! I reverted isTextInputActive earlier!
|
||||||
|
const isTextInputActive =
|
||||||
|
this.state.screen === 'setup' ||
|
||||||
|
(this.state.screen === 'repo-picker' && this.state.repoPickerActiveSearch) ||
|
||||||
|
(this.state.screen === 'list' && this.activeSearchInput) ||
|
||||||
|
this.state.screen === 'create-issue' ||
|
||||||
|
this.state.screen === 'add-comment' ||
|
||||||
|
this.state.screen === 'edit-issue' ||
|
||||||
|
this.state.screen === 'add-time' ||
|
||||||
|
this.state.screen === 'set-assignees' ||
|
||||||
|
this.state.screen === 'create-label' ||
|
||||||
|
this.state.screen === 'edit-label';
|
||||||
|
|
||||||
|
if (isTextInputActive) return;
|
||||||
|
|
||||||
|
// Background refresh
|
||||||
|
if (this.state.screen === 'list') {
|
||||||
|
this.loadIssues();
|
||||||
|
} else if (this.state.screen === 'details' && this.state.selectedIssue) {
|
||||||
|
this.reloadSingleIssue();
|
||||||
|
}
|
||||||
|
}, this.state.autoRefreshInterval * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constructor(initialState: AppState) {
|
constructor(initialState: AppState) {
|
||||||
this.state = initialState;
|
this.state = initialState;
|
||||||
|
|
@ -232,6 +268,7 @@ export class TuiEngine {
|
||||||
this.launchDestScreen = 'setup';
|
this.launchDestScreen = 'setup';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.restartAutoRefresh();
|
||||||
this.state.screen = 'launch';
|
this.state.screen = 'launch';
|
||||||
this.startLaunchAnimation();
|
this.startLaunchAnimation();
|
||||||
}
|
}
|
||||||
|
|
@ -249,6 +286,10 @@ export class TuiEngine {
|
||||||
clearInterval(this.launchInterval);
|
clearInterval(this.launchInterval);
|
||||||
this.launchInterval = null;
|
this.launchInterval = null;
|
||||||
}
|
}
|
||||||
|
if (this.autoRefreshTimer) {
|
||||||
|
clearInterval(this.autoRefreshTimer);
|
||||||
|
this.autoRefreshTimer = null;
|
||||||
|
}
|
||||||
// Exit alternate screen buffer and show standard cursor
|
// Exit alternate screen buffer and show standard cursor
|
||||||
process.stdout.write('\x1B[?1049l\x1B[?25h');
|
process.stdout.write('\x1B[?1049l\x1B[?25h');
|
||||||
if (process.stdin.isTTY) {
|
if (process.stdin.isTTY) {
|
||||||
|
|
@ -676,12 +717,12 @@ export class TuiEngine {
|
||||||
|
|
||||||
if (this.state.focusedPane === 'settings') {
|
if (this.state.focusedPane === 'settings') {
|
||||||
if (key && key.name === 'left') {
|
if (key && key.name === 'left') {
|
||||||
this.state.selectedSettingIndex = (this.state.selectedSettingIndex - 1 + 4) % 4;
|
this.state.selectedSettingIndex = (this.state.selectedSettingIndex - 1 + 5) % 5;
|
||||||
this.render();
|
this.render();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (key && key.name === 'right') {
|
if (key && key.name === 'right') {
|
||||||
this.state.selectedSettingIndex = (this.state.selectedSettingIndex + 1) % 4;
|
this.state.selectedSettingIndex = (this.state.selectedSettingIndex + 1) % 5;
|
||||||
this.render();
|
this.render();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -727,6 +768,17 @@ export class TuiEngine {
|
||||||
this.state.currentPage = 1;
|
this.state.currentPage = 1;
|
||||||
this.state.selectedIssueIndex = 0;
|
this.state.selectedIssueIndex = 0;
|
||||||
this.loadIssues();
|
this.loadIssues();
|
||||||
|
} else if (selIdx === 4) {
|
||||||
|
// Auto-Refresh
|
||||||
|
const intervals = [0, 15, 30, 60, 300];
|
||||||
|
const currentIdx = intervals.indexOf(this.state.autoRefreshInterval || 0);
|
||||||
|
this.state.autoRefreshInterval = intervals[(currentIdx + 1) % intervals.length];
|
||||||
|
this.state.config.autoRefreshInterval = this.state.autoRefreshInterval;
|
||||||
|
// Restart timer
|
||||||
|
this.restartAutoRefresh();
|
||||||
|
// Save to config globally
|
||||||
|
saveGlobalConfig(this.state.config);
|
||||||
|
this.render();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1385,8 +1437,18 @@ export class TuiEngine {
|
||||||
const seg2 = buildSegment(2, 'Type', typePlain, typeLabel);
|
const seg2 = buildSegment(2, 'Type', typePlain, typeLabel);
|
||||||
const seg3 = buildSegment(3, 'Sort', sortPlain, sortLabel);
|
const seg3 = buildSegment(3, 'Sort', sortPlain, sortLabel);
|
||||||
|
|
||||||
|
let refreshStr = 'Off';
|
||||||
|
if (this.state.autoRefreshInterval) {
|
||||||
|
if (this.state.autoRefreshInterval >= 60) {
|
||||||
|
refreshStr = `${this.state.autoRefreshInterval / 60}m`;
|
||||||
|
} else {
|
||||||
|
refreshStr = `${this.state.autoRefreshInterval}s`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const seg4 = buildSegment(4, 'Refresh', refreshStr, chalk.bold(refreshStr));
|
||||||
|
|
||||||
const separator = chalk.bold.hex('#4A90E2')(' ─ ');
|
const separator = chalk.bold.hex('#4A90E2')(' ─ ');
|
||||||
const filtersText = ` ${seg0}${separator}${seg1}${separator}${seg2}${separator}${seg3}`;
|
const filtersText = ` ${seg0}${separator}${seg1}${separator}${seg2}${separator}${seg3}${separator}${seg4}`;
|
||||||
|
|
||||||
const filtersPlainLen = stripAnsi(filtersText).length;
|
const filtersPlainLen = stripAnsi(filtersText).length;
|
||||||
const padding = Math.max(0, cols - 2 - filtersPlainLen);
|
const padding = Math.max(0, cols - 2 - filtersPlainLen);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ export interface Config {
|
||||||
userid: string;
|
userid: string;
|
||||||
owner: string;
|
owner: string;
|
||||||
repo: string;
|
repo: string;
|
||||||
|
autoRefreshInterval?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
|
|
@ -95,6 +96,7 @@ export interface AppState {
|
||||||
screen: ScreenType;
|
screen: ScreenType;
|
||||||
previousScreen?: ScreenType;
|
previousScreen?: ScreenType;
|
||||||
config: Config;
|
config: Config;
|
||||||
|
autoRefreshInterval: number;
|
||||||
|
|
||||||
focusedPane: 'list' | 'settings';
|
focusedPane: 'list' | 'settings';
|
||||||
selectedSettingIndex: number;
|
selectedSettingIndex: number;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue