v0.23.2 bfd5eb4

get_last_restorable_session.go RawHistoryFolder

1package usecase
2
3import (
4	"context"
5
6	"github.com/bnema/dumber/internal/domain/entity"
7	"github.com/bnema/dumber/internal/domain/repository"
8	"github.com/bnema/dumber/internal/logging"
9)
10
11const (
12	// maxSessionsToCheck is the number of recent sessions to check for restoration.
13	maxSessionsToCheck = 10
14)
15
16// GetLastRestorableSessionUseCase finds the most recent session that can be auto-restored.
17// A session is restorable if:
18//   - It is not the current session
19//   - It is not actively running (has ended or crashed without lock file)
20//   - It has a valid snapshot with at least one tab
21type GetLastRestorableSessionUseCase struct {
22	sessionRepo repository.SessionRepository
23	stateRepo   repository.SessionStateRepository
24	lockDir     string
25}
26
27// NewGetLastRestorableSessionUseCase creates a new GetLastRestorableSessionUseCase.
28func NewGetLastRestorableSessionUseCase(
29	sessionRepo repository.SessionRepository,
30	stateRepo repository.SessionStateRepository,
31	lockDir string,
32) *GetLastRestorableSessionUseCase {
33	return &GetLastRestorableSessionUseCase{
34		sessionRepo: sessionRepo,
35		stateRepo:   stateRepo,
36		lockDir:     lockDir,
37	}
38}
39
40// GetLastRestorableSessionInput contains the parameters for finding a restorable session.
41type GetLastRestorableSessionInput struct {
42	// ExcludeSessionID is the current session ID to exclude from results.
43	ExcludeSessionID entity.SessionID
44}
45
46// GetLastRestorableSessionOutput contains the found restorable session, if any.
47type GetLastRestorableSessionOutput struct {
48	// SessionID is the ID of the restorable session, or empty if none found.
49	SessionID entity.SessionID
50	// State is the session state to restore, or nil if none found.
51	State *entity.SessionState
52}
53
54// Execute finds the most recent restorable session.
55// Returns an empty output (not an error) if no restorable session is found.
56func (uc *GetLastRestorableSessionUseCase) Execute(
57	ctx context.Context,
58	input GetLastRestorableSessionInput,
59) (*GetLastRestorableSessionOutput, error) {
60	log := logging.FromContext(ctx)
61
62	sessions, err := uc.sessionRepo.GetRecent(ctx, maxSessionsToCheck)
63	if err != nil {
64		return nil, err
65	}
66
67	for _, session := range sessions {
68		// Only consider browser sessions
69		if session.Type != entity.SessionTypeBrowser {
70			continue
71		}
72
73		// Skip current session
74		if session.ID == input.ExcludeSessionID {
75			continue
76		}
77
78		// Check if session is actively running
79		// A session is "active" if ended_at IS NULL AND it has a lock file
80		if session.IsActive() && isSessionLocked(uc.lockDir, session.ID) {
81			log.Debug().
82				Str("session_id", string(session.ID)).
83				Msg("auto-restore: skipping active session")
84			continue
85		}
86
87		// Try to get snapshot
88		state, err := uc.stateRepo.GetSnapshot(ctx, session.ID)
89		if err != nil {
90			log.Debug().
91				Err(err).
92				Str("session_id", string(session.ID)).
93				Msg("auto-restore: failed to get snapshot")
94			continue
95		}
96		if state == nil {
97			continue
98		}
99
100		// Skip sessions with no tabs
101		if len(state.Tabs) == 0 {
102			log.Debug().
103				Str("session_id", string(session.ID)).
104				Msg("auto-restore: skipping session with no tabs")
105			continue
106		}
107
108		// Found a valid restorable session
109		log.Info().
110			Str("session_id", string(session.ID)).
111			Int("tabs", len(state.Tabs)).
112			Int("panes", state.CountPanes()).
113			Msg("auto-restore: found restorable session")
114
115		return &GetLastRestorableSessionOutput{
116			SessionID: session.ID,
117			State:     state,
118		}, nil
119	}
120
121	// No restorable session found
122	log.Debug().Msg("auto-restore: no restorable session found")
123	return &GetLastRestorableSessionOutput{}, nil
124}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close