shared allowed actions conditions between update and view

[?]
Aug 11, 2025, 9:46 AM
EJPSD5XO43DWUBBZGNQMY4TMCAXL5EWCGX3OEHUERQ5GRASGWQLQC

Dependencies

  • [2] WT3GA27P add cursor with selection
  • [3] UB2ITZJS refresh changed files on FS changes
  • [4] EC3TVL4X add untracked files
  • [5] S2NVIFXR allow to enter record msg
  • [6] YBJRDOTC make all repo actions async
  • [7] D7A7MSIH allow to defer or abandon record, add buttons
  • [8] NRCUG4R2 load changed files src when selected
  • [9] ZVI4AWER woot contents_diff
  • [10] AHWWRC73 navigate log entries
  • [11] TEI5NQ3S add log files selection
  • [12] BFN2VHZS refactor file stuff into sub-mod
  • [13] 23SFYK4Q big view refactor into a new crate
  • [14] MYGIBRRH wip custom theme
  • [15] PKJCFSBM theme improvements
  • [16] XSZZB47U refactor stuff into lib
  • [17] ACDXXAX2 refactor main's updates into smaller fns
  • [18] FVA36HBV restart repo manager task if it crashes
  • [19] ESMM3FEL test selection reindexing
  • [20] 7SSBM4UQ view: refactor repo view
  • [21] I2AG42PA new cols layout
  • [22] SASAN2XC use nav-scrollable
  • [23] YKHE3XMW refactor diffs handling
  • [24] K5YUSV2W auto-scroll to last offset
  • [25] 5MUEECMJ smooth scrolling nav
  • [26] 3TLPJ57B alt scroll via context and couple fixes
  • [27] PTWZYQFR use nav-scrollable for repo status
  • [28] UR4J677R nav for log changes and refactors
  • [29] A6Z4O6RC actions menu
  • [30] 7BLZN73O add a key to refresh repo
  • [31] JZXYSIYD channel selection!
  • [32] 5ZRDYL6K fork channel, fix recording esc key
  • [33] BAUK5BON pimp-up action buttons
  • [34] OJPGHVC3 entire log!
  • [35] 3XRG4BB6 rewritten nav-scrollable!
  • [36] S4WH75Y3 allow to select channels if there are any other. Conditional switch
  • [37] WAOGSCOJ very nice refactor, wip adding channels logs
  • [38] WH57EHNM update tests
  • [39] ELG3UDT6 allow to rm added files
  • [40] HC7ROIBC move main diffs state out of cursor
  • [41] RDRBP7AL auto-scroll status selection
  • [42] KM5PSZ4A watch repo once loaded
  • [43] 3SYSJKYL add app icon
  • [44] A5YBC77V record!
  • [45] Y5ATDI2H convert changed file diffs and load src only if any needs it
  • [46] JE44NYHM display log files diffs
  • [47] 4ELJZGRJ load and store all change diffs at once
  • [48] WGID4LS4 absolutely slayed testing with iced task
  • [49] L6KSEFQI move cursor related stuff into its module
  • [50] 6YZAVBWU Initial commit
  • [51] FDDPOH5R add arrow controls
  • [52] KWTBNTO3 diffs selection and scrolling
  • [53] KT5UYXGK fix selection after adding file, add changed file diffs
  • [54] 4WO3ZJM2 show untracked files' contents

