At the top of this file we add an event handler to the window's DOMContentLoaded event that will instantiate our App class into a global variable. Then we have an array of mocked todo items.

The rest of this file is dedicated to our App class. This will act as an interface between the user and our data. Eventually we are going to insert our bincode_parser logic into this but up until now we were just making sure everything else works.

import Renderer from './services/renderer';
import ToDo from './models/todo';

let app;
window.addEventListener('DOMContentLoaded', () => {
    app = new App();
});

const mock_todos = [
            new ToDo(
                0,
                false,
                "Take out the trash"
            ),
            new ToDo(
                1,
                false,
                "Walk the dog"
            ),
            new ToDo(
                2,
                true,
                "Feed the cat"
            ),
            new ToDo(
                3,
                true,
                "Learn Rust"
            )
        ]

class App {
    private todos: ToDo[] = [];
    constructor(
    ) { 
        this.registerEvents();
        this.todos = mockTodos
    }
    /**
     * Register the event handler for the new To Do button
     */
    registerEvents() {
        let button = document.getElementById('create-new-todo');
        if (!button) return Renderer.showMessage('Unable to find create new todo button', true);
        button.addEventListener('click', ev => this.newToDo(ev));
    }
    /**
     * The event handler for when the user clicks the + button
     * @param ev The event passed from the browser for this click event
     */
    newToDo(ev: MouseEvent) {
        let input = document.getElementById('new-todo') as HTMLInputElement;
        if (!input) return Renderer.showMessage('Unable to find new todo input', true);
        let action = input.value;
        if (action == '') return Renderer.showMessage('New items must have an action', true);
        input.value = '';

        this.todos.push(
            new ToDo(Math.max(...this.todos.map(t => t.id))+1, false, action)
        );
        this.render()
    }
    /**
     * Remote a single todo item from the either todo list
     * @param id The ID of the todo entry that will be removed
     */
    removeToDo(id: number) {
        this.todos = this.todos.filter(t => t.id == t.id)
    }
    /**
     * Mark a todo item as complete or incomplete
     * @param id The ID of the todo entry to be modified
     * @param newState The new state of the complete property for that todo
     */
    markToDo(id: number, newState: boolean) {
        let match = this.todos.find(t => t.id == id)
        if (!match) return Renderer.showMessage(`Unable to find todo with the id ${id}`, true);
        match.complete = newState;
    }
    /**
     * Clear the dom of existing content and insert the two todo lists
     */
    render() {
        let lists = document.getElementById('list-container');
        if (!lists) return Renderer.showMessage('Unable to find lists element', true);
        while (lists.hasChildNodes()) {
            lists.removeChild(lists.lastChild);
        }
        let todos = this.todos.reduce((acc, curr) => {
            if (curr.complete) {
                acc.complete.push(curr);
            } else {
                acc.incomplete.push(curr);
            }
            return acc;
        }, {complete: [], incomplete: []});
        lists.appendChild(
            Renderer.todoList(
                'Incomplete', 
                todos.incomplete, 
                id => this.removeToDo(id), 
                (id, newState) => this.markToDo(id, newState)
            )
        );
        lists.appendChild(
            Renderer.todoList(
                'Complete', 
                todos.complete, 
                id => this.removeToDo(id), 
                (id, newState) => this.markToDo(id, newState)
            )
        );
    }
}