solvent

package
v0.0.0-...-d8ab0b6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 30, 2024 License: BSD-3-Clause Imports: 19 Imported by: 0

README

Solvent

A minimalistic CRDT-based To-Do list.

Introduction

The CRDTs (Conflictfree-Replicated-Data-Types) representing the to-do list have to cover three basic operations:

  • Adding / removing items
  • Checking / unchecking items
  • Re-ordering items
Adding / Removing

Can be represented with a 2P-Set consisting of two G-Sets (append only sets). One tracking all the added items (called liveSet) and another one tracking all the removed items (called tombstoneSet).

An item is visible if it is contained in the liveSet set and not in the tombstoneSet.

Renaming of items is treated as deleting the old item and creating a new one with the changed name.

Checking / Unchecking

The items themself hold the current checked state as simple boolean flag. Items can only be checked but not unchecked on their own. Unchecking will be modeled as an item deletion followed by creating a new and unchecked item with the same title.

Re-Ordering

Each item will be assigned an ordering value representing its order in the to-do list. When an item gets moved the new order value will be the the average of the two adjacent items. For the last position the order value will be the order value of the second to last item plus 10.

Getting Started

To run Solvent locally make sure you have Go, NPM and Docker-Compose installed your system.

git clone https://github.com/eldelto/solvent.git

cd solvent

// Setup a local PostgreSQL DB
docker-compose up

// Launch backend server
go run web/main.go

// Launch frontend client
cd react-client
npm install
npm start

// Build Docker image
./docker_build.sh

To-Do

  • Frontend rework
  • Mark lists as done when all items are checked
  • Properly handle errors in controllers
  • Implement user handling
  • Implement list removal
  • Use Websockets instead of polling
  • Fix potential DB race condition on update
  • Register a service worker to cache data on reloads
  • Send delta in update request / response instead of the everything
  • Implement search functionality

Screens

List View

Detail View

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Notebook

type Notebook struct {
	Lists map[uuid.UUID]TodoList
}

func NewNotebook

func NewNotebook() *Notebook

func (*Notebook) GetLists

func (n *Notebook) GetLists() ([]TodoList, []TodoList)

GetLists returns all open and completed lists of the notebook.

func (*Notebook) NewList

func (n *Notebook) NewList(title string) (*TodoList, error)

type Service

type Service struct {
	// contains filtered or unexported fields
}

func NewService

func NewService(db *bbolt.DB,
	host string,
	smtpHost string,
	smtpAuth smtp.Auth,
	auth *web.Authenticator) (*Service, error)

func (*Service) ApplyListPatch

func (s *Service) ApplyListPatch(ctx context.Context, listID uuid.UUID, patch string, timestamp int64) error

func (*Service) FetchLists

func (s *Service) FetchLists(ctx context.Context, cookies []*http.Cookie) ([]TodoList, []TodoList, error)

func (*Service) FetchNotebook

func (s *Service) FetchNotebook(ctx context.Context) (*Notebook, error)

func (*Service) FetchTodoList

func (s *Service) FetchTodoList(ctx context.Context, listID uuid.UUID) (TodoList, error)

func (*Service) IsLocalHost

func (s *Service) IsLocalHost() bool

func (Service) SendLoginEmail

func (s Service) SendLoginEmail(email mail.Address, token web.TokenID) error

func (Service) ShareList

func (s Service) ShareList(ctx context.Context, listID uuid.UUID) (string, error)

func (*Service) UpdateNotebook

func (s *Service) UpdateNotebook(ctx context.Context, fn func(*Notebook) error) (*Notebook, error)

func (*Service) UpdateTodoList

func (s *Service) UpdateTodoList(ctx context.Context, listID uuid.UUID, updatedAt int64,
	fn func(*TodoList) error) (TodoList, bool, error)

type ShareTokenAuth

type ShareTokenAuth struct {
	Token  web.TokenID
	User   web.UserID
	ListID uuid.UUID
}

func (*ShareTokenAuth) UserID

func (a *ShareTokenAuth) UserID() web.UserID

type TodoItem

type TodoItem struct {
	Checked   bool
	CreatedAt int64
	Title     string
}

func NewTodoItem

func NewTodoItem(title string) TodoItem

func (*TodoItem) Check

func (t *TodoItem) Check()

func (*TodoItem) Merge

func (t *TodoItem) Merge(other TodoItem)

The CreatedAt timestamp signals the more recent item which will 'win' when merging. All fields are copied to the pointer receiver of the method.

func (*TodoItem) Rename

func (t *TodoItem) Rename(title string)

func (*TodoItem) String

func (t *TodoItem) String() string

func (*TodoItem) Uncheck

func (t *TodoItem) Uncheck()

type TodoList

type TodoList struct {
	CreatedAt  int64
	UpdatedAt  int64
	ID         uuid.UUID
	Title      string
	Items      []TodoItem
	ShareToken web.TokenID
}

func NewTodoList

func NewTodoList(title string) (*TodoList, error)

func (*TodoList) AddItem

func (l *TodoList) AddItem(title string) TodoItem

func (*TodoList) CheckItem

func (l *TodoList) CheckItem(title string) TodoItem

func (*TodoList) Done

func (l *TodoList) Done() bool

func (*TodoList) MoveItem

func (l *TodoList) MoveItem(title string, targetIndex uint) TodoItem

func (*TodoList) RemoveItem

func (l *TodoList) RemoveItem(title string)

func (*TodoList) Rename

func (l *TodoList) Rename(title string)

func (*TodoList) String

func (l *TodoList) String() string

func (*TodoList) UncheckItem

func (l *TodoList) UncheckItem(title string) TodoItem

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL