tiki/controller/plugin.go
2026-01-17 11:08:53 -05:00

202 lines
5.1 KiB
Go

package controller
import (
"log/slog"
"strings"
"time"
"github.com/boolean-maybe/tiki/model"
"github.com/boolean-maybe/tiki/plugin"
"github.com/boolean-maybe/tiki/store"
"github.com/boolean-maybe/tiki/task"
)
// PluginController handles plugin view actions: navigation, open, create, delete.
type PluginController struct {
taskStore store.Store
pluginConfig *model.PluginConfig
pluginDef *plugin.TikiPlugin
navController *NavigationController
registry *ActionRegistry
}
// NewPluginController creates a plugin controller
func NewPluginController(
taskStore store.Store,
pluginConfig *model.PluginConfig,
pluginDef *plugin.TikiPlugin,
navController *NavigationController,
) *PluginController {
return &PluginController{
taskStore: taskStore,
pluginConfig: pluginConfig,
pluginDef: pluginDef,
navController: navController,
registry: PluginViewActions(),
}
}
// GetActionRegistry returns the actions for the plugin view
func (pc *PluginController) GetActionRegistry() *ActionRegistry {
return pc.registry
}
// GetPluginName returns the plugin name
func (pc *PluginController) GetPluginName() string {
return pc.pluginDef.Name
}
// GetPluginDefinition returns the plugin definition
func (pc *PluginController) GetPluginDefinition() *plugin.TikiPlugin {
return pc.pluginDef
}
// HandleAction processes a plugin action
func (pc *PluginController) HandleAction(actionID ActionID) bool {
switch actionID {
case ActionNavUp:
return pc.handleNav("up")
case ActionNavDown:
return pc.handleNav("down")
case ActionNavLeft:
return pc.handleNav("left")
case ActionNavRight:
return pc.handleNav("right")
case ActionOpenFromPlugin:
return pc.handleOpenTask()
case ActionNewTask:
return pc.handleNewTask()
case ActionDeleteTask:
return pc.handleDeleteTask()
case ActionToggleViewMode:
return pc.handleToggleViewMode()
default:
return false
}
}
func (pc *PluginController) handleNav(direction string) bool {
tasks := pc.GetFilteredTasks()
return pc.pluginConfig.MoveSelection(direction, len(tasks))
}
func (pc *PluginController) handleOpenTask() bool {
taskID := pc.getSelectedTaskID()
if taskID == "" {
return false
}
pc.navController.PushView(model.TaskDetailViewID, model.EncodeTaskDetailParams(model.TaskDetailParams{
TaskID: taskID,
}))
return true
}
func (pc *PluginController) handleNewTask() bool {
task, err := pc.taskStore.NewTaskTemplate()
if err != nil {
slog.Error("failed to create task template", "error", err)
return false
}
pc.navController.PushView(model.TaskEditViewID, model.EncodeTaskEditParams(model.TaskEditParams{
TaskID: task.ID,
Draft: task,
Focus: model.EditFieldTitle,
}))
slog.Info("new tiki draft started from plugin", "task_id", task.ID, "plugin", pc.pluginDef.Name)
return true
}
func (pc *PluginController) handleDeleteTask() bool {
taskID := pc.getSelectedTaskID()
if taskID == "" {
return false
}
pc.taskStore.DeleteTask(taskID)
return true
}
func (pc *PluginController) handleToggleViewMode() bool {
pc.pluginConfig.ToggleViewMode()
return true
}
// HandleSearch processes a search query for the plugin view
func (pc *PluginController) HandleSearch(query string) {
query = strings.TrimSpace(query)
if query == "" {
return // Don't search empty/whitespace
}
// Save current position
pc.pluginConfig.SavePreSearchState()
// Get current user and time ONCE before filtering (not per task!)
now := time.Now()
currentUser, _, _ := pc.taskStore.GetCurrentUser()
// Get plugin's filter as a function
filterFunc := func(t *task.Task) bool {
if pc.pluginDef.Filter == nil {
return true
}
return pc.pluginDef.Filter.Evaluate(t, now, currentUser)
}
// Search within filtered results
results := pc.taskStore.Search(query, filterFunc)
pc.pluginConfig.SetSearchResults(results, query)
pc.pluginConfig.SetSelectedIndex(0)
}
// getSelectedTaskID returns the ID of the currently selected task
func (pc *PluginController) getSelectedTaskID() string {
tasks := pc.GetFilteredTasks()
idx := pc.pluginConfig.GetSelectedIndex()
if idx < 0 || idx >= len(tasks) {
return ""
}
return tasks[idx].ID
}
// GetFilteredTasks returns tasks filtered and sorted according to plugin rules
func (pc *PluginController) GetFilteredTasks() []*task.Task {
// Check if search is active - if so, return search results instead
searchResults := pc.pluginConfig.GetSearchResults()
if searchResults != nil {
// Extract tasks from search results
tasks := make([]*task.Task, len(searchResults))
for i, result := range searchResults {
tasks[i] = result.Task
}
return tasks
}
// Normal filtering path when search is not active
allTasks := pc.taskStore.GetAllTasks()
now := time.Now()
// Get current user for "my tasks" type filters
currentUser := ""
if user, _, err := pc.taskStore.GetCurrentUser(); err == nil {
currentUser = user
}
// Apply filter
var filtered []*task.Task
for _, task := range allTasks {
if pc.pluginDef.Filter == nil || pc.pluginDef.Filter.Evaluate(task, now, currentUser) {
filtered = append(filtered, task)
}
}
// Apply sort
if len(pc.pluginDef.Sort) > 0 {
plugin.SortTasks(filtered, pc.pluginDef.Sort)
}
return filtered
}