/** * @file sync_screen_action.c * @brief Backend-independent sync-screen keyboard action dispatcher. */ #include "trainlog/sync_screen_action.h" #include "trainlog/terminal.h" #include "trainlog/presentation.h" static TrainlogSyncScreenAction sync_screen_action( TrainlogSyncScreenEffect effect, TrainlogSyncDirection direction ) { TrainlogSyncScreenAction action; action.effect = effect; action.direction = direction; return action; } void trainlog_sync_screen_state_init( TrainlogSyncScreenState *state ) { if (state == NULL) { return; } state->confirming = false; state->direction = TRAINLOG_SYNC_BIDIRECTIONAL; } TrainlogSyncScreenAction trainlog_sync_screen_dispatch( TrainlogSyncScreenState *state, int key ) { TrainlogSyncDirection direction; if (state == NULL) { return sync_screen_action( TRAINLOG_SYNC_SCREEN_NONE, TRAINLOG_SYNC_BIDIRECTIONAL ); } direction = state->direction; /* CONTRACT: the desktop product exposes one sync operation. Directional * engine modes remain an internal compatibility capability for receipts * and existing callers, but the TUI always requests the complete exchange. */ if (key == 's' || key == 'S') { direction = TRAINLOG_SYNC_BIDIRECTIONAL; } else if (state->confirming) { if (key == '\n' || key == TRAINLOG_KEY_ENTER) { /* CONTRACT: consuming confirmation before execution guarantees * that one Enter event can yield at most one sync-engine call. */ state->confirming = false; return sync_screen_action( TRAINLOG_SYNC_SCREEN_RUN, state->direction ); } if (key == 27) { state->confirming = false; return sync_screen_action( TRAINLOG_SYNC_SCREEN_CANCEL, state->direction ); } return sync_screen_action( TRAINLOG_SYNC_SCREEN_NONE, state->direction ); } else if (key == 'r' || key == 'R') { return sync_screen_action( TRAINLOG_SYNC_SCREEN_REFRESH, state->direction ); } else if (key == 27) { return sync_screen_action( TRAINLOG_SYNC_SCREEN_EXIT, state->direction ); } else { return sync_screen_action( TRAINLOG_SYNC_SCREEN_NONE, state->direction ); } state->direction = direction; state->confirming = true; return sync_screen_action( TRAINLOG_SYNC_SCREEN_CONFIRM, direction ); } TrainlogStatus trainlog_sync_screen_execute( const TrainlogSyncScreenAction *action, TrainlogSyncScreenRunCallback callback, void *context, TrainlogSyncReport *report ) { if ( action == NULL || action->effect != TRAINLOG_SYNC_SCREEN_RUN || callback == NULL || report == NULL ) { return TRAINLOG_STATUS_INVALID_ARGUMENT; } return callback( action->direction, report, context ); } const char *trainlog_sync_screen_direction_label( TrainlogSyncDirection direction ) { if (direction == TRAINLOG_SYNC_ANDROID_TO_PC) { return "Android→PC"; } if (direction == TRAINLOG_SYNC_PC_TO_ANDROID) { return "PC→Android"; } return "PC↔Android"; } const char *trainlog_sync_screen_confirmation( TrainlogSyncDirection direction ) { if (direction == TRAINLOG_SYNC_ANDROID_TO_PC) { return trainlog_presentation_text("sync.confirm.import"); } if (direction == TRAINLOG_SYNC_PC_TO_ANDROID) { return trainlog_presentation_text("sync.confirm.publish"); } return trainlog_presentation_text("sync.confirm.both"); } const char *trainlog_sync_screen_footer( int terminal_columns ) { if (terminal_columns >= 100) { return trainlog_presentation_text("sync.footer.wide"); } return trainlog_presentation_text("sync.footer.compact"); }