Add syncing
This commit is contained in:
@ -40,9 +40,6 @@ func NewTimeEditorPage(com *common.Common, interval *timewarrior.Interval) *Time
|
||||
// Extract project from tags if it exists
|
||||
projects := com.TW.GetProjects()
|
||||
selectedProject, remainingTags := extractProjectFromTags(interval.Tags, projects)
|
||||
if selectedProject == "" && len(projects) > 0 {
|
||||
selectedProject = projects[0] // Default to first project (required)
|
||||
}
|
||||
|
||||
// Create project picker with onCreate support for new projects
|
||||
projectItemProvider := func() []list.Item {
|
||||
@ -66,18 +63,23 @@ func NewTimeEditorPage(com *common.Common, interval *timewarrior.Interval) *Time
|
||||
}
|
||||
}
|
||||
|
||||
opts := []picker.PickerOption{
|
||||
picker.WithOnCreate(projectOnCreate),
|
||||
}
|
||||
if selectedProject != "" {
|
||||
opts = append(opts, picker.WithDefaultValue(selectedProject))
|
||||
} else {
|
||||
opts = append(opts, picker.WithFilterByDefault(true))
|
||||
}
|
||||
|
||||
projectPicker := picker.New(
|
||||
com,
|
||||
"Project",
|
||||
projectItemProvider,
|
||||
projectOnSelect,
|
||||
picker.WithOnCreate(projectOnCreate),
|
||||
picker.WithFilterByDefault(true),
|
||||
opts...,
|
||||
)
|
||||
projectPicker.SetSize(50, 10) // Compact size for inline use
|
||||
if selectedProject != "" {
|
||||
projectPicker.SelectItemByFilterValue(selectedProject)
|
||||
}
|
||||
|
||||
// Create start timestamp editor
|
||||
startEditor := timestampeditor.New(com).
|
||||
@ -129,8 +131,14 @@ func (p *TimeEditorPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
case timeEditorProjectSelectedMsg:
|
||||
// Update selected project
|
||||
p.selectedProject = msg.project
|
||||
// Blur current field (project picker)
|
||||
p.blurCurrentField()
|
||||
// Advance to tags field
|
||||
p.currentField = 1
|
||||
// Refresh tag autocomplete with filtered combinations
|
||||
cmds = append(cmds, p.updateTagSuggestions())
|
||||
// Focus tags input
|
||||
cmds = append(cmds, p.focusCurrentField())
|
||||
return p, tea.Batch(cmds...)
|
||||
|
||||
case tea.KeyMsg:
|
||||
@ -144,18 +152,33 @@ func (p *TimeEditorPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return model, BackCmd
|
||||
|
||||
case key.Matches(msg, p.common.Keymap.Ok):
|
||||
// Don't save if the project picker is focused - let it handle Enter
|
||||
if p.currentField != 0 {
|
||||
// Save and exit
|
||||
p.saveInterval()
|
||||
model, err := p.common.PopPage()
|
||||
if err != nil {
|
||||
slog.Error("page stack empty")
|
||||
return nil, tea.Quit
|
||||
}
|
||||
return model, tea.Batch(tea.Batch(cmds...), refreshIntervals)
|
||||
// Handle Enter based on current field
|
||||
if p.currentField == 0 {
|
||||
// Project picker - let it handle Enter (will trigger projectSelectedMsg)
|
||||
break
|
||||
}
|
||||
// If picker is focused, let it handle the key below
|
||||
|
||||
if p.currentField == 1 {
|
||||
// Tags field
|
||||
if p.tagsInput.HasSuggestions() {
|
||||
// Let autocomplete handle suggestion selection
|
||||
break
|
||||
}
|
||||
// Tags confirmed without suggestions - advance to start timestamp
|
||||
p.blurCurrentField()
|
||||
p.currentField = 2
|
||||
cmds = append(cmds, p.focusCurrentField())
|
||||
return p, tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
// For all other fields (2-4: start, end, adjust), save and exit
|
||||
p.saveInterval()
|
||||
model, err := p.common.PopPage()
|
||||
if err != nil {
|
||||
slog.Error("page stack empty")
|
||||
return nil, tea.Quit
|
||||
}
|
||||
return model, tea.Batch(tea.Batch(cmds...), refreshIntervals)
|
||||
|
||||
case key.Matches(msg, p.common.Keymap.Next):
|
||||
// Move to next field
|
||||
@ -347,15 +370,16 @@ func (p *TimeEditorPage) saveInterval() {
|
||||
|
||||
// Add project to tags if not already present
|
||||
if p.selectedProject != "" {
|
||||
projectTag := "project:" + p.selectedProject
|
||||
projectExists := false
|
||||
for _, tag := range tags {
|
||||
if tag == p.selectedProject {
|
||||
if tag == projectTag {
|
||||
projectExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !projectExists {
|
||||
tags = append([]string{p.selectedProject}, tags...) // Prepend project
|
||||
tags = append([]string{projectTag}, tags...) // Prepend project tag
|
||||
}
|
||||
}
|
||||
|
||||
@ -415,11 +439,15 @@ func extractProjectFromTags(tags []string, projects []string) (string, []string)
|
||||
var remaining []string
|
||||
|
||||
for _, tag := range tags {
|
||||
if foundProject == "" && projectSet[tag] {
|
||||
foundProject = tag // First matching project
|
||||
} else {
|
||||
remaining = append(remaining, tag)
|
||||
// Check if this tag is a project tag (format: "project:projectname")
|
||||
if strings.HasPrefix(tag, "project:") {
|
||||
projectName := strings.TrimPrefix(tag, "project:")
|
||||
if foundProject == "" && projectSet[projectName] {
|
||||
foundProject = projectName // First matching project
|
||||
continue // Don't add to remaining tags
|
||||
}
|
||||
}
|
||||
remaining = append(remaining, tag)
|
||||
}
|
||||
|
||||
return foundProject, remaining
|
||||
@ -432,6 +460,8 @@ func filterTagCombinationsByProject(combinations []string, project string) []str
|
||||
return combinations
|
||||
}
|
||||
|
||||
projectTag := "project:" + project
|
||||
|
||||
var filtered []string
|
||||
for _, combo := range combinations {
|
||||
// Parse the combination into individual tags
|
||||
@ -439,11 +469,11 @@ func filterTagCombinationsByProject(combinations []string, project string) []str
|
||||
|
||||
// Check if project exists in this combination
|
||||
for _, tag := range tags {
|
||||
if tag == project {
|
||||
if tag == projectTag {
|
||||
// Found the project - now remove it from display
|
||||
var displayTags []string
|
||||
for _, t := range tags {
|
||||
if t != project {
|
||||
if t != projectTag {
|
||||
displayTags = append(displayTags, t)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user