pass fetch options through TUI
This commit is contained in:
parent
3a1500bfbb
commit
44f940c882
6 changed files with 24 additions and 8 deletions
|
|
@ -501,7 +501,7 @@ fn runCli(init: std.process.Init) !u8 {
|
||||||
// loader resolve + union-merge the same way the CLI does.
|
// loader resolve + union-merge the same way the CLI does.
|
||||||
// This is the load-bearing fix for "CLI and TUI report
|
// This is the load-bearing fix for "CLI and TUI report
|
||||||
// different totals" - there's exactly one code path now.
|
// different totals" - there's exactly one code path now.
|
||||||
tui.run(io, gpa_alloc, tui_config, globals.portfolio_patterns, globals.watchlist_path, resolved_theme, cmd_args, today) catch |err| switch (err) {
|
tui.run(io, gpa_alloc, tui_config, globals.portfolio_patterns, globals.watchlist_path, resolved_theme, cmd_args, today, globals.refresh_policy) catch |err| switch (err) {
|
||||||
// tui.run already printed an actionable stderr message
|
// tui.run already printed an actionable stderr message
|
||||||
// for invalid CLI args; surface as exit 1 without a
|
// for invalid CLI args; surface as exit 1 without a
|
||||||
// panic / stack trace.
|
// panic / stack trace.
|
||||||
|
|
|
||||||
10
src/tui.zig
10
src/tui.zig
|
|
@ -502,6 +502,12 @@ pub const App = struct {
|
||||||
local_tz: zfin.market.TimeZone,
|
local_tz: zfin.market.TimeZone,
|
||||||
config: zfin.Config,
|
config: zfin.Config,
|
||||||
svc: *zfin.DataService,
|
svc: *zfin.DataService,
|
||||||
|
/// Cache-vs-network policy from the global `--refresh-data` flag,
|
||||||
|
/// mapped once at init. Threaded into every automatic data fetch
|
||||||
|
/// (the initial `portfolio.load` and each tab's `loadData`) so the
|
||||||
|
/// TUI honors `--refresh-data=never` (offline) and `=force` the same
|
||||||
|
/// way the CLI commands do. Default `.{}` is auto (respect TTL).
|
||||||
|
fetch_options: zfin.FetchOptions = .{},
|
||||||
keymap: keybinds.KeyMap,
|
keymap: keybinds.KeyMap,
|
||||||
theme: theme.Theme,
|
theme: theme.Theme,
|
||||||
active_tab: Tab = .portfolio,
|
active_tab: Tab = .portfolio,
|
||||||
|
|
@ -2510,6 +2516,7 @@ pub fn run(
|
||||||
app_theme: theme.Theme,
|
app_theme: theme.Theme,
|
||||||
args: []const []const u8,
|
args: []const []const u8,
|
||||||
today: zfin.Date,
|
today: zfin.Date,
|
||||||
|
refresh_policy: framework.RefreshPolicy,
|
||||||
) !void {
|
) !void {
|
||||||
const watchlist_path: ?[]const u8 = global_watchlist_path;
|
const watchlist_path: ?[]const u8 = global_watchlist_path;
|
||||||
|
|
||||||
|
|
@ -2593,6 +2600,7 @@ pub fn run(
|
||||||
.local_tz = local_tz,
|
.local_tz = local_tz,
|
||||||
.config = config,
|
.config = config,
|
||||||
.svc = svc,
|
.svc = svc,
|
||||||
|
.fetch_options = cli.fetchOptionsFromPolicy(refresh_policy),
|
||||||
.keymap = keymap,
|
.keymap = keymap,
|
||||||
.theme = app_theme,
|
.theme = app_theme,
|
||||||
.symbol = symbol,
|
.symbol = symbol,
|
||||||
|
|
@ -2684,6 +2692,8 @@ pub fn run(
|
||||||
.progress = symbol_progress.callback(),
|
.progress = symbol_progress.callback(),
|
||||||
.aggregate_progress = aggregate_progress.callback(),
|
.aggregate_progress = aggregate_progress.callback(),
|
||||||
.watchlist_syms = watch_syms.items,
|
.watchlist_syms = watch_syms.items,
|
||||||
|
.force_refresh = app_inst.fetch_options.force_refresh,
|
||||||
|
.skip_network = app_inst.fetch_options.skip_network,
|
||||||
}) catch |err| blk: {
|
}) catch |err| blk: {
|
||||||
std.log.scoped(.tui).warn("portfolio load failed: {t}", .{err});
|
std.log.scoped(.tui).warn("portfolio load failed: {t}", .{err});
|
||||||
break :blk null;
|
break :blk null;
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,10 @@ pub const tab = struct {
|
||||||
/// frees current payload, clears flags, and re-runs the
|
/// frees current payload, clears flags, and re-runs the
|
||||||
/// fetch path.
|
/// fetch path.
|
||||||
pub fn reload(state: *State, app: *App) !void {
|
pub fn reload(state: *State, app: *App) !void {
|
||||||
if (app.symbol.len > 0) {
|
// Offline mode (--refresh-data=never): skip cache invalidation.
|
||||||
|
// loadData can't re-fetch, so dropping the entry would just blank
|
||||||
|
// the tab; instead re-render from the cached entry.
|
||||||
|
if (app.symbol.len > 0 and !app.fetch_options.skip_network) {
|
||||||
app.svc.invalidate(app.symbol, .earnings);
|
app.svc.invalidate(app.symbol, .earnings);
|
||||||
}
|
}
|
||||||
// Clear every flag so loadData has the same starting
|
// Clear every flag so loadData has the same starting
|
||||||
|
|
@ -135,7 +138,7 @@ fn loadData(state: *State, app: *App) void {
|
||||||
state.loaded = true;
|
state.loaded = true;
|
||||||
state.error_msg = null;
|
state.error_msg = null;
|
||||||
|
|
||||||
const result = app.svc.getEarnings(app.symbol, .{}) catch |err| {
|
const result = app.svc.getEarnings(app.symbol, app.fetch_options) catch |err| {
|
||||||
switch (err) {
|
switch (err) {
|
||||||
zfin.DataError.NoApiKey => {
|
zfin.DataError.NoApiKey => {
|
||||||
state.error_msg = "No API key. Set FMP_API_KEY (free at financialmodelingprep.com)";
|
state.error_msg = "No API key. Set FMP_API_KEY (free at financialmodelingprep.com)";
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,10 @@ pub const tab = struct {
|
||||||
pub const deactivate = framework.noopDeactivate(State);
|
pub const deactivate = framework.noopDeactivate(State);
|
||||||
|
|
||||||
pub fn reload(state: *State, app: *App) !void {
|
pub fn reload(state: *State, app: *App) !void {
|
||||||
if (app.symbol.len > 0) {
|
// Offline mode (--refresh-data=never): skip cache invalidation.
|
||||||
|
// loadData can't re-fetch, so dropping the entry would just blank
|
||||||
|
// the tab; instead re-render from the cached entry.
|
||||||
|
if (app.symbol.len > 0 and !app.fetch_options.skip_network) {
|
||||||
app.svc.invalidate(app.symbol, .options);
|
app.svc.invalidate(app.symbol, .options);
|
||||||
}
|
}
|
||||||
// Drop chains first so loadData starts clean.
|
// Drop chains first so loadData starts clean.
|
||||||
|
|
@ -298,7 +301,7 @@ fn loadData(state: *State, app: *App) void {
|
||||||
}
|
}
|
||||||
state.chains = null;
|
state.chains = null;
|
||||||
|
|
||||||
const result = app.svc.getOptions(app.symbol, .{}) catch |err| {
|
const result = app.svc.getOptions(app.symbol, app.fetch_options) catch |err| {
|
||||||
switch (err) {
|
switch (err) {
|
||||||
zfin.DataError.FetchFailed => app.setStatus("CBOE fetch failed (network error)"),
|
zfin.DataError.FetchFailed => app.setStatus("CBOE fetch failed (network error)"),
|
||||||
else => app.setStatus("Error loading options"),
|
else => app.setStatus("Error loading options"),
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ fn loadData(state: *State, app: *App) void {
|
||||||
app.symbol_data.trailing_me_price = null;
|
app.symbol_data.trailing_me_price = null;
|
||||||
app.symbol_data.trailing_me_total = null;
|
app.symbol_data.trailing_me_total = null;
|
||||||
|
|
||||||
const result = app.svc.getTrailingReturns(app.symbol, .{}) catch |err| {
|
const result = app.svc.getTrailingReturns(app.symbol, app.fetch_options) catch |err| {
|
||||||
switch (err) {
|
switch (err) {
|
||||||
zfin.DataError.NoApiKey => app.setStatus("No API key. Set TIINGO_API_KEY"),
|
zfin.DataError.NoApiKey => app.setStatus("No API key. Set TIINGO_API_KEY"),
|
||||||
zfin.DataError.FetchFailed => app.setStatus("Fetch failed (network error or rate limit)"),
|
zfin.DataError.FetchFailed => app.setStatus("Fetch failed (network error or rate limit)"),
|
||||||
|
|
@ -155,7 +155,7 @@ fn loadData(state: *State, app: *App) void {
|
||||||
// Try to load ETF profile (non-fatal, won't show for non-ETFs)
|
// Try to load ETF profile (non-fatal, won't show for non-ETFs)
|
||||||
if (!app.symbol_data.etf_loaded) {
|
if (!app.symbol_data.etf_loaded) {
|
||||||
app.symbol_data.etf_loaded = true;
|
app.symbol_data.etf_loaded = true;
|
||||||
if (app.svc.getEtfProfile(app.symbol, .{})) |etf_result| {
|
if (app.svc.getEtfProfile(app.symbol, app.fetch_options)) |etf_result| {
|
||||||
if (etf_result.data.isEtf()) {
|
if (etf_result.data.isEtf()) {
|
||||||
// Take ownership of the EtfProfile data. We
|
// Take ownership of the EtfProfile data. We
|
||||||
// deliberately don't call etf_result.deinit
|
// deliberately don't call etf_result.deinit
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ fn refreshLiveQuote(state: *State, app: *App, force: bool) void {
|
||||||
// "refreshed Xs ago" header timestamp.
|
// "refreshed Xs ago" header timestamp.
|
||||||
const now_s = std.Io.Timestamp.now(app.io, .real).toSeconds();
|
const now_s = std.Io.Timestamp.now(app.io, .real).toSeconds();
|
||||||
if (!force and !shouldFetchLiveQuote(market.marketSession(now_s), state.live != null, now_s - state.timestamp)) return;
|
if (!force and !shouldFetchLiveQuote(market.marketSession(now_s), state.live != null, now_s - state.timestamp)) return;
|
||||||
if (app.svc.getQuote(app.symbol, .{})) |q| {
|
if (app.svc.getQuote(app.symbol, app.fetch_options)) |q| {
|
||||||
state.live = q;
|
state.live = q;
|
||||||
state.timestamp = now_s;
|
state.timestamp = now_s;
|
||||||
} else |err| std.log.scoped(.quote_tab).debug("{s}: live-quote fetch failed: {t}", .{ app.symbol, err });
|
} else |err| std.log.scoped(.quote_tab).debug("{s}: live-quote fetch failed: {t}", .{ app.symbol, err });
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue