UNPKG

gun

Version:

A realtime, decentralized, offline-first, graph data synchronization engine.

164 lines (134 loc) 4.1 kB
<!doctype html> <html> <head> <title>Todo App: Gun + Vue</title> <script src="https://unpkg.com/vue" type="text/javascript" charset="utf-8"></script> <script src="https://cdn.jsdelivr.net/npm/gun/gun.js" type="text/javascript" charset="utf-8"></script> <link href='https://fonts.googleapis.com/css?family=Alegreya+Sans:300italic' rel='stylesheet' type='text/css'> <style> body, html { width: 100%; font-family: 'Alegreya Sans', sans-serif; } body { font-size: 20px; background-color: rgba(0,0,0,.3); } h1, h3, h5 { text-transform: uppercase; font-weight: 600; } #app { margin: 0 auto; background-color: #fff; max-width: 500px; padding: 2em; } .add-todo { font-size: 1em; border: 1px solid black; padding: .5em .25em; } .completed-header, .completed, .completed-time { color: rgba(0,0,0,.3); } .completed { text-decoration: line-through; } .completed-time { font-size: .8em; } </style> </head> <body> <div id="app"> <div class="todo-wrapper"> <h1>Todos App</h1> <input class="add-todo" type="text" placeholder="Add new todo" v-model="newTodo" @keyup.enter="addTodo"> <h3>Active</h3> <div v-for="active in activeTodos"> <label><input type="checkbox" @click.prevent="completeTodo(active.key)"> {{ active.todo.description }}</label> </div> <h5 v-show="completedTodos.length > 0" class="completed-header">Completed</h5> <div v-for="completed in completedTodos" @click="reopenTodo(completed.key)"> <span class="completed">{{ completed.todo.description }}</span> <span class="completed-time">(completed at {{ (new Date(completed.todo.completed)).toUTCString() }})</span> </div> </div> </div> </body> <script> // Clear out local storage so we can start fresh each time. localStorage.clear(); // No peers for the sake of example, // but this could easily be a collaborative // todolist by adding a few peers var gun = new Gun(); var todos = gun.get('todos'); new Vue({ data: { todos: [], newTodo: "" }, computed: { activeTodos: function() { return this.todos.filter(function(todo) { return todo.todo.completed === ''; }); }, completedTodos: function() { return this.todos.filter(function(todo) { return todo.todo.completed !== ''; }); } }, methods: { todoUpdated: function(todo, nodeKey) { if (/\D/.test(nodeKey)) { return; } var existingTodoIndex = this.todos.findIndex(function(todo) { return todo.key === nodeKey }); var todoToStore = { todo: todo, key: nodeKey }; if (existingTodoIndex === -1) { this.todos.push(todoToStore); } else { this.todos.splice(existingTodoIndex, 1, todoToStore); } }, addTodo: function() { var newTodo = todos.get(Date.now().toString()); newTodo.put({ description: this.newTodo, completed: '' }); todos.set(newTodo); this.newTodo = ""; }, completeTodo: function(key) { todos.get(key).put({ completed: Date.now() }); }, reopenTodo: function(key) { todos.get(key).put({ completed: '' }); } }, mounted: function() { // Subscribe to updates on the todos set todos.map().on(this.todoUpdated.bind(this)); } }).$mount('#app'); </script> </html>