Change contents

  • replacement in inflorescence_view/src/app.rs at line 9
    [37.3495][37.3495:3681]()
    get_entire_log_diffs_nav, get_entire_log_files_nav, get_files_diffs_nav,
    get_status_log_diffs_nav, get_status_log_files_nav, selection, Log,
    ReadyState, RecordMsg, SubState,
    [37.3495]
    [37.3681]
    self as model, action, get_entire_log_diffs_nav, get_entire_log_files_nav,
    get_files_diffs_nav, get_status_log_diffs_nav, get_status_log_files_nav,
    selection, Log, ReadyState, RecordMsg, SubState,
  • edit in inflorescence_view/src/app.rs at line 29
    [13.15358]
    [37.3720]
    #[derive(Debug, Clone)]
    pub enum Msg {
    /// Actions bound to keys
    Action(action::Msg),
    EditRecordMsg(text_editor::Action),
    EditForkChannelName(String),
    }
  • replacement in inflorescence_view/src/app.rs at line 38
    [37.3740][37.3740:3783]()
    state: &'a inflorescence_model::State,
    [37.3740]
    [37.3783]
    state: &'a model::State,
  • replacement in inflorescence_view/src/app.rs at line 45
    [37.3889][37.3889:3926]()
    let inflorescence_model::State {
    [37.3889]
    [28.2191]
    let model::State {
  • edit in inflorescence_view/src/app.rs at line 49
    [37.3940]
    [28.2300]
    allowed_actions,
  • replacement in inflorescence_view/src/app.rs at line 60
    [37.4199][37.4199:4310]()
    SubState::Ready(state) => {
    view_ready(*window_size, repo_path, state, get_diff)
    }
    [37.4199]
    [20.124]
    SubState::Ready(state) => view_ready(
    *window_size,
    repo_path,
    state,
    get_diff,
    allowed_actions,
    ),
  • edit in inflorescence_view/src/app.rs at line 81
    [37.4380]
    [37.4380]
    allowed_actions: &[action::Binding],
  • replacement in inflorescence_view/src/app.rs at line 112
    [34.2089][36.106:172]()
    has_other_channels.then_some(Msg::SelectChannel),
    [34.2089]
    [34.2155]
    has_other_channels
    .then_some(Msg::Action(action::Msg::SelectChannel)),
  • replacement in inflorescence_view/src/app.rs at line 129
    [15.2879][31.4328:4401]()
    .on_press(Msg::Selection(selection::Msg::Select(
    [15.2879]
    [31.4401]
    .on_press(Msg::Action(action::Msg::Selection(selection::Msg::Select(
  • replacement in inflorescence_view/src/app.rs at line 131
    [31.4487][13.16488:16516](),[13.16488][13.16488:16516]()
    )))
    [31.4487]
    [14.7757]
    ))))
  • replacement in inflorescence_view/src/app.rs at line 149
    [15.3005][31.4659:4732]()
    .on_press(Msg::Selection(selection::Msg::Select(
    [15.3005]
    [31.4732]
    .on_press(Msg::Action(action::Msg::Selection(selection::Msg::Select(
  • replacement in inflorescence_view/src/app.rs at line 151
    [31.4821][13.17214:17242](),[13.17214][13.17214:17242]()
    )))
    [31.4821]
    [14.7828]
    ))))
  • replacement in inflorescence_view/src/app.rs at line 179
    [28.3803][28.3803:3875](),[28.3875][29.993:1151]()
    el(button(text("Save")).on_press(Msg::SaveRecord)),
    el(button(text("Postpone")).on_press(Msg::PostponeRecord)),
    el(button(text("Discard")).on_press(Msg::DiscardRecord)),
    [28.3803]
    [28.4027]
    el(button(text("Save"))
    .on_press(Msg::Action(action::Msg::SaveRecord))),
    el(button(text("Postpone"))
    .on_press(Msg::Action(action::Msg::PostponeRecord))),
    el(button(text("Discard"))
    .on_press(Msg::Action(action::Msg::DiscardRecord))),
  • replacement in inflorescence_view/src/app.rs at line 268
    [37.8737][37.8737:8873]()
    Msg::Selection(selection::Msg::Select(selection::Select::LogChangeFile { ix, path: path.clone() }))
    [37.8737]
    [37.8873]
    Msg::Action(action::Msg::Selection(selection::Msg::Select(selection::Select::LogChangeFile { ix, path: path.clone() })))
  • replacement in inflorescence_view/src/app.rs at line 397
    [34.4974][34.4974:5084]()
    .on_press(Msg::Selection(
    selection::Msg::PressDir(selection::Dir::Left),
    [34.4974]
    [34.5084]
    .on_press(Msg::Action(
    action::Msg::Selection(selection::Msg::PressDir(
    selection::Dir::Left,
    )),
  • replacement in inflorescence_view/src/app.rs at line 448
    [37.13195][37.13195:13313]()
    .on_press(Msg::Selection(
    selection::Msg::PressDir(selection::Dir::Left),
    [37.13195]
    [37.13313]
    .on_press(Msg::Action(
    action::Msg::Selection(selection::Msg::PressDir(
    selection::Dir::Left,
    )),
  • replacement in inflorescence_view/src/app.rs at line 542
    [34.8091][34.8091:8223]()
    Msg::Selection(selection::Msg::Select(selection::Select::LogChangeFile { ix, path: path.clone() }))
    [34.8091]
    [34.8223]
    Msg::Action(action::Msg::Selection(selection::Msg::Select(selection::Select::LogChangeFile { ix, path: path.clone() })))
  • replacement in inflorescence_view/src/app.rs at line 576
    [34.9618][34.9618:9728]()
    .on_press(Msg::Selection(
    selection::Msg::PressDir(selection::Dir::Left),
    [34.9618]
    [34.9728]
    .on_press(Msg::Action(
    action::Msg::Selection(selection::Msg::PressDir(
    selection::Dir::Left,
    )),
  • replacement in inflorescence_view/src/app.rs at line 626
    [37.16951][37.16951:17069]()
    .on_press(Msg::Selection(
    selection::Msg::PressDir(selection::Dir::Left),
    [37.16951]
    [37.17069]
    .on_press(Msg::Action(
    action::Msg::Selection(selection::Msg::PressDir(
    selection::Dir::Left,
    )),
  • replacement in inflorescence_view/src/app.rs at line 648
    [31.6810][31.6810:6850]()
    .on_press(Msg::Confirm)
    [31.6810]
    [31.6850]
    .on_press(Msg::Action(action::Msg::Confirm))
  • replacement in inflorescence_view/src/app.rs at line 731
    [32.3487][32.3487:3532]()
    .on_input(Msg::ForkChannelName);
    [32.3487]
    [32.3532]
    .on_input(Msg::EditForkChannelName);
  • replacement in inflorescence_view/src/app.rs at line 736
    [32.3621][37.20093:20172]()
    let actions_inner = actions_inner.push(view_actions(action_state(state)));
    [32.3621]
    [29.2048]
    let actions_inner = actions_inner.push(view_actions(allowed_actions));
  • edit in inflorescence_view/src/app.rs at line 746
    [37.20176][37.20176:20593](),[29.2274][13.24344:24347](),[15.3137][13.24344:24347](),[21.4091][13.24344:24347](),[20.5817][13.24344:24347](),[37.20593][13.24344:24347](),[13.24344][13.24344:24347]()
    #[derive(Debug, Clone)]
    pub enum Msg {
    Confirm,
    Cancel,
    Selection(selection::Msg),
    ToRepo(repo::MsgIn),
    EditRecordMsg(text_editor::Action),
    PostponeRecord,
    SaveRecord,
    DiscardRecord,
    AddUntrackedFile,
    RmAddedFile,
    StartRecord,
    /// Show a list of channels to switch to
    SelectChannel,
    ForkChannel,
    ForkChannelName(String),
    RefreshRepo,
    ShowEntireLog,
    }
  • edit in inflorescence_view/src/app.rs at line 751
    [13.24506][29.2275:2390](),[29.2390][33.242:280](),[33.280][31.7503:7579](),[29.2413][31.7503:7579](),[31.7579][29.2480:2540](),[29.2480][29.2480:2540](),[29.2540][33.281:319](),[33.319][31.7580:7656](),[29.2563][31.7580:7656](),[31.7656][29.2630:2688](),[29.2630][29.2630:2688](),[29.2688][33.320:358](),[33.358][31.7657:7731](),[29.2711][31.7657:7731](),[31.7731][29.2776:2837](),[29.2776][29.2776:2837](),[29.2837][33.359:397](),[33.397][31.7732:7809](),[29.2860][31.7732:7809](),[31.7809][29.2928:2996](),[29.2928][29.2928:2996](),[29.2996][33.398:447](),[33.447][31.7810:7889](),[29.3032][31.7810:7889](),[31.7889][29.3102:3168](),[29.3102][29.3102:3168](),[29.3168][33.448:497](),[33.497][31.7890:7967](),[29.3204][31.7890:7967](),[31.7967][29.3272:3289](),[29.3272][29.3272:3289](),[29.3289][32.3702:3703](),[32.3703][29.3289:3313](),[29.3289][29.3289:3313](),[29.3313][33.498:647](),[33.647][32.3704:3705](),[29.3458][32.3704:3705](),[32.3705][33.648:801](),[33.801][29.3607:3633](),[29.3607][29.3607:3633](),[29.3633][33.802:875](),[33.875][29.3704:3729](),[29.3704][29.3704:3729](),[29.3729][33.876:947](),[33.947][32.3706:3707](),[29.3798][32.3706:3707](),[32.3707][36.173:267](),[36.267][33.1017:1097](),[33.1017][33.1017:1097](),[33.1097][32.3786:3787](),[32.3786][32.3786:3787](),[32.3787][33.1098:1188](),[33.1188][32.3788:3789](),[30.126][32.3788:3789](),[32.3789][36.268:361](),[36.361][37.20594:20688](),[37.20688][33.1360:1425](),[33.1360][33.1360:1425](),[30.126][29.3798:3799](),[33.1425][29.3798:3799](),[31.8192][29.3798:3799](),[29.3798][29.3798:3799](),[29.3799][32.3790:3869](),[32.3869][29.4038:4039](),[29.4038][29.4038:4039](),[29.4039][34.12899:12936](),[31.8230][30.127:155](),[34.12936][30.127:155](),[29.4039][30.127:155](),[30.155][29.4071:4176](),[29.4071][29.4071:4176](),[29.4176][36.362:394](),[36.394][29.4176:4234](),[29.4176][29.4176:4234](),[29.4234][31.8231:8271](),[31.8271][29.4264:4367](),[29.4264][29.4264:4367](),[29.4367][32.3870:3996](),[32.3996][29.4497:4627](),[29.4497][29.4497:4627](),[29.4627][32.3997:4123](),[32.4123][29.4757:4817](),[29.4757][29.4757:4817](),[29.4817][32.4124:4250](),[32.4250][29.4947:4965](),[29.4947][29.4947:4965](),[29.4965][31.8272:8287](),[31.8287][36.395:466](),[36.466][34.13001:13017](),[34.13001][34.13001:13017](),[32.4304][29.4979:5076](),[31.8369][29.4979:5076](),[34.13017][29.4979:5076](),[29.4979][29.4979:5076](),[29.5076][36.467:499](),[36.499][29.5076:5142](),[29.5076][29.5076:5142](),[29.5142][32.4305:4426](),[32.4426][36.500:571](),[36.571][34.13082:13098](),[34.13082][34.13082:13098](),[32.4479][29.5256:5266](),[31.8515][29.5256:5266](),[34.13098][29.5256:5266](),[29.5256][29.5256:5266](),[29.5266][36.572:671](),[36.671][29.5312:5393](),[29.5312][29.5312:5393](),[29.5393][32.4480:4541](),[32.4541][36.672:743](),[36.743][34.13163:13179](),[34.13163][34.13163:13179](),[32.4594][29.5445:5571](),[31.8661][29.5445:5571](),[34.13179][29.5445:5571](),[29.5445][29.5445:5571](),[29.5571][37.20689:20751](),[37.20751][31.8724:8911](),[31.8724][31.8724:8911](),[31.8911][36.744:874](),[36.874][37.20752:20941](),[37.20941][36.874:975](),[36.874][36.874:975](),[36.975][31.9026:9051](),[31.9026][31.9026:9051](),[31.9051][32.4595:4685](),[32.4685][36.976:1057](),[36.1057][37.20942:21045](),[37.21045][34.13180:13777](),[33.1499][34.13180:13777](),[34.13777][32.5080:5090](),[32.5080][32.5080:5090](),[32.5090][29.5571:5579](),[31.9051][29.5571:5579](),[29.5571][29.5571:5579](),[29.5579][32.5091:5150](),[32.5150][30.156:227](),[29.5579][30.156:227](),[30.227][13.24506:24508](),[29.5613][13.24506:24508](),[13.24506][13.24506:24508](),[13.24508][29.5614:5615](),[29.5615][32.5151:5398](),[32.5398][29.5615:5757](),[29.5615][29.5615:5757](),[29.5757][36.1058:1092](),[36.1092][29.5757:5882](),[29.5757][29.5757:5882](),[29.5882][36.1093:1127](),[36.1127][29.5882:5889](),[29.5882][29.5882:5889](),[29.5889][34.13778:13833](),[34.13833][31.9052:9089](),[29.5937][31.9052:9089](),[31.9089][36.1128:1162](),[36.1162][31.9089:9096](),[31.9089][31.9089:9096](),[31.9096][29.5968:6028](),[29.5968][29.5968:6028](),[29.6028][37.21046:21091](),[37.21091][32.5399:5463](),[31.9142][32.5399:5463](),[32.5463][34.13834:13916](),[34.13916][32.5463:5470](),[32.5463][32.5463:5470](),[32.5470][34.13917:14119]()
    }
    fn view_actions<'a>(state: ActionState) -> Element<'a, Msg, Theme> {
    let left = || {
    action_button(
    "←| h",
    "",
    Msg::Selection(selection::Msg::PressDir(selection::Dir::Left)),
    )
    };
    let down = || {
    action_button(
    "↓| j",
    "",
    Msg::Selection(selection::Msg::PressDir(selection::Dir::Down)),
    )
    };
    let up = || {
    action_button(
    "↑| k",
    "",
    Msg::Selection(selection::Msg::PressDir(selection::Dir::Up)),
    )
    };
    let right = || {
    action_button(
    "→| l",
    "",
    Msg::Selection(selection::Msg::PressDir(selection::Dir::Right)),
    )
    };
    let down_no_skip = || {
    action_button(
    "S-(↓| j)",
    "no skip",
    Msg::Selection(selection::Msg::AltPressDir(selection::Dir::Down)),
    )
    };
    let up_no_skip = || {
    action_button(
    "S-(↑| k)",
    "no skip",
    Msg::Selection(selection::Msg::AltPressDir(selection::Dir::Up)),
    )
    };
    let add_untracked =
    || action_button("a", "track file", Msg::AddUntrackedFile);
    let rm_added_file = || action_button("x", "untrack file", Msg::RmAddedFile);
    let start_record = || action_button("r", "record", Msg::StartRecord);
    let save_record = || action_button("C-s", "save record", Msg::SaveRecord);
    let postpone_record =
    || action_button("C-d", "postpone record", Msg::PostponeRecord);
    let discard_record =
    || action_button("C-d", "discard record", Msg::DiscardRecord);
    let select_channel =
    || action_button("c", "select channel", Msg::SelectChannel);
    let fork_channel = || action_button("f", "fork channel", Msg::ForkChannel);
    let refresh_repo =
    || action_button("C-r", "refresh repo", Msg::RefreshRepo);
    let confirm =
    |label: &'static str| action_button("Enter", label, Msg::Confirm);
    let cant_confirm =
    |label: &'static str| el(action_button_inner("Enter", label));
    let cancel = || action_button("Esc", "cancel", Msg::Cancel);
    let forking_channel = matches!(state, ActionState::ForkingChannel { .. });
    #[allow(clippy::let_and_return)]
    let row = match state {
    ActionState::Main {
    selection,
    can_select_right,
    can_record,
    has_other_channels,
    } => {
    let row = row([down(), up()]);
    let row = match selection {
    MainSelection::Untracked => {
    let row = row.push(add_untracked());
    let row = add_if(can_select_right, right, row);
    add_if(can_record, start_record, row)
    }
    MainSelection::AddedFromUntracked => {
    let row = row.push(rm_added_file());
    let row = add_if(can_select_right, right, row);
    add_if(can_record, start_record, row)
    }
    MainSelection::Other => {
    let row = add_if(can_select_right, right, row);
    add_if(can_record, start_record, row)
    }
    };
    let row = add_if(has_other_channels, select_channel, row);
    row
    }
    ActionState::LogChange {
    can_select_right,
    can_record,
    has_other_channels,
    } => {
    let row = row([left(), down(), up()]);
    let row = add_if(can_select_right, right, row);
    let row = add_if(can_record, start_record, row);
    let row = add_if(has_other_channels, select_channel, row);
    row
    }
    ActionState::Diff {
    can_record,
    has_other_channels,
    } => {
    let row = row([left(), down(), up(), down_no_skip(), up_no_skip()]);
    let row = add_if(can_record, start_record, row);
    let row = add_if(has_other_channels, select_channel, row);
    row
    }
    ActionState::Recording => {
    row([save_record(), postpone_record(), discard_record()])
    }
    ActionState::SelectingChannel(state) => match state {
    SwitchingChannelState::NoOtherChannels => row([cancel()]),
    SwitchingChannelState::NothingSelected => {
    row([down(), up(), cancel()])
    }
    SwitchingChannelState::SomethingSelected { can_switch } => {
    let row = row([down(), up(), cancel()]);
    let row = add_if(
    !can_switch,
    || cant_confirm("cannot switch with unrecorded changes"),
    row,
    );
    let row = add_if(can_switch, || confirm("switch channel"), row);
    row
    }
    },
    ActionState::ForkingChannel { empty, unique } => {
    let row = row([]);
    let row = add_if(!empty && unique, || confirm("confirm fork"), row);
    let row =
    add_if(!unique, || cant_confirm("channel already exists"), row);
    let row = row.push(cancel());
    row
    }
    ActionState::EntireLog { can_select_right } => {
    let row = row([down(), up()]);
    let row = add_if(can_select_right, right, row);
    row
    }
    ActionState::EntireLogChange { can_select_right } => {
    let row = row([left(), down(), up()]);
    let row = add_if(can_select_right, right, row);
    row
    }
    ActionState::EntireLogChangeDiff => {
    let row = row([left(), down(), up(), down_no_skip(), up_no_skip()]);
    row
    }
    };
    let row = add_if(!forking_channel, fork_channel, row);
    let row = row.push(refresh_repo());
    el(row.spacing(2).wrap())
    }
    fn add_if<'a, F>(
    predicate: bool,
    button: F,
    row: row::Row<'a, Msg, Theme>,
    ) -> row::Row<'a, Msg, Theme>
    where
    F: Fn() -> Element<'a, Msg, Theme>,
    {
    if predicate {
    row.push(button())
    } else {
    row
    }
    }
    enum ActionState {
    /// Main status
    Main {
    selection: MainSelection,
    can_select_right: bool,
    can_record: bool,
    has_other_channels: bool,
    },
    /// Selected a log change, but not diff
    LogChange {
    can_select_right: bool,
    can_record: bool,
    has_other_channels: bool,
    },
    /// Untracked, changed or status log change's diff
    Diff {
    can_record: bool,
    has_other_channels: bool,
    },
    /// Making a record from current changes
    Recording,
    SelectingChannel(SwitchingChannelState),
    ForkingChannel {
    empty: bool,
    unique: bool,
    },
    /// Viewing entire log
    EntireLog {
    can_select_right: bool,
    },
    /// Viewing entire log with some change selected
    EntireLogChange {
    can_select_right: bool,
    },
    /// Viewing entire log with some diff of a change selected
    EntireLogChangeDiff,
  • edit in inflorescence_view/src/app.rs at line 752
    [29.6030][29.6030:6105](),[29.6105][31.9143:9214](),[31.9214][36.1163:1207](),[36.1207][31.9237:9240](),[31.9237][31.9237:9240](),[31.9240][29.6105:6142](),[29.6105][29.6105:6142](),[29.6142][38.279:332](),[38.332][37.21152:21748](),[37.21152][37.21152:21748]()
    enum MainSelection {
    Untracked,
    AddedFromUntracked,
    Other,
    }
    enum SwitchingChannelState {
    NoOtherChannels,
    NothingSelected,
    SomethingSelected { can_switch: bool },
    }
    /// Determine state for actions menu
    fn action_state(state: &ReadyState) -> ActionState {
    let ReadyState {
    user_id: _,
    repo:
    repo::State {
    dir_name: _,
    channel,
    other_channels,
    untracked_files: _,
    changed_files,
    short_log: _,
    },
    selection,
    navigation,
    record_msg,
    forking_channel_name,
    logs: _,
    } = state;
    let selection::State {
    primary,
    status: status_selection,
    channel: channel_selection,
    entire_log: entire_log_selection,
    held_key: _,
    } = selection;
  • replacement in inflorescence_view/src/app.rs at line 753
    [37.21749][29.6279:6425](),[29.6279][29.6279:6425](),[29.6425][31.9348:9355](),[31.9355][37.21750:21806](),[37.21806][32.5527:5596](),[32.5527][32.5527:5596](),[32.5596][37.21807:21902](),[37.21902][32.5671:5740](),[32.5671][32.5671:5740](),[32.5740][37.21903:23159](),[37.23159][34.14560:14589](),[34.14560][34.14560:14589](),[34.14589][37.23160:23410](),[37.23410][34.14858:14906](),[34.14858][34.14858:14906](),[34.15053][34.15053:15071](),[34.15071][37.23411:25396](),[37.25396][36.1532:1554](),[36.1532][36.1532:1554](),[36.1554][37.25397:27432](),[36.1599][29.7118:7158](),[37.27432][29.7118:7158](),[29.7118][29.7118:7158](),[29.7158][37.27433:27530](),[37.27530][29.7272:7349](),[29.7272][29.7272:7349](),[29.7349][36.1600:1640](),[36.1640][37.27531:27550](),[37.27550][29.7367:7391](),[29.7367][29.7367:7391](),[29.7391][37.27551:27965](),[37.27965][29.7761:7776](),[29.7761][29.7761:7776](),[29.7776][38.333:386](),[38.386][37.28027:28269](),[37.28027][37.28027:28269](),[37.28269][38.387:416](),[38.416][37.28305:29160](),[37.28305][37.28305:29160](),[36.1828][29.8258:8280](),[37.29160][29.8258:8280](),[29.8258][29.8258:8280](),[29.8280][37.29161:29399](),[37.29399][38.417:435]()
    match record_msg {
    Some(RecordMsg::Typing(_)) => return ActionState::Recording,
    Some(RecordMsg::Canceled { .. }) | None => {}
    }
    if let Some(name) = forking_channel_name.as_ref() {
    let name = name.trim();
    let empty = name.is_empty();
    let unique =
    channel != name && !other_channels.iter().any(|n| n == name);
    return ActionState::ForkingChannel { empty, unique };
    }
    match primary {
    selection::Primary::Status => {
    let can_record = !changed_files.is_empty();
    let has_other_channels = !other_channels.is_empty();
    match status_selection {
    Some(selection::Status::UntrackedFile {
    ix: _,
    path,
    diff_selected,
    }) => {
    let id_hash =
    file::id_parts_hash(path, file::Kind::Untracked);
    let nav = get_files_diffs_nav(navigation, id_hash);
    if let Some(nav) = nav {
    if *diff_selected {
    ActionState::Diff {
    can_record,
    has_other_channels,
    }
    } else {
    ActionState::Main {
    selection: MainSelection::Untracked,
    can_select_right:
    nav_scrollable::needs_scrolling(nav),
    can_record,
    has_other_channels,
    }
    }
    } else {
    ActionState::Main {
    selection: MainSelection::Untracked,
    can_select_right: false,
    can_record,
    has_other_channels,
    }
    }
    }
    Some(selection::Status::ChangedFile {
    ix: _,
    path,
    diff_selected,
    }) => {
    let is_added_from_untracked = changed_files
    .get(path)
    .map(|diffs| {
    diffs.iter().any(|diff| {
    matches!(diff, repo::ChangedFileDiff::Add)
    })
    })
    .unwrap_or_default();
    let main_selection = || {
    if is_added_from_untracked {
    MainSelection::AddedFromUntracked
    } else {
    MainSelection::Other
    }
    };
    let id_hash =
    file::id_parts_hash(path, file::Kind::Changed);
    let nav = get_files_diffs_nav(navigation, id_hash);
    if let Some(nav) = nav {
    if *diff_selected {
    ActionState::Diff {
    can_record,
    has_other_channels,
    }
    } else {
    ActionState::Main {
    selection: main_selection(),
    can_select_right:
    nav_scrollable::needs_scrolling(nav),
    can_record,
    has_other_channels,
    }
    }
    } else {
    ActionState::Main {
    selection: main_selection(),
    can_select_right: false,
    can_record,
    has_other_channels,
    }
    }
    }
    Some(selection::Status::LogChange(selection::LogChange {
    ix: _,
    hash,
    message: _,
    file,
    })) => {
    let nav = get_status_log_files_nav(navigation, *hash);
    match file {
    Some(selection::LogChangeFileSelection {
    ix: _,
    path: _,
    diff_selected,
    }) => {
    if let Some(nav) = nav {
    if *diff_selected {
    ActionState::Diff {
    can_record,
    has_other_channels,
    }
    } else {
    ActionState::LogChange {
    can_select_right:
    nav_scrollable::needs_scrolling(nav),
    can_record,
    has_other_channels,
    }
    }
    } else {
    ActionState::LogChange {
    can_select_right: false,
    can_record,
    has_other_channels,
    }
    }
    }
    None => ActionState::Main {
    selection: MainSelection::Other,
    can_select_right: nav
    .map(nav_scrollable::needs_scrolling)
    .unwrap_or_default(),
    can_record,
    has_other_channels,
    },
    }
    }
    None => ActionState::Main {
    selection: MainSelection::Other,
    can_select_right: false,
    can_record,
    has_other_channels,
    },
    }
    }
    selection::Primary::Channel => {
    let sub_state = if other_channels.is_empty() {
    SwitchingChannelState::NoOtherChannels
    } else if channel_selection.is_some() {
    let can_switch = changed_files.is_empty();
    SwitchingChannelState::SomethingSelected { can_switch }
    } else {
    SwitchingChannelState::NothingSelected
    };
    ActionState::SelectingChannel(sub_state)
    }
    selection::Primary::EntireLog => {
    if let Some(selection::LogChange { hash, file, .. }) =
    entire_log_selection
    {
    let nav = get_entire_log_files_nav(navigation, *hash);
    match file {
    Some(selection::LogChangeFileSelection {
    ix: _,
    path: _,
    diff_selected,
    }) => {
    if let Some(nav) = nav {
    if *diff_selected {
    ActionState::EntireLogChangeDiff
    } else {
    ActionState::EntireLogChange {
    can_select_right:
    nav_scrollable::needs_scrolling(nav),
    }
    }
    } else {
    ActionState::EntireLogChange {
    can_select_right: false,
    }
    }
    }
    None => ActionState::EntireLog {
    can_select_right: nav
    .map(nav_scrollable::needs_scrolling)
    .unwrap_or_default(),
    },
    }
    [37.21749]
    [29.8298]
    fn view_actions<'a>(
    allowed_actions: &[action::Binding],
    ) -> Element<'a, Msg, Theme> {
    let buttons: Vec<_> = allowed_actions
    .iter()
    .map(|action::Binding { key, label, msg }| {
    if let Some(msg) = msg {
    action_button(key, label, Msg::Action(msg.clone()))
  • replacement in inflorescence_view/src/app.rs at line 762
    [29.8319][37.29419:29460](),[37.29460][29.8404:8449](),[29.8404][29.8404:8449](),[36.1869][29.8481:8499](),[29.8481][29.8481:8499]()
    ActionState::EntireLog {
    can_select_right: false,
    }
    [29.8319]
    [29.8499]
    el(action_button_inner(key, label))
  • replacement in inflorescence_view/src/app.rs at line 764
    [29.8513][29.8513:8523](),[29.9913][29.9913:9919]()
    }
    }
    [29.8513]
    [29.9919]
    })
    .collect();
    let row = row(buttons);
    el(row.spacing(2).wrap())
  • replacement in inflorescence_view/src/app.rs at line 825
    [34.15549][34.15549:15657]()
    .on_press(Msg::Selection(selection::Msg::Select(
    selection::Select::LogChange {
    [34.15549]
    [34.15657]
    .on_press(Msg::Action(action::Msg::Selection(
    selection::Msg::Select(selection::Select::LogChange {
  • replacement in inflorescence_view/src/app.rs at line 830
    [34.15760][34.15760:15779]()
    },
    [34.15760]
    [34.15779]
    }),
  • replacement in inflorescence_model/src/selection.rs at line 5
    [16.3202][16.3202:3226]()
    #[derive(Debug, Clone)]
    [16.3202]
    [16.3226]
    #[derive(Debug, Clone, PartialEq)]
  • replacement in inflorescence_model/src/selection.rs at line 84
    [16.4104][16.4104:4128]()
    #[derive(Debug, Clone)]
    [16.4104]
    [16.4128]
    #[derive(Debug, Clone, PartialEq)]
  • edit in inflorescence_model/src/lib.rs at line 1
    [37.31726]
    [37.31727]
    pub mod action;
  • edit in inflorescence_model/src/lib.rs at line 34
    [37.32401]
    [37.32401]
    /// Cache allowed actions. Used for action filter and for view.
    pub allowed_actions: Vec<action::Binding>,
  • file addition: action.rs (----------)
    [37.29627]
    use crate::{
    self as model, get_entire_log_diffs_nav, get_entire_log_files_nav,
    get_files_diffs_nav, get_status_log_diffs_nav, get_status_log_files_nav,
    selection, ReadyState, RecordMsg,
    };
    use iced_expl_widget::nav_scrollable;
    use libflorescence::{file, repo};
    #[derive(Debug)]
    pub struct Binding {
    pub key: &'static str,
    pub label: &'static str,
    pub msg: Option<Msg>,
    }
    /// Msgs that are bound to some key(s) and that are only allowed depending on the current state.
    #[derive(Debug, Clone)]
    pub enum Msg {
    Confirm,
    Cancel,
    Selection(selection::Msg),
    PostponeRecord,
    SaveRecord,
    DiscardRecord,
    AddUntrackedFile,
    RmAddedFile,
    StartRecord,
    /// Show a list of channels to switch to
    SelectChannel,
    ForkChannel,
    RefreshRepo,
    ShowEntireLog,
    }
    /// Eq used for action filters. Properties that are not determined by which actions are allowed are ignored.
    pub fn is_same_msg(left: &Msg, right: &Msg) -> bool {
    use Msg::*;
    match (left, right) {
    (Confirm, Confirm) => true,
    (Cancel, Cancel) => true,
    (Selection(left), Selection(right)) => left == right,
    (PostponeRecord, PostponeRecord) => true,
    (SaveRecord, SaveRecord) => true,
    (DiscardRecord, DiscardRecord) => true,
    (AddUntrackedFile, AddUntrackedFile) => true,
    (RmAddedFile, RmAddedFile) => true,
    (StartRecord, StartRecord) => true,
    (SelectChannel, SelectChannel) => true,
    (ForkChannel, ForkChannel) => true,
    (RefreshRepo, RefreshRepo) => true,
    (ShowEntireLog, ShowEntireLog) => true,
    (Confirm, _) => false,
    (Cancel, _) => false,
    (Selection(_), _) => false,
    (PostponeRecord, _) => false,
    (SaveRecord, _) => false,
    (DiscardRecord, _) => false,
    (AddUntrackedFile, _) => false,
    (RmAddedFile, _) => false,
    (StartRecord, _) => false,
    (SelectChannel, _) => false,
    (ForkChannel, _) => false,
    (RefreshRepo, _) => false,
    (ShowEntireLog, _) => false,
    }
    }
    pub fn get_allowed(state: &model::SubState) -> Vec<Binding> {
    match state {
    model::SubState::Loading { .. } => vec![],
    model::SubState::SelectingId { .. } => todo!(),
    model::SubState::Ready(ready_state) => get_ready_allowed(ready_state),
    }
    }
    fn get_ready_allowed(state: &ReadyState) -> Vec<Binding> {
    let State {
    has_other_channels,
    sub,
    } = derive_state(state);
    let left = || Binding {
    key: "←| h",
    label: "",
    msg: Some(Msg::Selection(selection::Msg::PressDir(
    selection::Dir::Left,
    ))),
    };
    let down = || Binding {
    key: "↓| j",
    label: "",
    msg: Some(Msg::Selection(selection::Msg::PressDir(
    selection::Dir::Down,
    ))),
    };
    let up = || Binding {
    key: "↑| k",
    label: "",
    msg: Some(Msg::Selection(selection::Msg::PressDir(selection::Dir::Up))),
    };
    let right = || Binding {
    key: "→| l",
    label: "",
    msg: Some(Msg::Selection(selection::Msg::PressDir(
    selection::Dir::Right,
    ))),
    };
    let down_no_skip = || Binding {
    key: "S-(↓| j)",
    label: "no skip",
    msg: Some(Msg::Selection(selection::Msg::AltPressDir(
    selection::Dir::Down,
    ))),
    };
    let up_no_skip = || Binding {
    key: "S-(↑| k)",
    label: "no skip",
    msg: Some(Msg::Selection(selection::Msg::AltPressDir(
    selection::Dir::Up,
    ))),
    };
    let add_untracked = || Binding {
    key: "a",
    label: "track file",
    msg: Some(Msg::AddUntrackedFile),
    };
    let rm_added_file = || Binding {
    key: "x",
    label: "untrack file",
    msg: Some(Msg::RmAddedFile),
    };
    let start_record = || Binding {
    key: "r",
    label: "record",
    msg: Some(Msg::StartRecord),
    };
    let save_record = || Binding {
    key: "C-s",
    label: "save record",
    msg: Some(Msg::SaveRecord),
    };
    let postpone_record = || Binding {
    key: "C-d",
    label: "postpone record",
    msg: Some(Msg::PostponeRecord),
    };
    let discard_record = || Binding {
    key: "C-d",
    label: "discard record",
    msg: Some(Msg::DiscardRecord),
    };
    let select_channel = || Binding {
    key: "c",
    label: "select channel",
    msg: Some(Msg::SelectChannel),
    };
    let fork_channel = || Binding {
    key: "f",
    label: "fork channel",
    msg: Some(Msg::ForkChannel),
    };
    let refresh_repo = || Binding {
    key: "C-r",
    label: "refresh repo",
    msg: Some(Msg::RefreshRepo),
    };
    let confirm = |label: &'static str| Binding {
    key: "Enter",
    label,
    msg: Some(Msg::Confirm),
    };
    let cant_confirm = |label: &'static str| Binding {
    key: "Enter",
    label,
    msg: None,
    };
    let cancel = || Binding {
    key: "Esc",
    label: "cancel",
    msg: Some(Msg::Cancel),
    };
    let exit_entire_log = || Binding {
    key: "Esc",
    label: "exit entire log",
    msg: Some(Msg::Cancel),
    };
    let show_entire_log = || Binding {
    key: "e",
    label: "entire log",
    msg: Some(Msg::ShowEntireLog),
    };
    let forking_channel = matches!(sub, SubState::ForkingChannel { .. });
    let mut actions = vec![];
    let ma = &mut actions;
    match sub {
    SubState::Main {
    selection,
    can_select_right,
    can_record,
    } => {
    ma.push(down());
    ma.push(up());
    match selection {
    MainSelection::Untracked => {
    ma.push(add_untracked());
    push_if(can_select_right, right, ma);
    push_if(can_record, start_record, ma)
    }
    MainSelection::AddedFromUntracked => {
    ma.push(rm_added_file());
    push_if(can_select_right, right, ma);
    push_if(can_record, start_record, ma)
    }
    MainSelection::Other => {
    push_if(can_select_right, right, ma);
    push_if(can_record, start_record, ma)
    }
    };
    ma.push(show_entire_log());
    push_if(has_other_channels, select_channel, ma);
    }
    SubState::StatusLogChange {
    can_select_right,
    can_record,
    } => {
    ma.push(left());
    ma.push(down());
    ma.push(up());
    push_if(can_select_right, right, ma);
    push_if(can_record, start_record, ma);
    ma.push(show_entire_log());
    push_if(has_other_channels, select_channel, ma);
    }
    SubState::StatusLogDiff { can_record } => {
    ma.push(left());
    ma.push(down());
    ma.push(up());
    ma.push(down_no_skip());
    ma.push(up_no_skip());
    push_if(can_record, start_record, ma);
    ma.push(show_entire_log());
    push_if(has_other_channels, select_channel, ma);
    }
    SubState::Recording => {
    ma.push(save_record());
    ma.push(postpone_record());
    ma.push(discard_record());
    }
    SubState::SelectingChannel(state) => {
    match state {
    SwitchingChannelState::NoOtherChannels => ma.push(cancel()),
    SwitchingChannelState::NothingSelected => {
    ma.push(down());
    ma.push(up());
    ma.push(cancel());
    }
    SwitchingChannelState::SomethingSelected { can_switch } => {
    ma.push(down());
    ma.push(up());
    ma.push(cancel());
    push_if(
    !can_switch,
    || {
    cant_confirm(
    "cannot switch with unrecorded changes",
    )
    },
    ma,
    );
    push_if(can_switch, || confirm("switch channel"), ma);
    }
    };
    ma.push(show_entire_log());
    }
    SubState::ForkingChannel { empty, unique } => {
    push_if(!empty && unique, || confirm("confirm fork"), ma);
    push_if(!unique, || cant_confirm("channel already exists"), ma);
    ma.push(cancel());
    }
    SubState::EntireLog { can_select_right } => {
    ma.push(down());
    ma.push(up());
    push_if(can_select_right, right, ma);
    ma.push(exit_entire_log());
    push_if(has_other_channels, select_channel, ma);
    }
    SubState::EntireLogChange { can_select_right } => {
    ma.push(left());
    ma.push(down());
    ma.push(up());
    push_if(can_select_right, right, ma);
    ma.push(exit_entire_log());
    push_if(has_other_channels, select_channel, ma);
    }
    SubState::EntireLogChangeDiff => {
    ma.push(left());
    ma.push(down());
    ma.push(up());
    ma.push(down_no_skip());
    ma.push(up_no_skip());
    ma.push(exit_entire_log());
    }
    };
    push_if(!forking_channel, fork_channel, ma);
    ma.push(refresh_repo());
    actions
    }
    fn push_if<'a, F>(predicate: bool, to_add: F, actions: &mut Vec<Binding>)
    where
    F: Fn() -> Binding,
    {
    if predicate {
    actions.push(to_add())
    }
    }
    #[derive(Debug, Clone, Copy)]
    struct State {
    has_other_channels: bool,
    sub: SubState,
    }
    #[derive(Debug, Clone, Copy)]
    enum SubState {
    /// Main status
    Main {
    selection: MainSelection,
    can_select_right: bool,
    can_record: bool,
    },
    /// Selected a log change, but not diff
    StatusLogChange {
    can_select_right: bool,
    can_record: bool,
    },
    /// Untracked, changed or status log change's diff
    StatusLogDiff {
    can_record: bool,
    },
    /// Making a record from current changes
    Recording,
    SelectingChannel(SwitchingChannelState),
    ForkingChannel {
    empty: bool,
    unique: bool,
    },
    /// Viewing entire log
    EntireLog {
    can_select_right: bool,
    },
    /// Viewing entire log with some change selected
    EntireLogChange {
    can_select_right: bool,
    },
    /// Viewing entire log with some diff of a change selected
    EntireLogChangeDiff,
    }
    #[derive(Debug, Clone, Copy)]
    enum MainSelection {
    Untracked,
    AddedFromUntracked,
    Other,
    }
    #[derive(Debug, Clone, Copy)]
    enum SwitchingChannelState {
    NoOtherChannels,
    NothingSelected,
    SomethingSelected { can_switch: bool },
    }
    /// Determine state for actions menu
    fn derive_state(state: &ReadyState) -> State {
    let has_other_channels = !state.repo.other_channels.is_empty();
    let sub = derive_sub_state(state);
    State {
    has_other_channels,
    sub,
    }
    }
    /// Determine state for actions menu
    fn derive_sub_state(state: &ReadyState) -> SubState {
    let ReadyState {
    user_id: _,
    repo:
    repo::State {
    dir_name: _,
    channel,
    other_channels,
    untracked_files: _,
    changed_files,
    short_log: _,
    },
    selection,
    navigation,
    record_msg,
    forking_channel_name,
    logs: _,
    } = state;
    let selection::State {
    primary,
    status: status_selection,
    channel: channel_selection,
    entire_log: entire_log_selection,
    held_key: _,
    } = selection;
    match record_msg {
    Some(RecordMsg::Typing(_)) => return SubState::Recording,
    Some(RecordMsg::Canceled { .. }) | None => {
    if let Some(name) = forking_channel_name.as_ref() {
    let name = name.trim();
    let empty = name.is_empty();
    let unique = channel != name
    && !other_channels.iter().any(|n| n == name);
    return SubState::ForkingChannel { empty, unique };
    }
    }
    }
    match primary {
    selection::Primary::Status => {
    let can_record = !changed_files.is_empty();
    match status_selection {
    Some(selection::Status::UntrackedFile {
    ix: _,
    path,
    diff_selected,
    }) => {
    let id_hash =
    file::id_parts_hash(path, file::Kind::Untracked);
    let nav = get_files_diffs_nav(navigation, id_hash);
    if let Some(nav) = nav {
    if *diff_selected {
    SubState::StatusLogDiff { can_record }
    } else {
    SubState::Main {
    selection: MainSelection::Untracked,
    can_select_right:
    nav_scrollable::needs_scrolling(nav),
    can_record,
    }
    }
    } else {
    SubState::Main {
    selection: MainSelection::Untracked,
    can_select_right: false,
    can_record,
    }
    }
    }
    Some(selection::Status::ChangedFile {
    ix: _,
    path,
    diff_selected,
    }) => {
    let is_added_from_untracked = changed_files
    .get(path)
    .map(|diffs| {
    diffs.iter().any(|diff| {
    matches!(diff, repo::ChangedFileDiff::Add)
    })
    })
    .unwrap_or_default();
    let main_selection = || {
    if is_added_from_untracked {
    MainSelection::AddedFromUntracked
    } else {
    MainSelection::Other
    }
    };
    let id_hash =
    file::id_parts_hash(path, file::Kind::Changed);
    let nav = get_files_diffs_nav(navigation, id_hash);
    if let Some(nav) = nav {
    if *diff_selected {
    SubState::StatusLogDiff { can_record }
    } else {
    SubState::Main {
    selection: main_selection(),
    can_select_right:
    nav_scrollable::needs_scrolling(nav),
    can_record,
    }
    }
    } else {
    SubState::Main {
    selection: main_selection(),
    can_select_right: false,
    can_record,
    }
    }
    }
    Some(selection::Status::LogChange(selection::LogChange {
    ix: _,
    hash,
    message: _,
    file,
    })) => match file {
    Some(selection::LogChangeFileSelection {
    ix: _,
    path,
    diff_selected,
    }) => {
    let log_id = file::log_id_parts_hash(*hash, path);
    let nav = get_status_log_diffs_nav(navigation, log_id);
    if let Some(nav) = nav {
    if *diff_selected {
    SubState::StatusLogDiff { can_record }
    } else {
    SubState::StatusLogChange {
    can_record,
    can_select_right:
    nav_scrollable::needs_scrolling(nav),
    }
    }
    } else {
    SubState::StatusLogChange {
    can_record,
    can_select_right: false,
    }
    }
    }
    None => {
    let nav = get_status_log_files_nav(navigation, *hash);
    SubState::Main {
    selection: MainSelection::Other,
    can_select_right: nav.is_some(),
    can_record,
    }
    }
    },
    None => SubState::Main {
    selection: MainSelection::Other,
    can_select_right: false,
    can_record,
    },
    }
    }
    selection::Primary::Channel => {
    let sub_state = if other_channels.is_empty() {
    SwitchingChannelState::NoOtherChannels
    } else if channel_selection.is_some() {
    let can_switch = changed_files.is_empty();
    SwitchingChannelState::SomethingSelected { can_switch }
    } else {
    SwitchingChannelState::NothingSelected
    };
    SubState::SelectingChannel(sub_state)
    }
    selection::Primary::EntireLog => {
    if let Some(selection::LogChange { hash, file, .. }) =
    entire_log_selection
    {
    match file {
    Some(selection::LogChangeFileSelection {
    ix: _,
    path,
    diff_selected,
    }) => {
    let log_id = file::log_id_parts_hash(*hash, path);
    let nav = get_entire_log_diffs_nav(navigation, log_id);
    if let Some(nav) = nav {
    if *diff_selected {
    SubState::EntireLogChangeDiff
    } else {
    SubState::EntireLogChange {
    can_select_right:
    nav_scrollable::needs_scrolling(nav),
    }
    }
    } else {
    SubState::EntireLogChange {
    can_select_right: false,
    }
    }
    }
    None => {
    let nav = get_entire_log_files_nav(navigation, *hash);
    SubState::EntireLog {
    can_select_right: nav.is_some(),
    }
    }
    }
    } else {
    SubState::EntireLog {
    can_select_right: false,
    }
    }
    }
    }
    }
  • edit in inflorescence/src/selection.rs at line 14
    [37.39742][37.39742:39771]()
    use inflorescence_view::app;
  • edit in inflorescence/src/selection.rs at line 1560
    [28.47343][37.75610:75664]()
    dbg!(diff::any_diff_has_contents(diffs));
  • replacement in inflorescence/src/selection.rs at line 1602
    [37.76251][37.76251:76365]()
    Task::done(crate::Msg::View(app::Msg::ToRepo(
    repo::MsgIn::LoadOtherChannelLog(name.clone()),
    [37.76251]
    [37.76365]
    Task::done(crate::Msg::ToRepo(repo::MsgIn::LoadOtherChannelLog(
    name.clone(),
  • replacement in inflorescence/src/selection.rs at line 1653
    [35.17382][35.17382:17498]()
    Task::done(crate::Msg::View(app::Msg::ToRepo(
    repo::MsgIn::GetChangeDiffs { hash },
    )))
    [35.17382]
    [28.49040]
    Task::done(crate::Msg::ToRepo(repo::MsgIn::GetChangeDiffs { hash }))
  • replacement in inflorescence/src/selection.rs at line 1705
    [35.18106][35.18106:18222]()
    Task::done(crate::Msg::View(app::Msg::ToRepo(
    repo::MsgIn::GetChangeDiffs { hash },
    )))
    [35.18106]
    [34.40427]
    Task::done(crate::Msg::ToRepo(repo::MsgIn::GetChangeDiffs { hash }))
  • replacement in inflorescence/src/main.rs at line 11
    [37.79677][37.79677:79774]()
    self as model, Log, Logs, ReadyState, RecordMsg, State as MState,
    SubState as MSubState,
    [37.79677]
    [37.79774]
    self as model, action, Log, Logs, ReadyState, RecordMsg,
  • edit in inflorescence/src/main.rs at line 92
    [9.965]
    [3.1438]
    let sub = model::State {
    window_size,
    repo_path,
    sub: model::SubState::Loading {
    user_ids: Vec::new(),
    repo: None,
    },
    allowed_actions: default(),
    };
  • replacement in inflorescence/src/main.rs at line 106
    [12.164][37.79800:80032]()
    sub: MState {
    window_size,
    repo_path,
    sub: MSubState::Loading {
    user_ids: Vec::new(),
    repo: None,
    },
    },
    [12.164]
    [3.1520]
    sub,
  • edit in inflorescence/src/main.rs at line 128
    [6.6196]
    [6.6196]
    ToRepo(repo::MsgIn),
  • replacement in inflorescence/src/main.rs at line 134
    [11.534][6.6702:6718](),[8.756][6.6702:6718](),[10.945][6.6702:6718](),[13.25858][6.6702:6718](),[4.2512][6.6702:6718]()
    match msg {
    [13.25858]
    [21.4849]
    let task = match msg {
  • replacement in inflorescence/src/main.rs at line 136
    [21.4884][13.25859:25919](),[6.6718][13.25859:25919]()
    Msg::View(msg) => update_from_app_view(state, msg),
    [21.4884]
    [21.4885]
    Msg::View(msg) => update_from_app(state, msg),
  • replacement in inflorescence/src/main.rs at line 140
    [37.80110][37.80110:80169]()
    MSubState::Loading { user_ids, repo } => {
    [37.80110]
    [37.80169]
    model::SubState::Loading { user_ids, repo } => {
  • replacement in inflorescence/src/main.rs at line 143
    [37.80301][37.80301:80371]()
    state.sub.sub = MSubState::Ready(ReadyState {
    [37.80301]
    [37.80371]
    state.sub.sub = model::SubState::Ready(ReadyState {
  • replacement in inflorescence/src/main.rs at line 156
    [37.80845][37.80845:80952]()
    MSubState::SelectingId { .. } => unreachable!(),
    MSubState::Ready(..) => {
    [37.80845]
    [37.80952]
    model::SubState::SelectingId { .. } => unreachable!(),
    model::SubState::Ready(..) => {
  • edit in inflorescence/src/main.rs at line 173
    [18.451]
    [6.6820]
    }
    Msg::ToRepo(msg) => {
    state.repo_tx_in.send(msg).unwrap();
    Task::none()
  • replacement in inflorescence/src/main.rs at line 230
    [13.26292][13.26292:26298]()
    }
    [22.3747]
    [18.452]
    };
    state.sub.allowed_actions = action::get_allowed(&state.sub.sub);
    task
  • replacement in inflorescence/src/main.rs at line 274
    [13.26301][13.26301:26374]()
    fn update_from_app_view(state: &mut State, msg: app::Msg) -> Task<Msg> {
    [13.26301]
    [13.26374]
    fn update_from_app(state: &mut State, msg: app::Msg) -> Task<Msg> {
    // Return early if the action is not allowed in the current state
    if let app::Msg::Action(msg) = &msg
    && let Some(_ready_state) = model::ready(&mut state.sub)
    {
    if !state.sub.allowed_actions.iter().any(
    |action::Binding {
    msg: allowed_msg, ..
    }| {
    if let Some(allowed_msg) = allowed_msg {
    action::is_same_msg(msg, allowed_msg)
    } else {
    false
    }
    },
    ) {
    return Task::none();
    }
    }
  • replacement in inflorescence/src/main.rs at line 295
    [13.26390][31.18092:18123]()
    app::Msg::Confirm => {
    [13.26390]
    [37.82966]
    app::Msg::Action(msg) => update_from_action(state, msg),
    app::Msg::EditRecordMsg(action) => edit_record_msg(state, action),
    app::Msg::EditForkChannelName(name) => {
    if let Some(ReadyState {
    forking_channel_name,
    ..
    }) = model::ready_mut(&mut state.sub)
    {
    *forking_channel_name = Some(name);
    }
    Task::none()
    }
    }
    }
    fn update_from_action(state: &mut State, msg: action::Msg) -> Task<Msg> {
    match msg {
    action::Msg::Confirm => {
  • replacement in inflorescence/src/main.rs at line 358
    [31.18598][31.18598:18628]()
    app::Msg::Cancel => {
    [31.18598]
    [37.83858]
    action::Msg::Cancel => {
  • replacement in inflorescence/src/main.rs at line 385
    [31.18886][37.84753:84791]()
    app::Msg::Selection(msg) => {
    [31.18886]
    [37.84791]
    action::Msg::Selection(msg) => {
  • replacement in inflorescence/src/main.rs at line 405
    [24.3499][13.26614:26733](),[23.4246][13.26614:26733](),[22.6527][13.26614:26733](),[28.9204][13.26614:26733](),[37.85298][13.26614:26733](),[13.26614][13.26614:26733](),[27.4982][17.343:470](),[13.26733][17.343:470](),[17.470][29.12719:12834](),[28.9883][29.12835:13009](),[29.13009][36.3160:3197](),[36.3197][37.85299:85404]()
    app::Msg::ToRepo(msg) => {
    state.repo_tx_in.send(msg).unwrap();
    Task::none()
    }
    app::Msg::EditRecordMsg(action) => edit_record_msg(state, action),
    app::Msg::SaveRecord => save_record(state),
    app::Msg::PostponeRecord => defer_record(state),
    app::Msg::DiscardRecord => abandon_record(state),
    app::Msg::AddUntrackedFile => add_untracked_file(state),
    app::Msg::RmAddedFile => rm_added_file(state),
    app::Msg::StartRecord => start_record(state),
    app::Msg::SelectChannel => {
    if let Some(ReadyState { selection, .. }) =
    model::ready_mut(&mut state.sub)
    [37.85298]
    [37.85404]
    action::Msg::SaveRecord => save_record(state),
    action::Msg::PostponeRecord => defer_record(state),
    action::Msg::DiscardRecord => abandon_record(state),
    action::Msg::AddUntrackedFile => add_untracked_file(state),
    action::Msg::RmAddedFile => rm_added_file(state),
    action::Msg::StartRecord => start_record(state),
    action::Msg::SelectChannel => {
    if let Some(ReadyState {
    selection, repo, ..
    }) = model::ready_mut(&mut state.sub)
    && !repo.other_channels.is_empty()
  • replacement in inflorescence/src/main.rs at line 421
    [32.7415][32.7415:7450]()
    app::Msg::ForkChannel => {
    [32.7415]
    [37.85484]
    action::Msg::ForkChannel => {
  • replacement in inflorescence/src/main.rs at line 432
    [31.19164][32.7615:7660](),[32.7660][37.85704:85914](),[37.85914][32.7765:7814](),[32.7765][32.7765:7814](),[32.7814][30.237:272](),[31.19164][30.237:272](),[29.13009][30.237:272]()
    app::Msg::ForkChannelName(name) => {
    if let Some(ReadyState {
    forking_channel_name,
    ..
    }) = model::ready_mut(&mut state.sub)
    {
    *forking_channel_name = Some(name);
    }
    Task::none()
    }
    app::Msg::RefreshRepo => {
    [31.19164]
    [30.272]
    action::Msg::RefreshRepo => {
  • replacement in inflorescence/src/main.rs at line 439
    [30.448][35.19983:20020]()
    app::Msg::ShowEntireLog => {
    [30.448]
    [37.85915]
    action::Msg::ShowEntireLog => {
  • replacement in inflorescence/src/main.rs at line 701
    [37.90814][37.90814:90868]()
    MSubState::Loading { user_ids, repo: _ } => {
    [37.90814]
    [37.90868]
    model::SubState::Loading { user_ids, repo: _ } => {
  • replacement in inflorescence/src/main.rs at line 704
    [37.90976][37.90976:91022]()
    MSubState::Ready(ReadyState {
    [37.90976]
    [37.91022]
    model::SubState::Ready(ReadyState {
  • replacement in inflorescence/src/main.rs at line 714
    [37.91347][37.91347:91388]()
    MSubState::SelectingId {
    [37.91347]
    [37.91388]
    model::SubState::SelectingId {
  • replacement in inflorescence/src/main.rs at line 722
    [37.91626][37.91626:91733]()
    MSubState::SelectingId { repo, .. } => *repo = Some(repo_state),
    MSubState::Ready(_) => {}
    [37.91626]
    [37.91733]
    model::SubState::SelectingId { repo, .. } => *repo = Some(repo_state),
    model::SubState::Ready(_) => {}
  • replacement in inflorescence/src/main.rs at line 755
    [17.8998][17.8998:9103]()
    Msg::View(app::Msg::ToRepo(
    repo::MsgIn::RefreshChangedAndUntrackedFiles,
    ))
    [17.8998]
    [17.9103]
    Msg::ToRepo(repo::MsgIn::RefreshChangedAndUntrackedFiles)
  • replacement in inflorescence/src/main.rs at line 769
    [37.91951][37.91951:92140]()
    MSubState::Loading { user_ids: _, repo } => *repo = Some(repo_state),
    MSubState::SelectingId { repo, .. } => *repo = Some(repo_state),
    MSubState::Ready(ReadyState {
    [37.91951]
    [37.92140]
    model::SubState::Loading { user_ids: _, repo } => {
    *repo = Some(repo_state)
    }
    model::SubState::SelectingId { repo, .. } => *repo = Some(repo_state),
    model::SubState::Ready(ReadyState {
  • replacement in inflorescence/src/main.rs at line 909
    [19.10218][19.10218:10363]()
    Task::done(Msg::View(app::Msg::ToRepo(
    repo::MsgIn::GetChangeDiffs { hash },
    )))
    [19.10218]
    [19.10363]
    Task::done(Msg::ToRepo(repo::MsgIn::GetChangeDiffs {
    hash,
    }))
  • replacement in inflorescence/src/main.rs at line 943
    [37.93803][37.93803:93926]()
    Task::done(Msg::View(app::Msg::ToRepo(
    repo::MsgIn::LoadOtherChannelLog(name.clone()),
    [37.93803]
    [37.93926]
    Task::done(Msg::ToRepo(repo::MsgIn::LoadOtherChannelLog(
    name.clone(),
  • replacement in inflorescence/src/main.rs at line 1004
    [37.95968][37.95968:96048]()
    Task::done(Msg::View(app::Msg::ToRepo(repo::MsgIn::LoadEntireLog)))
    [37.95968]
    [28.13066]
    Task::done(Msg::ToRepo(repo::MsgIn::LoadEntireLog))
  • edit in inflorescence/src/main.rs at line 1190
    [25.931]
    [2.2794]
    let action = |msg| Some(Msg::View(app::Msg::Action(msg)));
    let selection = |selection| action(action::Msg::Selection(selection));
  • replacement in inflorescence/src/main.rs at line 1198
    [32.8155][31.22615:22750](),[34.54338][31.22615:22750](),[31.22615][31.22615:22750](),[31.22750][25.1058:1083](),[25.1058][25.1058:1083](),[25.1083][31.22751:22884](),[31.22884][25.1207:1232](),[25.1207][25.1207:1232](),[25.1232][31.22885:23020](),[31.23020][25.1358:1383](),[25.1358][25.1358:1383](),[25.1383][31.23021:23157](),[31.23157][25.1510:1535](),[25.1510][25.1510:1535]()
    "j" => Some(Msg::View(app::Msg::Selection(
    selection::Msg::PressDir(selection::Dir::Down),
    ))),
    "k" => Some(Msg::View(app::Msg::Selection(
    selection::Msg::PressDir(selection::Dir::Up),
    ))),
    "h" => Some(Msg::View(app::Msg::Selection(
    selection::Msg::PressDir(selection::Dir::Left),
    ))),
    "l" => Some(Msg::View(app::Msg::Selection(
    selection::Msg::PressDir(selection::Dir::Right),
    ))),
    [34.54338]
    [34.54339]
    "j" => selection(selection::Msg::PressDir(
    selection::Dir::Down,
    )),
    "k" => {
    selection(selection::Msg::PressDir(selection::Dir::Up))
    }
    "h" => selection(selection::Msg::PressDir(
    selection::Dir::Left,
    )),
    "l" => selection(selection::Msg::PressDir(
    selection::Dir::Right,
    )),
  • replacement in inflorescence/src/main.rs at line 1212
    [34.54476][34.54476:54548](),[34.54548][36.3487:3556](),[36.3556][35.21801:21870](),[34.54617][35.21801:21870](),[35.21870][34.54682:54749](),[34.54682][34.54682:54749](),[34.54749][29.13083:13217](),[25.1535][29.13083:13217]()
    "a" => Some(Msg::View(app::Msg::AddUntrackedFile)),
    "c" => Some(Msg::View(app::Msg::SelectChannel)),
    "e" => Some(Msg::View(app::Msg::ShowEntireLog)),
    "f" => Some(Msg::View(app::Msg::ForkChannel)),
    "r" => Some(Msg::View(app::Msg::StartRecord)),
    "x" => Some(Msg::View(app::Msg::RmAddedFile)),
    [34.54476]
    [2.3009]
    "a" => action(action::Msg::AddUntrackedFile),
    "c" => action(action::Msg::SelectChannel),
    "e" => action(action::Msg::ShowEntireLog),
    "f" => action(action::Msg::ForkChannel),
    "r" => action(action::Msg::StartRecord),
    "x" => action(action::Msg::RmAddedFile),
  • replacement in inflorescence/src/main.rs at line 1220
    [2.3059][31.23158:23406]()
    Key::Named(key::Named::Enter) => {
    Some(Msg::View(app::Msg::Confirm))
    }
    Key::Named(key::Named::Escape) => {
    Some(Msg::View(app::Msg::Cancel))
    }
    [2.3059]
    [31.23406]
    Key::Named(key::Named::Enter) => action(action::Msg::Confirm),
    Key::Named(key::Named::Escape) => action(action::Msg::Cancel),
  • replacement in inflorescence/src/main.rs at line 1223
    [31.23461][31.23461:23613]()
    Some(Msg::View(app::Msg::Selection(
    selection::Msg::PressDir(selection::Dir::Down),
    )))
    [31.23461]
    [31.23613]
    selection(selection::Msg::PressDir(selection::Dir::Down))
  • replacement in inflorescence/src/main.rs at line 1226
    [31.23684][31.23684:23834]()
    Some(Msg::View(app::Msg::Selection(
    selection::Msg::PressDir(selection::Dir::Up),
    )))
    [31.23684]
    [31.23834]
    selection(selection::Msg::PressDir(selection::Dir::Up))
  • replacement in inflorescence/src/main.rs at line 1229
    [31.23907][31.23907:24059]()
    Some(Msg::View(app::Msg::Selection(
    selection::Msg::PressDir(selection::Dir::Left),
    )))
    [31.23907]
    [31.24059]
    selection(selection::Msg::PressDir(selection::Dir::Left))
  • replacement in inflorescence/src/main.rs at line 1232
    [31.24133][31.24133:24286]()
    Some(Msg::View(app::Msg::Selection(
    selection::Msg::PressDir(selection::Dir::Right),
    )))
    [31.24133]
    [31.24286]
    selection(selection::Msg::PressDir(selection::Dir::Right))
  • replacement in inflorescence/src/main.rs at line 1240
    [7.2290][29.13218:13283]()
    Some(Msg::View(app::Msg::DiscardRecord))
    [7.2290]
    [13.28327]
    action(action::Msg::DiscardRecord)
  • replacement in inflorescence/src/main.rs at line 1243
    [29.13340][29.13340:13406]()
    Some(Msg::View(app::Msg::PostponeRecord))
    [29.13340]
    [29.13406]
    action(action::Msg::PostponeRecord)
  • replacement in inflorescence/src/main.rs at line 1246
    [13.28405][13.28405:28467]()
    Some(Msg::View(app::Msg::SaveRecord))
    [13.28405]
    [26.106]
    action(action::Msg::SaveRecord)
  • replacement in inflorescence/src/main.rs at line 1249
    [30.505][30.505:590](),[30.590][26.128:185](),[26.128][26.128:185](),[26.185][31.24305:24444](),[31.24444][26.315:422](),[26.315][26.315:422](),[26.422][31.24445:24582](),[31.24582][26.550:578](),[26.550][26.550:578]()
    Some(Msg::View(app::Msg::RefreshRepo))
    }
    "j" if mods == Modifiers::SHIFT => {
    Some(Msg::View(app::Msg::Selection(
    selection::Msg::AltPressDir(selection::Dir::Down),
    )))
    }
    "k" if mods == Modifiers::SHIFT => {
    Some(Msg::View(app::Msg::Selection(
    selection::Msg::AltPressDir(selection::Dir::Up),
    )))
    [30.505]
    [7.2341]
    action(action::Msg::RefreshRepo)
  • replacement in inflorescence/src/main.rs at line 1251
    [7.2363][26.579:636](),[26.636][31.24583:24722](),[31.24722][26.766:873](),[26.766][26.766:873](),[26.873][31.24723:24863](),[31.24863][26.1004:1054](),[26.1004][26.1004:1054]()
    "h" if mods == Modifiers::SHIFT => {
    Some(Msg::View(app::Msg::Selection(
    selection::Msg::AltPressDir(selection::Dir::Left),
    )))
    }
    "l" if mods == Modifiers::SHIFT => {
    Some(Msg::View(app::Msg::Selection(
    selection::Msg::AltPressDir(selection::Dir::Right),
    )))
    }
    [7.2363]
    [5.1554]
    "j" if mods == Modifiers::SHIFT => selection(
    selection::Msg::AltPressDir(selection::Dir::Down),
    ),
    "k" if mods == Modifiers::SHIFT => selection(
    selection::Msg::AltPressDir(selection::Dir::Up),
    ),
    "h" if mods == Modifiers::SHIFT => selection(
    selection::Msg::AltPressDir(selection::Dir::Left),
    ),
    "l" if mods == Modifiers::SHIFT => selection(
    selection::Msg::AltPressDir(selection::Dir::Right),
    ),
  • replacement in inflorescence/src/main.rs at line 1268
    [26.1174][31.24864:25019]()
    Some(Msg::View(app::Msg::Selection(
    selection::Msg::AltPressDir(selection::Dir::Down),
    )))
    [26.1174]
    [26.1320]
    selection(selection::Msg::AltPressDir(selection::Dir::Down))
  • replacement in inflorescence/src/main.rs at line 1271
    [26.1419][31.25020:25173]()
    Some(Msg::View(app::Msg::Selection(
    selection::Msg::AltPressDir(selection::Dir::Up),
    )))
    [26.1419]
    [26.1563]
    selection(selection::Msg::AltPressDir(selection::Dir::Up))
  • replacement in inflorescence/src/main.rs at line 1276
    [26.1700][31.25174:25329]()
    Some(Msg::View(app::Msg::Selection(
    selection::Msg::AltPressDir(selection::Dir::Left),
    )))
    [26.1700]
    [26.1846]
    selection(selection::Msg::AltPressDir(selection::Dir::Left))
  • replacement in inflorescence/src/main.rs at line 1281
    [26.1984][31.25330:25486]()
    Some(Msg::View(app::Msg::Selection(
    selection::Msg::AltPressDir(selection::Dir::Right),
    )))
    [26.1984]
    [26.2131]
    selection(selection::Msg::AltPressDir(
    selection::Dir::Right,
    ))
  • replacement in inflorescence/src/main.rs at line 1294
    [25.2468][31.25487:25624]()
    "j" => Some(Msg::View(app::Msg::Selection(
    selection::Msg::ReleaseDir(selection::Dir::Down),
    [25.2468]
    [25.2596]
    "j" => Some(Msg::View(app::Msg::Action(
    action::Msg::Selection(selection::Msg::ReleaseDir(
    selection::Dir::Down,
    )),
  • replacement in inflorescence/src/main.rs at line 1299
    [25.2621][31.25625:25760]()
    "k" => Some(Msg::View(app::Msg::Selection(
    selection::Msg::ReleaseDir(selection::Dir::Up),
    [25.2621]
    [25.2747]
    "k" => Some(Msg::View(app::Msg::Action(
    action::Msg::Selection(selection::Msg::ReleaseDir(
    selection::Dir::Up,
    )),
  • replacement in inflorescence/src/main.rs at line 1304
    [25.2772][31.25761:25898]()
    "h" => Some(Msg::View(app::Msg::Selection(
    selection::Msg::ReleaseDir(selection::Dir::Left),
    [25.2772]
    [25.2900]
    "h" => Some(Msg::View(app::Msg::Action(
    action::Msg::Selection(selection::Msg::ReleaseDir(
    selection::Dir::Left,
    )),
  • replacement in inflorescence/src/main.rs at line 1309
    [25.2925][31.25899:26037]()
    "l" => Some(Msg::View(app::Msg::Selection(
    selection::Msg::ReleaseDir(selection::Dir::Right),
    [25.2925]
    [25.3054]
    "l" => Some(Msg::View(app::Msg::Action(
    action::Msg::Selection(selection::Msg::ReleaseDir(
    selection::Dir::Right,
    )),
  • replacement in inflorescence/src/main.rs at line 1317
    [25.3184][31.26038:26094]()
    Some(Msg::View(app::Msg::Selection(
    [25.3184]
    [31.26094]
    Some(Msg::View(app::Msg::Action(action::Msg::Selection(
  • replacement in inflorescence/src/main.rs at line 1319
    [31.26168][31.26168:26192]()
    )))
    [31.26168]
    [31.26192]
    ))))
  • replacement in inflorescence/src/main.rs at line 1322
    [31.26263][31.26263:26319]()
    Some(Msg::View(app::Msg::Selection(
    [31.26263]
    [31.26319]
    Some(Msg::View(app::Msg::Action(action::Msg::Selection(
  • replacement in inflorescence/src/main.rs at line 1324
    [31.26391][31.26391:26415]()
    )))
    [31.26391]
    [25.3329]
    ))))
  • replacement in inflorescence/src/main.rs at line 1327
    [25.3569][31.26416:26472]()
    Some(Msg::View(app::Msg::Selection(
    [25.3569]
    [31.26472]
    Some(Msg::View(app::Msg::Action(action::Msg::Selection(
  • replacement in inflorescence/src/main.rs at line 1329
    [31.26546][31.26546:26570]()
    )))
    [31.26546]
    [25.3714]
    ))))
  • replacement in inflorescence/src/main.rs at line 1332
    [25.3788][31.26571:26627]()
    Some(Msg::View(app::Msg::Selection(
    [25.3788]
    [31.26627]
    Some(Msg::View(app::Msg::Action(action::Msg::Selection(
  • replacement in inflorescence/src/main.rs at line 1334
    [31.26702][31.26702:26726]()
    )))
    [31.26702]
    [25.3934]
    ))))